Sunday, January 26, 2014

Parsley Interview Questions (Part 2)

Question: How do we implement synchronous and Asynchronous commands in Parsley? 
Answer:  A synchronous command may or may not return a result in execute method.
public class SimpleCommand {

    public function execute (): String {
    
        return "This is the result";
        
    }
    
}
A command is seen as asynchronous as soon as it has a public var of type Function named callback OR a method parameter of type Function in the execute method.
public class GetUserListCommand {

    [Inject("myService")]
    public var service: RemoteObject;

    public var callback: Function;

    public function execute (): void {
    
        service.getUserList().addResponder(new Responder(result, error));
        
    }
    
    private function result (result: ResultEvent): void {
        callback(result.result);
    }
    
    private function error (event: FaultEvent): void {
        callback(event.fault);
    }
    
}

When all we do is to invoke a method on RemoteObject and pass the result or fault back to parsley then there is a shortcut. This is an Asynchronous Command with AsyncToken.
public class GetUserListCommand {

    [Inject("myService")]
    public var service: RemoteObject;

    public function execute (): AsyncToken {
        return service.getUserList();
    }
    
}
If we use the above shortcut we may still want to process the result inside the command before it ispassed to other parts. The method name for result handler must be result and for error handler it must be error. It must accept one parameter of type we expect from the remote call.
public class GetUserListCommand {

    [Inject("myService")]
    public var service: RemoteObject;

    public function execute (): AsyncToken {
    
        return service.getUserList();
        
    }
    
    public function result (users: Array): void {
        
        // process users
    
    }
    
}
We can also modify the result before it gets passed to the other parts of the application.
public class GetUserProfileCommand {


    [Inject("myService")]
    public var service: RemoteObject;

    public function execute (msg: GetUserProfileMessage): AsyncToken {
    
        return service.getUserProfile(msg.userId);
        
    }
    
    public function result (profile: XML): UserProfile {
        
        return parse(profile);
    
    }
    
}




Question: How do we map command to message?
Answer:  We use MapCommand tag and may not provide the message type.
parsley:MapCommand type="{LoadUserCommand}"
In that case it will determine it from the message parameter of execute() method in the command.
public class GetUserProfileCommand {

    [Inject("myService")]
    public var service: RemoteObject;

    public function execute (msg: GetUserProfileMessage): AsyncToken {
    
        return service.getUserProfile(msg.userId);
        
    }
    
}
The commonly used way is to provide the message type as well.
parsley: MapCommand type="{LoadUserCommand}" messageType="{LoadUserMessage}"
If we want we can also provide the property values, constructor arguments etc.
parsley:MapCommand
    parsley:Command type="{LoadUserProfileCommand}"
        parsley:Property name="type" value="{UserProfile.EXTENDED}"
        parsley:Property name="cache" value="true"
    parsley:Command
parsley:MapCommand
This tag also supports various other parameters like selector, scope etc.

Question: What is a command group?
Answer:  It is a set of command that are executed as a group. This can be specified in MXML or XML.
parsley:MapCommand messageType="{LoginMessage}"
    parsley:CommandSequence
        parsley:Command type="{LoginCommand}"
        parsley:Command type="{LoadUserProfileCommand}"
    parsley:CommandSequence
parsley:MapCommand
The commands can also be executed in parallel.
parsley:MapCommand messageType="{LoadDashboardMessage}"
    parsley:ParallelCommands
        parsley:Command type="{LoadUserProfileCommand}"
        parsley:Command type="{LoadPrivateMailboxCommand}"
    parsley:ParallelCommands
parsley:MapCommand
We can also pass the results of one command to another in a sequence in a decoupled way. If LoginCommand produces an instance of User, the next command can accept it as a parameter in execute method along with other parameters.
public class GetUserProfileCommand {

    public function execute (user: User, callback: Function): void {
    
     [...]
        
    }
    
}

Question: What is a command flow?
Answer:  It adds the concept of decision points to define a dynamic sequence of commands where the next command to execute is determined by a link that inspects the result of preceding command. It can be specified in MXML and XML.
Linking by Result Type
parsley:Command type="{LoginCommand}"
    parsley:LinkResultType type="{AdminUser}" to="{loadAdminConsole}"
    parsley:LinkResultType type="{User}" to="{loadProfile}"
parsley:Command
Linking by Result Value
parsley:Command type="{LoginCommand}"
    parsley:LinkResultValue value="{Login.ADMIN}" to="{loadAdminConsole}"
    parsley:LinkResultValue value="{Login.USER}" to="{loadProfile}"
parsley:Command 
Linking by Property Value
parsley:Command type="{LoginCommand}"
    parsley:LinkResultProperty name="isAdmin" value="{true}" to="{loadAdminConsole}"
    parsley:LinkResultProperty name="isAdmin" value="{false}" to="{loadProfile}"
parsley:Command 
Linking all Results of a Command
parsley:Command type="{LoginCommand}"
    parsley:LinkAllResults to="{loadAdminConsole}"
parsley:Command 
Custom Links
A custom link class encapsulating more complex logic can be added, too. Anything that implements the LinkTaginterface can be inserted as a child tag inside the  tag:
Command type="{LoginCommand}"
    links:MyCustomLinkType
Command

Question: Can we have decoupled result handlers and command observers?
Answer:  Yes we can have and they are specified by various tags.
CommandResult: In this case the returned object of the remote call along with the original message is passed. The message type along with selector is used to determine the handler to invoke.
[CommandResult]
public function handleResult (user:User, message:LoginMessage) : void {
In case we need to process the result as early as possible we can use immediate attribute:
[CommandResult(immediate="true")]
public function handleResult (user:User, message:LoginMessage) : void {
CommandComplete: When we are not interseted in the result but want to execute some logic triggered by the completion of command we use it.
[CommandComplete]
public function userSaved (message:SaveUserMessage) : void {
CommandError: This is to used to receive the fault or error.
[CommandError]
public function handleResult (fault:FaultEvent, trigger:SaveUserMessage) : void {
Parsley also allows local result handler for the command that was executed in global scope.
[CommandResult(scope="local")]
public function handleResult (result:Object, msg:LoginMessage) : void {
    [...]
}

To be continued....

1 comment:

Anonymous said...

Finally some good questions on Parsley!!