Skip to end of metadata
Go to start of metadata


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 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:




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.


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.


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.


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:




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


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


images, icons, and other files used by your bundle


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.



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:




A human-readable name for the command.


A longer textual description of the bundle, in Markdown syntax


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


One or more author names


Self explanatory.


Self explanatory.


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.




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


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.


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)


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.




Parentheses, Group


Ampersand, Intersection




Negative Lookahead, Asymmetric Difference


Comma, Or, Union


Pipe, Or, Union


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






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



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.



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



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



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



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



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



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 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.






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



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



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.



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.


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)


The possible input specifiers are:




selected text in the editor


the character to the immediate left of the caret


the character to the immediate right of the caret


word surrounding the current caret


the line containing the caret


the entire current document


the contents of the clipboard


(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.


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


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


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


The possible output specifiers are:




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


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


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


replace the entire document with the output


replace the contents of the clipboard with the output


open an html browser window and intepret the output as html


show a tooltip containing the output


create a new editor document containing the output


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


throw any output away


what does this do? probably unnecessary


replace the line around the caret. probably unnecessary


replace the word around the caret. probably unnecessary


The possible working directory specifiers are:



'any string'

the string is interpreted as an absolute path


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


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


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


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.


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.


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




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


a longer, textual description of the bundle and its purpose


the author(s) of the bundle


any copyright message associated with the bundle


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


the website where more information about the bundle can be found


the public repository url where the canonical version of this bundle lives. E.g. git://

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


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:


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


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.








Other Platforms



All Platforms






Other Platforms





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:





























































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:




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


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


The Command object being executed.


The Bundle in which the command is defined.


The Scope in which the command was executed.


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


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:



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.


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:




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.


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


The scope specifier to be passed to the command


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.

  • No labels