http_router 0.6.5 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,22 +1,66 @@
1
- require 'rubygems'
2
- require 'bundler'
3
- require 'code_stats'
4
-
5
- desc "Run tests"
6
- task :test do
7
- $: << 'lib'
8
- require 'http_router'
9
- require './test/helper'
10
- Dir['./test/**/test_*.rb'].each { |test| require test }
11
- end
12
-
13
- require 'rake/rdoctask'
14
- desc "Generate documentation"
15
- Rake::RDocTask.new do |rd|
16
- rd.main = "README.rdoc"
17
- rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
18
- rd.rdoc_dir = 'rdoc'
19
- end
20
-
21
- Bundler::GemHelper.install_tasks
22
- CodeStats::Tasks.new(:reporting_depth => 3)
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ desc "Run all tests"
5
+ task :test => ['test:integration', 'test:examples']
6
+
7
+ namespace :test do
8
+ desc "Run integration tests"
9
+ task :integration do
10
+ $: << 'lib'
11
+ require 'http_router'
12
+ require './test/helper'
13
+ Dir['./test/**/test_*.rb'].each { |test| require test }
14
+ end
15
+ desc "Run example tests"
16
+ task :examples do
17
+ $: << 'lib'
18
+ require 'http_router'
19
+ require 'thin'
20
+ Dir['./examples/**/*.ru'].each do |example|
21
+ print "running example #{example}..."
22
+ comments = File.read(example).split(/\n/).select{|l| l[0] == ?#}
23
+ pid = nil
24
+ Thin::Logging.silent = true
25
+ begin
26
+ pid = fork {
27
+ code = "Proc.new { \n#{File.read(example)}\n }"
28
+ r = eval(code, binding, example, 2)
29
+ Thin::Server.start(:signals => false, &r)
30
+ }
31
+ sleep 0.5
32
+ out = nil
33
+ assertion_count = 0
34
+ comments.each do |c|
35
+ c.gsub!(/^# ?/, '')
36
+ case c
37
+ when /^\$/
38
+ out = `#{c[1, c.size]} 2>/dev/null`.split(/\n/)
39
+ raise "#{c} produced #{out}" unless $?.success?
40
+ when /^=> ?(.*)/
41
+ c = $1
42
+ raise "out was nil" if out.nil?
43
+ test = out.shift
44
+ raise "excepted #{c.inspect}, recieved #{test.inspect}" unless c.strip == test.strip
45
+ assertion_count += 1
46
+ end
47
+ end
48
+ raise "no assertions were raised in #{example}" if assertion_count.zero?
49
+ puts "✔"
50
+ ensure
51
+ Process.kill('HUP', pid) if pid
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ require 'rake/rdoctask'
58
+ desc "Generate documentation"
59
+ Rake::RDocTask.new do |rd|
60
+ rd.main = "README.rdoc"
61
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
62
+ rd.rdoc_dir = 'rdoc'
63
+ end
64
+
65
+ require 'code_stats'
66
+ CodeStats::Tasks.new(:reporting_depth => 3)
data/examples/glob.ru CHANGED
@@ -4,8 +4,8 @@ run HttpRouter.new {
4
4
  get('/*glob').to { |env| [200, {'Content-type' => 'text/plain'}, ["My glob is\n#{env['router.params'][:glob].map{|v| " * #{v}\n"}.join}"]]}
5
5
  }
6
6
 
7
- # crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/123/345/123
8
- # My glob is
9
- # * 123
10
- # * 345
11
- # * 123
7
+ # $ curl http://127.0.0.1:3000/123/345/123
8
+ # => My glob is
9
+ # => * 123
10
+ # => * 345
11
+ # => * 123
@@ -1,5 +1,5 @@
1
1
  require 'http_router'
2
- HttpRouter.override_rack_mapper!
2
+ HttpRouter::Rack.override_rack_builder!
3
3
 
4
4
  map('/get/:id') { |env|
5
5
  [200, {'Content-type' => 'text/plain'}, ["My id is #{env['router.params'][:id]}\n"]]
@@ -14,9 +14,9 @@ map('/get/:id', :matching => {:id => /\d+/}) { |env|
14
14
  [200, {'Content-type' => 'text/plain'}, ["My id is #{env['router.params'][:id]}, which is a number\n"]]
15
15
  }
16
16
 
17
- # crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/get/foo
18
- # My id is foo
19
- # crapbook-pro:~ joshua$ curl -X POST http://127.0.0.1:3000/get/foo
20
- # My id is foo and you posted!
21
- # crapbook-pro:~ joshua$ curl -X POST http://127.0.0.1:3000/get/123
22
- # My id is 123, which is a number
17
+ # $ curl http://127.0.0.1:3000/get/foo
18
+ # => My id is foo
19
+ # $ curl -X POST http://127.0.0.1:3000/get/foo
20
+ # => My id is foo and you posted!
21
+ # $ curl -X POST http://127.0.0.1:3000/get/123
22
+ # => My id is 123, which is a number
data/examples/simple.ru CHANGED
@@ -4,5 +4,5 @@ run HttpRouter.new {
4
4
  get('/hi').to { |env| [200, {'Content-type' => 'text/plain'}, ["hi!\n"]]}
5
5
  }
6
6
 
7
- # crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/hi
8
- # hi!
7
+ # $ curl http://127.0.0.1:3000/hi
8
+ # => hi!
@@ -5,22 +5,22 @@ require 'http_router'
5
5
  base = File.expand_path(File.dirname(__FILE__))
6
6
 
7
7
  run HttpRouter.new {
8
- get('/favicon.ico').static("#{base}/favicon.ico") # from a single file
9
- get('/images').static("#{base}/images") # or from a directory
8
+ add('/favicon.ico').static("#{base}/favicon.ico") # from a single file
9
+ add('/images').static("#{base}/images") # or from a directory
10
10
  }
11
11
 
12
- # crapbook-pro:~ joshua$ curl -I http://localhost:3000/favicon.ico
13
- # HTTP/1.1 200 OK
14
- # Last-Modified: Fri, 11 Jun 2010 21:02:22 GMT
15
- # Content-Type: image/vnd.microsoft.icon
16
- # Content-Length: 1150
17
- # Connection: keep-alive
18
- # Server: thin 1.2.7 codename No Hup
12
+ # $ curl -I http://localhost:3000/favicon.ico
13
+ # => HTTP/1.1 200 OK
14
+ # => Last-Modified: Sat, 26 Mar 2011 18:04:26 GMT
15
+ # => Content-Type: image/vnd.microsoft.icon
16
+ # => Content-Length: 1150
17
+ # => Connection: keep-alive
18
+ # => Server: thin 1.2.7 codename No Hup
19
19
  #
20
- # crapbook-pro:~ joshua$ curl -I http://localhost:3000/images/cat1.jpg
21
- # HTTP/1.1 200 OK
22
- # Last-Modified: Fri, 11 Jun 2010 21:54:16 GMT
23
- # Content-Type: image/jpeg
24
- # Content-Length: 29817
25
- # Connection: keep-alive
26
- # Server: thin 1.2.7 codename No Hup
20
+ # $ curl -I http://localhost:3000/images/cat1.jpg
21
+ # => HTTP/1.1 200 OK
22
+ # => Last-Modified: Sat, 26 Mar 2011 18:04:26 GMT
23
+ # => Content-Type: image/jpeg
24
+ # => Content-Length: 29817
25
+ # => Connection: keep-alive
26
+ # => Server: thin 1.2.7 codename No Hup
data/examples/variable.ru CHANGED
@@ -4,6 +4,6 @@ run HttpRouter.new {
4
4
  get('/:variable').to { |env| [200, {'Content-type' => 'text/plain'}, ["my variables are\n#{env['router.params'].inspect}\n"]]}
5
5
  }
6
6
 
7
- # crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/heyguys
8
- # my variables are
9
- # {:variable=>"heyguys"}
7
+ # $ curl http://127.0.0.1:3000/heyguys
8
+ # => my variables are
9
+ # => {:variable=>"heyguys"}
@@ -4,7 +4,7 @@ run HttpRouter.new {
4
4
  get('/get/:id').matching(:id => /\d+/).to { |env| [200, {'Content-type' => 'text/plain'}, ["id is #{Integer(env['router.params'][:id]) * 2} * 2\n"]]}
5
5
  }
6
6
 
7
- # crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/get/123
8
- # id is 246 * 2
9
- # crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/get/asd
10
- # Not Found
7
+ # $ curl http://127.0.0.1:3000/get/123
8
+ # => id is 246 * 2
9
+ # $ curl http://127.0.0.1:3000/get/asd
10
+ # => Your request couldn't be found
@@ -1,10 +1,6 @@
1
1
  class HttpRouter
2
2
  class Node
3
3
  class Request < Node
4
- def self.request_methods
5
- [:host, :scheme, :request_method, :user_agent]
6
- end
7
-
8
4
  attr_reader :request_method
9
5
 
10
6
  def initialize(router)
@@ -28,13 +24,14 @@ class HttpRouter
28
24
  @request_method = meth == :method ? :request_method : meth
29
25
  if @destination
30
26
  next_node = add_catchall
31
- next_node.instance_variable_set(:@destination, (next_node.instance_variable_get(:@destination) || []).concat(@destination))
32
- @destination.clear
27
+ next_node.instance_variable_set(:@destination, @destination)
28
+ @destination = nil
33
29
  end
34
30
  @request_method
35
31
  end
36
32
 
37
33
  def add_lookup(val)
34
+ @router.known_methods << val if @request_method == :request_method
38
35
  @lookup[val] ||= Request.new(@router)
39
36
  end
40
37
 
@@ -60,27 +60,7 @@ class HttpRouter
60
60
  def destination(request_obj, match_partially = true)
61
61
  request(request_obj)
62
62
  arbitrary(request_obj)
63
- if match_partially or request_obj.path.empty?
64
- @destination && @destination.each do |d|
65
- if request_obj.path.empty? or d.route.match_partially? or (@router.ignore_trailing_slash? and request_obj.path.size == 1 and request_obj.path.last == '')
66
- if request_obj.perform_call
67
- env = request_obj.rack_request.dup.env
68
- env['router.params'] ||= {}
69
- env['router.params'].merge!(d.hashify_params(request_obj.params))
70
- matched = if d.route.match_partially?
71
- env['PATH_INFO'] = "/#{request_obj.path.join('/')}"
72
- env['SCRIPT_NAME'] += request_obj.rack_request.path_info[0, request_obj.rack_request.path_info.size - env['PATH_INFO'].size]
73
- else
74
- env["PATH_INFO"] = ''
75
- env["SCRIPT_NAME"] += request_obj.rack_request.path_info
76
- end
77
- throw :success, d.route.dest.call(env)
78
- else
79
- throw :success, Response.new(request_obj, d)
80
- end
81
- end
82
- end
83
- end
63
+ @destination.call(request_obj, match_partially) if @destination
84
64
  end
85
65
 
86
66
  def add_variable
@@ -94,31 +74,25 @@ class HttpRouter
94
74
  def add_request(opts)
95
75
  @request ||= Request.new(@router)
96
76
  next_requests = [@request]
97
- Request.request_methods.each do |method|
98
- method_index = Request.request_methods.index(method)
77
+ @router.request_methods.each_with_index do |method, method_index|
99
78
  next_requests.map! do |next_request|
100
79
  if opts[method].nil? && next_request.request_method.nil?
101
80
  next_request
102
81
  else
103
- next_request_index = next_request.request_method && Request.request_methods.index(next_request.request_method)
82
+ next_request_index = next_request.request_method && @router.request_methods.index(next_request.request_method)
104
83
  rank = next_request_index ? method_index <=> next_request_index : 0
105
84
  case rank
106
85
  when 0
107
86
  next_request.request_method = method
108
87
  (opts[method].nil? ? [nil] : Array(opts[method])).map do |request_matcher|
109
88
  case request_matcher
110
- when nil
111
- next_request.add_catchall
112
- when String
113
- next_request.add_lookup(request_matcher)
114
- when Regexp
115
- next_request.add_linear(request_matcher)
89
+ when nil then next_request.add_catchall
90
+ when String then next_request.add_lookup(request_matcher)
91
+ when Regexp then next_request.add_linear(request_matcher)
116
92
  end
117
93
  end
118
- when -1
119
- next_request
120
- when 1
121
- next_request.transform_to(method)
94
+ when -1 then next_request
95
+ when 1 then next_request.transform_to(method)
122
96
  end
123
97
  end
124
98
  end
@@ -147,9 +121,8 @@ class HttpRouter
147
121
  @linear.last
148
122
  end
149
123
 
150
- def add_destination(route)
151
- @destination ||= []
152
- @destination << route
124
+ def add_destination(&dest)
125
+ @destination = dest
153
126
  end
154
127
 
155
128
  def add_lookup(part)
@@ -0,0 +1,69 @@
1
+ require 'http_router'
2
+
3
+ # Replacement for {Rack::Builder} which using HttpRouter to map requests instead of a simple Hash.
4
+ # As well, add convenience methods for the request methods.
5
+ module HttpRouter::Rack::BuilderMixin
6
+ def router
7
+ @router ||= HttpRouter.new
8
+ end
9
+
10
+ # Maps a path to a block.
11
+ # @param path [String] Path to map to.
12
+ # @param options [Hash] Options for added path.
13
+ # @see HttpRouter#add
14
+ def map(path, options = {}, method = nil, &block)
15
+ route = router.add(path, options)
16
+ route.send(method) if method
17
+ route.to(&block)
18
+ @ins << router unless @ins.last == router
19
+ route
20
+ end
21
+
22
+ # Maps a path with request methods `HEAD` and `GET` to a block.
23
+ # @param path [String] Path to map to.
24
+ # @param options [Hash] Options for added path.
25
+ # @see HttpRouter#add
26
+ def get(path, options = {}, &block)
27
+ map(path, options, :get, &block)
28
+ end
29
+
30
+ # Maps a path with request methods `POST` to a block.
31
+ # @param path [String] Path to map to.
32
+ # @param options [Hash] Options for added path.
33
+ # @see HttpRouter#add
34
+ def post(path, options = {}, &block)
35
+ map(path, options, :post, &block)
36
+ end
37
+
38
+ # Maps a path with request methods `PUT` to a block.
39
+ # @param path [String] Path to map to.
40
+ # @param options [Hash] Options for added path.
41
+ # @see HttpRouter#add
42
+ def put(path, options = {}, &block)
43
+ map(path, options, :put, &block)
44
+ end
45
+
46
+ # Maps a path with request methods `DELETE` to a block.
47
+ # @param path [String] Path to map to.
48
+ # @param options [Hash] Options for added path.
49
+ # @see HttpRouter#add
50
+ def delete(path, options = {}, &block)
51
+ map(path, options, :delete, &block)
52
+ end
53
+
54
+ # Maps a path with request methods `HEAD` to a block.
55
+ # @param path [String] Path to map to.
56
+ # @param options [Hash] Options for added path.
57
+ # @see HttpRouter#add
58
+ def head(path, options = {}, &block)
59
+ map(path, options, :head, &block)
60
+ end
61
+
62
+ def options(path, options = {}, &block)
63
+ map(path, options, :options, &block)
64
+ end
65
+ end
66
+
67
+ class HttpRouter::Rack::Builder < ::Rack::Builder
68
+ include HttpRouter::Rack::BuilderMixin
69
+ end
@@ -0,0 +1,16 @@
1
+ require 'http_router'
2
+
3
+ class HttpRouter
4
+ module Rack
5
+ class URLMap < ::Rack::URLMap
6
+ def initialize(map = {})
7
+ @router = HttpRouter.new
8
+ map.each { |path, app| (path =~ /^(https?):\/\/(.*?)(\/.*)/ ? @router.add($3).host($2).scheme($1) : @router.add(path)).partial.to(app) }
9
+ end
10
+
11
+ def call(env)
12
+ @router.call(env)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ class HttpRouter
2
+ module Rack
3
+ autoload :URLMap, 'http_router/rack/url_map'
4
+ autoload :Builder, 'http_router/rack/builder'
5
+ autoload :BuilderMixin, 'http_router/rack/builder'
6
+
7
+ # Monkey-patches Rack::Builder to use HttpRouter.
8
+ # See examples/rack_mapper.rb
9
+ def self.override_rack_builder!
10
+ ::Rack::Builder.class_eval("remove_method :map; include HttpRouter::Rack::BuilderMixin")
11
+ end
12
+
13
+ # Monkey-patches Rack::URLMap to use HttpRouter.
14
+ # See examples/rack_mapper.rb
15
+ def self.override_rack_urlmap!
16
+ ::Rack.class_eval("OriginalURLMap = URLMap; HttpRouterURLMap = HttpRouter::Rack::URLMap; remove_const :URLMap; URLMap = HttpRouterURLMap")
17
+ end
18
+ end
19
+ end
@@ -11,12 +11,15 @@ class HttpRouter
11
11
  @opts = opts
12
12
  @arbitrary = opts[:arbitrary] || opts[:__arbitrary__]
13
13
  @conditions = opts[:conditions] || opts[:__conditions__] || {}
14
- name(opts.delete(:name)) if opts.key?(:name)
14
+ name(opts[:name]) if opts.key?(:name)
15
+ @opts.merge!(opts[:matching]) if opts[:matching]
15
16
  @matches_with = {}
16
17
  @default_values = opts[:default_values] || {}
17
18
  if @original_path[-1] == ?*
18
19
  @match_partially = true
19
20
  path.slice!(-1)
21
+ elsif opts.key?(:partial)
22
+ @match_partially = opts[:partial]
20
23
  end
21
24
  @paths = OptionalCompiler.new(path).paths
22
25
  end
@@ -106,11 +109,12 @@ class HttpRouter
106
109
  self
107
110
  end
108
111
 
109
- def post; request_method('POST'); end
110
- def get; request_method('GET'); end
111
- def put; request_method('PUT'); end
112
- def delete; request_method('DELETE'); end
113
- def head; request_method('HEAD'); end
112
+ def post; request_method('POST'); end
113
+ def get; request_method('GET'); end
114
+ def put; request_method('PUT'); end
115
+ def delete; request_method('DELETE'); end
116
+ def head; request_method('HEAD'); end
117
+ def options; request_method('OPTIONS'); end
114
118
 
115
119
  def arbitrary(blk = nil, &blk2)
116
120
  arbitrary_with_continue { |req, params|
@@ -247,14 +251,31 @@ class HttpRouter
247
251
  end
248
252
 
249
253
  def add_non_path_to_tree(node, path, names)
250
- nodes = if @conditions && !@conditions.empty?
251
- node.add_request(@conditions)
252
- else
253
- [node]
254
- end
255
- @arbitrary.each{|a| nodes.map!{|n| n.add_arbitrary(a, match_partially?, names)} } if @arbitrary
256
254
  path_obj = Path.new(self, path, names)
257
- nodes.each{|n| n.add_destination(path_obj)}
255
+ destination = Proc.new { |req, use_partial_matching|
256
+ if (use_partial_matching or req.path.empty?)
257
+ if req.path.empty? or match_partially? or (@router.ignore_trailing_slash? and req.path.size == 1 and req.path.last == '')
258
+ if req.perform_call
259
+ env = req.rack_request.dup.env
260
+ env['router.params'] ||= {}
261
+ env['router.params'].merge!(path_obj.hashify_params(req.params))
262
+ matched = if match_partially?
263
+ env['PATH_INFO'] = "/#{req.path.join('/')}"
264
+ env['SCRIPT_NAME'] += req.rack_request.path_info[0, req.rack_request.path_info.size - env['PATH_INFO'].size]
265
+ else
266
+ env["PATH_INFO"] = ''
267
+ env["SCRIPT_NAME"] += req.rack_request.path_info
268
+ end
269
+ throw :success, @app.call(env)
270
+ else
271
+ throw :success, Response.new(req, path_obj)
272
+ end
273
+ end
274
+ end
275
+ }
276
+ nodes = @conditions && !@conditions.empty? ? node.add_request(@conditions) : [node]
277
+ @arbitrary.each{|a| nodes.map!{|n| n.add_arbitrary(a, match_partially?, names)} } if @arbitrary
278
+ nodes.map!{|n| n.add_destination(&destination)}
258
279
  if dest.respond_to?(:url_mount=)
259
280
  urlmount = UrlMount.new(@original_path, @default_values)
260
281
  urlmount.url_mount = router.url_mount if router.url_mount
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  class HttpRouter #:nodoc
3
- VERSION = '0.6.5'
3
+ VERSION = '0.6.6'
4
4
  end
data/lib/http_router.rb CHANGED
@@ -5,12 +5,13 @@ require 'http_router/request'
5
5
  require 'http_router/response'
6
6
  require 'http_router/route'
7
7
  require 'http_router/path'
8
+ require 'http_router/rack'
8
9
  require 'http_router/regex_route'
9
10
  require 'http_router/optional_compiler'
10
11
 
11
12
  class HttpRouter
12
13
 
13
- attr_reader :root, :routes, :known_methods, :named_routes
14
+ attr_reader :root, :routes, :known_methods, :named_routes, :request_methods
14
15
  attr_accessor :default_app, :url_mount
15
16
 
16
17
  # Raised when a Route is not able to be generated.
@@ -27,12 +28,16 @@ class HttpRouter
27
28
  # * :default_app -- Default application used if there is a non-match on #call. Defaults to 404 generator.
28
29
  # * :ignore_trailing_slash -- Ignore a trailing / when attempting to match. Defaults to +true+.
29
30
  # * :redirect_trailing_slash -- On trailing /, redirect to the same path without the /. Defaults to +false+.
31
+ # * :known_methods -- Array of http methods tested for 405s.
32
+ # * :request_methods -- Array of methods to use on request
30
33
  def initialize(*args, &blk)
31
- default_app, options = args.first.is_a?(Hash) ? [nil, args.first] : [args.first, args[1]]
34
+ default_app, options = args.first.is_a?(Hash) ? [nil, args.first] : [args.first, args[1]]
32
35
  @options = options
33
- @default_app = default_app || options && options[:default_app] || proc{|env| ::Rack::Response.new("Not Found", 404).finish }
34
- @ignore_trailing_slash = options && options.key?(:ignore_trailing_slash) ? options[:ignore_trailing_slash] : true
36
+ @default_app = default_app || options && options[:default_app] || proc{|env| ::Rack::Response.new("Not Found", 404).finish }
37
+ @ignore_trailing_slash = options && options.key?(:ignore_trailing_slash) ? options[:ignore_trailing_slash] : true
35
38
  @redirect_trailing_slash = options && options.key?(:redirect_trailing_slash) ? options[:redirect_trailing_slash] : false
39
+ @known_methods = Set.new(options && options[:known_methods] || [])
40
+ @request_methods = options && options[:request_methods] || [:host, :scheme, :request_method, :user_agent]
36
41
  reset!
37
42
  instance_eval(&blk) if blk
38
43
  end
@@ -71,17 +76,17 @@ class HttpRouter
71
76
  # Adds a path that only responds to the request method +GET+.
72
77
  #
73
78
  # Returns the route object.
74
- def get(path, opts = {}, &app); add_with_request_method(path, :get, opts, &app); end
79
+ def get(path, opts = {}, &app); add_with_request_method(path, :get, opts, &app); end
75
80
 
76
81
  # Adds a path that only responds to the request method +POST+.
77
82
  #
78
83
  # Returns the route object.
79
- def post(path, opts = {}, &app); add_with_request_method(path, :post, opts, &app); end
84
+ def post(path, opts = {}, &app); add_with_request_method(path, :post, opts, &app); end
80
85
 
81
86
  # Adds a path that only responds to the request method +HEAD+.
82
87
  #
83
88
  # Returns the route object.
84
- def head(path, opts = {}, &app); add_with_request_method(path, :head, opts, &app); end
89
+ def head(path, opts = {}, &app); add_with_request_method(path, :head, opts, &app); end
85
90
 
86
91
  # Adds a path that only responds to the request method +DELETE+.
87
92
  #
@@ -91,7 +96,12 @@ class HttpRouter
91
96
  # Adds a path that only responds to the request method +PUT+.
92
97
  #
93
98
  # Returns the route object.
94
- def put(path, opts = {}, &app); add_with_request_method(path, :put, opts, &app); end
99
+ def put(path, opts = {}, &app); add_with_request_method(path, :put, opts, &app); end
100
+
101
+ # Adds a path that only responds to the request method +OPTIONS+.
102
+ #
103
+ # Returns the route object.
104
+ def options(path, opts = {}, &app); add_with_request_method(path, :options, opts, &app); end
95
105
 
96
106
  def recognize(env)
97
107
  call(env, false)
@@ -101,7 +111,7 @@ class HttpRouter
101
111
  # the default application will be called. The router will be available in the env under the key <tt>router</tt>. And parameters matched will
102
112
  # be available under the key <tt>router.params</tt>.
103
113
  def call(env, perform_call = true)
104
- rack_request = Rack::Request.new(env)
114
+ rack_request = ::Rack::Request.new(env)
105
115
  if redirect_trailing_slash? && (rack_request.head? || rack_request.get?) && rack_request.path_info[-1] == ?/
106
116
  response = ::Rack::Response.new
107
117
  response.redirect(request.path_info[0, request.path_info.size - 1], 302)
@@ -111,9 +121,9 @@ class HttpRouter
111
121
  response = catch(:success) { @root[request] }
112
122
  if !response
113
123
  supported_methods = (@known_methods - [env['REQUEST_METHOD']]).select do |m|
114
- test_env = Rack::Request.new(rack_request.env.clone)
124
+ test_env = ::Rack::Request.new(rack_request.env.clone)
115
125
  test_env.env['REQUEST_METHOD'] = m
116
- test_env.env['HTTP_ROUTER_405_TESTING_ACCEPTANCE'] = true
126
+ test_env.env['_HTTP_ROUTER_405_TESTING_ACCEPTANCE'] = true
117
127
  test_request = Request.new(test_env.path_info, test_env, 405)
118
128
  catch(:success) { @root[test_request] }
119
129
  end
@@ -129,10 +139,9 @@ class HttpRouter
129
139
  # Resets the router to a clean state.
130
140
  def reset!
131
141
  @root = Node.new(self)
132
- @default_app = Proc.new{ |env| Rack::Response.new("Your request couldn't be found", 404).finish }
142
+ @default_app = Proc.new{ |env| ::Rack::Response.new("Your request couldn't be found", 404).finish }
133
143
  @routes = []
134
144
  @named_routes = {}
135
- @known_methods = ['GET', "POST", "PUT", "DELETE"]
136
145
  end
137
146
 
138
147
  # Assigns the default application.
@@ -107,12 +107,15 @@ class TestVariable < MiniTest::Unit::TestCase
107
107
  end
108
108
 
109
109
  def test_regex_and_greedy
110
- with_regex, without_regex = router {
110
+ with_regex, without_regex, with_post = router {
111
111
  add("/:common_variable/:matched").matching(:matched => /\d+/)
112
112
  add("/:common_variable/:unmatched")
113
+ post("/:common_variable/:unmatched")
113
114
  }
114
115
  assert_route with_regex, '/common/123', {:common_variable => 'common', :matched => '123'}
115
116
  assert_route without_regex, '/common/other', {:common_variable => 'common', :unmatched => 'other'}
117
+ assert_route with_regex, Rack::MockRequest.env_for('/common/123', :method => 'POST'), {:common_variable => 'common', :matched => '123'}
118
+ assert_route with_post, Rack::MockRequest.env_for('/common/other', :method => 'POST'), {:common_variable => 'common', :unmatched => 'other'}
116
119
  end
117
120
 
118
121
  if //.respond_to?(:names)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: http_router
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 6
9
- - 5
10
- version: 0.6.5
9
+ - 6
10
+ version: 0.6.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Joshua Hull
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-23 00:00:00 -07:00
18
+ date: 2011-03-27 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -160,7 +160,6 @@ files:
160
160
  - benchmarks/rec2.rb
161
161
  - benchmarks/recognition_bm.rb
162
162
  - examples/glob.ru
163
- - examples/middleware.ru
164
163
  - examples/rack_mapper.ru
165
164
  - examples/simple.ru
166
165
  - examples/static/config.ru
@@ -168,7 +167,6 @@ files:
168
167
  - examples/static/images/cat1.jpg
169
168
  - examples/static/images/cat2.jpg
170
169
  - examples/static/images/cat3.jpg
171
- - examples/unnamed_variable.ru
172
170
  - examples/variable.ru
173
171
  - examples/variable_with_regex.ru
174
172
  - http_router.gemspec
@@ -184,6 +182,9 @@ files:
184
182
  - lib/http_router/node/variable.rb
185
183
  - lib/http_router/optional_compiler.rb
186
184
  - lib/http_router/path.rb
185
+ - lib/http_router/rack.rb
186
+ - lib/http_router/rack/builder.rb
187
+ - lib/http_router/rack/url_map.rb
187
188
  - lib/http_router/regex_route.rb
188
189
  - lib/http_router/request.rb
189
190
  - lib/http_router/response.rb
@@ -1,46 +0,0 @@
1
- require 'http_router'
2
-
3
- use(HttpRouter, :middleware => true) {
4
- add('/test').name(:test)
5
- add('/:variable').name(:var)
6
- add('/more/*glob').name(:glob)
7
- add('/get/:id').matching(:id => /\d+/).name(:get)
8
- }
9
-
10
- run proc {|env|
11
- [
12
- 200,
13
- {'Content-type' => 'text/plain'},
14
- [<<-HEREDOC
15
- We matched? #{env['router.response'] && env['router.response'].matched? ? 'yes!' : 'no'}
16
- Params are #{env['router.response'] && env['router.response'].matched? ? env['router.response'].params_as_hash.inspect : 'we had no params'}
17
- That was fun
18
- HEREDOC
19
- ]
20
- ]
21
- }
22
-
23
- # crapbook-pro:polleverywhere joshua$ curl http://127.0.0.1:3000/hi
24
- # We matched? yes!
25
- # Params are {:variable=>"hi"}
26
- # That was fun
27
- # crapbook-pro:polleverywhere joshua$ curl http://127.0.0.1:3000/test
28
- # We matched? yes!
29
- # Params are {}
30
- # That was fun
31
- # crapbook-pro:polleverywhere joshua$ curl http://127.0.0.1:3000/hey
32
- # We matched? yes!
33
- # Params are {:variable=>"hey"}
34
- # That was fun
35
- # crapbook-pro:polleverywhere joshua$ curl http://127.0.0.1:3000/more/fun/in/the/sun
36
- # We matched? yes!
37
- # Params are {:glob=>["fun", "in", "the", "sun"]}
38
- # That was fun
39
- # crapbook-pro:polleverywhere joshua$ curl http://127.0.0.1:3000/get/what
40
- # We matched? no
41
- # Params are we had no params
42
- # That was fun
43
- # crapbook-pro:polleverywhere joshua$ curl http://127.0.0.1:3000/get/123
44
- # We matched? yes!
45
- # Params are {:id=>"123"}
46
- # That was fun
@@ -1,9 +0,0 @@
1
- require 'http_router'
2
-
3
- run HttpRouter.new {
4
- get('/:').to { |env| [200, {'Content-type' => 'text/plain'}, ["my variables are\n#{env['router.params'].inspect}\n"]]}
5
- }
6
-
7
- # crapbook-pro:~ joshua$ curl http://127.0.0.1:3000/heyguys
8
- # my variables are
9
- # {:$1=>"heyguys"}