0
0
Fork 0

added new router to the tools

This small router is intended to work like URLMap, but with the feature
that it can extract variables from routes.
This commit is contained in:
Gibheer 2012-10-11 06:38:55 +02:00
parent 1c1bda6b66
commit 79537632ac
6 changed files with 158 additions and 2 deletions

16
config.ru Normal file
View File

@ -0,0 +1,16 @@
require File.expand_path('../lib/zero.rb', __FILE__)
require 'json'
class Foo
def call(env)
req = Rack::Request.new(env)
[200, {'Content-Type' => 'text/html'}, ["this works #{req.params.inspect}"]]
end
end
routes = Zero::Router.new(
'/foo/:id' => Foo.new,
'/' => Foo.new
)
run routes

View File

@ -1,5 +1,7 @@
require 'rack'
module Zero
require_relative 'zero/controller'
require_relative 'zero/request'
require_relative 'zero/response'
require_relative 'zero/rack_request'
require_relative 'zero/router'
end

44
lib/zero/rack_request.rb Normal file
View File

@ -0,0 +1,44 @@
# as we need #update_param we patch it it into the request
# this is directly from thr request.rb of the rack repository, so this
# can be deleted, when rack was released
r = Rack::Request.new(Rack::MockRequest.env_for('foo'))
if not r.respond_to?('update_param') then
class Rack::Request
# Destructively update a parameter, whether it's in GET and/or POST.
# Returns nil.
#
# The parameter is updated wherever it was previous defined, so
# GET, POST, or both. If it wasn't previously defined, it's inserted into GET.
#
# env['rack.input'] is not touched.
def update_param(k, v)
found = false
if self.GET.has_key?(k)
found = true
self.GET[k] = v
end
if self.POST.has_key?(k)
found = true
self.POST[k] = v
end
unless found
self.GET[k] = v
end
@params = nil
nil
end
# Destructively delete a parameter, whether it's in GET or POST.
# Returns the value of the deleted parameter.
#
# If the parameter is in both GET and POST, the POST value takes
# precedence since that's how #params works.
#
# env['rack.input'] is not touched.
def delete_param(k)
v = [ self.POST.delete(k), self.GET.delete(k) ].compact.first
@params = nil
v
end
end
end

31
lib/zero/router.rb Normal file
View File

@ -0,0 +1,31 @@
module Zero
class Router
# match for variables in routes
VARIABLE_MATCH = %r{:(\w+)[^/]?}
# the replacement string to make it an regex
VARIABLE_REGEX = '(?<\1>.+?)'
def initialize(routes)
@routes = {}
routes.each do |route, target|
@routes[
Regexp.new(
route.gsub(VARIABLE_MATCH, VARIABLE_REGEX) + '$')] = target
end
end
def call(env)
request = Rack::Request.new(env)
@routes.each do |route, target|
match = route.match(request.path)
if match
match.names.each_index do |i|
request.update_param(match.names[i], match.captures[i])
end
return target.call(request.env)
end
end
[404, {'Content-Type' => 'text/html'}, ['Not found!']]
end
end
end

View File

@ -0,0 +1,49 @@
require 'spec_helper'
describe Zero::Router do
let(:router) { Zero::Router.new(routes) }
subject { router.call(env) }
# let(:app) do
# lambda {|env| [200, {'Content-Type' => 'text/html'}, 'correct'] }
# end
let(:app) { double }
let(:wrong_app) do
lambda {|env| [200, {'Content-Type' => 'text/html'}, 'Wrong'] }
end
before :each do
app.should_receive(:call)
end
context 'a working route' do
let(:routes) { { '/app' => app } }
let(:env) { generate_env('/app') }
it('takes a route') { subject }
end
context 'select the right route' do
let(:routes) do
{ '/wrong' => wrong_app,
'/correct' => app }
end
let(:env) { generate_env('/correct') }
it("selects the correct from multiple routes") { subject }
end
context 'uses the deepest path' do
let(:routes) { { '/wrong' => wrong_app,
'/deep' => wrong_app,
'/deep/wrong' => wrong_app,
'/deep/correct' => app }}
let(:env) { generate_env('/deep/correct') }
it("finds uses the deepest path first") { subject }
end
context 'converts parts of the url to parameters' do
let(:routes) { { '/foo/:id' => app } }
let(:env) { generate_env('/foo/42') }
it "should extract variables from the url" do
subject
end
end
end

14
spec/spec_helper.rb Normal file
View File

@ -0,0 +1,14 @@
require 'zero'
class SpecApp
attr_reader :env
def call(env)
@env = env
return [200, {'Content-Type' => 'text/html'}, ['success']]
end
end
def generate_env(path, options = {})
Rack::MockRequest.env_for(path, options = {})
end