This documentation relates to previous versions of Titanium Studio.

To see the latest documentation, visit docs.appcelerator.com.

Skip to end of metadata
Go to start of metadata

Introducing Rubles

Ruble (short for RUby BundLE) is a runtime environment that allows for the extensibility of IDEs and text editors using Ruby. The first implementation of Rubles will be in Studio 3, but we're making the specification available now because we want to get feedback from the community and because we want to encourage the developers of other development tools to adopt the specification as well. To further that end, we are making the specification available in an open manner, with no limits on use or distribution of this specification.

In addition, we are also making the source available for several working Ruble bundles, some of which are conversions of existing open-sourced TextMate bundles, and others are home-grown versions:

Please keep in mind that the specification and the bundles themselves are in active development and are very likely to change.

Design-level compatibility with TextMate bundles was a priority in the design - we wanted to make it reasonably straightforward to convert an existing TextMate bundle into a Ruble bundle, while still allowing commands and snippets to be more concise and more powerful. At the same time, we wanted Ruble bundles to run on Linux and Windows as well as the Mac, so we had to make some changes to deal with issues like key bindings and OS-specific assumptions.

The Ruble runtime includes a ruble.rb file that defines a DSL that can be used to create bundles, commands, and menus. ruble.rb must be included via a require in each file within the Bundle that defines bundles, commands, or snippets.

Bundles

Bundles are the top-level distribution format for Ruble extensions. A bundle directory must contain a bundle.rb at the top level. The bundle.rb file defines the metadata for the bundle and its menu(s). By convention, the name of a bundle directory ends with .ruble suffix but this is not required.

In addition to the bundle.rb file, there are four top-level directories within a bundle that are treated specially:

File/Dir

Description

commands/

contains the commands that make up the bundle.rb files within this directory will be loaded at startup, so you should keep it as minimal as possible to keep things fast.

lib/

contains Ruby code that is on the load path when executing commands within your bundle. Put code here you wish to invoke from within your commands, but which you do not necessarily want loaded at startup.

snippets/

contains the snippets that make up the bundle.rb files within this directory will be loaded at startup, so you should keep it as minimal as possible to keep things fast.

templates/

contains projects templates used by the "New Project Wizard" when creating a new project in Studio

By convention, there are several other files and directories found in most bundles:

File/Dir

Description

README

your usual README file (these also work: README.md, README.markdown, README.textile, and/or README.txt)

Rakefile

your Rakefile, typically used for testing, packaging, and so forth

assets/

images, icons, and other files used by your bundle

test/

test code

Bundle Loading

In Studio 3, Ruble bundles are loaded from the file system in a hierarchical order. The order, from highest to lowest priority, is:

  • Any top-level "bundles" directory in all active projects within the current workspace.
  • The $USER_DOCUMENT_DIRECTORY/Aptana Rubles/, where $USER_DOCUMENT_DIRECTORY is platform dependent.
  • The bundles directory within the Aptana Studio installation

When a bundle is defined in multiple places, only the bundle code defined in the highest priority location is used. In this way, you can easily do a git clone (or the equivalent) on a bundle from within your $USER_DOCUMENT_DIRECTORY/Aptana Rubles and it will override the ones built into Studio itself.

Other implementations are free to keep bundles in whatever locations on disk they desire, but if an implementation does support multiple locations for bundles, it should also use the priority system for determining which bundles are active.

Redefining, Overriding, and Extending Bundles

It is possible for a bundle directory and its members to redefine, to override or to extend another bundle directory's functionality. This is quite handy when you want to add a command or two to the menu of the existing bundle without having to maintain your own clone of a bundle repository.

Before we can discuss these mechanisms, we need to get some terminology out of the way. Below is a list of terms you may encounter when talking about bundles and how they are defined.

Term

Description

Bundle Directory

A directory containing a top-level bundle.rb file and optionally other .rb files defining commands, menus, and snippets as discussed in the Bundles section.

Bundle Name

The name of a bundle derived from its bundle directory name. If the directory name ends with .ruble, the bundle name is the directory name with that suffix removed; otherwise, the bundle name is simply the directory name itself.

Bundle Reference

When defining a bundle in a bundle.rb file, it is possible to include a name. This name refers to an existing bundle and is used as a mechanism to extend and override a bundle of equal or lower precedence.

Bundle Elements

