3. Overview
Parsley is Open Source, licensed under the Apache License 2.0
Written by Jens Halm
First version 1.0.RC1 (2007-12-09)
Last version 3.0.0 (2012-02-06) > 4 years
Parsley is an Application Framework for Flex and Flash Applications built upon an IOC
Container and Messaging Framework that can be used to create highly decoupled
architectures.
4. Features
Parsley Spicelib
Flexible IOC Container Command Framework
Dependency Injection XML-Mapper
Decoupled Bindings Reflection API
Messaging Framework Logging
Managed Commands
Dynamic View Wiring
Advanced Modularity
Object Lifecycle
Localization
Extensibility
6. Inversion of Control (IoC)
Hollywood Principle: Don't call us, we'll call you.
Program logic runs against abstractions
such as callbacks
7. IoC Example
Here is the simplest possible Here are a common smells that should lead
IoC component you to refactor to IoC
package com { package com {
public interface IMap { public class GPSNavigator implements INavigator {
//methods private var map:IMap;
} public function GPSNavigator() {
} this.map = new UkraineMapImpl();
}
package com { //other methods
public class GPSNavigator implements INavigator { }
private var map:IMap; }
public function GPSNavigator(map:IMap) {
}
this.map = map; Some other smells.
//other methods package com {
} public class GPSNavigator implements INavigator {
} private static var map:IMap = MapFactory.getMap();
public function GPSNavigator() {
}
//other methods
}
}
It is hard coded
Not reusable.
8. IOC Container
We may ask for the object from the container and the
container creates the object and its dependencies
<interface>
EBookReader
File
inject
creates
PDFFileImpl TXTFileImpl
IoC Container
Parsley is a classic IOC Container. It provides support for Dependency Injection,
Object Lifecycle Management and Messaging
13. Dependency Injection (DI)
You may have these different tools
OR just one tool with attachments
This brings us to the Design Principles -
Program to an interface, not an implementation
15. Dependency Injection (DI)
public class Controller {
private var model:Model;
public function Controller(model:Model) {
Constructor Injection this.model = model;
}
}
16. Dependency Injection (DI)
public class Controller {
private var model:Model;
public function Controller(model:Model) {
Constructor Injection this.model = model;
}
}
public class Controller {
private var _model:Model;
public function set model(value:Model):void {
Setter Injection _model = value;
}
}
17. Dependency Injection (DI)
public class Controller {
private var model:Model;
public function Controller(model:Model) {
Constructor Injection this.model = model;
}
}
public class Controller {
private var _model:Model;
public function set model(value:Model):void {
Setter Injection _model = value;
}
}
public interface IController {
function injectModel(model:Model):void
}
Interface Injection public class Controller implements IController {
private var model:Model;
public function injectModel(model:Model):void {
this.model = model;
}
}
18. NOT Dependency Injection
public class Controller {
private var model:Model;
public function Controller() {
model = new Model();
}
}
public class Controller {
private var model:Model;
public function Controller() {
model = ModelFactory.create(Model);
}
}
public class Controller {
private var model:Model;
public function Controller() {
model = Model.getInstance();
}
}
19. IoC and DI Benefits
• Dependency management
• Simplify the reuse of classes or components
• Offers configuration flexibility
• Simplify unit-testing
• The "cleaner" code
23. Configuration and Initialization
• Telling the IOC Container which classes it
should manage
1 • MXML, XML, AS.
• Configure DI or Messaging for each
individual class
2 • MXML, XML, Metadata tags
24. public class GetUsersCommand {
GetUsersCommand.as
[Inject]
public var service:IUsersService;
[Inject(id="userModel")]
public var model:Model;
public function execute():AsyncToken {
return service.getUsers();
}
public function result(users:ArrayCollection):void {
model.users = users;
}
}
public class UsersView extends SkinnableComponent {
UserView.as
[Inject]
public var model:Model;
[Publish(objectId="selectedUser")]
[Bindable]
public var seletedUser:User;
[MessageHandler(selector="user")]
public function externalUserSelection(msg:SelectMessage):void {
//do something
}
}
25. Configuration and Initialization
• Telling the IOC Container which classes it
should manage.
1 • MXML, XML, AS.
• Configure DI or Messaging for each
individual class
2 • MXML, XML, Metadata tags
• Initialize IOC Container
3
26. <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
You can use one big xmlns:s="library://ns.adobe.com/flex/spark"
configuration file xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:parsley="http://www.spicefactory.org/parsley"
<fx:Declarations>
<parsley:ContextBuilder config="{UserConfig}"/>
</fx:Declarations>
</s:Application>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
Or several files with xmlns:s="library://ns.adobe.com/flex/spark"
separated configuration xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:parsley="http://www.spicefactory.org/parsley"
<fx:Declarations>
<parsley:ContextBuilder>
<parsley:FlexConfig type="{UserConfig}"/>
<parsley:FlexConfig type="{ServiceConfig}"/>
<parsley:FlexConfig type="{MockServiceConfig}"/>
</parsley:ContextBuilder
</fx:Declarations>
</s:Application>
ContextBuilder.newBuilder()
Configuration DSL .config(FlexConfig.forClass(UserConfig))
.config(FlexConfig.forClass(ServiceConfig))
.config(XmlConfig.forFile("logging.xml"))
.build();
27. Decoupled Bindings
[Publish]
[Subscribe]
[PublishSubscribe]
• This feature is much more dynamic than Dependency Injection.
• It is really more the decoupled equivalent of the typical usage of Flex Bindings.
• The published object does not even have to be a container managed object.
28. Decoupled Bindings
ActionScript Examples
[Bindable]
[Publish(objectId="selectedUser")]
public var selectedUser:User;
[Subscribe(scope="window")]
public var selectedUser:User; uses Local
[Bindable]
SharedObjects
[PublishSubscribe(persistent="true", objectId="selectedUserId")]
public var selectedUserId:int;
MXML Example
<parsley:View type="{UsersView}">
<parsley:Publish property="selectedUser“ objectId="selectedUser"/>
</parsley:View>
30. ActionScript Examples
[Event(name="userSelected",type="com.xyz.UserSelectedEvent")] UserSelectedEvent.as
[ManagedEvents("userSelected, somethingSelected")]
public class UsersView extends SkinnableComponent { Event based class
private function handleUserSelect(user:User):void {
dispatchEvent(new UserSelectedEvent("userSelected", user));
}
}
public class UsersView extends SkinnableComponent {
UserSeletedMessage.as
[MessageDispatcher] Simple class
public var dispatcher:Function;
private function handleUserSelect(user:User):void {
dispatcher(new UserSeletedMessage(user));
}
}
[MessageHandler(type="com.xyz.UserSelectedEvent", messageProperties="user, role“)]
public function handleUserSelect(user:User, role:String):void {
}
[MessageBinding(messageProperty="user", type="com.xyz.UserSelectedEvent")]
public var user:User;
32. Object Lifecycle
[Init]
[Destroy]
Lifecycle phases
preConfigure preInit postInit preDestroy postDestroy
The methods marked with [Init] get invoked after the object has been instantiated and all
injections have been processed.
[Init]
public function init():void {
//
}
The methods marked with [Destroy] get invoked after the Context instance they belong
to has been destroyed with Context.destroy() or when the object was removed from
the Context.
[Destroy]
public function destroy():void {
//
}
33. Managed Commands
• Dynamically add a command to a Context only for the time it executes
• Declarative way to configure Commands
• Grouping commands (parallel, sequence, flow)
• Map command to message
34. Managed Commands
[CommandResult] <parsley:MapCommand type="{GetUserProfileCommand}"/>
[CommandError]
public class GetUserProfileCommand {
[CommandComplete]
[CommandStatus] [Inject("userService")]
public var service:RemoteObject;
public function execute(msg:GetUserMessage):AsyncToken {
return service.getUserProfile(msg.userId);
}
public function result(profile:Profile):void {
//
}
}
[CommandResult]
public function profileResult(profile:Profile, message:GetUserMessage):void {
[CommandError]
public function handleResult(fault:FaultEvent, trigger:GetUserMessage):void {
[Bindable]
[CommandStatus(type="com.xyz.GetUserMessage")]
public var isGettingProfile:Boolean;
<s:Button label="GetProfile" enabled="{isGettingProfile}" click="..." />
38. Custom Scopes
Default scope is global
<parsley:ContextBuilder> Add new “window” scope
<parsley:FlexConfig type="{UserConfig}"/>
<parsley:FlexConfig type="{ServiceConfig}"/>
<parsley:Scope name="window" inherited="true"/> Change default scope to local.
<parsley:MessageSettings defaultReceiverScope="local"/>
</parsley:ContextBuilder>
Actionscript Examples
[ManagedEvents("add, remove", scope=“global")]
[MessageDispatcher(scope="local")]
public var dispatcher:Function;
[MessageHandler(selector="add", scope="local")]
public function save (event:UdpateUser):void {
[Subscribe(objectId="selectedUser", scope="window")]
public var selectedUser:User;
39. Localization
Usual using with flex binding
<s:Label text="{resourceManager.getString('resources', ‘user.profile')}"/>
It is useful for properties of objects managed by the IOC Container
[ResourceBinding(bundle="resources", key="user.profile")]
public var message:String;
MXML Example
<parsley:View type="{ProfileView}">
<parsley:ResourceBinding property="header" bundle="resources" key="user.profile" />
</parsley:View>