soar_sc-rack-router 0.1.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d498fa3bf594aa2b22ca48aa31b8cb8097796c5d
4
- data.tar.gz: 8c925e0ddf11b97bb5818aedabba4e0b955b756b
3
+ metadata.gz: 06db72006f7280dbcdfd881a108d084e9fb67b79
4
+ data.tar.gz: 4ffcd46d095e341fce59740dad5a6faaa3ed8552
5
5
  SHA512:
6
- metadata.gz: 3642dec09f7242a7a0475790c2c92262efab5790be0361714c8a42d117a90b9e85ba78d140a3aa729da5812b602de625ee478747ae05b485476e019156a8a1ae
7
- data.tar.gz: 8e407af01156f52e78b79144c80cb225e471234c5d0ad608b298dbe389e26e627b37049faa91983a887dc6f4f6e3b60b0bf3e5ad546afae85672c0707ce3fd27
6
+ metadata.gz: 1962096dc371cc19ce1d25bde13a0724f9d934006bcf0a0c36036f7bc3b762a2154025effb39aa52aa8f90c2b5a024ddf502e22d8683c3f27376e5bfb38122f2
7
+ data.tar.gz: c7b0b28b6ec325022474b76bff8194de423beb484b185eba8516d7c9549eaaea0a60c16bbbda5a20793ad883663ca2201af1d38116313a3d877426dd30f7bc50
data/README.md CHANGED
@@ -14,6 +14,11 @@ Because it supports parameterized URLs, it is familiar to users of several Ruby
14
14
  into WADL and other service descriptions. The parameter support is compatible with Rack::Request, without pushing a dependency on Rack::Request
15
15
  down into the router action API; the only requirement for router actions is that they are rack apps.
16
16
 
17
+ Users of Ruby web frameworks like Rails and Sinatra may be surprised that double-slashes in request paths are not normalized.
18
+ In Sinatra, this is actually done by the `rack-protection` middleware, not `sinatra-router`.
19
+ Users of `SoarSc::Rack::Router` definitely should be using the [rack-protection](https://rubygems.org/gems/rack-protection) middleware
20
+ above the router in their stacks.
21
+
17
22
  ## Installation
18
23
 
19
24
  Add this line to your application's Gemfile:
@@ -74,10 +79,12 @@ stack = Rack::Builder.new do
74
79
  use Auditing
75
80
  run ->(...)
76
81
  end
77
-
78
- run ->(env) { [404, {'Content-Type' => 'text/plain'}, ['Object Not Found']] }
79
82
  end
83
+
84
+ run ->(env) { [404, {'Content-Type' => 'text/plain'}, ['Object Not Found']] }
80
85
  end
86
+
87
+ run stack
81
88
  ```
82
89
 
83
90
  An example of parameterized URL support:
@@ -100,6 +107,25 @@ end
100
107
  run stack
101
108
  ```
102
109
 
110
+ Note that, even though the router doesn't break the rack stack by demanding that routing actions receive a `Rack::Request`,
111
+ it's recommended that routing actions use `Rack::Request` internally to retrieve path parameters. The router adds them to rack's
112
+ `env` using `Rack::Request#update_param`, making `Rack::Request#params` the safest way to retrieve them.
113
+
114
+ Finally, here is a demonstration that doesn't use `Rack::Builder`, just to clarify that it's not a dependency:
115
+
116
+ ```ruby
117
+ require 'soar_sc/rack/router'
118
+
119
+ index = ->(env) { ... }
120
+ about = ->(env) { ... }
121
+ contact = ->(env) { ...]
122
+
123
+ run SoarSc::Rack::Router.new(index) do
124
+ get "/about", about
125
+ get "/contact", contact
126
+ end
127
+ ```
128
+
103
129
  ## Development
104
130
 
105
131
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,5 +1,6 @@
1
1
  require "soar_sc/rack/router/builder_syntax"
2
2
  require "soar_sc/rack/router/errors"
3
+ require "soar_sc/rack/router/http_method"
3
4
  require "soar_sc/rack/router/route"
4
5
  require "soar_sc/rack/router/version"
5
6
 
@@ -11,18 +12,17 @@ module SoarSc
11
12
 
12
13
  include BuilderSyntax
13
14
 
15
+ EMPTY_STRING = ""
16
+
14
17
  def initialize(app, routes = {}, &block)
15
18
  @app = app
16
- @static_routes = []
17
- @parameterized_routes = []
18
- routes.each { |(p, a)| add_route(Route.new('*', p, a)) }
19
+ @routes = []
20
+ routes.each { |(p, a)| add_route(Route.new(HttpMethod::ANY, p, a)) }
19
21
  instance_eval(&block) if block_given?
20
22
  end
21
23
 
22
24
  def call(env)
23
- if route = @static_routes.detect { |r| r.matches?(env) }
24
- route.action.call(env)
25
- elsif route = @parameterized_routes.detect { |r| r.matches?(env) }
25
+ if route = @routes.detect { |r| r.matches?(env) }
26
26
  update_path_parameters!(env, route.extract_path_parameters(env))
27
27
  route.action.call(env)
28
28
  else
@@ -34,18 +34,33 @@ module SoarSc
34
34
 
35
35
  # Called by BuilderSyntax methods
36
36
  def add_route(route)
37
- if (@static_routes + @parameterized_routes).detect { |r| r.path == route.path and (r.method == route.method or r.method = "*") }
37
+ if include?(route)
38
38
  raise DuplicateRouteError.for_route(route)
39
- elsif route.static?
40
- @static_routes << route
41
39
  else
42
- @parameterized_routes << route
40
+ @routes << route
41
+ end
42
+ end
43
+
44
+ def include?(route)
45
+ @routes.detect do |other|
46
+ route.matches?(env_for_route(other)) or other.matches?(env_for_route(route))
43
47
  end
44
48
  end
45
49
 
50
+ # Creates a minimal env that would be matched by the given route.
51
+ def env_for_route(route)
52
+ {
53
+ Route::REQUEST_METHOD => (route.method == HttpMethod::ANY ? HttpMethod::GET : route.method),
54
+ Route::SCRIPT_NAME => route.path,
55
+ Route::PATH_INFO => EMPTY_STRING
56
+ }
57
+ end
58
+
46
59
  def update_path_parameters!(env, params)
47
- req = ::Rack::Request.new(env)
48
- params.each { |p, v| req.update_param(p, v) }
60
+ unless params.empty?
61
+ req = ::Rack::Request.new(env)
62
+ params.each { |p, v| req.update_param(p, v) }
63
+ end
49
64
  end
50
65
 
51
66
  end
@@ -1,3 +1,5 @@
1
+ require 'soar_sc/rack/router/http_method'
2
+
1
3
  module SoarSc
2
4
  module Rack
3
5
  class Router
@@ -5,39 +7,39 @@ module SoarSc
5
7
  module BuilderSyntax
6
8
 
7
9
  def map(path, app)
8
- add_route(Route.new('*', path, app))
10
+ add_route(Route.new(HttpMethod::ANY, path, app))
9
11
  end
10
12
 
11
13
  def get(path, app)
12
- add_route(Route.new('GET', path, app))
14
+ add_route(Route.new(HttpMethod::GET, path, app))
13
15
  end
14
16
 
15
17
  def post(path, app)
16
- add_route(Route.new('POST', path, app))
18
+ add_route(Route.new(HttpMethod::POST, path, app))
17
19
  end
18
20
 
19
21
  def put(path, app)
20
- add_route(Route.new('PUT', path, app))
22
+ add_route(Route.new(HttpMethod::PUT, path, app))
21
23
  end
22
24
 
23
25
  def delete(path, app)
24
- add_route(Route.new('DELETE', path, app))
26
+ add_route(Route.new(HttpMethod::DELETE, path, app))
25
27
  end
26
28
 
27
29
  def options(path, app)
28
- add_route(Route.new('OPTIONS', path, app))
30
+ add_route(Route.new(HttpMethod::OPTIONS, path, app))
29
31
  end
30
32
 
31
33
  def head(path, app)
32
- add_route(Route.new('HEAD', path, app))
34
+ add_route(Route.new(HttpMethod::HEAD, path, app))
33
35
  end
34
36
 
35
37
  def trace(path, app)
36
- add_route(Route.new('TRACE', path, app))
38
+ add_route(Route.new(HttpMethod::TRACE, path, app))
37
39
  end
38
40
 
39
41
  def connect(path, app)
40
- add_route(Route.new('CONNECT', path, app))
42
+ add_route(Route.new(HttpMethod::CONNECT, path, app))
41
43
  end
42
44
 
43
45
  end
@@ -0,0 +1,19 @@
1
+ module SoarSc
2
+ module Rack
3
+ class Router
4
+ module HttpMethod
5
+
6
+ ANY = "*"
7
+ GET = "GET"
8
+ POST = "POST"
9
+ PUT = "PUT"
10
+ DELETE = "DELETE"
11
+ HEAD = "HEAD"
12
+ OPTIONS = "OPTIONS"
13
+ TRACE = "TRACE"
14
+ CONNECT = "CONNECT"
15
+
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,35 +1,50 @@
1
+ require 'soar_sc/rack/router/http_method'
2
+
3
+ # TODO Normalize paths (compress runs of slashes)
1
4
  module SoarSc
2
5
  module Rack
3
6
  class Router
4
7
 
5
8
  class Route
9
+ PARAMETER_PREFIX = ":"
10
+ PARAMETER_VALID_REGEXP = /:[a-z_][a-z0-9_]*/
11
+ REQUEST_METHOD = "REQUEST_METHOD"
12
+ SCRIPT_NAME = "SCRIPT_NAME"
13
+ PATH_INFO = "PATH_INFO"
14
+ EMPTY_HASH = {}.freeze
15
+
6
16
  attr_reader :method, :path, :action
7
17
 
8
18
  def initialize(method, path, action)
9
19
  validate_path(path)
10
20
  @method, @path, @action = method, path, action
11
- end
12
-
13
- def static?
14
- components(path).none? { |c| c.start_with?(":") }
21
+ @static = components(path).none? { |c| c.start_with?(PARAMETER_PREFIX) }
15
22
  end
16
23
 
17
24
  def matches?(env)
18
- return false unless (method == env["REQUEST_METHOD"] or method == "*")
25
+ return false unless method == env[REQUEST_METHOD] or method == HttpMethod::ANY
26
+
27
+ return request_path(env) == path if @static
19
28
 
29
+ route_components = components(path)
20
30
  req_components = components(request_path(env))
21
- components(path).each_with_index do |c, i|
22
- return false unless c.start_with?(":") or c == req_components[i]
31
+
32
+ return false unless route_components.size == req_components.size
33
+
34
+ route_components.each_with_index do |c, i|
35
+ return false unless c.start_with?(PARAMETER_PREFIX) or c == req_components[i]
23
36
  end
24
37
 
25
38
  return true
26
39
  end
27
40
 
28
41
  def extract_path_parameters(env)
42
+ return EMPTY_HASH if @static
43
+
29
44
  req_components = components(request_path(env))
30
45
  parameters = {}
31
46
  components(path).each_with_index do |c, i|
32
- parameters[c[1..-1]] = req_components[i] if c.start_with?(":")
47
+ parameters[c[1..-1]] = req_components[i] if c.start_with?(PARAMETER_PREFIX)
33
48
  end
34
49
  parameters
35
50
  end
@@ -37,7 +52,7 @@ module SoarSc
37
52
  private
38
53
 
39
54
  def request_path(env)
40
- env["SCRIPT_NAME"].to_s + env["PATH_INFO"].to_s
55
+ env[SCRIPT_NAME] + env[PATH_INFO]
41
56
  end
42
57
 
43
58
  def components(path)
@@ -46,7 +61,7 @@ module SoarSc
46
61
 
47
62
  def validate_path(path)
48
63
  components(path).each do |c|
49
- if c.start_with?(":") and c !~ /:[a-z_][a-z0-9_]*+/
64
+ if c.start_with?(PARAMETER_PREFIX) and c !~ PARAMETER_VALID_REGEXP
50
65
  raise InvalidParameterError, "Invalid parameter #{c} in path #{path}"
51
66
  end
52
67
  end
@@ -1,7 +1,7 @@
1
1
  module SoarSc
2
2
  module Rack
3
3
  class Router
4
- VERSION = "0.1.2"
4
+ VERSION = "1.0.0"
5
5
  end
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: soar_sc-rack-router
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sheldon Hearn
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-04-19 00:00:00.000000000 Z
11
+ date: 2016-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -86,6 +86,7 @@ files:
86
86
  - lib/soar_sc/rack/router.rb
87
87
  - lib/soar_sc/rack/router/builder_syntax.rb
88
88
  - lib/soar_sc/rack/router/errors.rb
89
+ - lib/soar_sc/rack/router/http_method.rb
89
90
  - lib/soar_sc/rack/router/route.rb
90
91
  - lib/soar_sc/rack/router/version.rb
91
92
  - soar_sc-rack-router.gemspec