RL 1.5 Beginner's trouble

Peter's Avatar

Peter

16 Oct, 2012 04:42 AM

Hi there.
I recently moved from the basic mvc stuff I wrote up myself to RL, heared alot of good things about it. When trying to mash up some basic stuff, I encouter a problem saying that a service previously mapped is null and I have trouble to wrap my head around this...

So I punshed out a simple MainContext where I define the following stuf:

public class MainContext extends Context implements IContext
{

public function TreeshirtContext(contextView:DisplayObjectContainer)
{
    super(contextView);
}

override public function startup():void
{                       
    commandMap.mapEvent(InitEvent.INITIALIZE, MyInitCommand, InitEvent);
    injector.mapSingletonOf(IMyService, MyService);
    super.startup();
    dispatchEvent(new InitEvent(InitEvent.INITIALIZE));
}

}

Then the Init Command, which is being called correctly:

public class MyInitCommand extends Command
{

[Inject]
public var event:MyEvent;

[Inject]
public var service:IMyService;

public function MyInitCommand()
{
    service.init();   // here the error is being thrown
}

}

and last but not least:

public class FacebookService extends Actor implements IFacebookService
{

public function FacebookService()
{
    trace("@constructor FacebookService");
    super();
}

public function init():void
{
    trace("@Service::init()");
}

}

However, I get an error indicating that the service has never been instantiated. The error is thrown in the Command, hinting that the service has never been initialized.
What am I missing?

Regards,
Peter

  1. 1 Posted by Paul Robertson on 16 Oct, 2012 05:47 AM

    Paul Robertson's Avatar

    The problem is you're calling service.init() in the command's constructor,
    which is apparently being called before the service is constructed. In this
    case Robotlegs is lazily constructing the service -- not actually creating
    it until it's needed -- so the order is something like this:

    - command is triggered
    - create MyInitCommand instance (call it's constructor) <-- your error here
    - Analyze the MyInitCommand for mappings to fulfill
    - Identify IMyService mapping, which is mapped but not created yet
    - Create IMyService concrete implementation (MyService/FacebookService
    instance)
    - Inject newly created instance
    - call command's execute() method <-- this is where you should do work

    In a RL command, your "entry point" is a method named execute(), so rather
    than calling service.init() in the command's constructor, you'll want to
    create an overridden execute() method and call service.init() there, e.g.:

    override public function execute():void
    {
        service.init();
    }

    An alternative pattern for initializing services is to use a PostConstruct
    method in the service class. When you tag a method with the [PostConstruct]
    metatag, that tells Robotlegs to treat the method like a constructor for
    the class, only one where all the injections are available. (See above for
    why injections aren't available in constructors.) So if you have a method
    marked [PostConstruct] the order is something like this:

    - some object that has an injection of your service is constructed
    - Identify IMyService mapping, which is mapped but not created yet
    - Create IMyService concrete implementation (MyService/FacebookService
    instance), calling MyService() constructor
    - fulfill any injections that the service requires
    ** Call [PostConstruct] method in service
    - Inject newly created service instance where it's needed
    - continue doing stuff

    Note that while in this example I'm saying to use [PostConstruct] in a
    service, you can really use it anywhere. Having said that, I'm pretty sure
    I've only ever used it in a service.

    Here's an example of what the service class would look like:

    public class FacebookService extends Actor implements IFacebookService
    {

    public function FacebookService()
    {
    trace("@constructor FacebookService");
    super();
    }

            [PostConstruct]
    public function init():void
    {
    trace("@Service::init()");
    }
    }

    The biggest potential downside to the [PostConstruct] approach is that your
    service's init() method isn't called until the service is actually needed
    for injection. So for example if your init() method kicks off some
    asynchronous process that you'd rather have done at startup rather than
    later, you'll definitely want to stick with the init command approach
    you're currently using rather than using [PostConstruct]. I've used both
    approaches depending on the needs of the app.

    Paul

  2. 2 Posted by Peter on 16 Oct, 2012 05:39 PM

    Peter's Avatar

    Oh ofcourse,
    override public function execute():void ... is the function supposed to call the service.init() methode. Silly me for not seeing that.
    Thanks a bunch for the very detailed answer.

    I indeed rather stick to the command approach, most of the stuff I need to accomplish (right now) is basically bootstrapping stuff and go on from there.

    Thanks again, best regards,
    Peter

  3. Ondina D.F. closed this discussion on 17 Oct, 2012 12:22 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac