123456789_123456789_123456789_123456789_123456789_

Goal

Show how to write a simple Rack application responding differently if the request is a POST or a GET request. Also show how to use the map method to implement a simple router.

Code

Save the following code in a file named: rack_example.ru

class Idea
  attr_accessor :title, :body, :created_at

  # Memory store, gets cleared as the process is restarted
  def self.store
    @ideas ||= []
  end

  class InvalidParams < StandardError; end

  # create an instance based on some passed params
  def initialize(params)
    raise InvalidParams, "You need to provide at least a title" unless params['title']
    self.title = params['title']
    self.body = params['body']
    self.created_at = Time.now
  end

  # Converts an instance into a string
  def to_s
    "#{title} at #{created_at.to_s}\n#{body}"
  end
end

class IdeaAPI
  def call(env)
    request = Rack::Request.new(env)
    case request.request_method
    when 'POST'
      begin
        idea = Idea.new(request.params)
      rescue Idea::InvalidParams => error
        [400, {"Content-Type" => "text/plain"}, [error.message] ]
      else
        Idea.store << idea
        [200, {"Content-Type" => "text/plain"}, ["Idea added, currently #{Idea.store.size} ideas are in memory!"]]
      end
    when 'GET'
      [200, {"Content-Type" => "text/plain"}, [Idea.store.map{|idea, idx| idea.to_s }.join("\n\n") + "\n"]]
    else
      [404, {}, ["Did you get lost?"]]
    end
  end
end

map '/ideas' do
  run IdeaAPI.new
end

The code is pretty straight-forward, but let me walk you through it nonetheless.

We have an Idea class which is just there for the demo. It creates an instance of itself when calling #new with a hash of params. If the passed hash doesn't have a value for the 'title' key, then an exception is raised.

At the bottom of the file, we can see that I'm mapping incoming requests for the '/ideas' URI to an instance of IdeaAPI.

The run method does what you expect, it runs the instance of the IdeaAPI passing it the Rack environment.

The IdeaAPI class implements the Rack protocol by providing a call(env) method which gets triggered when the request is dispatched to an instance of itself.

The environment object is then converted into a Request object which provides the developer with a few helpers.

Using one of these helpers, we check on the HTTP verb using the request_method instance method on our newly-created object.

Finally using a case statement, we can provide a response based on the HTTP verb. Note that the response follows the usual Rack response format.

Notes