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:
parent
1c1bda6b66
commit
79537632ac
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue