http_router 0.6.5 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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"}