You are here: Blogsphere Contribution

Rails BlogSphere

BlogSphere

Keep up to date with your favourite Rails bloggers in context.

Read more about how it works

245186169_46c171be89_m_d Tokyo’s N Building

by Marcel Molina Jr | over 2 years ago | Read more

Tokyo’s N Building

245186169_46c171be89_m_d coterie: A small group of p...

by Marcel Molina Jr | over 2 years ago | Read more

co•te•rie
[koh-tuh-ree]noun
A small group of people with shared interests or tastes, esp. one that is exclusive of other people.

245186169_46c171be89_m_d Be happy you’re not

by Marcel Molina Jr | over 2 years ago | Read more

Be-happy-youre-not

Be happy you’re not

245186169_46c171be89_m_d Resentment is like drinking...

by Marcel Molina Jr | over 2 years ago | Read more

Resentment is like drinking poison and waiting for the other person to die.

Carrie Fisher

Marmalade Design-first development

by Ben Scofield | over 2 years ago | Read more

The first RubyConf talk I gave was entitled Cleanliness Is Next to Domain-Specificity; in it, I spoke about DSLs, and gave an example of how you might want to create one (I also spent some time talking about regional variation in Little Bunny Foo Foo, but that’s neither here nor there). At the time, I said that [...]

Pratik Introducing Cramp

by Pratik Naik | over 2 years ago | Read more

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.

Install

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.

Two faces of Cramp

Cramp comes with two layers : Controller & Model.

1) Cramp::Controller

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 :

  • Initialization – This is the initial stage of an action when a request is received. During this stage, it’s possible to abort the request along with whatever headers/body you wish to send. Hence, it is typically useful for checking permissions and validating the request etc.
  • Response Initialization – If the request doesn’t get aborted during the initialization stage, Cramp::Controller::Action enters the second stage, where response headers and a deferrable body are sent to the client. It’s important to note that as the headers are already sent here, following stages will only be able to send body and not able to change the headers.
  • Starting – This is where the actual work happens. From this stage, you can send body to the clients as many times as you wish to or finish the request.
  • FinishingCramp::Controller::Action enters this stage if the request is marked as finished during the Starting stage or if the client closes the connection. This stage is useful for cleanup activities or anything else you may wish to do upon request completion.

To hook into each of these states, Cramp::Controller::Action provides the following callbacks/methods :

before_start

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.

respond_with

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.

on_start

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

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.

Helper Methods

Periodic Timers

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.

Keeping Connection Alive

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

Routing, Request and Parameters

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.

Views

Cramp::Controller does not have any built in mechanism for rendering files. However, using something like tilt should be very straight forward.

Testing

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.

Benchmarks

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.

Gotchas

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.

2) Cramp::Model

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 :

  • Validations using ActiveModel
  • CRUD operations
  • AR/ARel like Finder methods

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 :

  • all
  • first
  • each

It also provides the following methods for specifying options on the find :

  • where
  • select
  • group
  • order
  • limit
  • offset

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

Contributing to Cramp

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.

Me Web development’s next decade

by Thomas Fuchs | over 2 years ago | Read more

Goodbye 2000’s, hello 2010’s. What does the next decade on the web have in store for us? Let’s start by looking at what happened in the last decade. In the Year 2000… At the beginning of the year 2000 (shiver!), the current version of Internet Explorer was 5.0. (Though people were largely still using version 4.) IE’s market share [...]

Marmalade Practice isn’t fun

by Ben Scofield | over 2 years ago | Read more

It’s a new year, and it’s about time for a hard truth: practice, when done properly, isn’t fun. I’d love to be able to tell you that it is – that the transcendent joy you get when you practice a skill gives you the best feeling in the world – but that’s not true, and if [...]

OpenID Strategy for Warden

by Kamal Fariz | over 2 years ago | Read more

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.

245186169_46c171be89_m_d lilt: A characteristic risi...

by Marcel Molina Jr | over 2 years ago | Read more

lilt
[lilt]noun
A characteristic rising and falling of the voice when speaking; a pleasant gentle accent.

245186169_46c171be89_m_d C’est Si Bon – ...

by Marcel Molina Jr | over 2 years ago | Read more

C’est Si Bon – Eartha Kitt

245186169_46c171be89_m_d I’m not a fatalist, b...

by Marcel Molina Jr | over 2 years ago | Read more

I’m not a fatalist, but even if I were, what could I do about it?

Emo Phillips

Marmalade My five-year anniversary with Ruby

by Ben Scofield | over 2 years ago | Read more

My five-year anniversary with Viget Labs is approaching quickly – I started here back in February 2005. Before I reach that milestone, though, there’s another that’s been at least as influential in my life: this month is my five year anniversary of using Ruby and Rails. I started experimenting with ‘em during my last month at [...]

Marmalade Weekly goals

by Ben Scofield | over 2 years ago | Read more