A generic term for any item contained by a bundle: for example, commands, snippets, and menus.

Bundle Definition

A bundle instance created by the bundle method. This is a general term used when discussing bundle instances.

Active Bundle

This is the resulting bundle after all precedence and reference rules have been applied.

A Minimal Bundle

In it's simplest form, a bundle can be defined as follows:

Of course, this bundle is not of much use, but it is important to understand what this minimal example does. We see that the bundle directory is contained within the application bundles directory; therefore, this bundle has the lowest precedence as defined in Bundle Loading. Interpreting this will become clearer shortly. We also know that since the name of the bundle is derived from its bundle directory, the bundle's name is "FunBundle".

Redefining a Bundle

In this next example, we flesh out our bundle with a little skeleton content as well as define a like-named bundle directory at a higher precedence.

We've added an "A" command to our application bundle. We've also defined a bundle with the same name, but this time within the user bundles directory. The user bundles directory has a higher precedence than the application bundles directory. This means that our active FunBundle bundle will contain the "B" command only.

When two bundle directories have the same name, the bundle directory with higher precedence completely obscures the content of the lower precedence bundle directory. This can come in handy, for example, if you need to alter an application bundle. You can simply git clone the bundle into your user bundles directory and make your changes there. Since each bundle directory will have the same name, your user version will hide the default bundle implementation.

Extending a Bundle

In another scenario, you may be perfectly happy with the content of the application bundle, but you need to add another command to it.

This looks almost identical to the previous example; however, it has two very important differences. The first difference you may notice is that the bundle method now takes a string. This string changes this user bundle instance into a bundle reference.

A bundle reference applies its content to a bundle definition of equal or lower precedence and the bundle it modifies is located by the bundle method's string argument, or said another way, the bundle it modifies is located by name. Applying these rules, our active "FunBundle" bundle now contains the two commands "A" and "B". Note that additional bundle references may also augment the "FunBundle" bundle. You are not limited to a single bundle reference.

The second difference is that we've changed the bundle directory name to "FunBundleExtension". The importance of this is a bit tricky to explain. As mentioned earlier, when we use the bundle method without a string argument, the bundle instance's name is derived from the bundle directory name. Bundle's that have names that match their bundle directory's name have special functionality. As seen earlier, that functionality is to completely obscure like-named bundles of lower precedence. If we had not renamed our user bundle's directory, the bundle string argument would have matched the default name of "FunBundle". That would have made this example behave the same as our Redefining a Bundle example. You should *not* use bundle references explicitly when defining a bundle from scratch, only when referring to other bundles within your own bundle.

Overriding a Bundle Element

In our last scenario, perhaps you find that you are happy with the content of the application "FunBundle" bundle, but you want to change the behavior of one command. The following shows how you would go about that.

As you can see, we use the same mechanism we used to extend a bundle; however, this time since we are using the same name of a command that was defined in our target bundle, our active bundle only contains an "A" command and that is the command that is defined in the user bundles directory.

Bundle Properties

Bundles have the following properties, all of which are optional:

Element

Description

display_name

A human-readable name for the command.

description

A longer textual description of the bundle, in Markdown syntax

scope

The default scope for commands and snippets defined within the bundle. See 'Scopes' below.

author

One or more author names

copyright

Self explanatory.

license

Self explanatory.

repository

A URL to the repository or a webpage explaining how to get access

Associating a Bundle with a File-Type

A bundle can be associated with one or more file types. This makes it possible for a bundle to be created that defines a set of commands and snippets for a file type that doesn't have a built-in editor and parser. A future version of this API will also allow the definition of syntax coloring elements, auto-indent rules, and perhaps even the ability to create full fledged language parsers.

Method

Description

register_file_type(file_type,scope)

This is a convenience method that first calls associate_file_type and then registers that file_type to a particular scope by calling associate_scope

associate_file_type(file_type)

Given a filename pattern, we register files that match to our generic text editor (uses our theme and scripting). file_type must be either an exact filename to match, or a *.ext extension to associate with.

associate_scope(file_type,scope)

Registers a top-level scope to report back for any file whose name matches the passed in pattern. Understands * wildcard. Typical values for first arg: .yml, _spec.rb, Rakefile. scope is a string our a symbol. Underscores are converted to spaces (so 'source.yaml' and :source_yaml are equivalent)

Scopes

