truss-router 0.0.3 → 0.0.4
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/README.md +13 -4
- data/benchmarks/comparison_bench.rb +77 -0
- data/lib/truss/router/node.rb +17 -13
- data/lib/truss/router/request.rb +4 -2
- data/lib/truss/router/routeset.rb +13 -11
- data/lib/truss/router/version.rb +1 -1
- data/lib/truss/router.rb +1 -1
- data/spec/lib/truss/draw_routes_spec.rb +8 -5
- data/spec/lib/truss/route_dispatch_spec.rb +12 -12
- data/spec/lib/truss/router/node_spec.rb +24 -10
- data/spec/lib/truss/router/request_spec.rb +1 -0
- data/spec/lib/truss/router/routeset_spec.rb +5 -3
- data/truss-router.gemspec +1 -1
- metadata +4 -3
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Truss Router
|
2
2
|
[](https://travis-ci.org/truss-io/router)
|
3
|
+
[](https://codeclimate.com/github/truss-io/router)
|
3
4
|
|
4
5
|
Truss Router is the first released part of Truss - a new wrapper around Rack to make writing performant Ruby web endpoints easier.
|
5
6
|
Truss Router is currently considered alpha software, and as such please don't use in any production environment, but feel free to
|
@@ -7,10 +8,10 @@ explore and see how it handles for you.
|
|
7
8
|
|
8
9
|
Truss supports the following Ruby platforms in Pure Ruby:
|
9
10
|
|
10
|
-
* MRI 1.9.3
|
11
|
-
* MRI 2.0.0
|
12
|
-
* JRuby 1.7
|
13
|
-
* RBX 1.9 mode
|
11
|
+
* MRI 1.9.3 - [benchmarks](https://github.com/truss-io/router/wiki/Benchmarks-MRI-1.9.3)
|
12
|
+
* MRI 2.0.0 - [benchmarks](https://github.com/truss-io/router/wiki/Benchmarks-MRI-2.0.0)
|
13
|
+
* JRuby 1.7 - [benchmarks](https://github.com/truss-io/router/wiki/Benchmarks-JRuby)
|
14
|
+
* RBX 1.9 mode - [benchmarks](https://github.com/truss-io/router/wiki/Benchmarks-RBX)
|
14
15
|
|
15
16
|
## Installation
|
16
17
|
|
@@ -83,3 +84,11 @@ MRI 1.9.3, which is worth investigating.
|
|
83
84
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
84
85
|
4. Push to the branch (`git push origin my-new-feature`)
|
85
86
|
5. Create new Pull Request
|
87
|
+
|
88
|
+
## Thanks and Inspiration
|
89
|
+
|
90
|
+
Thanks first and foremost have to go to [Music Glue](http://musicglue.com) where I work for letting me play with amazing projects
|
91
|
+
and do what I love for a living.
|
92
|
+
|
93
|
+
The inspiration for this project came from using [joshbuddy](https://github.com/joshboddy)'s [http_router](https://github.com/joshbuddy/http_router)
|
94
|
+
project.
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'http_router'
|
3
|
+
require 'usher'
|
4
|
+
require 'benchmark'
|
5
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
6
|
+
require File.expand_path("../../lib/truss-router", __FILE__)
|
7
|
+
|
8
|
+
REQUEST = Rack::MockRequest.env_for("/home", method: "GET")
|
9
|
+
APP = ->(env){ [200, {'Content-Type' => 'text/plain'}, ["Hello World"]] }
|
10
|
+
TIMES = 50_000
|
11
|
+
|
12
|
+
puts "Running with a single route\n\n"
|
13
|
+
|
14
|
+
h = HttpRouter.new
|
15
|
+
h.add('/home').to(APP)
|
16
|
+
|
17
|
+
r = Usher::Interface.for(:rack) do
|
18
|
+
add('/home').to(APP)
|
19
|
+
end
|
20
|
+
|
21
|
+
Truss::Router.draw do |r|
|
22
|
+
r.get('/home', APP)
|
23
|
+
end
|
24
|
+
|
25
|
+
Benchmark.bmbm(12) do |x|
|
26
|
+
x.report("HTTP Router") { TIMES.times { h.call(REQUEST) } }
|
27
|
+
x.report("Usher Router") { TIMES.times { r.call(REQUEST) } }
|
28
|
+
x.report("Truss Router") { TIMES.times { Truss::Router.call(REQUEST) } }
|
29
|
+
end
|
30
|
+
|
31
|
+
puts "\n\nRunning with 10 routes\n\n"
|
32
|
+
BLOG_POST_REQ = Rack::MockRequest.env_for("/blog/posts/9", method: "GET")
|
33
|
+
|
34
|
+
a = HttpRouter.new
|
35
|
+
a.get('/home').to(APP)
|
36
|
+
a.get('/news').to(APP)
|
37
|
+
a.get('/articles/:id').to(APP)
|
38
|
+
a.get('/staff/:name').to(APP)
|
39
|
+
a.get('/contact-us').to(APP)
|
40
|
+
a.get('/about-us').to(APP)
|
41
|
+
a.post('/login').to(APP)
|
42
|
+
a.delete('/logout').to(APP)
|
43
|
+
a.get('/blog').to(APP)
|
44
|
+
a.get('/blog/posts/:id').to(APP)
|
45
|
+
|
46
|
+
routes = Usher::Interface.for(:rack) do
|
47
|
+
add('/home').to(APP)
|
48
|
+
add('/news').to(APP)
|
49
|
+
add('/articles/:id').to(APP)
|
50
|
+
add('/staff/:name').to(APP)
|
51
|
+
add('/contact-us').to(APP)
|
52
|
+
add('/about-us').to(APP)
|
53
|
+
add('/login').to(APP)
|
54
|
+
add('/logout').to(APP)
|
55
|
+
add('/blog').to(APP)
|
56
|
+
add('/blog/posts/:id').to(APP)
|
57
|
+
end
|
58
|
+
|
59
|
+
Truss::Router.reset!
|
60
|
+
Truss::Router.draw do |r|
|
61
|
+
r.get("/home", APP)
|
62
|
+
r.get("/news", APP)
|
63
|
+
r.get("/articles/:id", APP)
|
64
|
+
r.get("/staff/:name", APP)
|
65
|
+
r.get("/contact-us", APP)
|
66
|
+
r.get("/about-us", APP)
|
67
|
+
r.post("/login", APP)
|
68
|
+
r.delete("/logout", APP)
|
69
|
+
r.get("/blog", APP)
|
70
|
+
r.get("/blog/posts/:id", APP)
|
71
|
+
end
|
72
|
+
|
73
|
+
Benchmark.bmbm(12) do |x|
|
74
|
+
x.report("HTTP Router") { TIMES.times { a.call(BLOG_POST_REQ) } }
|
75
|
+
x.report("Usher Router") { TIMES.times { routes.call(BLOG_POST_REQ) } }
|
76
|
+
x.report("Truss Router") { TIMES.times { Truss::Router.call(BLOG_POST_REQ) } }
|
77
|
+
end
|
data/lib/truss/router/node.rb
CHANGED
@@ -2,11 +2,15 @@ module Truss
|
|
2
2
|
module Router
|
3
3
|
class Node
|
4
4
|
attr_accessor :request_method, :path, :has_dynamic_segments,
|
5
|
-
:endpoint, :matchable_regex, :options
|
5
|
+
:endpoint, :matchable_regex, :options, :allowed_methods,
|
6
|
+
:path_segments
|
7
|
+
|
6
8
|
def initialize(method, path, endpoint, options={})
|
7
9
|
@request_method, @path, @endpoint = method, path, endpoint
|
8
|
-
@has_dynamic_segments = false
|
10
|
+
@has_dynamic_segments = false
|
9
11
|
@matchable_regex = build_matchable_regex(method, path, options)
|
12
|
+
@allowed_methods = discover_allowed_methods(method, path, options)
|
13
|
+
@path_segments = get_path_segments(path)
|
10
14
|
@options = options
|
11
15
|
end
|
12
16
|
|
@@ -30,23 +34,23 @@ module Truss
|
|
30
34
|
def build_matchable_regex(method, path, options)
|
31
35
|
if path.include?(":")
|
32
36
|
self.has_dynamic_segments = true
|
33
|
-
/\A#{
|
37
|
+
/\A#{segment_string(path)}\Z/
|
34
38
|
else
|
35
|
-
%r[\A#{
|
39
|
+
%r[\A#{path}\Z]
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
"OPTIONS"
|
45
|
-
else
|
46
|
-
"(#{method.to_s.upcase}|OPTIONS)"
|
47
|
-
end
|
43
|
+
def discover_allowed_methods(method, path, options)
|
44
|
+
allowed = [method.to_s.upcase]
|
45
|
+
allowed << "OPTIONS" if options.has_key?(:cors)
|
46
|
+
allowed << "HEAD" if (method == :get)
|
47
|
+
allowed
|
48
48
|
end
|
49
49
|
|
50
|
+
def get_path_segments(path)
|
51
|
+
path.split("/").reject(&:empty?).count
|
52
|
+
end
|
53
|
+
|
50
54
|
def segment_string(path)
|
51
55
|
components = path.split("/").map do |comp|
|
52
56
|
if comp[0] == ":"
|
data/lib/truss/router/request.rb
CHANGED
@@ -3,28 +3,30 @@ module Truss
|
|
3
3
|
class Routeset
|
4
4
|
|
5
5
|
attr_accessor :nodes
|
6
|
-
def initialize(nodes=
|
6
|
+
def initialize(nodes={})
|
7
7
|
@nodes = nodes
|
8
|
+
%w[GET HEAD OPTIONS POST PATCH PUT DELETE].each do |req|
|
9
|
+
@nodes[req] ||= []
|
10
|
+
end
|
8
11
|
end
|
9
12
|
|
10
13
|
def add_node(node)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def <<(node)
|
15
|
-
@nodes << node
|
14
|
+
node.allowed_methods.each do |req|
|
15
|
+
@nodes[req] << node
|
16
|
+
end
|
16
17
|
end
|
17
18
|
|
18
|
-
def
|
19
|
-
|
19
|
+
def nodes_for(type)
|
20
|
+
nodes[type]
|
20
21
|
end
|
21
22
|
|
22
|
-
def
|
23
|
-
|
23
|
+
def total_nodes
|
24
|
+
nodes.map{|k,v| v.count}.flatten.inject(:+)
|
24
25
|
end
|
25
26
|
|
26
27
|
def find_route request
|
27
|
-
|
28
|
+
req_nodes = nodes_for(request.request_method).select{|n| (n.path_segments == request.segment_length) }
|
29
|
+
req_nodes.detect do |node|
|
28
30
|
node.matches?(request)
|
29
31
|
end
|
30
32
|
end
|
data/lib/truss/router/version.rb
CHANGED
data/lib/truss/router.rb
CHANGED
@@ -42,13 +42,16 @@ describe Truss::Router do
|
|
42
42
|
subject.draw(&map)
|
43
43
|
end
|
44
44
|
it "should have two route" do
|
45
|
-
subject.routeset.
|
45
|
+
subject.routeset.total_nodes.should eq(3)
|
46
46
|
end
|
47
|
-
it "should have a Get
|
48
|
-
subject.routeset
|
47
|
+
it "should have a Get routeset with one member" do
|
48
|
+
subject.routeset.nodes_for("GET").length.should eq(1)
|
49
49
|
end
|
50
|
-
it "should have
|
51
|
-
subject.routeset
|
50
|
+
it "should have an Head routeset with one member" do
|
51
|
+
subject.routeset.nodes_for("HEAD").length.should eq(1)
|
52
|
+
end
|
53
|
+
it "should have a Post routeset with one member" do
|
54
|
+
subject.routeset.nodes_for("POST").length.should eq(1)
|
52
55
|
end
|
53
56
|
end
|
54
57
|
end
|
@@ -17,7 +17,7 @@ describe Truss::Router do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
it "should return the first route for a matching path" do
|
20
|
-
subject[0].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
20
|
+
subject.nodes_for("GET")[0].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
21
21
|
described_class.call(env)
|
22
22
|
end
|
23
23
|
|
@@ -30,9 +30,9 @@ describe Truss::Router do
|
|
30
30
|
context "multiple routes present" do
|
31
31
|
let(:multiple_map) { ->(r){
|
32
32
|
r.get("/", app)
|
33
|
-
r.get("/home", app)
|
33
|
+
r.get("/home", app, cors: true)
|
34
34
|
r.get("/about", app)
|
35
|
-
r.post("/login", app)
|
35
|
+
r.post("/login", app, cors: true)
|
36
36
|
r.post("/home", app)
|
37
37
|
}
|
38
38
|
}
|
@@ -43,49 +43,49 @@ describe Truss::Router do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
it "should call the second node given a get request for /home" do
|
46
|
-
subject[1].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
47
|
-
described_class.call(env)
|
46
|
+
subject.nodes_for("GET")[1].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
47
|
+
puts described_class.call(env)
|
48
48
|
end
|
49
49
|
|
50
50
|
it "should call the third node given a get request for /about" do
|
51
51
|
about_env = Rack::MockRequest.env_for("/about", method: "GET")
|
52
|
-
subject[2].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
52
|
+
subject.nodes_for("GET")[2].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
53
53
|
described_class.call(about_env)
|
54
54
|
end
|
55
55
|
|
56
56
|
it "should call the fourth node given a post request for /login" do
|
57
57
|
login_env = Rack::MockRequest.env_for("/login", method: "POST")
|
58
|
-
subject[
|
58
|
+
subject.nodes_for("POST")[0].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
59
59
|
described_class.call(login_env)
|
60
60
|
end
|
61
61
|
|
62
62
|
it "should call the fifth node given a post request for /home" do
|
63
63
|
post_home_env = Rack::MockRequest.env_for("/home", method: "POST")
|
64
|
-
subject[
|
64
|
+
subject.nodes_for("POST")[1].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
65
65
|
described_class.call(post_home_env)
|
66
66
|
end
|
67
67
|
|
68
68
|
it "should call the second node given an options request for /home" do
|
69
69
|
options_home_env = Rack::MockRequest.env_for("/home", method: "OPTIONS")
|
70
|
-
subject[
|
70
|
+
subject.nodes_for("OPTIONS")[0].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
71
71
|
described_class.call(options_home_env)
|
72
72
|
end
|
73
73
|
|
74
74
|
it "should call the second node given a head request for /home" do
|
75
75
|
head_home_env = Rack::MockRequest.env_for("/home", method: "HEAD")
|
76
|
-
subject[1].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
76
|
+
subject.nodes_for("HEAD")[1].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
77
77
|
described_class.call(head_home_env)
|
78
78
|
end
|
79
79
|
|
80
80
|
it "should call the fourth node given an options request for /login" do
|
81
81
|
options_login_env = Rack::MockRequest.env_for("/login", method: "OPTIONS")
|
82
|
-
subject[
|
82
|
+
subject.nodes_for("OPTIONS")[1].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
83
83
|
described_class.call(options_login_env)
|
84
84
|
end
|
85
85
|
|
86
86
|
it "should call the first node given a get request for /" do
|
87
87
|
home_env = Rack::MockRequest.env_for("/", method: "GET")
|
88
|
-
subject[0].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
88
|
+
subject.nodes_for("GET")[0].should_receive(:call).exactly(1).times.with(kind_of(Truss::Router::Request))
|
89
89
|
described_class.call(home_env)
|
90
90
|
end
|
91
91
|
|
@@ -11,52 +11,66 @@ describe Truss::Router::Node do
|
|
11
11
|
it { should respond_to(:matchable_regex) }
|
12
12
|
it { should respond_to(:call) }
|
13
13
|
|
14
|
-
describe "routing
|
14
|
+
describe "routing constraints" do
|
15
15
|
context "for get requests" do
|
16
16
|
subject { Truss::Router::Node.new(:get, "/home", app) }
|
17
|
-
its(:matchable_regex) { should eq(%r[\A
|
17
|
+
its(:matchable_regex) { should eq(%r[\A/home\Z]) }
|
18
|
+
its(:path_segments) { should eq(1) }
|
19
|
+
its(:allowed_methods) { should eq(%w[GET HEAD]) }
|
18
20
|
end
|
19
21
|
|
20
22
|
context "for post requests" do
|
21
23
|
subject { Truss::Router::Node.new(:post, "/home", app) }
|
22
|
-
its(:matchable_regex) { should eq(%r[\A
|
24
|
+
its(:matchable_regex) { should eq(%r[\A/home\Z]) }
|
25
|
+
its(:path_segments) { should eq(1) }
|
26
|
+
its(:allowed_methods) { should eq(%w[POST]) }
|
23
27
|
end
|
24
28
|
|
25
29
|
context "for put requests" do
|
26
30
|
subject { Truss::Router::Node.new(:put, "/home", app) }
|
27
|
-
its(:matchable_regex) { should eq(%r[\A
|
31
|
+
its(:matchable_regex) { should eq(%r[\A/home\Z]) }
|
32
|
+
its(:path_segments) { should eq(1) }
|
33
|
+
its(:allowed_methods) { should eq(%w[PUT]) }
|
28
34
|
end
|
29
35
|
|
30
36
|
context "for patch requests" do
|
31
37
|
subject { Truss::Router::Node.new(:patch, "/home", app) }
|
32
|
-
its(:matchable_regex) { should eq(%r[\A
|
38
|
+
its(:matchable_regex) { should eq(%r[\A/home\Z]) }
|
39
|
+
its(:path_segments) { should eq(1) }
|
40
|
+
its(:allowed_methods) { should eq(%w[PATCH]) }
|
33
41
|
end
|
34
42
|
|
35
43
|
context "for head requests" do
|
36
44
|
subject { Truss::Router::Node.new(:head, "/home", app) }
|
37
|
-
its(:matchable_regex) { should eq(%r[\A
|
45
|
+
its(:matchable_regex) { should eq(%r[\A/home\Z]) }
|
46
|
+
its(:path_segments) { should eq(1) }
|
47
|
+
its(:allowed_methods) { should eq(%w[HEAD]) }
|
38
48
|
end
|
39
49
|
|
40
50
|
context "for options requests" do
|
41
51
|
subject { Truss::Router::Node.new(:options, "/home", app) }
|
42
|
-
its(:matchable_regex) { should eq(%r[\
|
52
|
+
its(:matchable_regex) { should eq(%r[\A/home\Z]) }
|
53
|
+
its(:path_segments) { should eq(1) }
|
54
|
+
its(:allowed_methods) { should eq(%w[OPTIONS]) }
|
43
55
|
end
|
44
56
|
|
45
57
|
context "for delete requests" do
|
46
58
|
subject { Truss::Router::Node.new(:delete, "/home", app) }
|
47
|
-
its(:matchable_regex) { should eq(%r[\A
|
59
|
+
its(:matchable_regex) { should eq(%r[\A/home\Z]) }
|
60
|
+
its(:path_segments) { should eq(1) }
|
61
|
+
its(:allowed_methods) { should eq(%w[DELETE]) }
|
48
62
|
end
|
49
63
|
end
|
50
64
|
|
51
65
|
describe "dynamic segments" do
|
52
66
|
context "single dynamic matcher" do
|
53
67
|
subject { Truss::Router::Node.new(:delete, "/:id", app) }
|
54
|
-
|
68
|
+
it { subject.matchable_regex.to_s.should eq(/\A\/(?<id>[\w\-]+)\Z/.to_s) }
|
55
69
|
end
|
56
70
|
|
57
71
|
context "multiple dynamic matchers" do
|
58
72
|
subject { Truss::Router::Node.new(:delete, "/posts/:post_id/comments/:id", app) }
|
59
|
-
|
73
|
+
it { subject.matchable_regex.to_s.should eq(/\A\/posts\/(?<post_id>[\w\-]+)\/comments\/(?<id>[\w\-]+)\Z/.to_s) }
|
60
74
|
end
|
61
75
|
end
|
62
76
|
end
|
@@ -3,18 +3,20 @@ require 'truss/router/node'
|
|
3
3
|
|
4
4
|
describe Truss::Router::Routeset do
|
5
5
|
it { should respond_to(:nodes) }
|
6
|
+
it { should respond_to(:nodes_for) }
|
6
7
|
it { should respond_to(:add_node) }
|
8
|
+
it { should respond_to(:total_nodes) }
|
9
|
+
it { subject.nodes.keys.sort.should eq(%w[GET HEAD OPTIONS PUT PATCH POST DELETE].sort) }
|
7
10
|
|
8
11
|
context "adding nodes" do
|
9
|
-
subject { Truss::Router::Routeset.new }
|
10
12
|
let(:app) { ->(env){[200, {'Content-Type' => 'text/plain'}, ["Hello World"]]} }
|
11
13
|
let(:node) { Truss::Router::Node.new(:get, "/home", app, {}) }
|
12
14
|
before(:each) { subject.add_node(node) }
|
13
15
|
|
14
|
-
it { subject.
|
16
|
+
it { subject.nodes_for("GET").length.should eq(1) }
|
15
17
|
it "can contain multiple nodes" do
|
16
18
|
subject.add_node(node)
|
17
|
-
subject.
|
19
|
+
subject.nodes_for("GET").length.should eq(2)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
data/truss-router.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["john@musicglue.com"]
|
11
11
|
spec.description = %q{Truss Router is a modular Rack Router for Truss}
|
12
12
|
spec.summary = %q{Truss Router is a modular Rack Router for Truss}
|
13
|
-
spec.homepage = "http://truss-io.github.io"
|
13
|
+
spec.homepage = "http://truss-io.github.io/router"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: truss-router
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -89,6 +89,7 @@ files:
|
|
89
89
|
- LICENSE.txt
|
90
90
|
- README.md
|
91
91
|
- Rakefile
|
92
|
+
- benchmarks/comparison_bench.rb
|
92
93
|
- benchmarks/simple_bench.rb
|
93
94
|
- lib/truss-router.rb
|
94
95
|
- lib/truss/router.rb
|
@@ -121,7 +122,7 @@ files:
|
|
121
122
|
- spec/lib/truss/router_spec.rb
|
122
123
|
- spec/spec_helper.rb
|
123
124
|
- truss-router.gemspec
|
124
|
-
homepage: http://truss-io.github.io
|
125
|
+
homepage: http://truss-io.github.io/router
|
125
126
|
licenses:
|
126
127
|
- MIT
|
127
128
|
post_install_message:
|