Emerald Hand Wiki

Sider - Dev - Tutorials - Type - NewType

Modified: 2007/03/21 18:54 by Ornus - Categorized as: Development, Sider, Tutorial
Back to tutorials section


Edit

Introduction

Type is a Sider extension describing data for the platform. Types are essential for working with documents, and for each document there has to be at least one type (usually there are more). All types represent a model in the MVC pattern and exist to separate data structure and its logic from the view code.

Each type provides document schema for validation when document is loaded or modified. This ensures type operations and views will be able to work correctly with the document.

In addition to the document schema each type must provide two types of operations. One type is described using XSLT and is used directly by Sider to create and manipulate the document. Another is used by the view scripts. Their goal is to allow view to get data from different parts of the document and update the document in response to the user interactions.

This page provides templates for the files you might need when creating a new type. Feel free to download them and update as you write a new type: type.xml, type.xsd, constructor.xslt, type.js.

Edit

Analysis

Before creating a new type it is useful to think about where it will be used. You don't have to, but it will help you to create a clear image of what you are trying to solve and how to do it. It is possible to create very abstract types (a tree or a list) or very concrete (collection of movies) and in-between (tree of HTML documents, that could be used, among other things, to store notes).

Sider types are very flexible and can be inherited from or composed when creating a new type. This promotes code reuse and leads to better design. For example abstract tree provides operations required to work with the tree. All those operations will be available no matter where the tree is used and what type of nodes it might have. Sider design allows you to focus only on the unique features of the document you want to add, and reusing existing code where appropriate.

Edit

Why

So, when you decide to create a type describe what problem you are trying to solve and set goals for it (what you want it to be able to do). One of the initial uses for Sider is to use it as a PIM. I tried to think about all information I would want to store on the computer and the split it up into separate types of data. For example I want to store all my notes in Sider and I want to be able to do the following (these are type goals):
  • Organize notes and create sub-notes
  • Rich-text for each note

Edit

Type goals

So, when you decide to create a type, describe what problem you are trying to solve and set goals for it (what you want it to be able to do). One of the initial uses for Sider is to use it as a PIM. I tried to think about all information I would want to store on the computer and the split it up into separate types of data. For example I want to store all my notes in Sider and I want to be able to do the following (these are type goals):
  • Organize notes and create sub-notes
  • Rich-text for each note

I think I need a tree-like structure to store notes (list would be too simple. It would quickly grow cumbersome. Graph is powerful, but would be hard to implement and is probably overkill. It's something I might want to do at a later date).

I also need to store a note. I could use simple text, but I want to have rich formatting. HTML seems to suit my needs the best.

To store notes I need to have 3 types: tree, HTML, and a type that combines both. This is a very simple example. In more complex scenarios number of used types can be quite large.

You don't have to split up your data like this. I could have easily created a tree with hard-coded HTML documents. This design is simpler, but it's a step back from the evolution of information management and doesn't use Sider flexibility.

Edit

Visual

When thinking about how to structure data in the type it might be helpful to imagine how it would be shown. In general data structure should be natural in a way that would make working with it as simple as possible. However, that you will still need to show it to the user and thinking about how that will be done can help you see the whole picture.

With notes, I wanted to have an explorer-like tree and current note shown next to it. This is a common interface used by the PIMs.

Edit

Types relationship (inheritance and composition)

Sider support different ways to use types: inheritance and composition. Inheritance doesn't have the same format as inheritance in OO languages (C++, Java, C#, etc.) where you would declare a class and specify base classes. In Sider you only specify which types you are going to use by the new type. Your use of referenced types determines if you're simply composing types, or inheriting from them. If you only mean to embed document of another type in the new type document you're composing. If your type includes specific elements from the parent type, possibly redefining you are inheriting.

Type inheritance allows you to extend or restrict existing data structure. Base type elements will be in their type namespace, but your type elements will be in its own namespace, effectively separating type elements, but allowing you to mix them. Of course, base type probably will not be able to validate document of your new type.

For example, an abstract tree is a tree with each node having no specific support for the content. A type inherited from the tree can extend it to allow each node hold HTML document, thus creating a tree of notes. That type also uses composition with HTML type.

In both instances of type usage all operations provided by the original types will be available to the view. With notes type view will be able to access tree operations, HTML operations and new operations provided by the HTML tree type. It will be view's responsibility to understand how different types are composed or extended and call correct methods. Inherited type can't hide base type methods (so all your methods should be as generic as possible :) ).

Edit

Location