A scope specifies a context within the IDE. For example, when editing a document, the current cursor position within that document has a context associated with it. These scopes are supplied by the IDE and may be queried using the Shift-Control-P key command.

Our bundle runtime contains an implementation of scopes that is intended to be compatible with TextMate's.

Scope Selectors

A scope selector describes a pattern of scopes and is composed of dotted names, descendant operators, basic set operation operators, and a grouping operator. In its simplest form, a scope selector consists of a single name.

Example - Simple Name Selector

A scope selector needs only to match the prefix of a name to be considered a match. In the above example, the selector will match "text", "text.html", "text.html.ruby"; however, it will not match "texts.physics". A prefix match must end at a dot within the scope or at the end of the scope itself.

Example - Dotted Name Selector

A scope selector name may be extended to create a hierarchy. Each name is delimited by a period without spaces between the names and the periods. The same prefix rule as described in "Simple Name Pattern" applies. For example, in the above snippet, the selector will match "text.html" and "text.html.ruby".

Example - Descendant Selector

A scope may be defined as a hierarchy of scopes. In this sense, scope selectors match against that hierarchy much like CSS matches against the HTML DOM. In the above example, the selector will match when the editor's cursor is within Ruby code which is within HTML. The same matching rules described in the simple name pattern apply to each dotted name. For instance, the last example will match "text.html.ruby source.ruby.embedded.html" since "text.html" is a prefix of "text.html.ruby" and likewise "source.ruby" is a prefix of "source.ruby.embedded.html".

It is important to note that a descendant match can occur anywhere within a scope but the order of the selectors must be matched. For example, if we had a pattern like "a b c", this would match "a b c", "x a b c", "a b c y", "x a b c y", but not "a b x c".

Example - Union Selector

In this example, our selector defines a list of selectors to be used for matching. The comma serves as an OR operator. Specifically, this selector is saying match "text.html.ruby" OR "text.html source.ruby". All matching rules described previously apply; however, if an alternation fails, matching will restart using the next alternation. Matching ends when either an alternation returns success or once all alternations have been exhausted.

Example - Union Selector 2

The pipe operator operator behaves exactly the same as the comma operator. The only difference between the two is in their precedence levels, with the pipe operator having lower precedence than the comma operator. This allows alternations to be expressed more naturally for those of you that are comfortable with C-like expressions.

Example - Intersection Selector

The ampersand or intersection operator matches only when the expressions on both sides of the operator are true. For example, the above "text & source" selector will match "text.html source.ruby" since "text" and "ruby" both match according to the name selector rules.

Example - Grouping

Parentheses can be used to clarify argument of a selector or to group and override operator precedence levels. For example, in the above selector, the parentheses are used to group "js" and "ruby" as part of the intersection with "source". If the parentheses were removed, the effective precedence would be equivalent to (source & js) | (ruby).

Example - Negative Lookahead

Sometimes you want to match a scope unless it is being used within a child scope. For instance, in the above selector, the negative lookahead operator, '-', is being used to select all "text.html" scopes that do not have a matching scope of "source.ruby" anywhere after it. If we had selector "a b - c", this would match "a b", "a b y", "x a b", "x a b y", but not "a b c", "x a b c", "x a b c y", nor "a b x y c". Notice in the last example that the lookahead extends to the end of the scope being matched and not just the next segment as with the descendant selector.

Precedence Levels

The following table lists all of the selector operators, by precedence level, highest precedence first. The higher the precedence, the more tightly the operator binds its operands.

Operator

Name(s)

()

Parentheses, Group

&

Ampersand, Intersection

<space>

Descendant

-

Negative Lookahead, Asymmetric Difference

,

Comma, Or, Union

|

Pipe, Or, Union

Commands

Commands are the fundamental building blocks of Bundles. A command has the following properties:

Element

Required?

Description

name

Yes

A human-readable name for the command. Must be unique within the bundle.

invoke

Yes

The code to be executed. If specified as a string, it is executed as a shell script. If passed a block, it is invoked as a block when the command is executed.

scope

 

The scope in which the command can be executed. If no scope is specified, the command is assumed to be active in all scopes.

input

 

The input to the command. See the Input Definition section below.

output

 

The output of the command. See the Output Definition section below.

working_directory

 

The working directory to use when invoking the command as a shell script. See the Working Directory Definition section below.

