tag:robotlegs.tenderapp.com,2009-10-18:/discussions/questions/1262-passing-result-of-one-command-as-an-input-for-anotherRobotlegs: Discussion 2018-10-18T16:35:44Ztag:robotlegs.tenderapp.com,2009-10-18:Comment/209102252012-11-16T08:28:58Z2012-11-16T08:28:59ZPassing result of one command as an input for another<div><p>Hi, guys!</p>
<p>In my application I have main configuration file (main.cfg)
which holds urls of other configuration files (e.g. localization
files). Main configuration is YAML file and looks like this:</p>
<pre>
<code>localization:
en: /some/locales/base/url/en.cfg
ru: /some/locales/base/url/ru.cfg
de: /some/locales/base/url/de.cfg
...
backgrounds: /some/backgrounds/base/url/backgrounds.cfg
...</code>
</pre>
<p>Currently, I have all configurations bootstraping performed at
appication startup in one single command
<em>PreloadAllConfigsCommand</em>, which loads main config and
after that it issue loading of localizations, backgrounds and all
that stuff. It looks like that:</p>
<pre>
<code>public calss PreloadAllConfigsCommand extends Command {
[Inject]
public var configsRetriever:IConfigsRetriever;
[Inject]
public var localizationService:ILocalizationService;
[Inject]
public var backgroundsModel:BackgroundsModel;
override public function execute():void {
commandMap.detain(this);
configsRetriever.loadConfig("some/url/main.cfg").onComplets(onMainConfigLoaded);
}
private function onMainConfigLoaded(config:Dictionary):void {
var currentLocale:String = localizationService.currentLocale;
configsRetriever.loadConfig(config["locales"][currentLocale]).onComplets(onLocalesLoadedLoaded);
}
private function onLocalesLoadedLoaded(config:Dictionary):void {
localizationService.localizationData = config;
configsRetriever.loadConfig(config["backgrounds"]).onComplets(onBackgroundsConfigLoaded);
}
private function onBackgroundsConfigLoaded(config:Dictionary):void {
commandMap.release(this);
backgroundsModel.doSomethigWithConfig(config);
dispatchEvent(new ConfigsLoadedingCompletedEvent());
}
}</code>
</pre>
<p>So far it worked well for me, but recently I got a task to let
users to switch locale in runtime. So, I have to break this command
in some sub-commands, and introduce couple of macro commands -
<em>PreloadAllConfigsCommand</em> and <em>SwitchLocaleCommand</em>.
<em>PreloadAllConfigsCommand</em> will sequentially run following
commands:</p>
<ul>
<li>LoadMainConfigCommand</li>
<li>LoadLocalizationsConfigCommand</li>
<li>LoadBackgroundsConfigCommand</li>
<li>LoadWhaterverConfigCommand</li>
</ul>
<p><em>SwitchLocaleCommand</em> will sequentially run:</p>
<ul>
<li>LoadMainConfigCommand</li>
<li>LoadLocalizationsConfigCommand</li>
</ul>
<p>Here we are getting to the point of this question. Sequential
commands execution is not a problem at all. But what is the best
way to pass results of <em>LoadMainConfigCommand</em> as input to
the <em>LoadLocalizationsConfigCommand</em>:</p>
<pre>
<code>public calss LoadMainConfigCommand extends Command {
[Inject]
public var configsRetriever:IConfigsRetriever;
override public function execute():void {
commandMap.detain(this);
configsRetriever.loadConfig("some/url/main.cfg").onComplets(onMainConfigLoaded);
}
private function onMainConfigLoaded(config:Dictionary):void {
commandMap.release(this);
// How should I let LoadLocalizationsConfigCommand know about parsed main config?
}
}
public calss LoadLocalizationsConfigCommand extends Command {
[Inject]
public var configsRetriever:IConfigsRetriever;
[Inject]
public var localizationService:ILocalizationService;
override public function execute():void {
commandMap.detain(this);
var mainConfig:Dictionary = ???; // Where should i get it from?
var currentLocale:String = localizationService.currentLocale;
configsRetriever.loadConfig(mainConfig["localization"][currentLocale]).onComplets(onLocalizationFileLoaded);
}
private function onLocalizationFileLoaded(config:Dictionary):void {
commandMap.release(this);
localizationService.localizationData = config;
}
}</code>
</pre>
<p>Right now I see couple of options:</p>
<ul>
<li>Dispatch <em>MainConfigLoadedEvent</em> with parsed config as a
payload from <em>LoadMainConfigCommand</em> and dynmicaly map and
unmap events to appropriate commands in macro-commands
<em>PreloadAllConfigsCommand</em> and <em>SwitchLocaleCommand</em>.
<em>PreloadAllConfigsCommand</em> execute methos will look
something like that:</li>
</ul>
<pre>
<code> override public function execute():void {
commandMap.mapEvent(MainConfigLoadedEvent.EVENT_TYPE, LoadLocalizationsConfigCommand);
commandMap.mapEvent(MainConfigLoadedEvent.EVENT_TYPE, LoadBackgroundsConfigCommand);
commandMap.mapEvent(MainConfigLoadedEvent.EVENT_TYPE, LoadWhaterverConfigCommand);
commandMap.execute(LoadMainConfigCommand);
}</code>
</pre>
<p>And <em>SwitchLocaleCommand</em> execute method will be:</p>
<pre>
<code> override public function execute():void {
commandMap.mapEvent(MainConfigLoadedEvent.EVENT_TYPE, LoadLocalizationsConfigCommand);
commandMap.execute(LoadMainConfigCommand);
}</code>
</pre>
<ul>
<li>Use some lib for sequencing commands (e.g. MacroBot) and store
all in-between results in appropriate models, e.g.
<em>LoadMainConfigCommand</em> will store parsed main config in
<em>MainConfigModel</em>. So commands
<em>LoadLocalizationsConfigCommand</em> and
<em>LoadBackgroundsConfigCommand</em> will expect filled
<em>MainConfigModel</em> to be injected in them, e.g.
<em>LoadLocalizationsConfigCommand</em> will be:</li>
</ul>
<pre>
<code>public calss LoadLocalizationsConfigCommand extends Command {
...
[Inject]
public var mainConfig:MainConfigModel;
override public function execute():void {
commandMap.detain(this);
var mainConfig:Dictionary = mainConfig.config;
var currentLocale:String = localizationService.currentLocale;
configsRetriever.loadConfig(mainConfig["localization"][currentLocale]).onComplets(onLocalizationFileLoaded);
}
...
}</code>
</pre>
<ul>
<li>Pass array of events as an input to
<em>LoadMainConfigCommand</em> and let it fill them with parsed
config and dispatch when work is finished. In this scenario I'll
end with <em>HandleMainConfigEvent</em> and its sub-classes
<em>LoadLocalesConfgEvent</em>,
<em>LoadBackgroundsConfigEvent</em>, etc. each mapped to
appropriate command. Again, in code it will looks like:</li>
</ul>
<pre>
<code>public calss LoadMainConfigCommand extends Command {
[Inject]
public var event:LoadMainConfigEvent;
[Inject]
public var configsRetriever:IConfigsRetriever;
override public function execute():void {
commandMap.detain(this);
configsRetriever.loadConfig("some/url/main.cfg").onComplets(onMainConfigLoaded);
}
private function onMainConfigLoaded(config:Dictionary):void {
commandMap.release(this);
for each (var e:HandleMainConfigEvent in event.eventsToHandlePayload) {
e.config = config;
dispatch(e);
}
}
}</code>
</pre>
<ul>
<li>Avoid resuable work to be done via commands at all and
implement preloadAllConfigs and switchLocales in some Actors.</li>
</ul>
<p>I really like the idea of using commands as resuable chunks of
work, but how will I pass output from one command as an input to
the next one without letting first command to decide with event to
fire at the end of it's work. Or may be I am driving completely
wrong direction and misunderstood the idea of commands?</p></div>pa3tag:robotlegs.tenderapp.com,2009-10-18:Comment/209102252012-11-16T10:29:14Z2012-11-16T10:29:14ZPassing result of one command as an input for another<div><p>Your commands should be blissfully unaware of their sequential
position. But they do need the correct input. The best way to keep
everything decoupled is to manipulate external models, even if
those models exist only for the duration of the process.</p></div>neiltag:robotlegs.tenderapp.com,2009-10-18:Comment/209102252012-11-19T11:10:59Z2012-11-19T11:11:00ZPassing result of one command as an input for another<div><p>Really, storing in-between data in models looks like the best
way to perform inter-commands communication. The drawback is that
if I want to clear that data at the end of commands sequence I have
to do it in some other place, not in the command which populated
that models, which sounds like a responsibilities mess for me. I
think, I have to introduce one more rule to our team's conventions:
commands should always produce final and meaningful data (i.e. no
model with unparsed/raw data).</p>
<p>Thanks for you advice, Neil!</p></div>pa3tag:robotlegs.tenderapp.com,2009-10-18:Comment/209102252012-11-19T11:41:51Z2012-11-19T11:41:51ZPassing result of one command as an input for another<div><p>No prob. Clear data in command at end of sequence?</p></div>neiltag:robotlegs.tenderapp.com,2009-10-18:Comment/209102252012-12-04T09:17:39Z2012-12-04T09:17:39ZPassing result of one command as an input for another<div><p>Exactly, what I've finally done. Thanks!</p></div>pa3