You are here: Blogsphere Contribution
Keep up to date with your favourite Rails bloggers in context.
coterie: A small group of p...
Resentment is like drinking...
“ Resentment is like drinking poison and waiting for the other person to die. ”
Carrie Fisher
Design-first development
Introducing Cramp
Cramp is the latest entry on the ruby web frameworks list. However, unlike all the others, Cramp is an asynchronous framework, always running inside EventMachine reactor loop. Cramp isn’t a good fit for most of the web applications out there. However, Cramp is good at holding and working with a large number of open connections. Hence it’ll work great for things like comet, long polling, streaming API or even when your application needs to handle thousands of concurrent connections.
This article assumes that you’re aware with the evented programming model. If you are not, things below this point might not make much of a sense. If you’re interested in learning, you could start by reading about EventMachine and Twisted.
As Cramp requires 3.0-pre versions of ActiveSupport and ActiveModel, you’ll have to install them first. This step will be irrelevant after Rails 3 gets released. But for now, the following should install them both :
gem install activemodel --pre
And then,
gem install cramp
That’ll install Cramp gem along with all the needed dependencies. Please note that Cramp depends on ActiveSupport 3.0-pre gem, which isn’t backwards compatible and this may affect any other gems requiring ActiveSupport without specifying the version number.
Cramp comes with two layers : Controller & Model.
Cramp::Controller is an asynchronous controller layer, that tries being rack compliant as much as possible. Currently, you must use thin in order to run it, as it’s one of the very few webservers capable of running an async rack app – happy to accept patches if someone can get this running on Rainbows!
Here’s the “hello world” of Cramp::Controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
require 'rubygems' require 'cramp/controller' class WelcomeAction < Cramp::Controller::Action on_start :send_hello_world def send_hello_world render "Hello World" finish end end Rack::Handler::Thin.run WelcomeAction, :Port => 3000 |
Now run it directly :
ruby welcome_action.rb
Here WelcomeAction is the rack endpoint, which you can use with config.ru or what have you.
Now let’s dig dipper to understand the code snippet above.
When serving a request, a Cramp::Controller::Action object goes through the following four stages :
To hook into each of these states, Cramp::Controller::Action provides the following callbacks/methods :
before_start callback provides hook into the initialization stage. Each before_start callback accepts a block and must call yield ( or block.call – whichever is appropriate ) or halt. Here’s what a typical usage looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
before_start :verify_id, :find_user def verify_id if params[:id] !~ /\A\d+\z/ halt 500, {'Content-Type' => 'text/plain'}, "Invalid ID" else yield end end def find_user User.where(User[:id].eq(params[:id])).first do |user| if @user = user yield else halt 404, {}, "User not found" end end end |
As you can see above, halt takes status, headers and body as parameters and sends them to the client. It would also halt the callback chain and the request itself. Please note that on_finish callbacks will not get run when you halt. Calling yield will continue the filter chain or enter the next stage if no filters are left to run.
After the before_start stage, Cramp::Controller::Action enters the next stage of Response Initialization. It’ll call the method respond_with, which must return an array of [status, headers]. If this method is not defined, [200, {‘Content-Type’ => ‘text/html’}] status and headers will be used by default.
Here’s an example of using respond_with to send custom status and headers :
1 2 3 4 |
def respond_with content_type = params[:format] == 'xml' ? 'application/xml' : 'application/json' [200, {'Content-Type' => content_type}] end |
A deferrable body is also sent out along with these headers.
After all the verifications and sending out headers, the real work starts. on_start callbacks provide multiple entry points into the Starting stage. These callbacks can send any body to the client using render() method or finish the request by invoking finish(). Note that the render() method can be called any number of times.
Here’s a full example imitating running two long running sql queries from on_start callbacks:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
require 'rubygems' require 'cramp/controller' require 'cramp/model' Cramp::Model.init(:username => 'root', :database => 'arel_development') class SleepingAction < Cramp::Controller::Action on_start :start_sleeping def start_sleeping Cramp::Model.select 'select sleep(1)', method(:on_first_sleep) end def on_first_sleep render "First Sleep Complete...\n" render "Going to sleep one more time now..\n" Cramp::Model.select 'select sleep(1)', method(:on_second_sleep) end def on_second_sleep render "Second Sleep Complete. Time to finish!\n" finish end end Rack::Handler::Thin.run SleepingAction, :Port => 3000 |
Now if you hit this using curl:
[lifo@null ~]$ curl http://0.0.0.0:3000/
First Sleep Complete...
Going to sleep one more time now..
Second Sleep Complete. Time to finish!
[lifo@null ~]$
on_finish provides hook into the last stage : Finishing. These callbacks are run when you call finish() from an on_start callback or when the client closes the connection. These callbacks are the perfect place for any cleaning up activities.
Here’s an example of a Cramp::Controller::Action using periodical timer and an on_finish callback.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
require 'rubygems' require 'cramp/controller' require 'cramp/model' class PollUserAction < Cramp::Controller::Action on_start :start_polling on_finish :stop_polling def start_polling # Call find_user every 10 seconds @timer = EventMachine::PeriodicTimer.new(2) { find_user } end def find_user User.first {|u| render "#{u.inspect}\n" } end def stop_polling puts "Cancelling the timer.." @timer.cancel end end Rack::Handler::Thin.run PollUserAction, :Port => 3000 |
In the case above, if you don’t call @timer.cancel, it’ll keep running even after the client closes the connection. The following section will cover the helper method provided by Cramp::Controller for the above pattern called periodic_timer.
The on_finish callback example above has a very common pattern : Starting periodic timers using on_start and cleaning them up using on_finish. Cramp::Controller provides a better alternative : periodic_timer
Using periodic_timer to rewrite the example above :
1 2 3 4 5 6 7 |
class PollUserAction < Cramp::Controller::Action periodic_timer :poll_user, :every => 2 def poll_user User.first {|u| render "#{u.inspect}\n" } end end |
And then Cramp::Controller will take care of starting and stopping EventMachine::PeriodicTimer from the appropriate stages.
You can use more than one periodic timers as well :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class PollUserAction < Cramp::Controller::Action periodic_timer :poll_user, :every => 2 periodic_timer :check_limit_exceed, :every => 10 def poll_user .. end def check_limit_exceed finish if request_limit_exceeded end end |
In the example above, check_limit_exceed() calls finish() if the request limit is exceeded, which in turn will terminate the connection and stop all the timers too.
If you’re using Cramp for streaming or long polling, you’d want to make sure the client doesn’t close the connection prematurely. Cramp::Controller has a handy helper method to make sure that doesn’t happen – keep_connection_alive
keep_connection_alive sends the client an empty string (” “) every 15 seconds by default.
1 2 3 4 |
class PollUserAction < Cramp::Controller::Action periodic_timer :poll_user, :every => 2 keep_connection_alive end |
Or you can change the period by supplying :every option :
1 2 3 4 |
class PollUserAction < Cramp::Controller::Action periodic_timer :poll_user, :every => 2 keep_connection_alive, :every => 30 end |
Cramp::Controller is capable of using a Rack middlewares for routing request and populating params. Here’s an example of a Cramp::Controller using Usher for routing :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
require 'rubygems' require 'cramp/controller' class HomeController < Cramp::Controller::Action on_start :send_hello_world def send_hello_world render "Hello World with #{params[:id]}" finish end end routes = Usher::Interface.for(:rack) do add('/:id').to(HomeController) end Rack::Handler::Thin.run routes, :Port => 3000 |
Cramp::Controller::Action implements the params method like:
1 2 3 |
def params @params ||= @env['usher.params'] end |
If you’re using another rack based router like rack-mount, you might want to override that with the appropriate definition.
As Cramp::Controller tries being a Rack based framework, you should be able to use Rails middlewares for cookies and sessions stores.
Cramp::Controller does not have any built in mechanism for rendering files. However, using something like tilt should be very straight forward.
Cramp::Controller comes with a few helper methods to write integration tests for the application. However, that’s not within the scope of this article. You should have a read through http://github.com/lifo/cramp/tree/master/test/controller to know more.
I haven’t really benchmarked Hello World for Cramp::Controller. However it should be very fast. I was able to have a single Cramp::Controller instance stream to 2000+ concurrent connections just fine. And this is where Cramp::Controller really shines, and not some silly Hello World masturbations.
Also, note that OS X isn’t the best environment for testing concurrent connections, you should be using Linux with a tuned Kernel for this. Check this article for details on tuning the Kernel.
Even though Cramp::Controller tries being as much Rack compliant as possible, it’s not a 100% Rack compliant framework. Rack specs are designed primarily for synchronous frameworks. As Cramp::Controller uses deferrable body, any middlewares operating on the response body will not work without modifications.
Cramp::Model is an asynchronous ORM ( only MySQL supported at the moment ) built on top of ARel and ActiveModel. It’s currently in a very primitive state and provides the following features :
Here’s what a Cramp model looks like :
1 2 3 4 5 6 7 8 9 10 11 |
require 'rubygems' require 'cramp/model' Cramp::Model.init(:username => 'root', :database => 'cramp_development') class User < Cramp::Model::Base attribute :id, :type => Integer, :primary_key => true attribute :name validates_presence_of :name end |
You are required to declare all the columns you wish to use as Attributes. If type is not supplied, String type is assumed.
Currently Cramp::Model provides the following finder method :
It also provides the following methods for specifying options on the find :
The above methods are delegated to Arel and can be chained. As Cramp::Model is an asynchronous ORM, you must supply a callback method for processing the result records. You could do that by calling all, first or each at the end of the chain and supply the callback as a block or a method.
Here are some example usages :
1 2 3 4 5 6 7 8 9 10 11 |
EM.run do User.select(:id, :name).limit(10).offset(20).first {|u| .. } User.where(User[:name].eq('Lush')).limit(2).all {|users| ... } User.each {|user| .. } def lush_users_found(users) ... end User.where(User[:name].eq('Lush'), User[:id].gt(5)).all method(:lush_users_found) end |
For the basic CRUD operations, Cramp::Model provides methods similar to Model#save, which accepts an optional callback. If a callback is provided, it’ll be called after the completion with a Status object, containing metadata about the success or failure of the save operation. Status object has just two methods defined on it : record & success?.
Here’s how you would typically want to save a record to the database :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
EM.run do def user_saved(status) if status.success? ... else user = status.record puts "Oops. Found errors : #{status.record.errors.inspect}" end end user = User.new user.save method(:user_saved) end |
Cramp is hosted on Github http://github.com/lifo/cramp. Please do fork and use the issues if you encounter a bug or need any kind of help.
Web development’s next decade
Practice isn’t fun
This Warden Strategy uses Rack::OpenID to support OpenID authentication for Rack apps with Warden. I hope someone adds this as a Devise module, complete with an OpenID form input and migration. That would be truly awesome.
lilt: A characteristic risi...
I’m not a fatalist, b...
“ I’m not a fatalist, but even if I were, what could I do about it? ”
Emo Phillips
My five-year anniversary with Ruby
Weekly goals
Extreme JavaScript Performance video
The flood of ideas
End of year Wrap-up
Irreversibility
Bob Dylan — Simple Twist of...
seditious: Inciting or caus...
Crystal Brass Knuckles — De...
The oldest and strongest em...
“ The oldest and strongest emotion of mankind is fear, and the oldest and strongest kind of fear is fear of the unknown. ”
H.P. Lovecraft
No returns
Chris Wanstrath makes a good point about an ugly way to initialize a variable, but I don't agree that an explicit return is the best style to use.
The original ugly:
def logger
unless @logger
@logger = Logger.new(STDOUT)
@logger.level = Logger::WARN
end
@logger
end
Chris' improvement:
def logger
return @logger if defined? @logger
@logger = Logger.new(STDOUT)
@logger.level = Logger::WARN
@logger
end
I prefer a functional style:
def logger
@logger || begin
@logger = Logger.new(STDOUT)
@logger.level = Logger::WARN
@logger
end
end
This is actually slightly awkward. I'd usually do @logger ||=, but that's hard to do gracefully since here you have to combine a few statements to create the new logger. A better constructor or a #level method that returned self would be so much nicer. A small refactoring seems a bit more readable.
def logger
@logger ||= default_logger
end
def default_logger
logger = Logger.new(STDOUT)
logger.level = Logger::WARN
logger
end
And if you really like functional style and are a fan of the K combinator:
def logger
@logger ||= Logger.new(STDOUT).tap {|l| l.level = Logger::WARN}
end
I am almost always opposed to using an explicit return in Ruby code. Whenever I see one I cringe. You can sometimes make a case for one as a top guard in a medium-sized method, but here's where I invoke the slippery-slope argument. It's too easy for those things to snowball into a handful of return statements, and then someday you'll have to wonder if they have to be in that order or not. And when your method gets long enough, you can easily miss that there was a return at the top and mess up a refactoring; using an if/then/else makes it clearer and harder to miss.
By the way, using if defined? @logger the way Chris does is not the same thing as if @logger.
>> defined?(@logger)
=> nil
>> @logger
=> nil
>> @logger = nil
=> nil
>> defined?(@logger)
=> "instance-variable"
If you're trying to make sure @logger is not nil, defined? is unlikely to be the way you want to test the variable was initialized. Be careful out there, kids...
Need JavaScript-fu? JavaScript Masterclass Philadelphia in January 2010
Failure is bad.
Simplest Prototype & scripty Slideshow
Slides from my RoR exchange talk
Get them here. I had a great time there - well worth going!
Sneak preview on scripty2 UI controls
Tell us what you think of the new BlogSphere feature. We are continually looking to improve and update the
functionality based on your feedback.

Find your next Ruby on Rails project or job.
Exclusive content,
regularly updated - onsite and tele-working positions listed.
I have a great pleasure to cooperate with bartek on various projects during and i find him very bright individual with invaluable knowledge and great attitude.
-
B.K, Poland