Sider types are located in the Types folder. Like other Sider extensions the actual type is located in the TypeName/x.x.x.x sub-folder. I.e. Tree type 0.5.0.0 version would be located in Types/Tree/0.5.0.0.

Edit

Declaration (type.xml)

Each type needs to be described to the Sider. type.xml provides information about type ID, name and files to use for validation and operations.

When creating a type you would take the following steps:
  • Create type folder based on its name and version
  • Create type.xml (download type.xml template) and open it for editing
  • First you need to describe the type
  • Set id attribute to unique type GUID
  • Set name attribute to the type name ("Tree" for example)
  • Set friendlyName to user friendly name. User will read this text when creating a new document of this type. This attribute is optional, and if absent, name will be used instead
  • Set version attribute to the type version. It needs to be in x.x.x.x format, with each x being an integer
  • Set type category. Possible choices are described in Type
  • Set prefix. This is a namespace prefix that will be used by the type script and anywhere else where it will be required to associate prefix with the type namespace
  • Set type/description. User will see it when creating a new type so it should describe type purpose and uses.
  • If you want to use other types, create type references in references element. Each reference is a references/type element referencing existing type by its ID and associating a simple alias to use referenced type everywhere else in the type (such as in schema).
  • Provide schema file name in schema element.
  • Provide script file name in scriptFile element.
  • Provide constructor file name in transformations/newDocument. Potentially there will be many operations in XSLT files, but currently only document constructor is supported. That constructor will be used to create a new document on user request.
  • If you want your type to inherit from a specific type you can specify constructor input (result of base type constructor transformation will be passed to the your constructor). If this attribute is absent, system base type specified by the
    /type/@category
    will be used as input.

That's it for type.xml. This was the simplest, mechanical part. Writing code for other parts of the type will not be so simple.

Edit

Schema (type.xsd)

Type schema (type.xsd) is used to validate document when it is loaded. This is important to ensure type operations and views will be able to handle the document correctly. If document is invalid, has invalid structure, working with it, even showing, would lead to errors. When creating a schema you need to specify type namespace. In time it will be generated automatically, but currently you specify it for the type prefix and targetNamespace.

Sider schema is created using XML schema (XSD). It has some limitations, but it is widely supported and a lot of people are familiar with the technology. In the future we might opt to adding additional or alternative support for other types of schemas.

You have to choose how open or close you want the schema to be when you are writing it, whether to allow inheritance and schema use, or restrict it. XSD provides mechanisms to prevent or allow different types of inheritance.

Edit

Document wrapper

All documents are wrapped in a single document element. It works as a border between one document and the other. It helps validation and type operations to work only on specific document, without getting their hands dirty with other documents.

Edit

Code

Schema declaration starts be defining type document wrapper element using the following code. The schema must have it, or validation will fail or won't be complete. Inside of it document structure is declared.

<xs:redefine schemaLocation="sider">
<xs:complexType name="DocumentContentType">
<xs:complexContent>
<xs:extension base="TYPE_NAMESPACE_PREFIX:DocumentContentType">

</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:redefine>

You inherit from a type by using its elements. For example this code inherits from an abstract tree and adds new node content element.

<xs:redefine schemaLocation="sider">
<xs:complexType name="DocumentContentType">
<xs:complexContent>
<xs:extension base=" TYPE_NAMESPACE_PREFIX:DocumentContentType">
<xs:sequence>
<xs:element ref="siderTree:nodes"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:redefine>
<xs:element name="CONTENT_ELEMENT_NAME" type="NODE_CONTENT_TYPE" substitutionGroup="siderTree:nodeContent"/>

Two things happen here. First type document is declared to support {siderTree:nodes} element, provided by the abstract tree type. Second a new node element is created using substitution group.

When your type uses other types (through inheritance or composition) it is most evident in the type schema. Schema inheritance is done be inclusion elements defined in other schemas and possibly changing their structure in the process. Composition is done simply by referencing another type document element, thus embedding another document inside of yours.

Edit

Transformation (constructor.xslt)

Each type needs to provide a constructor transformation (constructor.xslt) to create a new document. In the transformation you don't have to create a new document from scratch. You can, but you shouldn't. Instead Sider supports a chain of transformations based on the type inheritance. Result of each transformation is passed on to the next type in the inheritance chain, starting with a system base type from which all types inherit.

Chain of transformations will supply your constructor with a blank document with everything, except your type's unique features, created. You should copy all of it, adding new elements at the appropriate places.

