The Cairngorm Module library is designed to simplify the configuration, rendering and loading of modular content. Additonally it offers flexiblity to communicate to modules. It contains infrastructure classes, view components, a mechanism for loading and communicating to modular content on-demand in response to Parsley messages.
The biggest benefit of modularization we see is often not the reduced initial SWF size, although that is a benefit, but rather the development efficiency and encapsulation of functional areas. A module can be developed, tested and profiled in relative isolation by a small group of developers, and the contracts can be agreed between modules. Build times are quick and one module can effectively offer services to another. The implementation of one module can change without impact on others. This approach can scale to large delivery teams.
The Flex SDK provides a ModuleLoader view component for placing modular content in-line with other views. The ModuleManager singleton offers a manual management of modules. The IModuleInfo interface abstracts the loading and unloading of modular content.
The Cairngorm Module library provides additional features on top of the Flex SDK and Parsley module support:
The ParsleyModuleDescriptor can be used to describe the location and application domain of the module. This can be specified in a Parsley MXML or XML context.
When configuring the module library via MXML, use the CairngormModuleSupport ContextBuilder when creating the Parsley context:
<spicefactory:ContextBuilder>
<cairngorm:CairngormModuleSupport/>
<spicefactory:FlexConfig type="{ CairngormModuleLibSampleContext }"/>
</spicefactory:ContextBuilder>
Then, describe the module in the MXML context:
<cg:ParsleyModuleDescriptor objectId="moduleA"
url="example/moduleA/ModuleA.swf"
domain="{ ClassInfo.currentDomain }"/>
When configuring the module library via XML that can be loaded at runtime, use the CairngormModuleXMLSupport additionally to the CairngormModuleSupport ContextBuilder when creating the Parsley XML context:
<spicefactory:ContextBuilder>
<cairngorm:CairngormModuleSupport/>
<cairngorm:CairngormModuleXMLSupport/>
<spicefactory:XmlConfig file="runtimeContext.xml"/>
</spicefactory:ContextBuilder>
Then, describe the module in the XML context:
<objects xmlns="http://www.spicefactory.org/parsley"
xmlns:cairngorm="http://www.adobe.com/cairngorm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.spicefactory.org/parsley
http://www.spicefactory.org/parsley/schema/2.0/parsley-core.xsd">
<cairngorm:parsley-module-descriptor object-id="moduleA"
url="example/moduleA/ModuleA.swf"/>
</objects>
In order to reference a definition of a module you need to take the objectId of a ParsleyModuleDescriptor...
<cg:ParsleyModuleDescriptor objectId="moduleA"
url="example/moduleA/ModuleA.swf"/>
...and connect it with an IModuleManager instance defined in the view that loads the module.
<mx:Script>
<![CDATA[
import com.adobe.cairngorm.module.IModuleManager;
[Inject(id="moduleA")]
public var moduleManager:IModuleManager;
]]>
</mx:Script>
<mx:Label text="moduleId: { moduleId == null ? 'NOT SET' : moduleId }"/>
<module:ModuleViewLoader moduleManager="{ moduleManager }"
skinClass="com.adobe.cairngorm.module.ModuleViewLoaderSkin">
<module:loadPolicy>
<module:BasicLoadPolicy/>
</module:loadPolicy>
</module:ModuleViewLoader>
As soon as the IModuleManager implementation is injected into the view, (which would be the DisplayObject's addedToStage event following the default of Parsley), the module starts loading through the ModuleViewLoader component. The skinClass property allows to define the visual display during the module loading and error state, which is likley to be custom to most applications.
For Flex 3, you can use a ViewLoader component like the below
<example:MyViewLoader moduleManager="{ moduleManager}"/>
In order to customize the presentation component, the above MyViewLoader is a subclass of the ViewLoader component within the module library. The ViewLoader allows to define the visual display during the module loading and error state, which is likley to be custom to most applications.
<module:ViewLoader xmlns:module="com.adobe.cairngorm.module.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
progress="message = 'Loading: ' + event.bytesLoaded + ' of ' + event.bytesTotal"
error="message = event.errorText"
horizontalAlign="center"
verticalAlign="middle">
<mx:Script>
<![CDATA[
[Bindable]
public var message:String;
]]>
</mx:Script>
<module:progressView>
<mx:Label text="{ message }"/>
</module:progressView>
<module:errorView>
<mx:Label color="0xFF0000"
text="{ message }"/>
</module:errorView>
</module:ViewLoader>
When events need to be send to modules using Parsley messaging, the modules need to be loaded and initialized in order to receive Parsley events. The module library takes care of the lazy loading in case the module isn't ready yet to receive the event.
The user needs to define the event in inside the context with a ModuleMessageInterceptor definition. For example, the following loads the module defined in the ParsleyModuleDescriptor from above with the objectId "moduleA", once the user dispatches a BroadcastMessage event.
<cg:ModuleMessageInterceptor type="{ BroadcastMessage }"
moduleRef="moduleA"/>
Behind the scences the module library intercepts the message until the loading is complete and proceeds the dispatching process once the module is ready to receive the message (loaded and instantiated with a Parsley context that can receive it).
For the loaded module to receive the message the module needs to implement the IParsleyModule interface, which points to the Parsley context that can receive the message (using a Parsley MessageHandler).
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:spicefactory="http://www.spicefactory.org/parsley"
implements="com.adobe.cairngorm.module.IParsleyModule" >
<mx:Script>
<![CDATA[
import com.adobe.cairngorm.module.IParsleyModule;
import org.spicefactory.parsley.flex.FlexContextBuilder;
public function get contextBuilder():ContextBuilderTag
{
return contextBuilderTag;
}
]]>
</mx:Script>
<spicefactory:ContextBuilder id="contextBuilderTag"
config="{ ModuleAContext }" />
Now, any object within the ModuleAContext can accept the module message like this:
[MessageHandler(scope="local")]
public function broadcastMessageHandler(message:BroadcastMessage):void
{
The relationship between a module definition and a module view does not have to be 1:1. Applications can for example display multiple instances of one module. The module library allows to control the communication between the loader and one or many module definitions and one or many module instances.
The ModuleMessageInterceptor from above...
<cg:ParsleyModuleDescriptor objectId="moduleA"
url="example/moduleA/ModuleA.swf"/>
<cg:ModuleMessageInterceptor type="{ BroadcastMessage }"
moduleRef="moduleA"/>
...directs every BroadcastMessage to all module instances from the definition "moduleA".
The below message is being directed to all instances of all module definitions.
<cg:ModuleMessageInterceptor type="{ ClearLogMessage }"/>
In order to dispatch to one particular instance of one module definition, the ModuleMessageInterceptor searches for a property annotated with a ModuleId definition.
<cg:ModuleMessageInterceptor type="{ PingMessage }"/>
:::ActionScript
public class PingMessage
{
private var _moduleId:String;
public function PingMessage(moduleId:String)
{
this._moduleId=moduleId;
}
[ModuleId]
public function get moduleId():String
{
return _moduleId;
}
}
The value of this ModuleId property must be defined on the ViewLoader component's moduleId property.
For more information, please view the examples within the ModuleTest project.