In light of the impending new year, I thought I’d offer an alternative to the associated flood of well-intentioned but ultimately-doomed resolutions: weekly goals. (And yes, I contributed to the flood myself. Color me somewhat hypocritical.) I see several problems with annual resolutions, all related to their length. When you’re making year-long goals, it’s extremely difficult to figure [...]

Me Extreme JavaScript Performance video

by Thomas Fuchs | over 2 years ago | Read more

The nice folks from JSConf.eu have posted more and more videos of the conference talks, and here’s my talk on Extreme JavaScript performance! For reference, here are the talk slides, if you want to see an enlarged version/download them: View more documents from Thomas Fuchs. Finally, here’s a PDF (750k) of the slides, if you want to read [...]

Marmalade The flood of ideas

by Ben Scofield | over 2 years ago | Read more

Seth’s post yesterday struck a chord with me, as it reminded me of how I felt a few months ago. At the time, I was posting extremely regularly here in the blog, and it seemed like ideas were flowing easily – my idea notebooks in Evernote were overflowing. This continued into November, even when I [...]

Nik_wakelin End of year Wrap-up

by Nik Wakelin | over 2 years ago | Read more

Long time since our last blog post! We’ve been flat out with our projects, so here is the latest update. We are nearing the end of our second year at Code to Customer, and it marks the end to a year where we have been shifting our focus as a company away from consulting to web [...]

Marmalade Irreversibility

by Ben Scofield | over 2 years ago | Read more

I’m generally a pretty calm guy. We’ve all got pet peeves, though, and one of my occurs all too frequently. I’m talking about people who think their decisions and mistakes while driving are irreversible. You know, people who cut across three lanes of traffic to make a left turn into a mall parking lot, when [...]

245186169_46c171be89_m_d Bob Dylan — Simple Twist of...

by Marcel Molina Jr | over 2 years ago | Read more

Bob Dylan — Simple Twist of Fate

245186169_46c171be89_m_d seditious: Inciting or caus...

by Marcel Molina Jr | over 2 years ago | Read more

se•di•tious
[si-dish-uhs]adjective
Inciting or causing people to rebel against the authority of a state or monarch.

245186169_46c171be89_m_d Crystal Brass Knuckles — De...

by Marcel Molina Jr | over 2 years ago | Read more

Crystal-brass-knuckes

Crystal Brass KnucklesDebra Baxter

245186169_46c171be89_m_d The oldest and strongest em...

by Marcel Molina Jr | over 2 years ago | Read more

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

Josh No returns

by Josh Susser | over 2 years ago | Read more

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

Me Need JavaScript-fu? JavaScript Masterclass Philadelphia in January 2010

by Thomas Fuchs | over 2 years ago | Read more

Need some JavaScript-fu? Tired of being stuck with not knowing what those magic framework functions do? Want to tweak your code for ultimate performance? Or a mad programming skillz boost for the new Year? Come visit our full-day JavaScript Masterclass in Philadelphia, on Monday, January 25, 2010, at National Mechanics. Amy Hoy, teacher extraordinaire and yours truly [...]

Marmalade Failure is bad.

by Ben Scofield | over 2 years ago | Read more

I just read a post from Micah Baldwin that I found very interesting, so here’s a quick response: Failure is bad. Success is good. Sure, failure can teach you valuable lessons – but so can success, and success has the added benefit of being success. What the “failure is good” mentality misses is that failure is good only as a means to [...]

Me Simplest Prototype & scripty Slideshow

by Thomas Fuchs | over 2 years ago | Read more

Here’s a more extended version of my comment on Jonathan Snook’s post on the simplest jQuery Slideshow (good article!). So here’s my version of the simplest slideshow (done in Prototype/script.aculo.us 1.8), compare to Jonathan’s version in jQuery. I’ve optimized what’s going on by only fading on element at a time (versus a more processor-hungry crossfade). The code: setInterval(function(){ [...]

245186169_46c171be89_m_d Wizard Smoke

by Marcel Molina Jr | over 2 years ago | Read more

Wizard Smoke

245186169_46c171be89_m_d Simple as… by KiD CuDi

by Marcel Molina Jr | over 2 years ago | Read more

Simple as...

KiD CuDi

Fred_small Slides from my RoR exchange talk

by Frederick Cheung | over 2 years ago | Read more

Get them here. I had a great time there - well worth going!

Me Sneak preview on scripty2 UI controls

by Thomas Fuchs | over 2 years ago | Read more

So how’s scripty2 progressing? To give you some more insight than just “Very well, thank you”, here’s a status update. Next to the big chunks for the effects engine which has been stable for quite a while now, and the support for multi-touch input (hat tip to Nokia), several additions are queued up, the most important [...]


Tell us what you think of the new BlogSphere feature. We are continually looking to improve and update the functionality based on your feedback.

Job Board

Job Boards
Find your next Ruby on Rails project or job.
Exclusive content, regularly updated - onsite and tele-working positions listed.

View the opportunities

Latest from the Weblog

Bartłomiej Danek:

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.

- Sbubble B.K, Poland