Skip to content

Instantly share code, notes, and snippets.

@xaviershay
Created November 24, 2011 19:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xaviershay/1392039 to your computer and use it in GitHub Desktop.
Save xaviershay/1392039 to your computer and use it in GitHub Desktop.
Simple rack app with routing. Is this possible with http_router?
require 'rack/request'
require 'rack/response'
class App
attr_accessor :provider
# This constructor DI is not possible in Sinatra(?), since the framework
# creates the new instance for you.
def initialize(provider)
@provider = provider
end
def call(env)
request = Rack::Request.new(env)
router.process(request).finish
end
# Can't figure out how to re-use http_router in this case,
# it operates at the middleware level. Maybe I can extract
# the matched path out of rack.env?
def router
@router ||= Router.build do |b|
id = "([0-9a-z:_]+)"
b.get '/' do |_|
Rack::Response.new('Home Page')
end
b.put "/entity/#{id}" do |_, entity_id|
provider.create_entity(entity_id)
Rack::Response.new('Created')
end
end
end
end
class Router
METHODS = 1
def initialize(routes)
@routes = compile(routes)
end
# Returns a `Rack::Response`
def process(request)
route, params = match_route(request.path_info)
if route
action = route[METHODS][request.request_method]
if action
action.call(request, *params)
else
respond(406, "NOT ACCEPTABLE")
end
else
respond(404, "NOT FOUND")
end
end
def self.build(&block)
builder = Builder.new
block.call(builder)
builder.build
end
class Builder
def initialize
@map = {}
end
def get(path, &block)
route_with_method('GET', path, &block)
end
def put(path, &block)
route_with_method('PUT', path, &block)
end
def post(path, &block)
route_with_method('POST', path, &block)
end
def route_with_method(method, path, &block)
@map[path] ||= {}
@map[path][method] = block
end
def build
Router.new(@map)
end
end
private
attr_reader :config, :routes
def respond(code, description)
Rack::Response.new([description], code).tap do |response|
response.headers['Content-Type'] = 'text/plain'
end
end
def match_route(path)
params = nil
[routes.detect {|regex, _|
params = path.match(regex)
}, params ? params.captures : nil]
end
def compile(routes)
Hash[routes.map {|path, mappings|
[Regexp.new("^%s$" % path), mappings]
}]
end
end
require './app'
bogus_provider = Class.new do
def create_entity(id)
# derp
end
end
run App.new(bogus_provider)
@ngauthier
Copy link

Router:

run Rack::Builder.new do
  map '/' { run App::Home.new }
  map App::Entity.route { run App::Entity.new }
end

entity:

class App::Entity
  def self.route
    /^\/entity\/\d+/
  end
  def call(env)
    req = Rack::Request.new(env)
    id = req.path.scan(self.class.route).flatten.first
    # etc
  end
end

@xaviershay
Copy link
Author

So I guess I'm not after routing then so much as "path interpretation" - I don't want to have to write the regex matching stuff. http_router for instance can use a much more efficient hash lookup algorithm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment