add back in rack helpers. make examples verifiable

This commit is contained in:
Josh Hull 2011-03-27 00:49:00 -07:00
parent 9fd85e83c3
commit 30b6c1709f
15 changed files with 190 additions and 96 deletions

View File

@ -20,3 +20,43 @@ end
Bundler::GemHelper.install_tasks
CodeStats::Tasks.new(:reporting_depth => 3)
task :test_examples do
$: << 'lib'
require 'http_router'
require 'thin'
Dir['./examples/**/*.ru'].each do |example|
print "running example #{example}..."
comments = File.read(example).split(/\n/).select{|l| l[0] == ?#}
pid = nil
Thin::Logging.silent = true
begin
pid = fork {
code = "Proc.new { \n#{File.read(example)}\n }"
r = eval(code, binding, example, 2)
Thin::Server.start(:signals => false, &r)
}
sleep 0.5
out = nil
assertion_count = 0
comments.each do |c|
c.gsub!(/^# ?/, '')
case c
when /^\$/
out = `#{c[1, c.size]} 2>/dev/null`.split(/\n/)
raise "#{c} produced #{out}" unless $?.success?
when /^=> ?(.*)/
c = $1
raise "out was nil" if out.nil?
test = out.shift
raise "excepted #{c.inspect}, recieved #{test.inspect}" unless c.strip == test.strip
assertion_count += 1
end
end
raise "no assertions were raised in #{example}" if assertion_count.zero?
puts ""
ensure
Process.kill('HUP', pid) if pid
end
end
end

View File

@ -4,8 +4,8 @@ run HttpRouter.new {
get('/*glob').to { |env| [200, {'Content-type' => 'text/plain'}, ["My glob is\n#{env['router.params'][:glob].map{|v| " * #{v}\n"}.join}"]]}
}
# crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/123/345/123
# My glob is
# * 123
# * 345
# * 123
# $ curl http://127.0.0.1:3000/123/345/123
# => My glob is
# => * 123
# => * 345
# => * 123

View File

@ -1,46 +0,0 @@
require 'http_router'
use(HttpRouter, :middleware => true) {
add('/test').name(:test)
add('/:variable').name(:var)
add('/more/*glob').name(:glob)
add('/get/:id').matching(:id => /\d+/).name(:get)
}
run proc {|env|
[
200,
{'Content-type' => 'text/plain'},
[<<-HEREDOC
We matched? #{env['router.response'] && env['router.response'].matched? ? 'yes!' : 'no'}
Params are #{env['router.response'] && env['router.response'].matched? ? env['router.response'].params_as_hash.inspect : 'we had no params'}
That was fun
HEREDOC
]
]
}
# crapbook-pro:polleverywhere joshua$ curl http://127.0.0.1:3000/hi
# We matched? yes!
# Params are {:variable=>"hi"}
# That was fun
# crapbook-pro:polleverywhere joshua$ curl http://127.0.0.1:3000/test
# We matched? yes!
# Params are {}
# That was fun
# crapbook-pro:polleverywhere joshua$ curl http://127.0.0.1:3000/hey
# We matched? yes!
# Params are {:variable=>"hey"}
# That was fun
# crapbook-pro:polleverywhere joshua$ curl http://127.0.0.1:3000/more/fun/in/the/sun
# We matched? yes!
# Params are {:glob=>["fun", "in", "the", "sun"]}
# That was fun
# crapbook-pro:polleverywhere joshua$ curl http://127.0.0.1:3000/get/what
# We matched? no
# Params are we had no params
# That was fun
# crapbook-pro:polleverywhere joshua$ curl http://127.0.0.1:3000/get/123
# We matched? yes!
# Params are {:id=>"123"}
# That was fun

View File

@ -1,5 +1,5 @@
require 'http_router'
HttpRouter.override_rack_mapper!
HttpRouter::Rack.override_rack_builder!
map('/get/:id') { |env|
[200, {'Content-type' => 'text/plain'}, ["My id is #{env['router.params'][:id]}\n"]]
@ -14,9 +14,9 @@ map('/get/:id', :matching => {:id => /\d+/}) { |env|
[200, {'Content-type' => 'text/plain'}, ["My id is #{env['router.params'][:id]}, which is a number\n"]]
}
# crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/get/foo
# My id is foo
# crapbook-pro:~ joshua$ curl -X POST http://127.0.0.1:3000/get/foo
# My id is foo and you posted!
# crapbook-pro:~ joshua$ curl -X POST http://127.0.0.1:3000/get/123
# My id is 123, which is a number
# $ curl http://127.0.0.1:3000/get/foo
# => My id is foo
# $ curl -X POST http://127.0.0.1:3000/get/foo
# => My id is foo and you posted!
# $ curl -X POST http://127.0.0.1:3000/get/123
# => My id is 123, which is a number

View File

@ -4,5 +4,5 @@ run HttpRouter.new {
get('/hi').to { |env| [200, {'Content-type' => 'text/plain'}, ["hi!\n"]]}
}
# crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/hi
# hi!
# $ curl http://127.0.0.1:3000/hi
# => hi!

View File

@ -5,22 +5,22 @@ require 'http_router'
base = File.expand_path(File.dirname(__FILE__))
run HttpRouter.new {
get('/favicon.ico').static("#{base}/favicon.ico") # from a single file
get('/images').static("#{base}/images") # or from a directory
add('/favicon.ico').static("#{base}/favicon.ico") # from a single file
add('/images').static("#{base}/images") # or from a directory
}
# crapbook-pro:~ joshua$ curl -I http://localhost:3000/favicon.ico
# HTTP/1.1 200 OK
# Last-Modified: Fri, 11 Jun 2010 21:02:22 GMT
# Content-Type: image/vnd.microsoft.icon
# Content-Length: 1150
# Connection: keep-alive
# Server: thin 1.2.7 codename No Hup
# $ curl -I http://localhost:3000/favicon.ico
# => HTTP/1.1 200 OK
# => Last-Modified: Sat, 26 Mar 2011 18:04:26 GMT
# => Content-Type: image/vnd.microsoft.icon
# => Content-Length: 1150
# => Connection: keep-alive
# => Server: thin 1.2.7 codename No Hup
#
# crapbook-pro:~ joshua$ curl -I http://localhost:3000/images/cat1.jpg
# HTTP/1.1 200 OK
# Last-Modified: Fri, 11 Jun 2010 21:54:16 GMT
# Content-Type: image/jpeg
# Content-Length: 29817
# Connection: keep-alive
# Server: thin 1.2.7 codename No Hup
# $ curl -I http://localhost:3000/images/cat1.jpg
# => HTTP/1.1 200 OK
# => Last-Modified: Sat, 26 Mar 2011 18:04:26 GMT
# => Content-Type: image/jpeg
# => Content-Length: 29817
# => Connection: keep-alive
# => Server: thin 1.2.7 codename No Hup

View File

@ -1,9 +0,0 @@
require 'http_router'
run HttpRouter.new {
get('/:').to { |env| [200, {'Content-type' => 'text/plain'}, ["my variables are\n#{env['router.params'].inspect}\n"]]}
}
# crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/heyguys
# my variables are
# {:$1=>"heyguys"}

View File

@ -4,6 +4,6 @@ run HttpRouter.new {
get('/:variable').to { |env| [200, {'Content-type' => 'text/plain'}, ["my variables are\n#{env['router.params'].inspect}\n"]]}
}
# crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/heyguys
# my variables are
# {:variable=>"heyguys"}
# $ curl http://127.0.0.1:3000/heyguys
# => my variables are
# => {:variable=>"heyguys"}

View File

@ -4,7 +4,7 @@ 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"]]}
}
# crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/get/123
# id is 246 * 2
# crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/get/asd
# Not Found
# $ curl http://127.0.0.1:3000/get/123
# => id is 246 * 2
# $ curl http://127.0.0.1:3000/get/asd
# => Your request couldn't be found

View File

@ -5,6 +5,7 @@ require 'http_router/request'
require 'http_router/response'
require 'http_router/route'
require 'http_router/path'
require 'http_router/rack'
require 'http_router/regex_route'
require 'http_router/optional_compiler'
@ -110,7 +111,7 @@ 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)
rack_request = ::Rack::Request.new(env)
if redirect_trailing_slash? && (rack_request.head? || rack_request.get?) && rack_request.path_info[-1] == ?/
response = ::Rack::Response.new
response.redirect(request.path_info[0, request.path_info.size - 1], 302)
@ -120,7 +121,7 @@ class HttpRouter
response = catch(:success) { @root[request] }
if !response
supported_methods = (@known_methods - [env['REQUEST_METHOD']]).select do |m|
test_env = Rack::Request.new(rack_request.env.clone)
test_env = ::Rack::Request.new(rack_request.env.clone)
test_env.env['REQUEST_METHOD'] = m
test_env.env['_HTTP_ROUTER_405_TESTING_ACCEPTANCE'] = true
test_request = Request.new(test_env.path_info, test_env, 405)
@ -138,7 +139,7 @@ class HttpRouter
# Resets the router to a clean state.
def reset!
@root = Node.new(self)
@default_app = Proc.new{ |env| Rack::Response.new("Your request couldn't be found", 404).finish }
@default_app = Proc.new{ |env| ::Rack::Response.new("Your request couldn't be found", 404).finish }
@routes = []
@named_routes = {}
end

19
lib/http_router/rack.rb Normal file
View File

@ -0,0 +1,19 @@
class HttpRouter
module Rack
autoload :URLMap, 'http_router/rack/url_map'
autoload :Builder, 'http_router/rack/builder'
autoload :BuilderMixin, 'http_router/rack/builder'
# Monkey-patches Rack::Builder to use HttpRouter.
# See examples/rack_mapper.rb
def self.override_rack_builder!
::Rack::Builder.class_eval("remove_method :map; include HttpRouter::Rack::BuilderMixin")
end
# Monkey-patches Rack::URLMap to use HttpRouter.
# See examples/rack_mapper.rb
def self.override_rack_urlmap!
::Rack.class_eval("OriginalURLMap = URLMap; HttpRouterURLMap = HttpRouter::Rack::URLMap; remove_const :URLMap; URLMap = HttpRouterURLMap")
end
end
end

View File

@ -0,0 +1,69 @@
require 'http_router'
# Replacement for {Rack::Builder} which using HttpRouter to map requests instead of a simple Hash.
# As well, add convenience methods for the request methods.
module HttpRouter::Rack::BuilderMixin
def router
@router ||= HttpRouter.new
end
# Maps a path to a block.
# @param path [String] Path to map to.
# @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.to(&block)
@ins << router unless @ins.last == router
route
end
# Maps a path with request methods `HEAD` and `GET` to a block.
# @param path [String] Path to map to.
# @param options [Hash] Options for added path.
# @see HttpRouter#add
def get(path, options = {}, &block)
map(path, options, :get, &block)
end
# Maps a path with request methods `POST` to a block.
# @param path [String] Path to map to.
# @param options [Hash] Options for added path.
# @see HttpRouter#add
def post(path, options = {}, &block)
map(path, options, :post, &block)
end
# Maps a path with request methods `PUT` to a block.
# @param path [String] Path to map to.
# @param options [Hash] Options for added path.
# @see HttpRouter#add
def put(path, options = {}, &block)
map(path, options, :put, &block)
end
# Maps a path with request methods `DELETE` to a block.
# @param path [String] Path to map to.
# @param options [Hash] Options for added path.
# @see HttpRouter#add
def delete(path, options = {}, &block)
map(path, options, :delete, &block)
end
# Maps a path with request methods `HEAD` to a block.
# @param path [String] Path to map to.
# @param options [Hash] Options for added path.
# @see HttpRouter#add
def head(path, options = {}, &block)
map(path, options, :head, &block)
end
def options(path, options = {}, &block)
map(path, options, :options, &block)
end
end
class HttpRouter::Rack::Builder < ::Rack::Builder
include HttpRouter::Rack::BuilderMixin
end

View File

@ -0,0 +1,16 @@
require 'http_router'
class HttpRouter
module Rack
class URLMap < ::Rack::URLMap
def initialize(map = {})
@router = HttpRouter.new
map.each { |path, app| (path =~ /^(https?):\/\/(.*?)(\/.*)/ ? @router.add($3).host($2).scheme($1) : @router.add(path)).partial.to(app) }
end
def call(env)
@router.call(env)
end
end
end
end

View File

@ -12,6 +12,7 @@ class HttpRouter
@arbitrary = opts[:arbitrary] || opts[:__arbitrary__]
@conditions = opts[:conditions] || opts[:__conditions__] || {}
name(opts.delete(:name)) if opts.key?(:name)
@opts.merge!(opts[:matching]) if opts[:matching]
@matches_with = {}
@default_values = opts[:default_values] || {}
if @original_path[-1] == ?*

View File

@ -107,12 +107,15 @@ class TestVariable < MiniTest::Unit::TestCase
end
def test_regex_and_greedy
with_regex, without_regex = router {
with_regex, without_regex, with_post = router {
add("/:common_variable/:matched").matching(:matched => /\d+/)
add("/:common_variable/:unmatched")
post("/:common_variable/:unmatched")
}
assert_route with_regex, '/common/123', {:common_variable => 'common', :matched => '123'}
assert_route without_regex, '/common/other', {:common_variable => 'common', :unmatched => 'other'}
assert_route with_regex, Rack::MockRequest.env_for('/common/123', :method => 'POST'), {:common_variable => 'common', :matched => '123'}
assert_route with_post, Rack::MockRequest.env_for('/common/other', :method => 'POST'), {:common_variable => 'common', :unmatched => 'other'}
end
if //.respond_to?(:names)