truss-router 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
- [![Build Status](https://travis-ci.org/truss-io/router.png)](https://travis-ci.org/truss-io/router)
2
1
  # Truss Router
2
+ [![Build Status](https://travis-ci.org/truss-io/router.png)](https://travis-ci.org/truss-io/router)
3
3
 
4
4
  Truss Router is the first released part of Truss - a new wrapper around Rack to make writing performant Ruby web endpoints easier.
5
5
  Truss Router is currently considered alpha software, and as such please don't use in any production environment, but feel free to
@@ -34,7 +34,7 @@ it doesn't actually support much, but features will come!
34
34
 
35
35
  So, how do I use it? Basically, just require the gem, and then ```draw``` a map of your routes. Routes are evaluated from top to bottom, so routes at the top of the route block will match and return before routes below them. The route builder takes the following arguments:
36
36
 
37
- 1. path, e.g. "/home" (currently only exact matches match, dynamic segments coming soon!)
37
+ 1. path, e.g. "/home" or "/posts/:id"
38
38
  2. rack app, e.g. ```->(env){[200, {'Content-Type' => 'text/plain'}, ["Hi, I'm a Rack App"]]}```
39
39
  3. an optional hash of options (currently not used!)
40
40
 
@@ -46,8 +46,16 @@ Truss::Router.draw do |r|
46
46
  r.post("/login", LoginApp)
47
47
  r.patch("/update", DhhApp)
48
48
  r.delete("/goodbye", RemoveThisApp)
49
+ r.get("/posts/:id", PostApp) # :id is a dynamic segment
49
50
  end
50
51
  ```
52
+ Any valid Rack endpoint is supported as an endpoint in this map, but please be aware that Truss Router will pass a
53
+ Truss::Router::Request object (a thin layer over Rack::Request) into the rack app rather than just the env hash. This
54
+ allows for passing segment options and similar without too much drama.
55
+
56
+ Any dynamic segments (declared by prefixing that segment with ```:```) will be passed into the application as part of the Truss
57
+ Request object, available under ```#routing_params``` for now, which is a simple hash of the segment name to the value. All keys
58
+ are strings and no attempt is made to coerce values, so they are all strings as well.
51
59
 
52
60
  Finally, you can then run the Truss Router as a rack app:
53
61
 
@@ -63,7 +71,10 @@ request methods to different endpoints on the same path.
63
71
 
64
72
  Having said that, there isn't much point in having candy if it makes it slow. HTTP Router inspired me to include benchmarks within
65
73
  the repo that make it easy to check how performant the solution is. I'll include the current benchmark output in the wiki for each
66
- supported platform (1.9.3/2.0.0/JRuby1.7/RBX) and update with each release to keep things current.
74
+ supported platform ([1.9.3](https://github.com/truss-io/router/wiki/Benchmarks-MRI-1.9.3) / [2.0.0](https://github.com/truss-io/router/wiki/Benchmarks-MRI-2.0.0) / [JRuby1.7](https://github.com/truss-io/router/wiki/Benchmarks-JRuby) / [RBX](https://github.com/truss-io/router/wiki/Benchmarks-RBX)) and update with each release to keep things current.
75
+
76
+ Currently, 2.0.0 is by far the slowest, which is somewhat curious - I had expected it to lose to JRuby and possibly RBX but not to
77
+ MRI 1.9.3, which is worth investigating.
67
78
 
68
79
  ## Contributing
69
80
 
@@ -20,7 +20,7 @@ end
20
20
 
21
21
  puts "Benchmarking in progress with 50k iterations, please wait\n\n"
22
22
 
23
- Benchmark.bm(12) do |x|
23
+ Benchmark.bmbm(12) do |x|
24
24
  x.report("Plain app") { TIMES.times{ app.call(REQUEST) } }
25
25
  x.report("Single route") { TIMES.times { Truss::Router.call(REQUEST) } }
26
26
  end
@@ -44,9 +44,9 @@ end
44
44
 
45
45
  HOME_REQUEST = Rack::MockRequest.env_for("/home", method: "GET")
46
46
 
47
- Benchmark.bm(12) do |x|
47
+ Benchmark.bmbm(10) do |x|
48
48
  x.report("Plain app") { TIMES.times{ app.call(REQUEST) } }
49
- x.report("Single route") { TIMES.times { Truss::Router.call(HOME_REQUEST) } }
49
+ x.report("Last route") { TIMES.times { Truss::Router.call(HOME_REQUEST) } }
50
50
  end
51
51
 
52
52
 
@@ -67,8 +67,23 @@ Truss::Router.draw do |route|
67
67
  route.get("/home", app2)
68
68
  end
69
69
 
70
- Benchmark.bm(10) do |x|
70
+ Benchmark.bmbm(10) do |x|
71
71
  x.report("Plain app") { TIMES.times{ app.call(REQUEST) } }
72
72
  x.report("Last route") { TIMES.times { Truss::Router.call(HOME_REQUEST) } }
73
73
  end
74
74
 
75
+ puts "\n\nBenchmarking 1 dynamic segment with 50k requests\n\n"
76
+ Truss::Router.reset!
77
+
78
+ Truss::Router.draw do |route|
79
+ route.get("/posts/:id", app)
80
+ end
81
+
82
+ POSTS_REQUEST = Rack::MockRequest.env_for("/posts/9", method: "GET")
83
+
84
+ Benchmark.bmbm(11) do |x|
85
+ x.report("Plain app") { TIMES.times{ app.call(REQUEST) } }
86
+ x.report("One Dynamic") { TIMES.times { Truss::Router.call(POSTS_REQUEST) } }
87
+ end
88
+
89
+
@@ -1,15 +1,25 @@
1
1
  module Truss
2
2
  module Router
3
3
  class Node
4
- attr_accessor :request_method, :path, :endpoint, :matchable_regex, :options
4
+ attr_accessor :request_method, :path, :has_dynamic_segments,
5
+ :endpoint, :matchable_regex, :options
5
6
  def initialize(method, path, endpoint, options={})
6
7
  @request_method, @path, @endpoint = method, path, endpoint
8
+ @has_dynamic_segments = false
7
9
  @matchable_regex = build_matchable_regex(method, path, options)
8
10
  @options = options
9
11
  end
10
12
 
11
13
  def matches? request
12
- matchable_regex.match(request.routing_path)
14
+ if has_dynamic_segments
15
+ match = matchable_regex.match(request.routing_path)
16
+ if match
17
+ request.routing_params = Hash[match.names.zip(match.captures)]
18
+ end
19
+ match
20
+ else
21
+ matchable_regex.match(request.routing_path)
22
+ end
13
23
  end
14
24
 
15
25
  def call request
@@ -18,7 +28,12 @@ module Truss
18
28
 
19
29
  private
20
30
  def build_matchable_regex(method, path, options)
21
- %r[\A#{method_group(method)}#{path}\Z]
31
+ if path.include?(":")
32
+ self.has_dynamic_segments = true
33
+ /\A#{method_group(method)}#{segment_string(path)}\Z/
34
+ else
35
+ %r[\A#{method_group(method)}#{path}\Z]
36
+ end
22
37
  end
23
38
 
24
39
  def method_group(method)
@@ -31,6 +46,17 @@ module Truss
31
46
  "(#{method.to_s.upcase}|OPTIONS)"
32
47
  end
33
48
  end
49
+
50
+ def segment_string(path)
51
+ components = path.split("/").map do |comp|
52
+ if comp[0] == ":"
53
+ "(?<#{comp[1..-1]}>[\\w\\-]+)"
54
+ else
55
+ comp
56
+ end
57
+ end
58
+ "#{components.join('/')}"
59
+ end
34
60
  end
35
61
  end
36
62
  end
@@ -1,6 +1,13 @@
1
1
  module Truss
2
2
  module Router
3
3
  class Request < Rack::Request
4
+ attr_accessor :routing_params
5
+
6
+ def initialize(*args)
7
+ @routing_params = {}
8
+ super(*args)
9
+ end
10
+
4
11
  def routing_path
5
12
  "#{request_method}#{path}"
6
13
  end
@@ -1,5 +1,5 @@
1
1
  module Truss
2
2
  module Router
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.3"
4
4
  end
5
5
  end
@@ -0,0 +1,20 @@
1
+ require 'truss/router'
2
+
3
+ describe Truss::Router do
4
+ context "dynamic matchers instantiate routing_params in the request object" do
5
+ subject { described_class }
6
+ let(:app) { ->(env){ [200, {'Content-Type' => 'text/plain'}, [env.routing_params['id']]] } }
7
+ let(:env) { Rack::MockRequest.env_for("/posts/9", method: "GET") }
8
+ before :each do
9
+ Truss::Router.reset!
10
+ Truss::Router.draw do |r|
11
+ r.get("/posts/:id", app)
12
+ end
13
+ end
14
+
15
+ it "should return the id as the body of the request" do
16
+ response = subject.call(env)
17
+ response[2].first.should eq("9")
18
+ end
19
+ end
20
+ end
@@ -46,6 +46,17 @@ describe Truss::Router::Node do
46
46
  subject { Truss::Router::Node.new(:delete, "/home", app) }
47
47
  its(:matchable_regex) { should eq(%r[\A(DELETE|OPTIONS)/home\Z]) }
48
48
  end
49
-
50
49
  end
50
+
51
+ describe "dynamic segments" do
52
+ context "single dynamic matcher" do
53
+ subject { Truss::Router::Node.new(:delete, "/:id", app) }
54
+ its(:matchable_regex) { should eq(/\A(DELETE|OPTIONS)\/(?<id>[\w\-]+)\Z/) }
55
+ end
56
+
57
+ context "multiple dynamic matchers" do
58
+ subject { Truss::Router::Node.new(:delete, "/posts/:post_id/comments/:id", app) }
59
+ its(:matchable_regex) { should eq(/\A(DELETE|OPTIONS)\/posts\/(?<post_id>[\w\-]+)\/comments\/(?<id>[\w\-]+)\Z/) }
60
+ end
61
+ end
51
62
  end
@@ -5,4 +5,7 @@ describe Truss::Router::Request do
5
5
  subject { described_class.new(env) }
6
6
 
7
7
  it { should be_kind_of(Rack::Request) }
8
+ it { should respond_to(:routing_path) }
9
+ it { should respond_to(:routing_params) }
10
+ its(:routing_params) { should eq({}) }
8
11
  end
metadata CHANGED
@@ -1,71 +1,80 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: truss-router
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - John Maxwell
8
- autorequire:
9
+ autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-05-25 00:00:00.000000000 Z
12
+ date: 2013-05-26 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
+ version_requirements: !ruby/object:Gem::Requirement
16
17
  requirements:
17
18
  - - ~>
18
19
  - !ruby/object:Gem::Version
19
20
  version: '1.3'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirement: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
+ none: false
28
+ prerelease: false
29
+ type: :development
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: rake
29
- requirement: !ruby/object:Gem::Requirement
32
+ version_requirements: !ruby/object:Gem::Requirement
30
33
  requirements:
31
34
  - - '>='
32
35
  - !ruby/object:Gem::Version
33
36
  version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirement: !ruby/object:Gem::Requirement
37
39
  requirements:
38
40
  - - '>='
39
41
  - !ruby/object:Gem::Version
40
42
  version: '0'
43
+ none: false
44
+ prerelease: false
45
+ type: :development
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: rspec
43
- requirement: !ruby/object:Gem::Requirement
48
+ version_requirements: !ruby/object:Gem::Requirement
44
49
  requirements:
45
50
  - - '>='
46
51
  - !ruby/object:Gem::Version
47
52
  version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirement: !ruby/object:Gem::Requirement
51
55
  requirements:
52
56
  - - '>='
53
57
  - !ruby/object:Gem::Version
54
58
  version: '0'
59
+ none: false
60
+ prerelease: false
61
+ type: :development
55
62
  - !ruby/object:Gem::Dependency
56
63
  name: rack
57
- requirement: !ruby/object:Gem::Requirement
64
+ version_requirements: !ruby/object:Gem::Requirement
58
65
  requirements:
59
66
  - - ~>
60
67
  - !ruby/object:Gem::Version
61
68
  version: 1.5.0
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirement: !ruby/object:Gem::Requirement
65
71
  requirements:
66
72
  - - ~>
67
73
  - !ruby/object:Gem::Version
68
74
  version: 1.5.0
75
+ none: false
76
+ prerelease: false
77
+ type: :runtime
69
78
  description: Truss Router is a modular Rack Router for Truss
70
79
  email:
71
80
  - john@musicglue.com
@@ -96,9 +105,10 @@ files:
96
105
  - lib/truss/router/routeset.rb
97
106
  - lib/truss/router/version.rb
98
107
  - spec/lib/truss/draw_routes_spec.rb
99
- - spec/lib/truss/request_spec.rb
108
+ - spec/lib/truss/dynamic_matcher_spec.rb
100
109
  - spec/lib/truss/route_dispatch_spec.rb
101
110
  - spec/lib/truss/router/node_spec.rb
111
+ - spec/lib/truss/router/request_spec.rb
102
112
  - spec/lib/truss/router/routes/delete_spec.rb
103
113
  - spec/lib/truss/router/routes/get_spec.rb
104
114
  - spec/lib/truss/router/routes/head_spec.rb
@@ -114,8 +124,7 @@ files:
114
124
  homepage: http://truss-io.github.io
115
125
  licenses:
116
126
  - MIT
117
- metadata: {}
118
- post_install_message:
127
+ post_install_message:
119
128
  rdoc_options: []
120
129
  require_paths:
121
130
  - lib
@@ -123,23 +132,32 @@ required_ruby_version: !ruby/object:Gem::Requirement
123
132
  requirements:
124
133
  - - '>='
125
134
  - !ruby/object:Gem::Version
135
+ segments:
136
+ - 0
126
137
  version: '0'
138
+ hash: 2
139
+ none: false
127
140
  required_rubygems_version: !ruby/object:Gem::Requirement
128
141
  requirements:
129
142
  - - '>='
130
143
  - !ruby/object:Gem::Version
144
+ segments:
145
+ - 0
131
146
  version: '0'
147
+ hash: 2
148
+ none: false
132
149
  requirements: []
133
- rubyforge_project:
134
- rubygems_version: 2.0.3
135
- signing_key:
136
- specification_version: 4
150
+ rubyforge_project:
151
+ rubygems_version: 1.8.24
152
+ signing_key:
153
+ specification_version: 3
137
154
  summary: Truss Router is a modular Rack Router for Truss
138
155
  test_files:
139
156
  - spec/lib/truss/draw_routes_spec.rb
140
- - spec/lib/truss/request_spec.rb
157
+ - spec/lib/truss/dynamic_matcher_spec.rb
141
158
  - spec/lib/truss/route_dispatch_spec.rb
142
159
  - spec/lib/truss/router/node_spec.rb
160
+ - spec/lib/truss/router/request_spec.rb
143
161
  - spec/lib/truss/router/routes/delete_spec.rb
144
162
  - spec/lib/truss/router/routes/get_spec.rb
145
163
  - spec/lib/truss/router/routes/head_spec.rb
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 9b56c1774dc9e3e09defdd552298a3a10bfc61bd
4
- data.tar.gz: 76018cd3ef0a703260177cc158ca4223f91bcbd1
5
- SHA512:
6
- metadata.gz: d3c1fc3a28002932edb13878189dca29d9b7a3cc60d76d2907263238f6d39edb6ff20b46395a00f151f98f1c5a7f7e38ddad91f1f4428e27767156b1afd21066
7
- data.tar.gz: f02753a22f37ea3837d34c819dcae69964a53c8e2aabca5cac9bfe1069f3983e2e8b0497d1d33852246ce61b6f32b816a7eb91486d720bb8774ce1016f214b39