This chapter gives a quick overview over all the planned changes for the upcoming releases. It is also meant as a basis for discussion. You'll find links to the individual Jira tickets in most sections. If you have a comment specific for a particular feature it is preferred that you comment directly in Jira. If you have some general comments or want to propose a feature not listed here you can also use this thread in the forum that was created for feedback concerning the road map.
The sequence of updates was arranged in a way that only the 2.1 release will contain changes which are not backwards-compatible. It is a bit unfortunate that such an update is necessary so shortly after the 2.0 release. But the view wiring mechanism had limitations which can only be fixed in an elegant way through changing all the static entry points used to initialize a Context. After the 2.1 release Parsley should be consolidated so that subsequent versions come with enhancements and new features which are fully backwards-compatible except for eventual changes in the internal APIs. But even with the 2.1 release at least none of your configuration files (MXML or XML) nor the metadata tags used for configuration are affected.
Work on this version will start in late August. A rough estimate for a release would be late September or early October. Some of the changes are rather subtle, so if you only used basic features of Parsley 2.0 in small applications you may skip this section and look at the new features planned for the 2.2 release.
Unfortunately the view wiring mechanism in version 2.0 had some limitations: It did not work well with Flex Popups, it did not work at all for native AIR windows and it could not be used in pure Flash applications. The next release will address all these issues at once.
The major problem with the initial approach was to listen to bubbling events in the Flex SystemManager to be notified whenever a component wants to get wired to the IOC container. Although this approach is used by many other Flex frameworks too, it has severe limitations. First native AIR windows come with their own SystemManager, so bubbling events in such a window will not be caught at all. Second when such an event arrives at the SystemManager level it is difficult to find out which Context it wants to get wired to in a multi-Context environment. The approach in version 2.0 to inspect the ApplicationDomain a component belongs to or to use custom trigger events was fragile and difficult to understand and document. It was also slighty cumbersome to implement.
So the new idea is to make it more explicit, to allow the application developer to connect a hierarchy of Context instances to a view hierarchy. To make this as convenient as possible, the static entry point methods will be changed. They will all get a new second parameter that allows to specify the root of the view hierarchy the new Context should be connected to:
public static function build (
configClass:Class,
viewRoot:DisplayObject = null,
parent:Context = null,
domain:ApplicationDomain = null
) : Context {
The new parameter will be inserted at the second position. Although it is optional it is very likely that it will be used much more often than the other two optional parameters. In many cases you'll only use the first two, passing the component in which you build the Context as a view root:
FlexContextBuilder.build(MyConfig, this);
Now this somewhat unimpressive new parameter opens a lot of new doors:
Internally this mechanism will be handled by a ViewManager, which is a new core interface in the 2.1 release. The signature is quite simple:
public interface ViewManager {
function addViewRoot (viewRoot:DisplayObject) : void;
function removeViewRoot (viewRoot:DisplayObject) : void;
}
Each Context instance will have its own ViewManager. It can be accessed through a new property in the Context interface:
function get viewManager () : ViewManager;
Access to that manager is needed because application developers will have to specify additional view roots to support Flex popups or native AIR windows. Those are view hierarchies which are disconnected from the view hierarchy below the main Application component. For the view root passed to the ContextBuilder methods the builder will call addViewRoot internally, but for popups and windows an extra step is required to tell the Context that there is an additional view hierarchy it should manage. For popups it would look like this:
[Inject]
public var context:Context;
private function showPopup () : void {
var win:TitleWindow = new TitleWindow();
// set properties
context.viewManager.addViewRoot(win);
PopUpManager.addPopup(win, this);
}
For an AIR window the example would be analogous:
[Inject]
public var context:Context;
private function openWindow () : void {
var win:Window = new Window();
// set properties
context.viewManager.addViewRoot(win);
win.open();
}
The removeViewRoot method of the ViewManager interface will not be called by application code directly
in most cases, as the default implementation of the interface will automatically remove a view root when it is removed
from the stage. When the final view root is removed from a ViewManager the Context is destroyed. Like with most core
services this manager can be replaced by custom implementations if required.
This was a long explanation, but although it may sound complicated this will in fact be a significant simplification for modular applications and large Context hierarchies. It will also make the internal implementation much cleaner. With the MXML tags for Context creation planned for version 2.2 even the specification of the view root will be implicit:
<parsley:FlexContext configClass="{MyConfig}"/>
The default view root will then be the document parameter passed to the initialize method
of the IMXMLObject. This is really the final simplification then, since at that point all other
aspects which are relevant for Context building will have sensible defaults then or will be auto-detected
by the framework, including the view root, the parent Context and the ApplicationDomain to use.
Due to these changes the concept of custom viewTriggerEvents is no longer needed. The viewTriggerEvent
parameter of the various FlexContextBuilder methods will be removed. Likewise the corresponding attribute of
the <parsley:Configure> tag will also be removed.
Finally there will still be an additional listener on the SystemManager level for Flex applications. But all this listener will do in future versions is logging an error if it catches a bubbling Parsley event because this would indicate that some component was used outside of the view hierarchies known by the framework.
The support for Flex Modules can be simplified for version 2.1 in a way that it will be completely transparent for the
application developer. Thus the custom <parsley:ContextModule> and <parsley:ModuleLoader> components
will be removed. They are no longer needed because the additional work they performed can now be taken over by other parts
of the framework, in particular the new view wiring mechanism. The old module support helped with two things:
<parsley:ContextModule> tag. That parent would then be passed
to the Context built by the module after it was loaded. That was convenient because within the module you wouldn't have
easy access to the parent instance otherwise. But the new view wiring mechanism explained in the previous section also
works for any module instance that you add to your view hierarchy. It would simply search for the parent within the
view hierarchy. A Context built within a module is no longer different than any other sub-Context attached to the
view. So the good news is that you can revert to using the original <mx:ModuleLoader> and <mx:Module> tags.
As a side effect the Parsley integration now also works seamlessly when you use the Flex ModuleManager directly to load modules.
Finally Parsley will no longer create a single Context for the whole module. A Context within a module will be treated like
any other sub-Context, which means that when you attach it to a view root it will be destroyed when the view is removed
from the stage.
In Parsley 2.0 every child Context simply shared the MessageRouter with its parent Context. This way messages were always dispatched globally. An approach that may turn out to be too simplistic for large and modular applications where a loaded module, a window or a popup may wish to create its own local messaging context while still being able to inject objects from the root Context or dispatching selected messages globally. The new scope feature now offers that flexibility, allowing to dispatch into a subset of Contexts only.
Global and Local Scopes
In the new default scope setup for Parsley 2.1 there will be a global scope that is created for each Context that does not have a parent (usually only the one root Context created at application startup) and then shared will all children of that Context (including grandchildren of course). Additionally each Context will create its own local scope which will not be shared with its children. The following diagram shows this default setup:
Since the global scope represents exactly the only option that was available in Parsley 2.0 it will always be the default scope for all configuration tags where no scope is explicitly specified. This way existing configuration files and metadata tags on classes will still work the same way in the new version.
[MessageHandler(selector="save")]
public function save (event:ProductEvent) : void {
The handler above listens to ProductEvents dispatched from any Context in the hierarchy. It listens to the global scope.
[MessageHandler(selector="save", scope="local")]
public function save (event:ProductEvent) : void {
Now the handler only listens to events dispatched from the same Context.
For the sending side the default behaviour is different. For any [ManagedEvents] tag
where no scope is explicitly specified the message will be dispatched through all scopes available
for that particular Context. This way the receiving side can decide which scope it wants to listen to,
allowing global and local receivers for the same message instance.
For cases where you even want to restrict the sending side to a single scope there will also be a new scope attribute
for the [ManagedEvents] tag:
[ManagedEvents("save,delete", scope="local")]
Custom Scopes
Finally you can also create your own custom scope when neither global nor local messaging is the right choice. This may be the case when you create a large AIR window for example. The root window component may create a Context with the root application Context as a parent but then also a few child Contexts for parts within that window. If you then want to setup a messaging scope for that window only you need a scope that spans multiple Contexts but still is not global. With Parsley 2.1 you can create a scope setup like in the following example diagram:
The window scope is a custom scope that exists side-by-side with the two default scopes. Now how do you
instruct the framework to create that scope? This has to be done for the root Context of the scope,
in this case the two root Contexts for the two window scopes. The scope can be added to the
CompositeContextBuilder:
var builder:CompositeContextBuilder = new DefaultCompositeContextBuilder();
builder.addScope("window", true);
FlexContextBuilder.merge(MyWindowConfig, builder);
builder.build();
The first parameter for the addScope method is the name of the scope. It does not have to
be unique as long as you make sure that two scopes with the same name do never overlap. This is convenient
as it allows to define a message handler for the window scope without having to think about which
window instance it belongs to:
[MessageHandler(selector="save", scope="window")]
public function save (event:ProductEvent) : void {
The second boolean parameter specifies whether the scope should be shared with child Contexts. So you can also create custom local scopes, although this is probably a rather unsual use case.
When the new MXML tags for Context creation will be introduced in Parsley 2.2, scopes can also be conveniently specified directly on the tag:
<parsley:FlexContext config="{MyConfig}">
<parsley:scopes>
<parsley:Scope name="window" inherited="true"/>
<parsley:scopes>
</parsley:FlexContext>
Scope API
Finally the introduction of scopes makes significant changes of the internal APIs necessary.
First the MessageRouter is now an internal API, not exposed to the application directly.
Instead the messageRouter property on the Context interface will be replaced
by a new scopeManager property:
function get scopeManager () : ScopeManager;
The ScopeManager interface is quite simple:
function hasScope (name:String) : Boolean;
function getScope (name:String) : Scope;
function getAllScopes () : Array;
function dispatchMessage (message:Object, selector:* = undefined) : void;
A ScopeManager is always associated with a particular Context and only "sees" the
scopes that the Context actually belongs to. The dispatchMessage method is a convenient shortcut
which allows you to dispatch a message to all scopes managed by that instance.
The Scope interface itself looks like this:
function get name () : String;
function get inherited () : Boolean;
function get messageReceivers () : MessageReceiverRegistry;
function get objectLifecycle () : ObjectLifecycleScope;
function dispatchMessage (message:Object, selector:* = undefined) : void;
The name and inherited properties correspond to the values you specify when
creating a scope. The MessageReceiverRegistry allows you to add receivers (handlers or
interceptors) for messages of that particular scope. This API is mostly used by configuration tags,
but may also be used by application code. The ObjectLifecycleScope allows to add lifecycle
listeners for a scope, a new feature in 2.1 which will be enhanced with a new set of custom tags
in 2.2. In the following example you'd be notified whenever an object of type UserProfileController
(or a subtype) gets created within the window scope:
[PreInit(scope="window")]
public function controllerInit (con:UserProfileController) : void {
Again the default scope is global if not specified. Since these tags are introduced
in 2.2, you have to register the listener programmatically in 2.1:
var scope:Scope = context.scopeManager.getScope("window");
scope.objectLifecycle.addListener(UserProfileController, ObjectLifecycle.PRE_INIT,
controllerInit);
While it is easy in general to create custom configuration tags that can be used as MXML, XML or Metadata tags,
it was not very easy to create a tag that registers a target for the Messaging Framework that behaves significantly different
than the builtin ones. That is because the MessageRouter interface in version 2.0 closely matches the capabilities of the
three builtin tags (MessageHandler, MessageBinding and MessageInterceptor):
function registerMessageHandler (targetInstance:Object, targetMethod:String,
messageType:Class = null, messageProperties:Array = null, selector:* = undefined,
domain:ApplicationDomain = null) : MessageTarget;
function registerMessageBinding (targetInstance:Object, targetProperty:String,
messageType:Class, messageProperty:String, selector:* = undefined,
domain:ApplicationDomain = null) : MessageTarget;
function registerMessageInterceptor (targetInstance:Object, targetMethod:String,
messageType:Class = null, selector:* = undefined,
domain:ApplicationDomain = null) : MessageTarget;
function dispatchMessage (message:Object, domain:ApplicationDomain = null) : void;
As you see each of the three tags had its corresponding register method in the interface. The parameters largely correspond to the attributes of the configuration tag. In version 2.1 the interface will be simplified and generalized:
public interface MessageRouter {
function get receivers () : MessageReceiverRegistry;
function dispatchMessage (message:Object, domain:ApplicationDomain,
selector:* = undefined) : void;
}
The MessageReceiverRegistry interface will look like this:
public interface MessageReceiverRegistry {
function addTarget (target:MessageTarget) : void;
function removeTarget (target:MessageTarget) : void;
function addInterceptor (target:MessageInterceptor) : void;
function removeInterceptor (target:MessageInterceptor) : void;
function addErrorHandler (target:MessageErrorHandler) : void;
function removeErrorHandler (target:MessageErrorHandler) : void;
}
There are now three categories of message receivers which behave differently.
The MessageInterceptor existed in 2.0 already, but did not come with
a separate interface. The MessageTarget interface now bundles all
regular receivers like MessageHandlers, MessageBindings
or the new short-lived Command objects to be introduced in 2.2. Finally the MessageErrorHandler
interface is new. It allows to declare error handlers for a particular type of message
(and a particular type of Error if you want to differentiate them):
[MessageError(type="com.bookstore.LoginEvent")]
public function handleError (processor:MessageProcessor, error:Error) : void;
The handler above will be invoked whenever a message handler for a
message of type LoginEvent dispatched trough the global scope threw an Error.
A custom configuration tag may now create a new implementation of those three interfaces or reuse
the existing implementations (MessageHandler, MessageBinding and so on) and register it
with the registry. The registry is exposed through the new Scope interface explained in the
previous section while the MessageRouter interface itself becomes an internal interface
in 2.1.
The fourth and final larger enhancement for version 2.1 will be adding new hooks to replace internal
services. While the internal APIs were already quite clean in version 2.0 in that they were modularized
and each internal service already lived behind an interface. But the problem was that the creation of the
concrete instances largely happened somewhere deep within the Context building process, so even if you wrote
a custom implementation of a core interface, there was no easy way to tell Parsley to use it. This will
now change. There will be a new global FactoryRegistry where you can specify replacements
for core services that should be applied to all Context instances built by the framework. Furthermore
you can also define replacements for an individual Context by specifying the alternative implementation
in the CompositeContextBuilder you use to create the Context.
This is the list of interfaces you can specify custom implementations for:
MessageRouter: The core interface of the Messaging Framework. Already discussed in a previous section. ViewManager: The manager for the new view wiring mechanism. Also discussed in a previous section. Context: You can also pick a custom implementation for the Context itself. ObjectLifecycleManager: This manager is reponsible for processing ObjectDefinitions to
instantiate, configure and destroy instances managed by the container. The name of the interface was ObjectFactory
in version 2.0, but it will be renamed since the new name is a better match semantically for the task this service performs. ObjectDefinitionRegistry: The central registry for ObjectDefinitions. Probably the least likely
candidate for replacements. As an example, a custom factory for a ViewManager can be specified like this:
GlobalFactoryRegistry.getInstance().viewManager = new MyViewManagerFactory();
For an individual Context you can specify a custom factory on the CompositeContextBuilder:
var builder:CompositeContextBuilder = new DefaultCompositeContextBuilder();
builder.factories.viewManager = new MyViewManagerFactory();
FlexContextBuilder.merge(MyConfig, builder);
builder.build();
You cannot use the static entry point short cuts in this case. But it is very likely that you install custom extensions globally in most cases anyway.
Additionally you'll find a few tickets for smaller enhancements planned for the 2.1 release in the Jira Road Map. This emcompasses support for Flex 4, an Ant Build that also works on the Mac or enhancements for the initialization order for messaging in sub-Contexts and several more.
This is a list of instructions how you adjust existing applications for the changes that are not backwards-compatible. Even for large and complex applications it should be no big deal. Many sections in this migration guide only affect you when you have built extensions for the framework like custom configuration tags. If you don't have a clue what a particular section is referring to, it is very likely that you can safely ignore it.
Adjust static entry point methods
For all Context instances you want to use view wiring with, you now have to add a second parameter specifying the view root for the Context:
FlexContextBuilder.build(MyConfig, this);
or:
XmlContextBuilder.build("config.xml", this, parentContext);
When building the Context from within a Component you can simply pass this to the builder in most cases.
Switch to addedToStage event for Context creation
The ContextBuilder now looks for parent Context and ApplicationDomain in the view hierarchy if you specify a view root for the builder. To be able to dispatch a bubbling event the specified view root must have been added to the stage:
<mx:Panel
addedToStage="FlexContextBuilder.build(MyConfig, this)"
...
>
Remove custom viewTrigger event types
Those are no longer needed and no longer supported (in case you used them at all). Such a custom event type could be specified
for the static methods of the FlexContextBuilder and for the Configure tag:
<parsley:Configure triggerEvent="myCustomEventType"/>
Simply remove all references to custom event types.
Attach Flex Popups to the Context they belong to
Flex popups now require an extra step to explicitly attach them to the Context that components in that popup should be wired to:
[Inject]
public var context:Context;
private function showPopup () : void {
var win:TitleWindow = new TitleWindow();
// set properties
context.viewManager.addViewRoot(win);
PopUpManager.addPopup(win, this);
}
Attach native AIR windows to the Context they belong to
This is basically the same mechanism like how you now have to deal with Flex Popups:
[Inject]
public var context:Context;
private function openWindow () : void {
var win:Window = new Window();
// set properties
context.viewManager.addViewRoot(win);
win.open();
}
Remove Parsley's ContextModule and ModuleLoader tags
They are no longer needed. Simply replace them with the default mx:Module
and mx:ModuleLoader components. There is no longer a need to specify the parent Context for a ModuleLoader,
it will be auto-detected in the view hierarchy.
2.0
<parsley:ContextModule
configClass="{MyModuleConfig}"
...
>
2.1
<mx:Module
addedToStage="FlexContextBuilder.build(MyModuleConfig, this)"
...
>
Organize imports for Parsley's internal APIs
If you are talking to the internal API you simply have to organize most imports. There was a huge refactoring and cleaning up for the internal API and several packages and classes have been moved. If your IDE supports a project-wide "Organize imports" that's the fastest way to migrate.
Adjust any custom configuration tags for the Messaging Framework
If you talked to the MessageRouter API directly, in a custom configuration tag or elsewhere in your
application code, you have to adjust the code for the new API. In 2.0 a MessageHandler registration looked like
this:
context.messageRouter.registerMessageHandler(target, "execute", MyMessage);
In 2.1 there is now a convenient base class that handles a lot of low-level details for you.
For example it registers a proxy for non-lazy singletons, so that it does not miss messages even
if they are dispatched during Context initialization before the receiver is actually instantiated.
Therefor it is recommended to simply overwrite the createReceiver and removeReceiver
methods of the AbstractMessageReceiverDecorator base class and not implement the decorate
method yourself. You can examine the builtin receiver tag implementations in the package
org.spicefactory.parsley.tag.messaging if you want to see how the default messaging tags
are implemented. For example the [MessageInterceptor] tag implementation looks like this:
protected override function createReceiver
(provider:ObjectProvider, scopeManager:ScopeManager) : MessageReceiver {
var ic:MessageInterceptor
= new DefaultMessageInterceptor(provider, method, type, selector);
scopeManager.getScope(scope).messageReceivers.addInterceptor(ic);
return ic;
}
protected override function removeReceiver
(receiver:MessageReceiver, scopeManager:ScopeManager) : void {
var ic:MessageInterceptor = MessageInterceptor(receiver);
scopeManager.getScope(scope).messageReceivers.removeInterceptor(ic);
}
In the createReceiver method you can see that the framework passes a prebuilt ObjectProvider
instance to the method. This may either be a wrapper for an existing instance or a proxy
where the instance is fetched from the Context in case a matching message is heard.
Change code that programmatically dispatches messages
This change is straightforward. Version 2.1 introduces scopes. Therefor the messageRouter property
has been replaced by the new scopeManager property.
2.0
context.messageRouter.dispatchMessage(new MyMessage());
2.1
context.scopeManager.dispatchMessage(new MyMessage());
The ScopeManager dispatches through all scopes available in that Context. This way the receivers can decide which scope they want to listen to. You can alternatively dispatch to a single scope only:
context.scopeManager.getScope(ScopeName.LOCAL).dispatchMessage(new MyMessage());
Adjust configuration tags that register lifecycle listeners on an ObjectDefinition
The lifecycle mechanism has been expanded, there are now five events for the lifecycle of each
object: preConfigure, preInit, postInit, preDestroy and
postDestroy. Therefor the old ObjectLifecycleListener interface has been removed
and you can register a function reference directly:
2.0
definition.lifecycleListeners.addLifecycleListener(this);
2.1
definition.objectLifecycle.addListener(ObjectLifecycle.PRE_INIT, preInit);
In the example for 2.0 this would refer to a tag that additionally implements
ObjectLifecycleListener. In the example for 2.1 preInit references a function.
The signature for the function is the same as in 2.0, the framework passes the instance itself
and the Context. But the method could now be private as it is no longer required to implement
an interface.
After the 2.1 consolidation release it is time to add some new features. For this release it is very likely that it will be fully backwards-compatible, so you can move to the new features gradually.
The new version will allow to create Context instances with MXML tags:
<parsley:FlexContext config="{MyConfig}"/>
<parsley:XmlContext config="config.xml"/>
<parsley:CompositeContext>
<parsley:FlexConfig config="{ServiceConfig}"/>
<parsley:FlexConfig config="{ControllerConfig}"/>
<parsley:XmlConfig config="logging.xml"/>
</parsley:CompositeContext>
These tags will not introduce any new functionality. They are just syntactical sugar for all of you who prefer this declaration style over the old static entry point methods. Those will remain unchanged, they will be available as an alternative for Flex applications and are still needed for Flash applications.
This new feature is inspired by the configuration mechanism used in Flicc. It allows you to configure view components in MXML or XML and let the container apply the configuration to the view when it is wired to the Context. This is an optional feature. You can still configure view classes with Metadata exclusively like with previous versions. But the new MXML and XML support allows to define view configuration alongside your regular object configurations. It has the side effect that you can then define different configurations for individual instances of the same component. The configuration to apply to a wired component will be matched either by id or by type:
<View id="myPanel" type="{MyPanel}">
<Property name="url" value="http://www.somewhere.com/"/>
<MessageHandler method="execute"/>
</View>
As you see it is basically the same syntax like for regular object configurations. But the use of the View tag instructs Parsley not to attempt to create instances of that class but instead wait until any matching component gets dynamically wired to the Context and then apply the configuration.
If you specify an id, type and id have to match for a wired component. If you omit the id the component is only matched by type.
This new feature allows the use of short-lived commands instead of regular message handlers defined in objects
that are tied to the Context lifecycle. This makes it easier to use Parsley with a classic FrontController/Command
approach. The new Command tag has the same attributes like the existing MessageHandler tag, but
the mechanism behind this tag is significantly different:
<Object id="myPanel" type="{MyCommandObject} singleton="false">
<Property name="url" value="http://www.somewhere.com/"/>
<Command method="execute"/>
</Object>
The lifecycle for an object configured as a Command is completely different. The creation of the object does not happen until a matching message is dispatched. Note that it is not declared as a singleton. When the object is created for a command execution it is configured like any regular object, so it can have dependencies, it can declare managed events and so on. It becomes a container managed object just for the duration of the command execution. It will then immediately be removed from the Context. There is also a tag variant for aynschronously operating commands:
<Object id="myPanel" type="{MyCommandObject} singleton="false">
<Property name="url" value="http://www.somewhere.com/"/>
<AsyncCommand method="execute"/>
</Object>
The use of this tag has the side effect that the command instance remains a container managed object until the
task is performed. So it can listen for messages during execution (for example there might be application messages that would
cause the command to stop its execution). It can also dispatch a message itself when it has completed its operation,
since it is still part of the Context then. Such a command instance has to dispatch an event to signal that it has
completed. The default is Event.COMPLETE like with the AsyncInit tag, but it can be overridden through
an attribute of the AsyncCommand tag. Only after it has dispatched that event it will be
removed from the Context.
There is also a plan to add a new order attribute to the MessageHandler and Command
tags so that you are able to define a fixed sequence of commands and handlers. The AsyncCommand tag might
get a boolean block attribute to indicate that other handlers and command in the message target chain have
to wait for the completion of that command. That would make it easier to declare a fixed sequence of commands that
contains asynchronous operations.
And finally, of course the new tags also exist as a Metadata variant:
[Command(selector="deleteUser")]
public function execute (event:DeleteUserEvent) : void {
This is a powerful new feature that opens some new doors for convenient ways to observe or modify other objects
without the need to add something to their configuration. This might be particularly useful for short-lived objects
like views or commands which might enter and leave the Context at any point in time and thus are not valid sources
for regular injection points. By observing these instances you can still get hold of a reference to such a short-lived
object. This feature works with the existing [PostConstruct] and [PreDestroy] tags.
Until this version they were only used on methods without parameters:
[PostConstruct]
public function init () : void;
In such a case the init method would have been called when the object where it was place on was fully
configured. Now there is a new variant for this tag:
[PostConstruct]
public function enhance (panel:ShoppingCartPanel) : void;
Now this method will be invoked whenever a new instance of ShoppingCartPanel (or any subclass) has
been fully configured. Likewise with a [PreDestroy] method it will be invoked when any object with
a matching type leaves the Context. This works globally, so you can also observe objects in sub- or super-Contexts.
With this mechanism you simply plug in some new class that contains such a tag and suddenly existing classes
can be enhanced without the need to touch their configuration. This is somewhat analogous to the existing
[Factory] tag which can be used to customize the object instantiation. With this tag you can customize
the object configuration after it has been instantiated. In both cases you do not even depend on the Parsley API
in any way.
This version is not fully specified yet. Until work on this version starts the road map might still change due to user feedback. But these two features are the initial plan.
This enhancement will make the definition of custom configuration tags easier. With MXML or Metadata it was already quite easy, since at most a single line of code was needed to register such a tag. With XML tags the use of the XML-Object Mapper from Spicelib is required and might involve the need to programmatically specify subtle details of how the new XML tag maps to the configuration class.
The feature set as such will remain the same. The new mechanism will still be based on the existing
PropertyMapperBuilder class, but the need to programmatically create such a builder will go away.
First there will be sensible defaults. A property without metadata will automatically map to a corresponding
XML attribute. You can then override the default for individual properties:
public class Person {
[ChildTextNode]
public var name:String;
[ChildElement]
public var address:Address;
}
There will be a corresponding metadata tag for all the features currently available in the PropertyMapperBuilder.
This tag will allow to specify Flex Bindings with Metadata. This might be convenient for regular classes managed by the container, where you cannot create bindings with MXML. You can then create unidirectional or bidirectional bindings between properties of two managed objects:
[PropertyBinding(objectId="environment",property="defaultTimeout")]
public var defaultTimeout:uint;
The name PropertyBinding was chosen to distinguish this tag from the existing MessageBinding and
ResourceBinding tags.