[Ann] Commander. Command pattern library
Hi. I am glad to announce Commander library which implements command pattern based on first class objects. In Commander every application action is implemented as separate class with #execute method and all state required for execution. Commands are reusable objects and applications provide various ways to access them: shortcuts, context menu, buttons, etc.. This information is attached to command classes as activator objects. Currently there are three types of activators: - CmdShortcutCommandActivator - CmdContextMenuCommandActivator - CmdDragAndDropCommandActivator Activators are declared in command class side methods marked with pragma #commandActivator. For example following method will allow RenamePackageCommand to be executed by shortcut in possible system browser: RenamePackageCommand class>>packageBrowserShortcutActivator <commandActivator> ^CmdShortcutCommandActivator by: $r meta for: PackageBrowserContext And for context menu it will be: RenamePackageCommand class>>packageBrowserMenuActivator <commandActivator> ^CmdContextMenuCommandActivator byRootGroupItemFor: PackageBrowserContext Activators are always declared with application context where they can be applied (PackageBrowserContext in example). Application should provide such contexts with information about application state. Every widget can bring own context to interact with application as separate tool. For example system browser shows multiple panes which provide package context, class context and method context. And depending on context browser shows different menu and provides different shortcuts. For more details look at my blog http://dionisiydk.blogspot.fr/2017/04/commander-command-pattern-library.html and read docs here <https://ci.inria.fr/pharo-contribution/view/Books/job/PharoBookWorkInProgres...> . Best regards, Denis
Was is really necessary to introduce pragmas here? Alexandre
On Apr 11, 2017, at 11:49 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
Hi.
I am glad to announce Commander library which implements command pattern based on first class objects. In Commander every application action is implemented as separate class with #execute method and all state required for execution.
Commands are reusable objects and applications provide various ways to access them: shortcuts, context menu, buttons, etc.. This information is attached to command classes as activator objects. Currently there are three types of activators:
- CmdShortcutCommandActivator - CmdContextMenuCommandActivator - CmdDragAndDropCommandActivator
Activators are declared in command class side methods marked with pragma #commandActivator. For example following method will allow RenamePackageCommand to be executed by shortcut in possible system browser:
RenamePackageCommand class>>packageBrowserShortcutActivator <commandActivator> ^CmdShortcutCommandActivator by: $r meta for: PackageBrowserContext
And for context menu it will be:
RenamePackageCommand class>>packageBrowserMenuActivator <commandActivator> ^CmdContextMenuCommandActivator byRootGroupItemFor: PackageBrowserContext
Activators are always declared with application context where they can be applied (PackageBrowserContext in example). Application should provide such contexts with information about application state. Every widget can bring own context to interact with application as separate tool. For example system browser shows multiple panes which provide package context, class context and method context. And depending on context browser shows different menu and provides different shortcuts.
For more details look at my blog http://dionisiydk.blogspot.fr/2017/04/commander-command-pattern-library.html and read docs here.
Best regards, Denis
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
Just in case of: I do not want my email to be read as I did not intent to. You are doing a great work Denis. Keep going! Alexandre
On Apr 11, 2017, at 12:34 PM, Alexandre Bergel <alexandre.bergel@me.com> wrote:
Was is really necessary to introduce pragmas here?
Alexandre
On Apr 11, 2017, at 11:49 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
Hi.
I am glad to announce Commander library which implements command pattern based on first class objects. In Commander every application action is implemented as separate class with #execute method and all state required for execution.
Commands are reusable objects and applications provide various ways to access them: shortcuts, context menu, buttons, etc.. This information is attached to command classes as activator objects. Currently there are three types of activators:
- CmdShortcutCommandActivator - CmdContextMenuCommandActivator - CmdDragAndDropCommandActivator
Activators are declared in command class side methods marked with pragma #commandActivator. For example following method will allow RenamePackageCommand to be executed by shortcut in possible system browser:
RenamePackageCommand class>>packageBrowserShortcutActivator <commandActivator> ^CmdShortcutCommandActivator by: $r meta for: PackageBrowserContext
And for context menu it will be:
RenamePackageCommand class>>packageBrowserMenuActivator <commandActivator> ^CmdContextMenuCommandActivator byRootGroupItemFor: PackageBrowserContext
Activators are always declared with application context where they can be applied (PackageBrowserContext in example). Application should provide such contexts with information about application state. Every widget can bring own context to interact with application as separate tool. For example system browser shows multiple panes which provide package context, class context and method context. And depending on context browser shows different menu and provides different shortcuts.
For more details look at my blog http://dionisiydk.blogspot.fr/2017/04/commander-command-pattern-library.html and read docs here.
Best regards, Denis
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
2017-04-11 17:34 GMT+02:00 Alexandre Bergel <alexandre.bergel@me.com>:
Was is really necessary to introduce pragmas here?
In short: Commander needs pragma to query methods for command lookup. What alternative you could suggest? In details: Commander caches all activators for each command class. It searches all methods which supply them using pragma and store all retrieved instances in class variable. To lookup required commands users enumerate cached activators declared for given context: activatorClass allDeclaredFor: aToolContext do: blockWithActivator This process is fast and not produces any garbage. Also activation methods are supposed to be packaged together with application which provide context for them. They can be in different package than commands. Commands are reusable and can be extended by multiple applications with different set of activation methods. For example in Calypso I will extract separate SystemCommands package which can be reused by other tools like inspector and debugger. And Calypso will "attach" these commands by own set of extension methods.
Was is really necessary to introduce pragmas here?
In short: Commander needs pragma to query methods for command lookup. What alternative you could suggest?
I would imagine that your activatorClass can lookup the command simply as: PackageCommand allSubclasses select: [ :c | c isEnabledFor: ⦠] Would this not work? I am personally not keen of using pragma. The worth is when you have no idea what a pragma does and looking for implementors or sender does not help. Here is an example: -=-=-=-=-=-=-=-=-=-=-=-= RadioButtonModel>>canDeselectByClick <api: #inspect> "Return true clicking on a selected radio button deselects it" ^ canDeselectByClick value -=-=-=-=-=-=-=-=-=-=-=-= No idea what is the effect of the #api: pragma. There is no implementor of it. I could also the example of the Fame metamodel. Anyway, the fact that I am kind of complaining should not prevent you from moving ahead. I would be happy having Calypso in the image, even if pragmas. Go go go! Cheers, Alexandre
In details:
Commander caches all activators for each command class. It searches all methods which supply them using pragma and store all retrieved instances in class variable.
To lookup required commands users enumerate cached activators declared for given context: activatorClass allDeclaredFor: aToolContext do: blockWithActivator This process is fast and not produces any garbage.
Also activation methods are supposed to be packaged together with application which provide context for them. They can be in different package than commands. Commands are reusable and can be extended by multiple applications with different set of activation methods. For example in Calypso I will extract separate SystemCommands package which can be reused by other tools like inspector and debugger. And Calypso will "attach" these commands by own set of extension methods.
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
On Wed, Apr 12, 2017 at 6:28 AM, Alexandre Bergel <alexandre.bergel@me.com> wrote:
Was is really necessary to introduce pragmas here?
In short: Commander needs pragma to query methods for command lookup. What alternative you could suggest?
I would imagine that your activatorClass can lookup the command simply as:
PackageCommand allSubclasses select: [ :c | c isEnabledFor: ⦠]
Would this not work? I am personally not keen of using pragma. The worth is when you have no idea what a pragma does and looking for implementors or sender does not help.
The other way to look at it is we need to improve implementor/sender lookups around pragmas (speaking very generically. I don't know what the gaps are.) cheers -ben
Here is an example: -=-=-=-=-=-=-=-=-=-=-=-= RadioButtonModel>>canDeselectByClick <api: #inspect> "Return true clicking on a selected radio button deselects it"
^ canDeselectByClick value -=-=-=-=-=-=-=-=-=-=-=-=
No idea what is the effect of the #api: pragma. There is no implementor of it. I could also the example of the Fame metamodel. Anyway, the fact that I am kind of complaining should not prevent you from moving ahead. I would be happy having Calypso in the image, even if pragmas.
Go go go!
Cheers, Alexandre
In details:
Commander caches all activators for each command class. It searches all
methods which supply them using pragma and store all retrieved instances in class variable.
To lookup required commands users enumerate cached activators declared
for given context:
activatorClass allDeclaredFor: aToolContext do: blockWithActivator This process is fast and not produces any garbage.
Also activation methods are supposed to be packaged together with application which provide context for them. They can be in different package than commands. Commands are reusable and can be extended by multiple applications with different set of activation methods. For example in Calypso I will extract separate SystemCommands package which can be reused by other tools like inspector and debugger. And Calypso will "attach" these commands by own set of extension methods.
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
2017-04-12 0:28 GMT+02:00 Alexandre Bergel <alexandre.bergel@me.com>:
I would imagine that your activatorClass can lookup the command simply as:
PackageCommand allSubclasses select: [ :c | c isEnabledFor: ⦠]
This approach restricts command implementation and queries to single hierarchy which is not scale. For example in Calypso there is ShowClassRefsCommand which is shown as toolbar button in both system and method browsers. But this command is in ClassCommand hierarchy because it operates on collection of classes. We can lookup both MethodCommands and ClassCommands in method browser. But then we will have a lot of commands which we do not need in method browser. Another example is "browse" command which is accessed by cmd+b and from context menu of any browser pane. Here is how they all connected to Calypso browsers: ClyShowClassRefCommand class>>systemBrowserToolbarActivator <commandActivator> ^ClyToolbarCommandActivator byItemOf: ClyButtonToolbarGroup for: ClyClassSystemBrowserContext ClyShowClassRefCommand class>methodBrowserToolbarActivator <commandActivator> ^ClyToolbarCommandActivator byItemOf: ClyButtonToolbarGroup for: ClyMethodBrowserContext ClySpawnSystemBrowserCommand class>>browserShortcutActivator <commandActivator> ^CmdShortcutCommandActivator by: $b meta for: ClyBrowserContext ClySpawnSystemBrowserCommand class>>browserMenuActivator <commandActivator> ^CmdContextMenuCommandActivator byRootGroupItemFor: ClyBrowserContext (ClyBrowserContext is superclass of any Calypso browser contexts)
I see. But why not to let the class subscribe to what it needs? Maybe something like: -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Command subclass: #ClyShowClassRefCommand ClyShowClassRefCommand class>>registerFor: aCommandActivator aCommandActivator add: self for: ClyButtonToolbarGroup. aCommandActivator add: self for: ClyBrowserContext. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= This is just a suggestion. Alexandre -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
On Apr 12, 2017, at 6:54 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-04-12 0:28 GMT+02:00 Alexandre Bergel <alexandre.bergel@me.com <mailto:alexandre.bergel@me.com>>: I would imagine that your activatorClass can lookup the command simply as:
PackageCommand allSubclasses select: [ :c | c isEnabledFor: ⦠]
This approach restricts command implementation and queries to single hierarchy which is not scale. For example in Calypso there is ShowClassRefsCommand which is shown as toolbar button in both system and method browsers. But this command is in ClassCommand hierarchy because it operates on collection of classes. We can lookup both MethodCommands and ClassCommands in method browser. But then we will have a lot of commands which we do not need in method browser.
Another example is "browse" command which is accessed by cmd+b and from context menu of any browser pane.
Here is how they all connected to Calypso browsers:
ClyShowClassRefCommand class>>systemBrowserToolbarActivator <commandActivator> ^ClyToolbarCommandActivator byItemOf: ClyButtonToolbarGroup for: ClyClassSystemBrowserContext
ClyShowClassRefCommand class>methodBrowserToolbarActivator <commandActivator> ^ClyToolbarCommandActivator byItemOf: ClyButtonToolbarGroup for: ClyMethodBrowserContext
ClySpawnSystemBrowserCommand class>>browserShortcutActivator <commandActivator> ^CmdShortcutCommandActivator by: $b meta for: ClyBrowserContext
ClySpawnSystemBrowserCommand class>>browserMenuActivator <commandActivator> ^CmdContextMenuCommandActivator byRootGroupItemFor: ClyBrowserContext
(ClyBrowserContext is superclass of any Calypso browser contexts)
2017-04-12 15:27 GMT+02:00 Alexandre Bergel <alexandre.bergel@me.com>:
I see. But why not to let the class subscribe to what it needs? Maybe something like:
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Command subclass: #ClyShowClassRefCommand
I guess you mean:
ClyShowClassRefCommand class>>registerFor: aCommandActivator aCommandActivator add: self for: *ClyClassSystemBrowserContext*. aCommandActivator add: self for: *ClyMethodBrowserContext*.
How different applications will extend such registration? Probably they will need multiple version of #registerFor:. And system will need to find them somehow. For example by pragma. No?
Nice work, I like the approach. Did you inspire your work in how Dolphin and VAST implements menu creation, command execution, and so on? I find your solution similar to how Dolphin implements it, where even each Presenter is even queried about each command by using a command query and routing solution. Will you use this in the context of Calypso and friends? Regards! Esteban A. Maringolo 2017-04-11 11:49 GMT-03:00 Denis Kudriashov <dionisiydk@gmail.com>:
Hi.
I am glad to announce Commander library which implements command pattern based on first class objects. In Commander every application action is implemented as separate class with #execute method and all state required for execution.
Commands are reusable objects and applications provide various ways to access them: shortcuts, context menu, buttons, etc.. This information is attached to command classes as activator objects. Currently there are three types of activators:
- CmdShortcutCommandActivator - CmdContextMenuCommandActivator - CmdDragAndDropCommandActivator
Activators are declared in command class side methods marked with pragma #commandActivator. For example following method will allow RenamePackageCommand to be executed by shortcut in possible system browser:
RenamePackageCommand class>>packageBrowserShortcutActivator
<commandActivator> ^CmdShortcutCommandActivator by: $r meta for: PackageBrowserContext
And for context menu it will be:
RenamePackageCommand class>>packageBrowserMenuActivator <commandActivator> ^CmdContextMenuCommandActivator byRootGroupItemFor: PackageBrowserContext
Activators are always declared with application context where they can be applied (PackageBrowserContext in example). Application should provide such contexts with information about application state. Every widget can bring own context to interact with application as separate tool. For example system browser shows multiple panes which provide package context, class context and method context. And depending on context browser shows different menu and provides different shortcuts.
For more details look at my blog http://dionisiydk.blogspot.fr/2017/04/commander-command-pattern-library.html and read docs here.
Best regards, Denis
Yes in calypso and friends. On Tue, Apr 11, 2017 at 7:38 PM, Esteban A. Maringolo <emaringolo@gmail.com> wrote:
Nice work, I like the approach.
Did you inspire your work in how Dolphin and VAST implements menu creation, command execution, and so on? I find your solution similar to how Dolphin implements it, where even each Presenter is even queried about each command by using a command query and routing solution.
Will you use this in the context of Calypso and friends?
Regards!
Esteban A. Maringolo
2017-04-11 11:49 GMT-03:00 Denis Kudriashov <dionisiydk@gmail.com>:
Hi.
I am glad to announce Commander library which implements command pattern based on first class objects. In Commander every application action is implemented as separate class with #execute method and all state required for execution.
Commands are reusable objects and applications provide various ways to access them: shortcuts, context menu, buttons, etc.. This information is attached to command classes as activator objects. Currently there are three types of activators:
- CmdShortcutCommandActivator - CmdContextMenuCommandActivator - CmdDragAndDropCommandActivator
Activators are declared in command class side methods marked with pragma #commandActivator. For example following method will allow RenamePackageCommand to be executed by shortcut in possible system browser:
RenamePackageCommand class>>packageBrowserShortcutActivator
<commandActivator> ^CmdShortcutCommandActivator by: $r meta for: PackageBrowserContext
And for context menu it will be:
RenamePackageCommand class>>packageBrowserMenuActivator <commandActivator> ^CmdContextMenuCommandActivator byRootGroupItemFor: PackageBrowserContext
Activators are always declared with application context where they can be applied (PackageBrowserContext in example). Application should provide such contexts with information about application state. Every widget can bring own context to interact with application as separate tool. For example system browser shows multiple panes which provide package context, class context and method context. And depending on context browser shows different menu and provides different shortcuts.
For more details look at my blog http://dionisiydk.blogspot.fr/2017/04/commander-command- pattern-library.html and read docs here.
Best regards, Denis
2017-04-11 19:38 GMT+02:00 Esteban A. Maringolo <emaringolo@gmail.com>:
Nice work, I like the approach.
Thank's.
Did you inspire your work in how Dolphin and VAST implements menu creation, command execution, and so on?
No. I actually not remember that there were first class commands in Dolphin. I remember kind of CommandDescription with selector/block inside. No hierarchy of commands. But I could be wrong. I was more inspired by OmniBrowser and AltBrowser commands. But there were no activators idea inside. (I never worked with VAST)
I find your solution similar to how Dolphin implements it, where even each Presenter is even queried about each command by using a command query and routing solution.
Will you use this in the context of Calypso and friends?
Yes. And Calypso extends it with specific activators: - ClyToolbarCommandActivator to build middle bar of browser (switch browser to instance/class side are commands) - ClyTableIconCommandActivator to decorate table rows with iconic buttons (to provide method icons like in Nautilus)
participants (5)
-
Alexandre Bergel -
Ben Coman -
Denis Kudriashov -
Esteban A. Maringolo -
Stephane Ducasse