key_binding

 

The keyboard binding for the command. It is also possible to define platform-specific key bindings, as described in the Key Bindings section below.

trigger

 

A tab trigger that will invoke the command if typed into an editor window and then the <TAB> key is pressed. It is possible to define both a trigger and a key_binding for a single command.

Example command:

Snippets

Snippets are a specialized form of commands, designed to make it easier to specify the behavior of tab triggers. Snippets have a name, a scope, a trigger, and an expansion. You can think of these two declarations as being equivalent:

Here are the properties specific to snippets. Note that the trigger is required, unlike the case for commands.

Element

Required?

Description

name

Yes

A human-readable name for the command. Must be unique within the bundle.

scope

 

The scope in which the command can be executed. If no scope is specified, the command is assumed to be active in all scopes.

trigger

Yes

A tab trigger that will invoke the command if typed into an editor window and then the <TAB> key is pressed. It is possible to define both a trigger and a key_binding for a single command.

expansion

Yes

The snippet text that will be substituted for the command trigger.

FORTHCOMING: the syntax for the expansion string (which roughly follows TextMate, but doesn't support nesting at present)

Conventions and Definitions

Several properties defined in this document can have a variety of different values, and these types of values are defined by names in ALL_CAPS. Specifiers that are used in multiple places within this document are defined in this section.

PATH_SPECIFIER

Paths can be specified in many places in the API. Whenever you see the term PATH_SPECIFIER, the path can be specified as one of the following:

  • a string specifying either a full path or a relative path. If only a relative path is provided, it will be be interpreted relative to the bundle directory if it is evaluated at command definition time, and relative to the command's working directory if it is evaluated at command invocation time (the latter is more common).
  • an object that will return a path string as defined directly above when its to_s method is called (e.g. File)
  • a Ruby symbol with special meaning. For example, :current_project might refer to the currently loaded project (NOT YET IMPLEMENTED)

INPUT_SPECIFIER

The possible input specifiers are:

Specifier

Description

:selection

selected text in the editor

:left_character

the character to the immediate left of the caret

:right_character

the character to the immediate right of the caret

:word

word surrounding the current caret

:line

the line containing the caret

:document

the entire current document

:clipboard

the contents of the clipboard

:scope

(NOT YET IMPLEMENTED) As in TextMate: search backwards and forwards for the first character which is not matched by the scope selector of the command and use those as boundaries for the input.

:input_from_console

take input from a shell window? How do we specify which console?

:none

no input is needed by this command. When encountered in the multiple symbol specifier case, this symbol always terminates fallback evaluation

:selected_lines

I'm not sure what this does or how it differs from :selection!!!

OUTPUT_SPECIFIER

The possible output specifiers are:

Specifier

Description

:insert_as_text

insert text at the caret position. If there is a selection, the text is inserted immediately following the selection and the selection is lost.

:insert_as_snippet

as with :insert_as_text, but the output is interpreted as snippet expansion text

:replace_selection

replace the currently selected text with the output. If no text is selected, this is equivalent to the :insert_as_text specifier

:replace_document

replace the entire document with the output

:copy_to_clipboard

replace the contents of the clipboard with the output

:show_as_html

open an html browser window and intepret the output as html

:show_as_tooltip

show a tooltip containing the output

:create_new_document

create a new editor document containing the output

:output_to_console

display the output in a console. HOW DO WE SPECIFY WHICH CONSOLE

:discard

throw any output away

:replace_selected_lines

what does this do? probably unnecessary

:replace_line

replace the line around the caret. probably unnecessary

:replace_word

replace the word around the caret. probably unnecessary

WORKING_DIRECTORY

The possible working directory specifiers are:

Specifier

Description

'any string'

the string is interpreted as an absolute path

:current_file

Use the command's parent dir as the working dir. This is the default behavior.

:current_project

Use the active project in the App Explorer as the working directory.

:current_bundle

Use the command's owning bundle's directory as the working directory. Typically this is the parent dir of what you'd get with :current_file

PLATFORM_SPECIFIER

Supported platform identifiers are all, mac, windows, linux, and unix. The unix identifier will match Linux, BSD, Solaris, and most other unix-like OSes (but not Mac OS X). The all specifier is the same as not providing a platform specifier at all.

Bundle.rb

As discussed in the Runtime Architecture section above, the bundle.rb file defines the metadata and menus for the bundle. This section specifies these items in more detail.

Metadata

Bundles have metadata associated with them. The following properties are pre-defined:

Property

Description

name

the user-visible name of the bundle. E.g, "Ruby on Rails". This name will be shown in menus, error messages, and so forth.

description

a longer, textual description of the bundle and its purpose

author

the author(s) of the bundle

copyright

any copyright message associated with the bundle

license

the name or URL of the license associated with the bundle. E.g. "MIT License" or

http://www.gnu.org/licenses/gpl-3.0.txt

website

the website where more information about the bundle can be found

repository

the public repository url where the canonical version of this bundle lives. E.g. git://github.com/aptana/rails.ruble.git

Additional properties can be accessed, though, simply by referring to them as though they already exist, e.g.:

Menus

A bundle may define one or more menus to expose its commands and snippets to the user. Most bundles will only define a single menu. RadRails shows these menus in a number of places. First and foremost, it shows up in the main menu underneath the 'Commands' menu. It also shows up in the right-click context menu of editor windows.

A menu is a container for commands, sub-menus, and separators. Commands are specified by their names, with all other information coming from the command itself (e.g., its key_binding or trigger). As discussed earlier, these names are scoped to the current bundle.

Menus also have a scope, which defines the contexts in which the entire menu is applicable. We interpret the scopes for menus more loosely than we do for commands, erring to show the menu more often than not. And the Commands menu will still show your menu in the "Other" submenu of the Commands menu even if your scope operator doesn't match. Note that while you can define a scope for a sub-menu, but it is currently ignored.

Sample bundle.rb File

The following is a real but extremely minimal bundle.rb file which defines a 'Rails' menu which in turn contains only a single 'Go To' submenu.

Commands and Snippets

As discussed in the Runtime Architecture section above, commands and snippets are the backbone of the bundle runtime. This section lays out the definition and invocation of commands and snippets in more detail.

Key Bindings

A command can optionally have one or more key bindings associated with it. These key bindings are specified using one of the following forms:

If there is only one key binding associated with the command:

Examples:

If there are more than one key bindings associated with the command, an array of key binding sequences can be specified.

Example:

The KEY_SEQUENCE syntax follows the Eclipse conventions, since those are more portable than the ones used in TextMate. A KEY_SEQUENCE should consist of one or more key strokes. Key strokes are separated by spaces. A key stroke consists of one or more keys held down at the same time. This should be zero or more modifier keys, and one other key. The keys are separated by the + character. The recognized modifiers keys are ALT or OPTION, COMMAND, CTRL, and SHIFT. In addition M1, M2, M3, M4 modifier keys are also recognized. The "M" modifier keys are a platform-independent way of representing keys, and these are generally preferred. M1 is the COMMAND key on MacOS X, and the CTRL key on most other platforms. M2 is the SHIFT key. M3 is the Option key on MacOS X, and the ALT key on most other platforms. M4 is the CTRL key on MacOS X, and is undefined on other platforms. Since M2+M3+<Letter> (Alt+Shift+<Letter>) is reserved on MacOS X for writing special characters.

Shortcut

Platform

Key

M1

OS X

COMMAND

M1

Other Platforms

CONTROL (CTRL)

M2

All Platforms

SHIFT

M3

OS X

OPTION

M3

Other Platforms

ALT

M4

OS X

CONTROL (CTRL)

The actual key is generally specified simply as the ASCII character, in uppercase. So, for example F or X, are examples of such keys. However, there are some special keys; keys that have no printable ASCII representation. The following is a list of the current special keys:

Key

...

...

ARROW_DOWN

F1

NUMPAD_0

ARROW_LEFT

F2

NUMPAD_1

ARROW_RIGHT

F3

NUMPAD_2

ARROW_UP

F4

NUMPAD_3

BREAK

F5

NUMPAD_4

BS

F6

NUMPAD_5

CAPS_LOCK

F7

NUMPAD_6

CR

F8

NUMPAD_7

DEL

F9

NUMPAD_8

END

F10

NUMPAD_9

ESC

F11

NUMPAD_ADD

HOME

F12

NUMPAD_DECIMAL

INSERT

F13

NUMPAD_DIVIDE

LF

F14

NUMPAD_ENTER

FF

F15

NUMPAD_EQUAL

NUL

PRINT_SCREEN

NUMPAD_MULTIPLY

PAGE_UP

PAUSE

NUMPAD_SUBTRACT

PAGE_DOWN

SCROLL_LOCK

NUM_LOCK

SPACE

TAB

VT

Alternate names for some common special keys are also allowed. For example, ESC/ESCAPE, and CR/ENTER/RETURN are synonyms.

We strongly recommend against the use of SHIFT or M2 without including another CONTROL_KEY_SPECIFIER, as the results can be unpredictable.

It is possible for commands to define specific key bindings for specific platforms. To enable this, the key_binding can be made platform specific. Platform-specific bindings always take
precedence over the generic binding regardless of declaration order. See the example below:

The allowed specifications are defined in the PLATFORM_SPECIFIER section.

Input Definition

Commands can take their input from a variety of different sources, or a combination of those sources. This makes it very easy to create commands that can process input from editors without writing a bunch of if/then statements within your command.

Inputs can be specified using the single input specifier form:

or the multiple input specifier form:

or the path specifier form:

In the multiple input specifier form, each INPUT_SPECIFIER is processed in order until a valid INPUT_SPECIFIER was found. So if, for example, the command specified:

Then the :selection specifier would be checked to see if it evaluated to a non-empty value. If it did not have such a value, the :word specifier would be checked. If the caret was not within a valid word, then the command would be invoked with a nil input.

Output Definition

As with input, commands may wish to generate output in a number of different ways. In Ruble, output can be specified using the single output specifier form:

or the path specifier form:

As with the input specifier, the path specifier form allows you to define a filename or other stream specifier to write the output to.

Invocation and Context

As described above, ever command must define an invoke property that specifies the actions to perform when the command is run. The action can either be a string, which is run in the user's default shell, or a ruby block, which is executed inline.

In addition, you can specify platform specific invoke properties by specifying the platform definitions:

The allowed platforms are defined in the PLATFORM_SPECIFIER section of this document.

Context During Command Invocation

When a command's invoke property is set a ruby block, that block is passed a context object as an argument. The context object has the following properties:

Property

Description

input

The input to the command as a string. See the Input During Command Invocation section below.

invoked_via

How the command was invoked. Can be :key_binding, :menu, :unknown, :command, or :trigger.

command

The Command object being executed.

bundle

The Bundle in which the command is defined.

scope

The Scope in which the command was executed.

project

The Project that owns the file currently being edited, or nil. See Project API for more information.

editor

The currently active Aptana file editor. See Editor API for more information.

In addition, the context has a few handy-dandy functions for use by commands, as described below:

Method

Description

exit_with_message(message, OUTPUT_SPECIFIER)

Short-circuit the execution of the command, using the specified OUTPUT_SPECIFIER

invoke(name, INVOKE_OPTIONS)

(NOT YET IMPLEMENTED) Invoke another command or snippet by name, and return its context object so that its output and other properties can be extracted.

INVOKE_OPTIONS

You can supply additional options to the invoke method via its INVOKE_OPTIONS hash. These options effect the state of the context object passed to the command when it is invoked:

Key

Description

:input

If specified, the context's input property will be set to this value. This is useful when you want your command to process user input and then have another command further process that input. If it is not defined the INPUT_SPECIFIER in the command's definition will be used as normal.

:output

If specified, this OUTPUT_SPECIFIER overrides the one defined by the command.

:scope

The scope specifier to be passed to the command

:bundle

If specified, look for the command to invoke in the named bundle instead of the current bundle.

Input During Command Invocation

If a command is defined to invoke a shell script, the command supplies the input text on STDIN and as environment variables, for compatibility with TextMate.

If a command is defined to invoke an inline Ruby block, the input is passed to the block via STDIN. Alternately, the context's input property can be used to access the input a simple string. Note that all environment variables that are defined for shell script commands are also available to command blocks via the ENV hash.

Output During Command Invocation

By default, any output to STDOUT during command invocation is treated as the output of the command. If the command is defined to invoke a ruby inline block, the return value may be used to define the command result. The return value will be converted to a string via to_s. In the event output is written to STDOUT and a non-nil value is returned from the block, the return value will take precedence. Or said another way, if you want to make sure your output to STDOUT serves as your command's result, then be sure to return nil as your command's result.

Output written to STDERR during command execution will be displayed in the Console.

Labels
  • None