LimeLight at RailsConf 2008 1
Back at RubyConf 2007 I prepared a 1 minute presentation, well… more of a teaser, about an application framework called LimeLight.
What is it? LimeLight is a selfish dream of mine. In a nutshell it’s a light weight ruby framework for building rich client applications. To explain further, know this. I hate building web applications. Not because they’re hard to build or anything silly like that. It’s because they’re so perverted. Writing web apps makes me feel dirty; as though I’ve sunk into a pit of waste and decay where the foundation of my work is a pool of sludge. No matter how hard I may try, the very nature of modern web apps taints my code and leaves me a sour, grumpy developer.
To understand what I mean, consider the trivial little widget on the right here. Try clicking the button and watch the light blink. Simple huh? Can you count the number of languages/technologies used in implementing this widget? And don’t forget the code required on the server side…..
I count 5. That is, in most cases this widget would require about 5 or more different languages. Let’s count. HTML of course. CSS to make it look right. JavaScript. That’s 3, but in most cases you’ve got server-side code which, if you’re lucky, involves Ruby and ERB. Think about it. You need to know 5 difference languages to build that silly widget. Yikes!
I’ll include the code below. Know that I’ve made every effort to make this code as clean and simple as possible. Still, I would need to borrow your hands and feet to count all the things I find distasteful about it. Have a close look. Ask yourself, “Couldn’t there be an easier way to do this?” I say there is.
If you’d like to learn more, I’ll be presenting on the topic at RailsConf 2008. Or you can come back this this blog site later. I’ll be sure to post any exciting progress.
<div style="border: 1px solid blue; width: 100px; height: 100px;
text-align: center; background-color: white;
float: right;">
<div id="light"
style="border: 1px solid black; width: 50px;
height: 35px; margin: 10px 24px 10px 24px;
background-color: red; text-align: center;
padding-top: 15px;">
Stop
</div>
<input id="button" type="submit"
value="Start" onclick="stopOrGo();"/>
<script type="text/javascript">
function stopOrGo() {
var button = document.getElementById('button');
var light = document.getElementById('light');
if(button.value == 'Start') {
start(button, light);
}
else {
stop(button, light);
}
}
function stop(button, light) {
light.style.backgroundColor = "red";
light.innerHTML = "Stop";
button.value = "Start";
}
function start(button, light) {
light.innerHTML = "Go!";
button.value = "Stop";
blink();
}
function blink() {
var light = document.getElementById('light');
if(light.innerHTML == "Go!")
{
if(light.style.backgroundColor == "green") {
light.style.backgroundColor = "lightgrey";
}
else {
light.style.backgroundColor = "green";
}
setTimeout("blink()", 500);
}
}
</script>
</div>
Micah's General Guidelines on Ruby require 6
Ruby files have to require other files. There’s not avoiding it. Techniques to manage require statements are numerous and varied. Having tried most of them, I’ve found a system that works well for me. What follows are the guidelines I use to manage Ruby require statements.
1. Establish a Convenient Search Path
Although it’s possible to use absolute paths or complex relative paths such as below,
require "/Users/micahmartin/Projects/ttt/lib/game"
require File.dirname(__FILE__) + "../../../lib/ai/winner"
this should be avoided as much as possible. Otherwise you’ll be fixing dozens of require statements every time you move a file. It’s no fun. You’ll want your requires to look like this:
require "game"
require "ai/winner"
To achieve this, add your lib directory to the ruby search path.
$: << File.expand_path(File.dirname(FILE) + “/../lib”))
This will have to go in one of the first files that gets loaded. If you’ve got a standalone Ruby app, you could add this to the startup script. In a Rails app, it can go in environment.rb. If you’re using RSpec, you may want to add it to spec_helper.rb.
2. Independent Requiring over Require Farms
In some projects, you’ll find files that contain nothing but require statements. It’s common in gem projects. The advantage is that users of the library need only require one file which in turn requires everything else you’ll need. Convenient huh? Sure is. But there are 2 major consequences of this:
- Maintenance Mayhem - Every time you add, delete, or rename a file you have to remember to update the require farm file. It’s easy to forget. And the order of requires can get very hard to manage especially if you end up with cyclic dependancies.
- Drowning in Dependancies - Require Farms have a tendency to require more than you want. If there’s only a portion of the library you’d like to use, with the Require Farm you get the whole thing. The extra dependancies will consume more memory at run time and they may add undesired behavior to your system. In general, this is a violation of the Dependancy Inversion Principle.
Rather than build Require Farms, allow each file to be responsible for it’s own dependancies. Let each file require the files it needs to work. This approach can be a bit annoying as you’ll find your self using altogether more require statements, but it’ll pay off in the long run. With each file independently managing it’s dependancies, the system will be easier to maintain and it’s components more reusable.
3. Building Absolute Paths
Try as you may, you can’t always avoid using absolute file paths in your require statements. The following is reliable way to refer to other files.
File.join(File.expand_path(File.dirname(__FILE__)), "..", "spec_helper")
Let me break it down. First:
File.dirname(__FILE__)
This gives you the directory of the current file that is being executed. However, you never know what you’re gonna get since the form of the path is based on how the program was executed. This might give you an absolute path or it might give you a relative path from anywhere on the system. In some cases, Ruby won’t do what you expect with relative paths so it’s best to expand this into an absolute path.
File.expand_path(File.dirname(__FILE__)
Now you’ve got an absolute file path and you just have to add a relative path to the desired file. Normally I’d do this:
File.expand_path(File.dirname(__FILE__) + "../spec_helper")
However, this is not quite portable since I’m using forward slashes. To get this to work on any system independent of file separator, use File.join as shown above. Use this technique to require files when you don’t have your search path configured.
Happy Requiring!
Ruby DSL Blocks 3
There’s a common pattern I’ve seen for developing DSLs (Domain Specific Language) in Ruby. It’s used in RSpec, the Statemachine Gem, and Unclebob’s Clean Code talk at RailsConf 2007. I haven’t seen a name for this pattern so I’ll call it the DSL Block Pattern.
RSpec
describe "Bowling Game" do
it "should score 0 on a gutter game" do
game = Game.new
20.times { game.roll(0) }
game.score.should eql(0)
end
end
Statemachine
sm = Statemachine.build do
trans :locked, :coin, :unlocked
trans :locked, :pass, :locked
trans :unlocked, :pass, :locked
trans :unlocked, :coin, :unlocked
end
Parser
parser = Args.expect do
boolean "l"
number "p"
string "d"
end
Here’s the problem. You’ve got to write code for specific domain such as writing specifications (RSpec), defining a Statemachine, or defining command line arguments (Unclebob’s Clean Code talk). These domains have a contained and well defined terminology set. Often the cleanest, most elegant way to express this code is to create a DSL.
Before diving into the example, let me say that I like coffee as much as the next guy. But I feel lost when ever I go to a Starbucks. As you know, Starbucks has a it’s own language, DSL if you will, for ordering coffee. What follows is a DSL Block for ordering Starbucks coffee.
The general grammar for ordering coffee is: Size, Adjective (optional), Type of Coffee. This is by no means comprehensive but it’s sufficient for the example. So if you wanted to order a large coffee, for example, you would say, Grande Coffee. A small espresso: Short Americano. An extra large mixture of regular and decaffeinated coffee with some half and half: Venti Breve Half Caff.
Given the task to code these coffee orders, I’d like to be able to code it like this:
Starbucks.order do
grande.coffee
short.americano
venti.breve.half_caff
end
Ok that looks good, but as you look closely, you’ll start to wonder about those methods, grande, short, and venti “Do they have to be defined on the Kernel?” you may ask. Defining them on the Kernel is a scary prospect. And that may convince you to clutter the syntax by passing an object into the block like this:
Starbucks.order do |order|
order.grande.coffee
order.short.americano
order.venti.breve.half_caff
end
This would allow you to define the grande, short, and venti methods on the object passed into the block. Although you do need an object where grande, short, and venti will be defined, you don’t need to add an argument to the block. You’ll find code out there, such as Migrations, that uses this less optimal route. It’s not necessary. The trick to get rid of the argument is below:
module Starbucks
def self.order(&block)
order = Order.new
order.instance_eval(&block)
return order.drinks
end
class Order
attr_reader :drinks
def initialize
@drinks = []
end
def short
@size = "small"
return self
end
def grande
@size = "large"
return self
end
def venti
@size = "extra large"
return self
end
def coffee
@drink = "coffee"
build_drink
end
def half_caff
@drink = "regular and decaffeinated coffee mixed together"
build_drink
end
def americano
@drink = "espresso"
build_drink
end
def breve
@adjective = "with half and half"
return self
end
private
def build_drink
drink = "#{@size} cup of #{@drink}"
drink << " #{@adjective}" if @adjective
@drinks << drink
@size = @drink = @adjective = nil
end
end
end
You can see that the Order object is doing all the work. It’s got the responsibility of interpreting the DSL, so let’s call it the Interpreter Object. The Module::order method simply creates an instance of Order and calls istance_eval on it. This causes the block to execute using the binding of the Order instance. All of the methods on Order will be accessible to the block.
The Interpreter Object can do any number of things as it interprets the DSL. In this case it simply generates a translation for Starbucks newbies. But, the sky’s the limit really.