first pass

This commit is contained in:
Josh Hull 2011-09-06 19:10:49 -07:00
parent cd68d13696
commit 6c7285f9ca
14 changed files with 174 additions and 258 deletions

View File

@ -1,7 +1,7 @@
require 'http_router'
HttpRouter::Rack.override_rack_builder!
map('/get/:id', :matching => {:id => /\d+/}) { |env|
map('/get/:id', :match_with => {:id => /\d+/}) { |env|
[200, {'Content-type' => 'text/plain'}, ["My id is #{env['router.params'][:id]}, which is a number\n"]]
}

View File

@ -1,7 +1,7 @@
require 'http_router'
run HttpRouter.new {
get('/get/:id').matching(:id => /\d+/).to { |env| [200, {'Content-type' => 'text/plain'}, ["id is #{Integer(env['router.params'][:id]) * 2} * 2\n"]]}
get('/get/:id', :id => /\d+/).to { |env| [200, {'Content-type' => 'text/plain'}, ["id is #{Integer(env['router.params'][:id]) * 2} * 2\n"]]}
}
# $ curl http://127.0.0.1:3000/get/123

View File

@ -20,12 +20,12 @@ class HttpRouter
InvalidRouteException = Class.new(RuntimeError)
# Raised when a Route is not able to be generated due to a missing parameter.
MissingParameterException = Class.new(RuntimeError)
# Raised when a Route is compiled twice
DoubleCompileError = Class.new(RuntimeError)
# Raised an invalid request value is used
InvalidRequestValueError = Class.new(RuntimeError)
# Raised when there are extra parameters passed in to #url
TooManyParametersException = Class.new(RuntimeError)
# Raised when there are left over options
LeftOverOptions = Class.new(RuntimeError)
# Creates a new HttpRouter.
# Can be called with either <tt>HttpRouter.new(proc{|env| ... }, { .. options .. })</tt> or with the first argument omitted.
@ -62,6 +62,7 @@ class HttpRouter
#
# Returns the route object.
def add(*args, &app)
uncompile
opts = args.last.is_a?(Hash) ? args.pop : {}
path = args.first
route = add_route((Regexp === path ? RegexRoute : Route).new(self, path, opts))
@ -114,15 +115,10 @@ class HttpRouter
# the default application will be called. The router will be available in the env under the key <tt>router</tt>. And parameters matched will
# be available under the key <tt>router.params</tt>.
def call(env, perform_call = true)
rack_request = ::Rack::Request.new(env)
request = Request.new(rack_request.path_info, rack_request, perform_call)
response = catch(:success) { @root[request] }
if perform_call
response or no_response(env)
else
request.matches.empty? ? nil : request.matches
end
compile
call(env, perform_call)
end
alias_method :compiling_call, :call
# Resets the router to a clean state.
def reset!
@ -140,7 +136,7 @@ class HttpRouter
#
# Example:
# router = HttpRouter.new
# router.add('/:foo.:format').name(:test).to{|env| [200, {}, []]}
# router.add('/:foo.:format', :name => :test).to{|env| [200, {}, []]}
# router.url(:test, 123, 'html')
# # ==> "/123.html"
# router.url(:test, 123, :format => 'html')
@ -150,12 +146,10 @@ class HttpRouter
# router.url(:test, :foo => 123, :format => 'html', :fun => 'inthesun')
# # ==> "/123.html?fun=inthesun"
def url(route, *args)
case route
when Symbol then @named_routes.key?(route) && @named_routes[route].each{|r| url = r.url(*args); return url if url}
when Route then return route.url(*args)
end
raise(InvalidRouteException)
compile
raw_url(route, *args)
end
alias_method :compiling_url, :url
# This method is invoked when a Path object gets called with an env. Override it to implement custom path processing.
def process_destination_path(path, env)
@ -184,7 +178,6 @@ class HttpRouter
@routes.each do |route|
new_route = route.clone(cloned_router)
cloned_router.add_route(new_route)
new_route.name(route.named) if route.named
begin
new_route.to route.dest.clone
rescue
@ -218,6 +211,7 @@ class HttpRouter
end
def to_s
compile
"#<HttpRouter:0x#{object_id.to_s(16)} number of routes (#{routes.size}) ignore_trailing_slash? (#{ignore_trailing_slash?}) redirect_trailing_slash? (#{redirect_trailing_slash?}) known_methods (#{known_methods.to_a.join(', ')})>"
end
@ -226,9 +220,44 @@ class HttpRouter
"#{to_s}\n#{'=' * head.size}\n#{@root.inspect}"
end
def compile
return if @compiled
@routes.each {|r| r.send(:compile)}
instance_eval "undef :url; alias :url :raw_url", __FILE__, __LINE__
instance_eval "undef :call; alias :call :raw_call", __FILE__, __LINE__
@compiled = true
end
def uncompile
return unless @compiled
instance_eval "undef :url; alias :url :compiling_url", __FILE__, __LINE__
instance_eval "undef :call; alias :call :compiling_call", __FILE__, __LINE__
@compiled = false
end
def raw_url(route, *args)
case route
when Symbol then @named_routes.key?(route) && @named_routes[route].each{|r| url = r.url(*args); return url if url}
when Route then return route.url(*args)
end
raise(InvalidRouteException)
end
def raw_call(env, perform_call = true)
rack_request = ::Rack::Request.new(env)
request = Request.new(rack_request.path_info, rack_request, perform_call)
response = catch(:success) { @root[request] }
if perform_call
response or no_response(env)
else
request.matches.empty? ? nil : request.matches
end
end
private
def add_with_request_method(path, method, opts = {}, &app)
route = add(path, opts).send(method.to_sym)
opts[:request_method] = method
route = add(path, opts)
route.to(app) if app
route
end

View File

@ -54,7 +54,7 @@ class HttpRouter
end
def inspect_label
"Path: #{original_path.inspect} for route #{route.named || 'unnamed route'} to #{route.dest.inspect}"
"Path: #{original_path.inspect} for route #{route.name || 'unnamed route'} to #{route.dest.inspect}"
end
private

View File

@ -38,6 +38,7 @@ class HttpRouter
private
def compile
router.send(:compile)
root.extend(root.methods_module)
instance_eval "def [](request)\n#{to_code}\nnil\nend", __FILE__, __LINE__
@compiled = true

View File

@ -12,8 +12,7 @@ module HttpRouter::Rack::BuilderMixin
# @param options [Hash] Options for added path.
# @see HttpRouter#add
def map(path, options = {}, method = nil, &block)
route = router.add(path, options)
route.send(method) if method
route = router.send(method || :add, path, options)
route.to(&block)
@ins << router unless @ins.last == router
route

View File

@ -1,16 +1,16 @@
class HttpRouter
class RegexRoute < Route
def initialize(router, path, opts = {})
def initialize(router, path, opts = nil)
@router, @original_path, @opts = router, path, opts
@param_names = @original_path.respond_to?(:names) ? @original_path.names.map(&:to_sym) : []
@path_validation_regex = original_path
Util.add_path_generation(self, self, opts.delete(:path_for_generation), @original_path) if opts.key?(:path_for_generation)
process_opts
process_opts if opts
end
def add_path_to_tree
def compile
@paths = [@original_path]
add_non_path_to_tree(@router.root.add_free_match(@original_path), path, @param_names)
add_non_path_to_tree(@router.root.add_free_match(@original_path), @original_path, @param_names)
end
def significant_variable_names

View File

@ -1,92 +1,24 @@
class HttpRouter
class Route
attr_reader :default_values, :router, :path, :conditions, :original_path, :match_partially, :dest, :regex, :named, :matches_with
attr_reader :default_values, :router, :conditions, :original_path, :match_partially, :dest, :regex, :name, :matches_with, :app
alias_method :dest, :app
alias_method :match_partially?, :match_partially
alias_method :regex?, :regex
def initialize(router, path, opts = {})
@router, @original_path, @opts = router, path, opts
if @original_path
@match_partially = true and path.slice!(-1) if @original_path[/[^\\]\*$/]
@original_path[0, 0] = '/' if @original_path[0] != ?/
else
@match_partially = true
end
process_opts
def initialize(router, original_path, opts = nil)
@router, @original_path, @opts = router, original_path, opts
process_path
process_opts if opts
process_match_with
post_process
end
def process_opts
@default_values = @opts[:__default_values__] || @opts[:default_values] || {}
@arbitrary = @opts[:__arbitrary__] || @opts[:arbitrary]
@matches_with = significant_variable_names.include?(:matching) ? @opts : @opts[:__matching__] || @opts[:matching] || {}
significant_variable_names.each do |name|
@matches_with[name] = @opts[name] if @opts.key?(name) && !@matches_with.key?(name)
end
@conditions = @opts[:__conditions__] || @opts[:conditions] || {}
@match_partially = @opts[:__partial__] if @match_partially.nil? && !@opts[:__partial__].nil?
@match_partially = @opts[:partial] if @match_partially.nil? && !@opts[:partial].nil?
name(@opts[:__name__] || @opts[:name]) if @opts.key?(:__name__) || @opts.key?(:name)
@needed_keys = significant_variable_names - @default_values.keys
def name=(name)
@name = name
@router.named_routes[name] << self
@router.named_routes[name].sort!{|r1, r2| r2.significant_variable_names.size <=> r1.significant_variable_names.size }
end
def as_options
{:__matching__ => @matches_with, :__conditions__ => @conditions, :__default_values__ => @default_values, :__name__ => @named, :__partial__ => @partially_match, :__arbitrary__ => @arbitrary}
end
def compiled?
!@paths.nil?
end
def partial(match_partially = true)
@match_partially = match_partially
self
end
def to(dest = nil, &dest2)
@dest = dest || dest2
add_path_to_tree
self
end
def name(n)
@named = n
@router.named_routes[n] << self
@router.named_routes[n].sort!{|r1, r2| r2.significant_variable_names.size <=> r1.significant_variable_names.size }
self
end
def request_method(*method)
add_to_contitions(:request_method, method)
end
def host(*host)
add_to_contitions(:host, host)
end
def scheme(*scheme)
add_to_contitions(:scheme, scheme)
end
def user_agent(*user_agent)
add_to_contitions(:user_agent, user_agent)
end
def add_to_contitions(name, *vals)
((@conditions ||= {})[name] ||= []).concat(vals.flatten)
self
end
def matching(matchers)
@matches_with.merge!(matchers.is_a?(Array) ? Hash[*matchers] : matchers)
self
end
def default(defaults)
(@default_values ||= {}).merge!(defaults)
self
end
# Sets the destination of this route to redirect to an arbitrary URL.
def redirect(path, status = 302)
raise ArgumentError, "Status has to be an integer between 300 and 399" unless (300..399).include?(status)
to { |env|
@ -95,38 +27,31 @@ class HttpRouter
response.redirect(eval(%|"#{path}"|), status)
response.finish
}
self
end
# Sets the destination of this route to serve static files from either a directory or a single file.
def static(root)
if File.directory?(root)
partial.to ::Rack::File.new(root)
else
to {|env| env['PATH_INFO'] = File.basename(root); ::Rack::File.new(File.dirname(root)).call(env) }
@match_partially = true if File.directory?(root)
to File.directory?(root) ?
::Rack::File.new(root) :
proc {|env|
env['PATH_INFO'] = File.basename(root)
::Rack::File.new(File.dirname(root)).call(env)
}
end
def to(app = nil, &app2)
@app = app || app2 || raise("you didn't specify a destination")
if @app.respond_to?(:url_mount=)
urlmount = UrlMount.new(original_path, @default_values || {})
urlmount.url_mount = router.url_mount if router.url_mount
dest.url_mount = urlmount
end
self
end
def post; request_method('POST'); end
def get; request_method('GET'); end
def put; request_method('PUT'); end
def delete; request_method('DELETE'); end
def head; request_method('HEAD'); end
def options; request_method('OPTIONS'); end
def patch; request_method('PATCH'); end
def trace; request_method('TRACE'); end
def conenct; request_method('CONNECT'); end
def arbitrary(blk = nil, &blk2)
arbitrary_with_continue { |req, params|
req.continue[(blk || blk2)[req, params]]
}
end
def arbitrary_with_continue(blk = nil, &blk2)
(@arbitrary ||= []) << (blk || blk2)
self
def as_options
{:__match_with__ => @matches_with, :__conditions__ => @conditions, :__default_values__ => @default_values, :__name__ => @name, :__partial__ => @partially_match}
end
def url(*args)
@ -135,7 +60,7 @@ class HttpRouter
end
def clone(new_router)
Route.new(new_router, @original_path.dup, as_options)
Route.new(new_router, @original_path.dup, as_options).to(@app)
end
def url_with_params(*a)
@ -151,7 +76,7 @@ class HttpRouter
options = options.nil? ? default_values.dup : default_values.merge(options) if default_values
options.delete_if{ |k,v| v.nil? } if options
result, params = yield args, options
mount_point = router.url_mount && router.url_mount.url(options)
mount_point = router.url_mount && (options ? router.url_mount.url(options) : router.url_mount.url)
mount_point ? [File.join(mount_point, result), params] : [result, params]
end
@ -162,24 +87,68 @@ class HttpRouter
def matching_path(params, other_hash = nil)
return @paths.first if @paths.size == 1
case params
when Array
when Array, nil
significant_keys = other_hash && significant_variable_names & other_hash.keys
@paths.find { |path| path.param_names.size == (significant_keys ? params.size + significant_keys.size : params.size) }
@paths.find { |path|
params_size = params ? params.size : 0
path.param_names.size == (significant_keys ? (params_size) + significant_keys.size : params_size) }
when Hash
@paths.find { |path| (params && !params.empty? && (path.param_names & params.keys).size == path.param_names.size) || path.param_names.empty? }
end
end
def to_s
"#<HttpRouter:Route #{object_id} @original_path=#{@original_path.inspect} @conditions=#{@conditions.inspect} @arbitrary=#{@arbitrary.inspect}>"
"#<HttpRouter:Route #{object_id} @original_path=#{@original_path.inspect} @conditions=#{@conditions.inspect}>"
end
private
def add_to_contitions(name, *vals)
((@conditions ||= {})[name] ||= []).concat(vals.flatten)
self
end
def process_path
@path_for_processing = @original_path && @original_path.dup
if @path_for_processing
@match_partially = true and @path_for_processing.slice!(-1) if @path_for_processing[/[^\\]\*$/]
@path_for_processing[0, 0] = '/' if @path_for_processing[0] != ?/
else
@match_partially = true
end
end
def post_process
raise LeftOverOptions.new("There are still options left, #{@opts.inspect}") unless @opts.empty?
end
def process_opts
@default_values = @opts.delete(:__default_values__) || @opts.delete(:default_values)
@matches_with = significant_variable_names.include?(:match_with) ?
@opts :
@opts.delete(:__match_with__) || @opts.delete(:match_with)
@conditions = @opts.delete(:__conditions__) || @opts.delete(:conditions)
if @match_partially.nil?
@match_partially = @opts.delete(:__partial__) if @opts.key?(:__partial__)
@match_partially = @opts.delete(:partial) if @opts.key?(:partial) && match_partially.nil?
end
self.name = @opts.delete(:__name__) || @opts.delete(:name)
add_to_contitions :request_method, @opts.delete(:request_method) if @opts.key?(:request_method)
add_to_contitions :host, @opts.delete(:host) if @opts.key?(:host)
add_to_contitions :scheme, @opts.delete(:scheme) if @opts.key?(:scheme)
add_to_contitions :user_agent, @opts.delete(:user_agent) if @opts.key?(:user_agent)
end
def process_match_with
significant_variable_names.each do |name|
(@matches_with ||= {})[name] = @opts.delete(name) if @opts.key?(name) && (@matches_with.nil? || !@matches_with.key?(name))
end
end
def raw_paths
return [] if @original_path.nil?
@raw_paths ||= begin
start_index, end_index = 0, 1
@raw_paths, chars = [""], @original_path.split('')
@raw_paths, chars = [""], @path_for_processing.split('')
until chars.empty?
case fc = chars.first[0]
when ?(
@ -206,10 +175,10 @@ class HttpRouter
node.add_lookup(part[1].chr)
when ?:
param_names << name.to_sym
@matches_with[name.to_sym] ? node.add_spanning_match(@matches_with[name.to_sym]) : node.add_variable
@matches_with && @matches_with[name.to_sym] ? node.add_spanning_match(@matches_with[name.to_sym]) : node.add_variable
when ?*
param_names << name.to_sym
@matches_with[name.to_sym] ? node.add_glob_regexp(@matches_with[name.to_sym]) : node.add_glob
@matches_with && @matches_with[name.to_sym] ? node.add_glob_regexp(@matches_with[name.to_sym]) : node.add_glob
else
node.add_lookup(part)
end
@ -227,9 +196,9 @@ class HttpRouter
name = part[1, part.size].to_sym
param_names << name
if spans
@matches_with[name] ? "((?:#{@matches_with[name]}\\/?)+)" : '(.*?)'
@matches_with && @matches_with[name] ? "((?:#{@matches_with[name]}\\/?)+)" : '(.*?)'
else
"(#{(@matches_with[name] || '[^/]*?')})"
"(#{(@matches_with && @matches_with[name] || '[^/]*?')})"
end
else
Regexp.quote(part)
@ -239,35 +208,26 @@ class HttpRouter
node.add_match(Regexp.new("#{regex}$"), capturing_indicies, splitting_indicies)
end
def add_path_to_tree
raise DoubleCompileError if compiled?
@paths ||= begin
if raw_paths.empty?
add_non_path_to_tree(@router.root, nil, [])
else
raw_paths.map do |path|
param_names = []
node = @router.root
path.split(/\//).each do |part|
next if part == ''
parts = part.scan(/\\.|[:*][a-z0-9_]+|[^:*\\]+/)
node = parts.size == 1 ? add_normal_part(node, part, param_names) : add_complex_part(node, parts, param_names)
end
add_non_path_to_tree(node, path, param_names)
def compile
@paths = if raw_paths.empty?
add_non_path_to_tree(@router.root, nil, [])
else
raw_paths.map do |path|
param_names = []
node = @router.root
path.split(/\//).each do |part|
next if part == ''
parts = part.scan(/\\.|[:*][a-z0-9_]+|[^:*\\]+/)
node = parts.size == 1 ? add_normal_part(node, part, param_names) : add_complex_part(node, parts, param_names)
end
add_non_path_to_tree(node, path, param_names)
end
end
end
def add_non_path_to_tree(node, path, names)
node = node.add_request(@conditions) unless @conditions.empty?
@arbitrary.each{|a| node = node.add_arbitrary(a, match_partially?, names)} if @arbitrary
node = node.add_request(@conditions) if @conditions && !@conditions.empty?
path_obj = node.add_destination(self, path, names)
if dest.respond_to?(:url_mount=)
urlmount = UrlMount.new(@original_path, @default_values)
urlmount.url_mount = router.url_mount if router.url_mount
dest.url_mount = urlmount
end
path_obj
end
@ -275,7 +235,7 @@ class HttpRouter
case value
when Array then value.each{ |v| append_querystring_value(uri, "#{key}[]", v) }
when Hash then value.each{ |k, v| append_querystring_value(uri, "#{key}[#{k}]", v) }
else uri << '&' << CGI.escape(key.to_s) << '=' << CGI.escape(value.to_s)
else uri << "&#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
end
end

View File

@ -12,7 +12,7 @@ class HttpRouter
code << part
dynamic = true
else
regex << (route.matches_with[part[1, part.size].to_sym] || '.*?').to_s unless path_validation_regex
regex << (route.matches_with && route.matches_with[part[1, part.size].to_sym] || '.*?').to_s unless path_validation_regex
code << "\#{args.shift || (options && options.delete(:#{part[1, part.size]})) || return}"
dynamic = true
end

View File

@ -83,11 +83,11 @@ class AbstractTest
@routes.case.each do |route_definition|
error("Too many keys! #{route_definition.keys.inspect}") unless route_definition.keys.size == 1
route_name, route_properties = route_definition.keys.first, route_definition.values.first
opts = {:name => route_name.to_sym}
route = case route_properties
when String
@router.add(route_properties)
@router.add(route_properties, opts)
when Hash
opts = {}
route_path = interpret_val(route_properties.delete("path"))
if route_properties.key?("conditions")
opts[:conditions] = Hash[route_properties.delete("conditions").map{|k, v| [k.to_sym, interpret_val(v)]}]
@ -102,7 +102,6 @@ class AbstractTest
else
error("Route isn't a String or hash")
end
route.name(route_name.to_sym)
route.to{|env| [200, {"env-to-test" => env.dup}, [route_name]]}
end
run_tests

View File

@ -26,11 +26,6 @@ class TestRouteExtensions < MiniTest::Unit::TestCase
assert_equal __FILE__, body.path
end
def test_chainable
router.get("/index.html").redirect("/").name(:root)
assert_equal "/index.html", router.url(:root)
end
def test_custom_status
router.get("/index.html").redirect("/", 303)
response = router.call(Rack::MockRequest.env_for("/index.html"))

View File

@ -1,9 +1,9 @@
class TestMisc < MiniTest::Unit::TestCase
def test_cloning
r1 = HttpRouter.new { add('/test').name(:test_route).to(:test) }
r1 = HttpRouter.new { add('/test', :name => :test_route).to(:test) }
r2 = r1.clone
r2.add('/test2').name(:test).to(:test2)
r2.add('/test2', :name => :test).to(:test2)
assert_equal 2, r2.routes.size
assert_equal nil, r1.recognize(Rack::Request.new(Rack::MockRequest.env_for('/test2')))
@ -11,7 +11,7 @@ class TestMisc < MiniTest::Unit::TestCase
assert_equal r1.routes.first, r1.named_routes[:test_route].first
assert_equal r2.routes.first, r2.named_routes[:test_route].first
r1.add('/another').name(:test).to(:test2)
r1.add('/another', :name => :test).to(:test2)
assert_equal r1.routes.size, r2.routes.size
assert_equal '/another', r1.url(:test)
@ -50,9 +50,9 @@ class TestMisc < MiniTest::Unit::TestCase
def test_multi_name_gen
r = HttpRouter.new
r.add('/').name(:index).default_destination
r.add('/:name').name(:index).default_destination
r.add('/:name/:category').name(:index).default_destination
r.add('/', :name => :index).default_destination
r.add('/:name', :name => :index).default_destination
r.add('/:name/:category', :name => :index).default_destination
assert_equal '/', r.url(:index)
assert_equal '/name', r.url(:index, 'name')
assert_equal '/name/category', r.url(:index, 'name', 'category')
@ -60,13 +60,13 @@ class TestMisc < MiniTest::Unit::TestCase
def test_regex_generation
r = HttpRouter.new
r.add(%r|/test/.*|, :path_for_generation => '/test/:variable').name(:route).default_destination
r.add(%r|/test/.*|, :path_for_generation => '/test/:variable', :name => :route).default_destination
assert_equal '/test/var', r.url(:route, "var")
end
def test_too_many_params
r = HttpRouter.new
r.add(%r|/test/.*|, :path_for_generation => '/test/:variable').name(:route).default_destination
r.add(%r|/test/.*|, :path_for_generation => '/test/:variable', :name => :route).default_destination
assert_equal '/test/var', r.url(:route, "var")
assert_equal '/test/var', r.url(:route, :variable => "var")
assert_raises(HttpRouter::InvalidRouteException) { r.url(:route) }
@ -74,7 +74,7 @@ class TestMisc < MiniTest::Unit::TestCase
def test_too_many_args
r = HttpRouter.new
r.add('/').name(:route).default_destination
r.add('/', :name => :route).default_destination
assert_raises(HttpRouter::TooManyParametersException) { r.url(:route, "hi") }
end

View File

@ -2,7 +2,7 @@ class TestMounting < MiniTest::Unit::TestCase
def setup
@r1 = HttpRouter.new
@r2 = HttpRouter.new
@r2.add("/bar").name(:test).to{|env| [200, {}, []]}
@r2.add("/bar", :name => :test).to{|env| [200, {}, []]}
end
def test_url_mount_for_child_route
@ -12,25 +12,25 @@ class TestMounting < MiniTest::Unit::TestCase
end
def test_default_values
route = @r1.add("/foo/:bar").default(:bar => "baz").to(@r2)
route = @r1.add("/foo/:bar", :default_values => {:bar => "baz"}).to(@r2)
assert_equal "/foo/baz/bar", @r2.url(:test)
assert_equal "/foo/haha/bar", @r2.url(:test, :bar => "haha")
end
def test_multiple_values
@r1.add("/foo/:bar/:baz").default(:bar => "bar").to(@r2)
@r1.add("/foo/:bar/:baz", :default_values => {:bar => "bar"}).to(@r2)
assert_equal "/foo/bar/baz/bar", @r2.url(:test, :baz => "baz")
end
def test_bubble_params
route = @r1.add("/foo/:bar").default(:bar => "baz").to(@r2)
route = @r1.add("/foo/:bar", :default_values => {:bar => 'baz'}).to(@r2)
assert_equal "/foo/baz/bar?bang=ers", @r2.url(:test, :bang => "ers")
assert_equal "/foo/haha/bar?bang=ers", @r2.url(:test, :bar => "haha", :bang => "ers")
end
def test_path_with_optional
@r1.add("/foo(/:bar)").to(@r2)
@r2.add("/hey(/:there)").name(:test2).to{|env| [200, {}, []]}
@r2.add("/hey(/:there)", :name => :test2).to{|env| [200, {}, []]}
assert_equal "/foo/hey", @r2.url(:test2)
assert_equal "/foo/bar/hey", @r2.url(:test2, :bar => "bar")
assert_equal "/foo/bar/hey/there", @r2.url(:test2, :bar => "bar", :there => "there")
@ -38,10 +38,10 @@ class TestMounting < MiniTest::Unit::TestCase
def test_nest3
@r3 = HttpRouter.new
@r1.add("/foo(/:bar)").default(:bar => "barry").to(@r2)
@r2.add("/hi").name(:hi).to{|env| [200, {}, []]}
@r1.add("/foo(/:bar)", :default_values => {:bar => 'barry'}).to(@r2)
@r2.add("/hi", :name => :hi).to{|env| [200, {}, []]}
@r2.add("/mounted").to(@r3)
@r3.add("/endpoint").name(:endpoint).to{|env| [200, {}, []]}
@r3.add("/endpoint", :name => :endpoint).to{|env| [200, {}, []]}
assert_equal "/foo/barry/hi", @r2.url(:hi)
assert_equal "/foo/barry/mounted/endpoint", @r3.url(:endpoint)
@ -49,7 +49,7 @@ class TestMounting < MiniTest::Unit::TestCase
end
def test_with_default_host
@r1.add("/mounted").default(:host => "example.com").to(@r2)
@r1.add("/mounted", :default_values => {:host => "example.com"}).to(@r2)
assert_equal "http://example.com/mounted/bar", @r2.url(:test)
end

View File

@ -8,63 +8,6 @@ class TestRecognition < MiniTest::Unit::TestCase
EOT
end
def test_match
hello, love80, love8080 = router {
add('test').arbitrary(Proc.new{|req, params| req.rack.host == 'hellodooly' })
add("test").arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 80}
add("test").arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 8080}
}
assert_route love8080, 'http://lovelove:8080/test'
end
def test_less_specific_node
hello, love80, love8080, general = router {
add("/test").arbitrary(Proc.new{|req, params| req.rack.host == 'hellodooly' })
add("/test").arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 80}
add("/test").arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 8080}
add("/test")
}
assert_route general, 'http://lovelove:8081/test'
assert_route hello, 'http://hellodooly:8081/test'
assert_route love80, 'http://lovelove:80/test'
assert_route love8080, 'http://lovelove:8080/test'
end
def test_match_request
love80, love8080 = router {
add("/test").get.arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 80}
add("/test").get.arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 8080}
}
assert_route love80, 'http://lovelove:80/test'
assert_route love8080, 'http://lovelove:8080/test'
end
def test_less_specific_with_request
love80, love8080, general = router {
add("test").post.arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 80}
add("test").post.arbitrary(Proc.new{|req, params| req.rack.host == 'lovelove' }).arbitrary{|req, params| req.rack.port == 8080}
add("test").post
}
assert_route love8080, Rack::MockRequest.env_for('http://lovelove:8080/test', :method => :post)
assert_route love80, Rack::MockRequest.env_for('http://lovelove:80/test', :method => :post)
assert_route general, Rack::MockRequest.env_for('/test', :method => :post)
end
def test_pass_params
r = router {
add(":test").get.arbitrary(Proc.new{|req, params, dest| params[:test] == 'test' })
}
assert_route r, '/test', {:test => 'test'}
end
def test_continue
no, yes = router {
add('test').arbitrary_with_continue{|req, p| req.continue[false]}
add('test').arbitrary_with_continue{|req, p| req.continue[true]}
}
assert_route yes, '/test'
end
def test_passing
passed, working = router {
add('/').to { |env| throw :pass; [200, {}, ['pass']] }
@ -90,16 +33,6 @@ class TestRecognition < MiniTest::Unit::TestCase
assert_body 'working', router.call(Rack::MockRequest.env_for('/'))
end
def test_request_mutation
got_this_far = false
non_matching, matching = router {
add("/test/:var/:var2/*glob").matching(:var2 => /123/, :glob => /[a-z]+/).get.arbitrary{|env, params| got_this_far = true; false}
add("/test/:var/:var2/*glob").matching(:var2 => /123/, :glob => /[a-z]+/).get
}
assert_route matching, '/test/123/123/asd/aasd/zxcqwe/asdzxc', {:var => '123', :var2 => '123', :glob => %w{asd aasd zxcqwe asdzxc}}
assert got_this_far, "matching should have gotten this far"
end
def test_compiling_uncompiling
@router = HttpRouter.new
root = @router.add('/').default_destination