When type constructor runs Sider automatically imports a common constructor XSLT {Configuration/Common Transformation/commonConstructor.xslt}. It provides default templates to copy nodes when you apply templates and ensures input document is copied by default. It also helps you to simply add your custom elements to the output stream and resume by applying templates.

Most constructors will only handle document element and add type specific structure. But you can also work with other elements as they are being copied to the output. For example, a type that inherits from an abstract tree to add specific content to each node would provide templates to match tree node elements and insert custom content before resuming.

Simple transformation would provide this template:

<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()" />

</xsl:copy>
</xsl:template>

It matches the document, copies it with all elements (such as document properties) and adds custom elements after them.

Edit

Operations (type.js)

Most of the types should provide script operations (type.js) to assist the view working with the document. These operations are exposed through a single script object. View never works with the document directly. Instead it relies on the operations provided by the type.

Each type script object is a singleton class. It is automatically created when page loads and the view script only interacts with it. The view doesn't have to worry about initializing type scripts.

When type object is created it is also initialized with information that comes directly from type.xml, such as type name, GUID, namespace, prefix, etc. This prevents you from duplicating your efforts in describing the type in several locations.

All type scripts are exposed independently of each other. When a type inherits from another type, the script doesn't. New type object is separate and is used independently of the base type. It can call methods from the base type script, but it doesn't inherit from it directly.

For example, let's say we have an abstract tree type list, a movie type describing a single movie movie and a new type that creates a list of movies movieCollection. Each type provides a script for the view.

When view page is loaded there will be three, independent type scripts, sider.types.list, sider.types.movie and sider.types.movieCollection. list type will know how to work with an abstract list of things, movie type will provide interface to get movie title, actors or imdb number. movieCollection type provides only methods to assist working with a collection, such as adding a new movie to the collection. It will probably use both list and movie types.

View is responsible for correct use of different type scripts in various situations. However it is a good practice for the new type to provide most of the needed interface, to avoid possible confusion. It should be easy to figure out which type to use. If there's any doubt it's a good indicator that movieCollection interface is not well designed. It probably needs to have a specific method, even if it will simply call list method.

Edit

XPath

View and types communicate with each other through the use of XPath. The view might not update or work with the document directly, but it needs to be aware of its structure and how types compose each other. Often type methods will return XPath to the part of the document that the view will need to store and later reuse to access the same part of the document. A lot of the type methods expect just a path to the type document (which can be embedded anywhere inside other documents) and find correct child elements on their own.

For example when movie in a movie collection is being edited, movieCollection type can provide a method to get a movie by its ID. The method will get a path to the list item using list methods and then get movie information by passing the path to some method in the movie type. View is not aware of what is happening inside. It just gets path to the movie and can invoke other movie methods with specific movie path to update it or get additional information.

As an alternative the actual movie could be wrapped in a dynamic movieData object that knows the path to the movie and exposes movie properties to the view. movieCollection would create it once it finds the movie. View wouldn't need to worry about XPath. This scenario could prove to be simpler or more complicated, depending on the situation; it might be slower in some instances, but it might not matter. It's up to you to decide how you're going to implement the type and whether the view will need to work with XPath or there will be objects to wrap it. Whatever path you choose, remember that you should require view to do as little type specific work. In general, if code works with the document and can be used in different situations, it should be in a type. If the code relies on certain controls it should be in the view.

Edit

Code

All type objects inherit from sider.types.AbstractType (declared in sider view component). It provides common interface to simplify working and writing type objects. In some instances you might even get away just by declaring new type class, but without defining any custom methods.

To actually work with the document type script will need to use sider.xml package (again, declared in sider). xml packages abstracts away DOM and provides methods to work with the document using XPath. This is done since in the future the document might not be included with the view, and there will be no DOM object to use.

Simple type class declaration looks like this:

dojo.require( "sider.exceptions" );
dojo.require( "sider.types" );

sider.types.addType( null,
{
// Type specific methods
});

Edit

Conclusion

Type creation is not really complicated. Using Sider flexible design it is possible to split data structure into simple documents and work with each separately. This will lead to more elegant and simple code.

When creating a type, please take a moment to figure out what problem you are trying to solve and how you could use existing types to assist you. Also, keep in mind that type operations is reusable logic. It will be used by different views and by Sider directly. Don't just create a type for a specific view, and don't write common logic in your view. That's what types are for.

Copyright © 2006-2008 Emerald Hand, Inc. All rights reserved.
Powered by ScrewTurn Wiki version 2.0.34. Some of the icons created by FamFamFam.