rutter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe "Rack integration" do
6
+ let(:router) do
7
+ Rutter.new do
8
+ get "/books", to: ->(_env) { [200, {}, ["Books"]] }
9
+ end
10
+ end
11
+
12
+ def app
13
+ router.freeze
14
+ end
15
+
16
+ describe "with match" do
17
+ it "calls the matched endpoint" do
18
+ get "/books"
19
+ assert_equal 200, last_response.status
20
+ assert_equal "Books", last_response.body
21
+ end
22
+ end
23
+
24
+ describe "with no match" do
25
+ it "returns 404" do
26
+ get "/authors"
27
+ assert_equal 404, last_response.status
28
+ assert_equal "Route Not Found", last_response.body
29
+ assert_equal "pass", last_response.headers["X-Cascade"]
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe "Rutter::Builder#redirect" do
6
+ let(:router) do
7
+ Rutter.new do
8
+ get "/legacy-path", to: redirect("/new_path")
9
+ get "/legacy-path-custom-status", to: redirect("/new_path", status: 301)
10
+ get "/new_path", to: ->(_env) { [200, {}, ["Redirected"]] }
11
+ end
12
+ end
13
+
14
+ def app
15
+ router.freeze
16
+ end
17
+
18
+ it "redirect the request to the new path" do
19
+ get "/legacy-path"
20
+ assert_equal 302, last_response.status
21
+ assert_equal "", last_response.body
22
+ follow_redirect!
23
+ assert_equal 200, last_response.status
24
+ assert_equal "Redirected", last_response.body
25
+ end
26
+
27
+ it "support setting custom status" do
28
+ get "/legacy-path-custom-status"
29
+ assert_equal 301, last_response.status
30
+ follow_redirect!
31
+ assert_equal 200, last_response.status
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+
5
+ if ENV["COVERAGE"] == "true"
6
+ require "simplecov"
7
+ require "codeclimate-test-reporter"
8
+
9
+ SimpleCov.start do
10
+ command_name "spec"
11
+ add_filter "spec"
12
+ end
13
+ end
14
+
15
+ require "minitest/autorun"
16
+ require "minitest/pride"
17
+ require "rack/test"
18
+ require "mocha/mini_test"
19
+
20
+ # Require support (helper) modules
21
+ Dir["./spec/support/**/*.rb"].each { |f| require f }
22
+
23
+ require "rutter"
24
+
25
+ class Minitest::Spec
26
+ include Rack::Test::Methods
27
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Minitest::Assertions
4
+ def assert_file_exist(file)
5
+ assert File.exist?(file), "Expected #{file} to be a file"
6
+ end
7
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ module Rutter
6
+ describe Builder do
7
+ let(:router) { Builder.new }
8
+
9
+ describe "#add" do
10
+ it "returns the created route object" do
11
+ route = router.get "/books", to: ->(env) {}
12
+ named_route = router.get "/named_books", to: ->(env) {}, as: :books
13
+ assert_instance_of Route, route
14
+ assert_instance_of Route, named_route
15
+ end
16
+
17
+ it "adds a route to the collection" do
18
+ router.get "/books", to: ->(env) {}
19
+ router.post "/books", to: ->(env) {}
20
+
21
+ assert_equal 2, router.flat_map.size
22
+ end
23
+
24
+ it "stores the name of the route" do
25
+ route = router.add "GET", "/books", as: :books, to: ->(env) {}
26
+ assert_equal route, router.named_map[:books]
27
+ end
28
+
29
+ it "normalizes route names" do
30
+ router.get "/books", as: :_bookie__books__, to: ->(env) {}
31
+ assert router.named_map.key?(:bookie_books)
32
+ end
33
+
34
+ it "raises an error if named route already been defined" do
35
+ router.get "/books", as: :books, to: ->(env) {}
36
+
37
+ err = assert_raises RuntimeError do
38
+ router.get "/books", as: :books, to: ->(env) {}
39
+ end
40
+
41
+ assert_equal "a route called 'books' has already been defined",
42
+ err.message
43
+ end
44
+ end
45
+
46
+ describe "#root" do
47
+ it "adds a root path with a root name" do
48
+ route = router.root to: ->(env) {}
49
+ assert_equal "/", route.path
50
+ assert_equal route, router.named_map[:root]
51
+ end
52
+ end
53
+
54
+ describe "verbs" do
55
+ Route::VERBS.each do |verb|
56
+ it "accept the #{verb} verb as method" do
57
+ router.send verb.downcase, "/", to: ->() {}
58
+ assert_equal verb, router.flat_map[0].method
59
+ end
60
+ end
61
+ end
62
+
63
+ describe "#path" do
64
+ it "generates a URL path from the arguments" do
65
+ router.get "/users/:id(/:username)", to: "Users#show", as: :user
66
+ assert_equal "/users/45/sandelius",
67
+ router.path(:user, id: 45, username: "sandelius")
68
+ assert_equal "/users/45", router.path(:user, id: 45)
69
+ end
70
+
71
+ it "raises an error if route has not been defined" do
72
+ err = assert_raises ArgumentError do
73
+ router.path(:user)
74
+ end
75
+ assert_equal "no route called 'user'", err.message
76
+ end
77
+ end
78
+
79
+ describe "#url" do
80
+ it "generates a full URL" do
81
+ router.get "/users/:id", to: "Users#show", as: :user
82
+ assert_equal "http://example.com/users/54", router.url(:user, id: 54)
83
+ end
84
+
85
+ it "allows host scheme and subdomain to be set" do
86
+ router.get "/users/:id", to: "Users#show", as: :user
87
+ assert_equal(
88
+ "https://api.example.com/users/54",
89
+ router.url(
90
+ :user,
91
+ id: 54, _host: "example.com", _scheme: "https", _subdomain: "api"
92
+ )
93
+ )
94
+ end
95
+
96
+ it "adds port if no 80 or 433" do
97
+ router.get "/users/:id", to: "Users#show", as: :user
98
+ assert_equal(
99
+ "http://example.com:333/users/54",
100
+ router.url(:user, id: 54, _port: 333)
101
+ )
102
+ end
103
+ end
104
+
105
+ describe "#freeze" do
106
+ it "freezes all objects" do
107
+ router.freeze
108
+
109
+ assert router.frozen?
110
+ assert router.flat_map.frozen?
111
+ assert router.named_map.frozen?
112
+ assert router.verb_map.frozen?
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ module Rutter
6
+ describe Route do
7
+ it "raises an error if request method is invalid" do
8
+ err = assert_raises ArgumentError do
9
+ Route.new("NONE", "/", ->(env) {})
10
+ end
11
+
12
+ assert_equal "invalid verb: 'NONE'", err.message
13
+ end
14
+
15
+ it "normalizes path" do
16
+ route = Route.new("GET", "//about////us/", ->(env) {})
17
+ assert_equal "/about/us", route.path
18
+ end
19
+
20
+ describe "#expand" do
21
+ it "expand dynamic routes" do
22
+ route = Route.new("GET", "/books/:id(/:title)", -> {})
23
+ assert_equal "/books/54/eloquent?include[]=foo",
24
+ route.expand(id: 54, title: "eloquent", include: [:foo])
25
+ assert_equal "/books/54?include[]=foo",
26
+ route.expand(id: 54, include: [:foo])
27
+ assert_equal "/books/54/eloquent",
28
+ route.expand(id: 54, title: "eloquent")
29
+ assert_equal "/books/54", route.expand(id: 54)
30
+ end
31
+
32
+ it "expand static routes" do
33
+ route = Route.new("GET", "/books", -> {})
34
+ assert_equal "/books?include[]=foo", route.expand(include: [:foo])
35
+ assert_equal "/books", route.expand
36
+ end
37
+ end
38
+
39
+ describe "#match?" do
40
+ it "matches non regexp" do
41
+ route = Route.new("GET", "/books", -> {})
42
+ assert route.match?("/books")
43
+ end
44
+
45
+ it "support named parameters" do
46
+ route = Route.new("GET", "/pages/:id(/:title)", -> {})
47
+
48
+ assert route.match?("/pages/54/eloquent-ruby")
49
+ assert route.match?("/pages/54")
50
+ refute route.match?("/pages/54.rb")
51
+ refute route.match?("/pages")
52
+ end
53
+
54
+ it "support catch-all parameter" do
55
+ route = Route.new("GET", "/pages/*slug", -> {})
56
+ assert route.match?("/pages/54/eloquent-ruby")
57
+ assert route.match?("/pages/54/eloquent-ruby.rb")
58
+ assert route.match?("/pages/54/eloquent-ruby/with/more/parts")
59
+ refute route.match?("/pages")
60
+ end
61
+ end
62
+
63
+ describe "#params" do
64
+ it "extract params" do
65
+ route = Route.new("GET", "/pages/:id(/:title)", -> {})
66
+ assert_equal(
67
+ { "id" => "54", "title" => "eloquent-ruby" },
68
+ route.params("/pages/54/eloquent-ruby")
69
+ )
70
+
71
+ assert_equal(
72
+ { "id" => "54", "title" => nil },
73
+ route.params("/pages/54")
74
+ )
75
+ end
76
+
77
+ it "returns nil if no match" do
78
+ route = Route.new("GET", "/books/:id", -> {})
79
+ assert_nil route.params("/posts/54")
80
+ end
81
+
82
+ it "returns empty array if no params present" do
83
+ route = Route.new("GET", "/books", -> {})
84
+ assert_equal({}, route.params("/books"))
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ module Rutter
6
+ describe Routes do
7
+ let(:router) { Builder.new }
8
+ let(:routes) { Routes.new(router) }
9
+
10
+ it "has a *_path helper method" do
11
+ router.get "/books/:id", to: "Books#show", as: :book
12
+ assert_equal "/books/54", routes.book_path(id: 54)
13
+ end
14
+
15
+ it "has a *_url helper method" do
16
+ router.get "/books/:id", to: "Books#show", as: :book
17
+ assert_equal "http://example.com/books/54",
18
+ routes.book_url(id: 54)
19
+ end
20
+
21
+ it "raises ArgumentError if route not found" do
22
+ assert_raises(ArgumentError) { routes.invalid_path }
23
+ end
24
+
25
+ it "raises NoMethodError if unknown method is called" do
26
+ assert_raises(NoMethodError) { routes.invalid }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Rutter do
6
+ it "has a version number" do
7
+ assert_instance_of String, Rutter::VERSION
8
+ end
9
+
10
+ describe ".new" do
11
+ it "returns a new Builder object" do
12
+ assert_instance_of Rutter::Builder, Rutter.new
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ module Rutter
6
+ describe Builder do
7
+ let(:router) { Builder.new }
8
+
9
+ describe "scope" do
10
+ it "prepends prefixes" do
11
+ router.scope path: "web", namespace: "Web" do
12
+ get "/books", to: "Books#index"
13
+ end
14
+
15
+ route = router.flat_map.first
16
+ assert_equal "/web/books", route.path
17
+ assert_equal "Web::Books", route.endpoint[:controller]
18
+ end
19
+
20
+ it "recognizes all verbs" do
21
+ router.scope path: "/admin", namespace: "Secure", as: :koolaid do
22
+ get "/", to: ->(env) {}
23
+ post "/", to: ->(env) {}
24
+ put "/", to: ->(env) {}
25
+ patch "/", to: ->(env) {}
26
+ delete "/", to: ->(env) {}
27
+ options "/", to: ->(env) {}
28
+ head "/", to: ->(env) {}
29
+ trace "/", to: ->(env) {}
30
+ end
31
+
32
+ assert_equal "GET", router.flat_map[0].method
33
+ assert_equal "POST", router.flat_map[1].method
34
+ assert_equal "PUT", router.flat_map[2].method
35
+ assert_equal "PATCH", router.flat_map[3].method
36
+ assert_equal "DELETE", router.flat_map[4].method
37
+ assert_equal "OPTIONS", router.flat_map[5].method
38
+ assert_equal "HEAD", router.flat_map[6].method
39
+ assert_equal "TRACE", router.flat_map[7].method
40
+ end
41
+
42
+ describe "#root" do
43
+ it "adds a root path with a root name" do
44
+ router.scope path: "/admin", namespace: "Secure", as: :koolaid do
45
+ root to: ->(env) {}
46
+ end
47
+
48
+ assert_equal "/admin", router.flat_map.first.path
49
+ assert router.named_map.key?(:koolaid_root)
50
+ end
51
+ end
52
+
53
+ it "support nested scopes" do
54
+ router.scope path: "animals", namespace: "Species", as: :animals do
55
+ scope path: "mammals", namespace: "Mammals", as: :mammals do
56
+ get "/cats", to: "Cats#index", as: :cats
57
+ end
58
+ end
59
+
60
+ route = router.flat_map.first
61
+ assert_equal "/animals/mammals/cats", route.path
62
+ assert_equal "Species::Mammals::Cats", route.endpoint[:controller]
63
+ assert_equal "index", route.endpoint[:action]
64
+ assert router.named_map.key?(:animals_mammals_cats)
65
+ end
66
+
67
+ describe "#mount" do
68
+ it "calls the root mount with path prefix" do
69
+ app = ->(_env) {}
70
+ router.expects(:mount).with(app, at: "/admin/books")
71
+ router.scope(path: "/admin") { mount app, at: "books" }
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rutter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tobias Sandelius
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-09-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mocha
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rack-test
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email: tobias@sandeli.us
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - ".gitignore"
104
+ - ".rubocop.yml"
105
+ - ".travis.yml"
106
+ - ".yardopts"
107
+ - Gemfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - bench/config.ru
112
+ - bench/dynamic_routes
113
+ - bench/expand
114
+ - bench/helper.rb
115
+ - bench/mount
116
+ - bench/routes_helper
117
+ - bench/static_routes
118
+ - lib/rutter.rb
119
+ - lib/rutter/builder.rb
120
+ - lib/rutter/route.rb
121
+ - lib/rutter/routes.rb
122
+ - lib/rutter/scope.rb
123
+ - lib/rutter/version.rb
124
+ - rutter.gemspec
125
+ - spec/integration/mount_spec.rb
126
+ - spec/integration/params_spec.rb
127
+ - spec/integration/rack_spec.rb
128
+ - spec/integration/redirect_spec.rb
129
+ - spec/spec_helper.rb
130
+ - spec/support/assertions.rb
131
+ - spec/unit/builder_spec.rb
132
+ - spec/unit/route_spec.rb
133
+ - spec/unit/routes_spec.rb
134
+ - spec/unit/rutter_spec.rb
135
+ - spec/unit/scope_spec.rb
136
+ homepage: https://github.com/sandelius/rutter
137
+ licenses:
138
+ - MIT
139
+ metadata: {}
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: 2.4.0
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: 2.5.0
154
+ requirements: []
155
+ rubyforge_project:
156
+ rubygems_version: 2.6.11
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: HTTP router for Rack.
160
+ test_files:
161
+ - spec/integration/mount_spec.rb
162
+ - spec/integration/params_spec.rb
163
+ - spec/integration/rack_spec.rb
164
+ - spec/integration/redirect_spec.rb
165
+ - spec/spec_helper.rb
166
+ - spec/support/assertions.rb
167
+ - spec/unit/builder_spec.rb
168
+ - spec/unit/route_spec.rb
169
+ - spec/unit/routes_spec.rb
170
+ - spec/unit/rutter_spec.rb
171
+ - spec/unit/scope_spec.rb