-
Notifications
You must be signed in to change notification settings - Fork 2
Rack app with uri and HTTP specific responses
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.
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 pass 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 Rack::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.
- In real life, you probably don't want to raise an exception and rescue it. This is an expensive approach and should be avoided.
- Using map the way is shown doesn't prevent uris such as '/ideas/foo' to be routed to our API. This is by design, you can also lose
map
all together and got for an approach such as: https://gist.github.com/1447478