A Cook's Tour of Limelight
From Limelight
Contents |
Theater Metaphor
The name Limelight came first. It was a fun, dynamic, roll-off-the-tongue type of name. It also contained the word 'light' which, coming from a company named 8th Light, was the clincher.
Early in the development of Limelight I struggled over and over with the design and naming of objects. At first the fundamental building blocks, now knows as "Props", were called "Blocks". And Blocks composed a Page. I know... not very original. The names didn't quite flow. The name "Block" was especially confusing given that the Ruby language has a different meaning of block.
It wasn't until I was implementing the "Controller" concept, now known as "Player", that I hit a wall. I didn't know what to call it when you install a Controller into a Block. So I asked my colleagues Eric Smith, Doug Bradbury, and Paul Pagel, "What verb would use to describe bringing a inanimate block to life?" We tossed around verbs like invigorate, vitalize, spiritualize, posses, etc. At last one of us mentioned, "With a name like Lightlight, illuminate fits." We looked at each other approvingly. The name just seemed to fit.
Like a chain reaction, classes and methods throughout the Ruby code acquired new names. No longer did you build Pages with Blocks and control them with Controllers. Now you build Scenes with Props and animate them with Players. You develop your Scenes and put them together into a Production. Then all you need is a Theater (desktop) with a Stage (window) or two to open your show. These are supplied at runtime. Limelight spawns a Producer to take your Production and bring it to life on your computer's screen.
Directory Structures
There are two variations on directory structure. One is for Single-Scene Productions and another for Multi-Scene Productions.
Single-Scene Production
This directory structure is convenient for simple Productions. Everything is optional. However you'll need at least a root directory and props.rb file to do anything interesting.
+ <production/scene name> (optional) | - props.rb (optional) | - styles.rb (optional) | + players (optional) | - <player_name>.rb (optional) | - init.rb (optional) | - stages.rb (optional) | - production.rb (optional)
Here's a brief overview of each file:
- props.rb - Defines the Prop structure for the Scene.
- styles.rb - Defines the Styles for the Scene.
- players - Directory containing Players for the Scene.
- init.rb - For configuring the Ruby environment and creating needed objects.
- stages.rb - Defines and configures the Stages in the Production.
- production.rb - For configuring the Production.
Multi-Scene Production
This directory structure allows Productions to host multiple Scenes. With multiple Stages, you may display multiple Scenes at the same time.
+ <production/scene name> (required)
| - stages.rb (required)
| - production.rb (optional)
| - init.rb (optional)
| - styles.rb (optional)
| + <scene_name> (required - at least 1)
| props.rb (optional)
| styles.rb (optional)
| + players (optional)
| - <player_name>.rb (optional)
Typically a Production will not contain a props.rb file. For the most part, the various file represent the same thing as in a #Single-Scene Production.
The stages.rb is required to determine which Scene should be loaded on each Stage.
Notice that each Scene has it's own directory and should contains its own props.rb and styles.rb files as well as its own players directory.
The root directory may also contain a styles.rb file that defines Styles shared by all the Scenes in the Production.
Building Props
Props are the fundamental building blocks of a Scene. They form a tree structure where one Prop may contain other Props. In fact, the Scene is itself a Prop that is the root of all Props within it.
PropBuilder DSL
As such a core component, the syntax to define Props must be a simple as possible. Limelight provides a DSL, or simple syntax, for building Props. Every Scene should have a props.rb file that defined its Prop structure using of the PropBuilder DSL syntax. See the following example.
trunk do
branch do
leaf
leaf
end
branch do
leaf
end
end
To create a Prop, you simply name it. In this example the first Prop is named tree. Prop names may contain letters, numbers, and underscores(_) though they may not start with a number.
To nest Props, name the children within the do ... end block of their parent. The do ... end block is not needed if a Prop has no children. In this example, the tree Prop has two child Props named branch and, in turn, each branch has children named leaf.
Ruby code in props.rb
The Ruby savvy reader will notice the above example is Ruby code. The names are methods, via the magic of method_missing that take a Ruby Block or Proc object. As such you may use other Ruby syntax to build Props like so:
10.times do
foo
end
%w{ red orange yellow green blue indigo violet }.each do |color|
color_prop :text => color
end
In this example 10 foo Props are created followed by 7 color_prop Props. This example also demonstrates that each Prop takes an optional Initialization Options hash. In this case the text of each color_prop Prop is being set to the name of a color.
Initialization Options
In addition to text, you may also use the Initialization Options to set a Prop's name, id, styles, individual style attributes, players, events, or any attributes defined by included Players (more on this later). Here's an example.
__ :name => "the_scene" bar :id => "the_bar", :styles => "foo", :background_color => :blue, :players => "button", :widget => "blah" do __ :width => "100%", :height => "50%" __ :border_width => 1, :border_color => :black __ :on_mouse_clicked => "puts 'I was clicked'" end
The above example is setting a bunch of options. Too many to fit on one line. So it makes use of the __ methods to make the code more readable. __ is a convenience method that set options on a Prop. It's the only way to add options to the root Prop (the Scene).
A description of each option is in order:
- :name - Sets the name of the Prop. This should really only be used on the Scene as in the example above.
- :id - Sets the id of a Prop. id's are unique within a Scene while names are not.
- :styles - A space-separated list of Style names. The names used here should refer to Styles defined in
styles.rb. The Prop will inherit style attributes from each Style named. - style attributes - i.e. :background_color, :width, :border_color, etc. These style attributes will be applied directly to the Prop and override the attributes defined in
styles.rb. You should apply minimal styling in theprops.rbfile. - :players - A space-separated list of Player names. Each name refers to a Player in the
playersdirectory that will be included by the Prop. - events - Code that will be evaluated when the specified event is triggered on the Prop. See Prop Events.
- player attributes - Players may define attributes of their own. If they do, and the Prop includes the Player, it's attributes can be added here. In the example above, the
widgetattribute will be set to"blah".
It may be important to note that Initialization Options will not be applied to a Prop until it added to a Scene.
Installable Props
Undoubtedly when building a reasonably sized Production, you'll find yourself duplicating a group of Props. You can avoid duplication by installing Props from shared files.
__install "header.rb" arena do top __install "scene1/middle.rb" bottom end
__install will find the file specified, build the Props, and install them in place. The filename needs to be relative to the root directory of the Production. The installed files should contain PropBuilder DSL syntax.
Sterilized Props
For various reasons a Prop may become sterilized. That means it may not have any more children. Text is one reason. A Prop may have text or child Props, but not both. If you try to add child Props to a Prop with text, you'll get an error. Likewise, if you try to set text on a Prop that already has children, you'll get an error.
There are other instances where a Prop will become sterilized. Including a Button Player for example. The Button Player requires that a Prop not have any children so it sterilizes any Prop it plays. The same holds true the other Input Players.
You need not worry much about sterilization. Limelight will tell you if a sterilized Prop is violated.
Styling
Styles are the mechanism by which you make your Production look good. Props dictate a Scene's structure while Styles describe its appearance. This separation of concerns adheres to well established design principles. If you compare the concept to website development, Props fulfill the role of HTML and Styles fulfill the role of CSS. But that's a crumby comparison because HTML and CSS stink. I digress.
A complete list of Style Attributes can be found on the Style Attributes page.
Styling with styles.rb
The majority of styling will be done in your styles.rb files. Defining Styles is common activity that, again, we need a simple syntax for. All the styles.rb files make use of the StylesBuilder DSL. See the example below.
wallace {
width 200
height 100
font_size 20
text_color :red
}
grommit {
background_image "images/dog.png"
float :on
x 123
y 456
}
This example defines two Styles: wallace and grommit. They each specify style attributes, of which their purposes are not important at this time. What is important is the name of the Styles. For every Prop named "wallace" in a Scene, the wallace Style will be applied. Likewise, the grommit Style will be applied to every Prop named "grommit" within the Scene.
The StylesBuilder DSL is Ruby code and Ruby expressions can be used. Despite using numbers and symbols above, all the attribute values will get converted internally to strings.
Hover Styles
It's commonly desirable for a Prop to change its appearance when the mouse hovers over it. Not a problem.
cheese {
background_color :yellow
hover {
border_width 1
border_color :black
}
}
Now any Prop using the cheese style will display a black border when hovered by the mouse.
Styling in props.rb
You may set style attributes in the PropBuilder DSL like so:
optimus :background_color => :red, :height => "100%", :width => 200, :font_size => 12
It can be convenient to set style Props using this technique but doing so is generally frowned upon. Use the styles.rb files for the majority of styling.
You may also specify Styles that a Prop should make use of.
megaton :styles => "metallic boxy"
Assuming that the metallic and boxy Styles are defined in styles.rb, both styles will be applied to the Prop. Both Styles will be added to the bottom of the Prop's Style Stack (described below) in the order they are mentioned.
Styling in Ruby Code
Styling in Ruby code is simple. You just need a Style object on which to set attributes. Probably the most common way to do this is in the Player modules. Let's say you wanted a Prop to changes it background color to black when it's clicked...
def mouse_clicked(e) self.style.background_color = :black self.style.text_color = :white end
Or maybe you wanted to change the text color of every Prop using the blue_light Style. You can access all the named Styles in the Scene and change them.
def mouse_clicked(e) scene.styles["blue_light"].text_color = :blue end
Style Structure
Put simply, a Style is a set of string attributes. There are roughly 50 different Style attributes, a complete list of which can be found on the Style Attributes page.
Styles are stacked. Every Prop has its own personal Style object which is on the top of its Style Stack. When a named Style, usually from styles.rb, is applied to a Prop, it is added to the bottom of the Prop's Style Stack. Props may have unlimited Styles.
When a Prop looks for one of its Style attributes, it starts at the top of the stack. If it finds the attribute, it stops searching. Otherwise it continues down the stack until it finds the desired attribute or reaches the bottom of the stack. If a style attribute is not found, the default value is used.
It's important to note this precedence of styling. Setting a style attribute directly on a Prop (prop.style.width = 10) will override any other styling done to the Prop. Ant the order in which Style objects are applied to a Prop will determine subsequent precedence.
A Prop's hover style works slightly differently. It is applied to the top of the stack when the mouse hovers over the Prop.
Players
Players are the life of a Limelight Production. A Prop by itself is an inanimate, unexciting object. When a Prop includes a Player it becomes a dynamic, interactive beast.
Creating Players
Players live in the players directory of a Scene. Let's say we have a Player named Kumquat. It would live in a the file players/kumquat.rb and define a Ruby module named Kumquat.
module Kumquat
def self.extended(prop)
# initialization
end
# behavior
end
When a Prop includes a Player, it will acquire all the behavior defined within the Player. That is, every method and attribute defined in the Player module will be added to the including Prop object. So if the Kumquat Player defines the method is_ripe?, you may call the method like this: prop.is_ripe? on any Prop including the Kumquat Player. This is achieved using Ruby's Object#extend method.
When a Player is included, it is often desirable to initialize the Prop for one reason or another. By implementing the self.extended(prop) method on the Player module, you can effectively initialize any Prop including the Player.
There are a set of events that a Prop may respond to. The complete list can be found on the Prop Events page. Implementations for these events are often added to Players.
Assigning Players to Props
The simplest way to assign Players to Props is to name them the same. When a Prop is illuminated (added to a Scene), Limelight will search the players directory for a file with the same name, followed by the .rb extension, defining a module by the same name. When found, the Player will automatically be included into the Prop.
You may assign Players using the Initialization Options.
kumquat :players => "fruit citrus"
In this case, the kumquat Prop will include the fruit and citrus Players, assuming they both exist in the players directory.
Adding Behavior Without Players
A Prop's Initialization Options may contain behavior for the various events of the Prop. See Prop Events for a complete list of events. To define behavior this way, add an option to the Initialization Options hash using the name of the event prefixed with on_.
kumquat :on_mouse_clicked => "puts 'kumquat clicked'"
Built-In Players
Several Built-In Players provide commonly desired behavior. They are:
- button - gives the Prop the look and feel of a native button
- check_box - gives the Prop the look and feel of a native check box
- combo_box - gives the Prop the look and feel of a native combo box
- radio_button - gives the Prop the look and feel of a native radio button
- text_area - gives the Prop the look and feel of a native text area
- text_box - gives the Prop the look and feel of a native text box
See the | Players RDoc for more information on these built-in Players.
Configuring Stages
A Limelight Production may have one or many Stages. Each Stage may have a name, size, location, and may display any Scene. Remember, Stages represent windows on the desktop. Stages within a Production are defined and configured in the stages.rb file. Here's an example:
stage "inspector" do default_scene "inspector" title "Limelight Composer Inspector" location [0, 0] size [300, 800] end stage "viewer" do title "Limelight Composer Viewer" location [350, 0] size [800, 800] end
This example specifies two stages. The first is named "inspector". Its default_scene is set to "inspector" which means that when the Production is loaded, the Scene named "inspector" will be loaded into this Stage. Its title "Limelight Composer Inspector" will be visible on the top of the window. It will appear at the top left corner of the screen, have a width of 300 pixels, and a height of 800 pixels. The second Stage is similarly configured; however, it doesn't have a default_scene. This will cause the Stage to appear when the Production is opened, but it will be blank until a Scene is loaded.
The content of the stages.rb file is Ruby code using the StageBuilder DSL. Within the stage block you may set any attribute of the Stage object (See the | Stage RDoc), however, there are no attributes not demonstrated in the above example.
Configuring The Production
The ProductionBuilder DSL is the simplest of all. It is used to configure a Production and belongs in the production.rb file in the root of the directory structure. It has two functions.
- Give the Production a name. This is used to identify a Production seeing as there may be several Productions running at once.
- Add attributes to the Production. The Production is accessible by all the Props and Players so it's a convenience place to keep resources.
name "Stage Composer" attribute :controller attribute :inspector
Configuring the Production is the very first action taken when Limelight opens a Production.
Initializing The Production
In the root of your Production directory, you may optionally include an init.rb file. This files contains regular Ruby code, no DSL. Limelight will load init.rb immediately after configuring the Production. init.rb is a great place to:
- append search paths to
$: - require Ruby files or Gems
- create key business logic objects used in the Production
- set values on the attributes of the Production
- open needed resources
- anything else that has to be done before your Production opens.

