pendragon 0.6.2 → 1.0.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +4 -2
- data/Gemfile +7 -0
- data/Gemfile.lock +27 -83
- data/README.md +69 -192
- data/Rakefile +3 -18
- data/benchmark.rb +31 -0
- data/lib/pendragon.rb +60 -44
- data/lib/pendragon/constants.rb +27 -0
- data/lib/pendragon/errors.rb +72 -0
- data/lib/pendragon/linear.rb +9 -0
- data/lib/pendragon/realism.rb +49 -0
- data/lib/pendragon/router.rb +295 -142
- data/lib/pendragon/version.rb +1 -1
- data/pendragon.gemspec +1 -6
- data/test/helper.rb +9 -84
- data/test/router/test_linear.rb +6 -0
- data/test/router/test_realism.rb +6 -0
- data/test/supports/shared_examples_for_routing.rb +277 -0
- data/test/test_router.rb +40 -0
- metadata +22 -101
- data/lib/pendragon/configuration.rb +0 -42
- data/lib/pendragon/engine/compiler.rb +0 -74
- data/lib/pendragon/engine/recognizer.rb +0 -72
- data/lib/pendragon/error.rb +0 -71
- data/lib/pendragon/matcher.rb +0 -93
- data/lib/pendragon/padrino.rb +0 -15
- data/lib/pendragon/padrino/ext/class_methods.rb +0 -318
- data/lib/pendragon/padrino/ext/instance_methods.rb +0 -63
- data/lib/pendragon/padrino/route.rb +0 -50
- data/lib/pendragon/padrino/router.rb +0 -34
- data/lib/pendragon/route.rb +0 -124
- data/test/compile_helper.rb +0 -3
- data/test/padrino_test.rb +0 -2113
- data/test/pendragon_configuration.rb +0 -32
- data/test/pendragon_test.rb +0 -229
data/lib/pendragon/version.rb
CHANGED
data/pendragon.gemspec
CHANGED
@@ -11,10 +11,5 @@ Gem::Specification.new "pendragon", Pendragon::VERSION do |s|
|
|
11
11
|
s.license = "MIT"
|
12
12
|
|
13
13
|
s.add_dependency "rack", ">= 1.3.0"
|
14
|
-
s.add_dependency "
|
15
|
-
s.add_development_dependency "rake", ">= 0.8.7"
|
16
|
-
s.add_development_dependency "rack-test", ">= 0.5.0"
|
17
|
-
s.add_development_dependency "mocha", ">= 0.10.0"
|
18
|
-
s.add_development_dependency "haml"
|
19
|
-
s.add_development_dependency "padrino", "~> 0.12.2"
|
14
|
+
s.add_dependency "mustermann"
|
20
15
|
end
|
data/test/helper.rb
CHANGED
@@ -1,92 +1,17 @@
|
|
1
1
|
require 'bundler/setup'
|
2
|
-
|
3
|
-
|
4
|
-
require File.expand_path('
|
2
|
+
require File.expand_path('../lib/pendragon', __dir__)
|
3
|
+
require File.expand_path('../lib/pendragon/realism', __dir__)
|
4
|
+
require File.expand_path('../lib/pendragon/linear', __dir__)
|
5
5
|
|
6
|
-
require
|
7
|
-
|
6
|
+
Bundler.require(:default)
|
7
|
+
|
8
|
+
require 'test/unit'
|
8
9
|
require 'mocha/setup'
|
9
|
-
require 'padrino-core'
|
10
|
-
require 'padrino/rendering'
|
11
10
|
require 'rack'
|
12
11
|
require 'rack/test'
|
13
12
|
|
14
|
-
|
15
|
-
require 'ruby-debug'
|
16
|
-
rescue LoadError; end
|
17
|
-
|
18
|
-
class Sinatra::Base
|
19
|
-
include MiniTest::Assertions
|
13
|
+
module Supports
|
20
14
|
end
|
21
15
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def pendragon(&block)
|
26
|
-
if !block_given? && defined?(ENABLE_COMPILER)
|
27
|
-
block = Proc.new do |config|
|
28
|
-
config.enable_compiler = true
|
29
|
-
end
|
30
|
-
end
|
31
|
-
@app = Pendragon.new(&block)
|
32
|
-
end
|
33
|
-
|
34
|
-
def mock_app(base = nil, &block)
|
35
|
-
@app = Sinatra.new(base || ::Padrino::Application, &block)
|
36
|
-
@app.class_eval{ set :pendragon, :enable_compiler => defined?(ENABLE_COMPILER) }
|
37
|
-
end
|
38
|
-
|
39
|
-
def app
|
40
|
-
Rack::Lint.new(@app)
|
41
|
-
end
|
42
|
-
|
43
|
-
def method_missing(name, *args, &block)
|
44
|
-
if response && response.respond_to?(name)
|
45
|
-
response.send(name, *args, &block)
|
46
|
-
else
|
47
|
-
super(name, *args, &block)
|
48
|
-
end
|
49
|
-
rescue Rack::Test::Error # no response yet
|
50
|
-
super(name, *args, &block)
|
51
|
-
end
|
52
|
-
alias response last_response
|
53
|
-
|
54
|
-
class << self
|
55
|
-
alias :setup :before unless defined?(Rails)
|
56
|
-
alias :teardown :after unless defined?(Rails)
|
57
|
-
alias :should :it
|
58
|
-
alias :context :describe
|
59
|
-
def should_eventually(desc)
|
60
|
-
it("should eventually #{desc}") { skip("Should eventually #{desc}") }
|
61
|
-
end
|
62
|
-
end
|
63
|
-
alias :assert_no_match :refute_match
|
64
|
-
alias :assert_not_nil :refute_nil
|
65
|
-
alias :assert_not_equal :refute_equal
|
66
|
-
end
|
67
|
-
|
68
|
-
|
69
|
-
if defined?(MiniTest::Unit.output)
|
70
|
-
class ColoredIO
|
71
|
-
def initialize(io)
|
72
|
-
@io = io
|
73
|
-
end
|
74
|
-
|
75
|
-
def print(o)
|
76
|
-
case o
|
77
|
-
when "." then @io.send(:print, o.colorize(:green))
|
78
|
-
when "E" then @io.send(:print, o.colorize(:red))
|
79
|
-
when "F" then @io.send(:print, o.colorize(:yellow))
|
80
|
-
when "S" then @io.send(:print, o.colorize(:magenta))
|
81
|
-
else @io.send(:print, o)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def puts(*o)
|
86
|
-
super
|
87
|
-
end
|
88
|
-
end
|
89
|
-
MiniTest::Unit.output = ColoredIO.new($stdout)
|
90
|
-
else
|
91
|
-
require 'minitest/pride'
|
92
|
-
end
|
16
|
+
$:.unshift(File.expand_path('..', __dir__))
|
17
|
+
Dir.glob('test/supports/*.rb').each(&method(:require))
|
@@ -0,0 +1,277 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Supports::SharedExamplesForRouting
|
4
|
+
extend Modulla
|
5
|
+
include Rack::Test::Methods
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def router_class(klass)
|
9
|
+
define_method(:router) { klass }
|
10
|
+
end
|
11
|
+
|
12
|
+
def disable(*features)
|
13
|
+
features.each do |feature|
|
14
|
+
define_method('%p disabled' % feature) {}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
alias_method :response, :last_response
|
20
|
+
|
21
|
+
def disabled?(feature)
|
22
|
+
respond_to?('%p disabled' % feature)
|
23
|
+
end
|
24
|
+
|
25
|
+
def mock_env_for(path = ?/, **options)
|
26
|
+
Rack::MockRequest.env_for(path, **options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def mock_app(base = nil, &block)
|
30
|
+
@app = router.new(&block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def app
|
34
|
+
Rack::Lint.new(@app)
|
35
|
+
end
|
36
|
+
|
37
|
+
def assert_response(status, body, headers = {})
|
38
|
+
assert { last_response.status == status }
|
39
|
+
assert { last_response.body == body }
|
40
|
+
headers.each_pair do |key, val|
|
41
|
+
assert { last_response.headers[key] == val }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
sub_test_case '#call' do
|
46
|
+
test 'return response conformed rack format' do
|
47
|
+
assert_nothing_raised do
|
48
|
+
Rack::Lint.new(router.new).call(mock_env_for)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
sub_test_case 'default response' do
|
54
|
+
setup { mock_app { } }
|
55
|
+
test 'return default response if given request does not match with any routes' do
|
56
|
+
get ?/
|
57
|
+
assert_response 404, 'not found', 'Content-Type' => 'text/plain'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
sub_test_case 'basic' do
|
62
|
+
setup do
|
63
|
+
mock_app do
|
64
|
+
get(?/) { [200, {}, ['hello']] }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
test 'return response if given request matches with any routes' do
|
68
|
+
get ?/
|
69
|
+
assert_response 200, 'hello'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
sub_test_case 'duck typing' do
|
74
|
+
setup do
|
75
|
+
_lambda = -> { [200, {}, ['lambda']] }
|
76
|
+
rack_app_class = Class.new {
|
77
|
+
def call(env)
|
78
|
+
[200, {}, ['rackapp']]
|
79
|
+
end
|
80
|
+
}
|
81
|
+
mock_app do
|
82
|
+
get '/lambda', to: _lambda
|
83
|
+
get '/rack_app', to: rack_app_class.new
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
test 'duck typing for lambda' do
|
88
|
+
get '/lambda'
|
89
|
+
assert_response 200, 'lambda'
|
90
|
+
end
|
91
|
+
|
92
|
+
test 'duck typing for rack app' do
|
93
|
+
get '/rack_app'
|
94
|
+
assert_response 200, 'rackapp'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
sub_test_case 'namespacing' do
|
99
|
+
setup do
|
100
|
+
mock_app do
|
101
|
+
namespace :foo do
|
102
|
+
get('/123') { [200, {}, ['hey']] }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
test 'append given namespace as a prefix' do
|
108
|
+
get '/foo/123'
|
109
|
+
assert_response 200, 'hey'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
sub_test_case 'nested namespacing' do
|
114
|
+
setup do
|
115
|
+
mock_app do
|
116
|
+
namespace :foo do
|
117
|
+
get('/') { [200, {}, ['foo']] }
|
118
|
+
namespace :bar do
|
119
|
+
get('/') { [200, {}, ['bar']] }
|
120
|
+
namespace :baz do
|
121
|
+
get('/') { [200, {}, ['baz']] }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
test 'append given namespace as a prefix' do
|
129
|
+
get '/foo/'
|
130
|
+
assert_response 200, 'foo'
|
131
|
+
get '/foo/bar/'
|
132
|
+
assert_response 200, 'bar'
|
133
|
+
get '/foo/bar/baz/'
|
134
|
+
assert_response 200, 'baz'
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
sub_test_case 'complex routing' do
|
139
|
+
setup do
|
140
|
+
mock_app do
|
141
|
+
1000.times do |n|
|
142
|
+
[:get, :post, :put, :delete, :options].each do |verb|
|
143
|
+
public_send(verb, "/#{n}") { [200, {}, ["#{verb} #{n}"]] }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
test 'recognize a route correctly' do
|
150
|
+
put '/376'
|
151
|
+
assert_response 200, 'put 376'
|
152
|
+
end
|
153
|
+
|
154
|
+
test 'recognize a route correctly (part 2)' do
|
155
|
+
delete '/999'
|
156
|
+
assert_response 200, 'delete 999'
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
sub_test_case 'method not allowed' do
|
161
|
+
setup do
|
162
|
+
mock_app do
|
163
|
+
get '/testing' do
|
164
|
+
[200, {}, ['hello testing']]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
test 'returns 405 if given method is not allowed' do
|
170
|
+
post '/testing'
|
171
|
+
assert_response 405, 'method not allowed', { 'Allows' => 'GET' }
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
sub_test_case 'block arguments' do
|
176
|
+
setup do
|
177
|
+
mock_app do
|
178
|
+
get '/foo/:name/*/*' do |name, a, b|
|
179
|
+
body = "#{name}, #{a}, #{b}"
|
180
|
+
[200, {}, [body]]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
test 'gets params as block parameters' do
|
186
|
+
omit_if disabled?(:multiple_splats)
|
187
|
+
get '/foo/yoman/1234/5678'
|
188
|
+
assert_response 200, 'yoman, 1234, 5678'
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
sub_test_case '#params' do
|
193
|
+
setup do
|
194
|
+
mock_app do
|
195
|
+
get '/foo/:name/*/*' do
|
196
|
+
body = params.to_json
|
197
|
+
[200, {}, [body]]
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
test 'gets params correctly' do
|
203
|
+
omit_if disabled?(:multiple_splats)
|
204
|
+
get '/foo/heyman/1234/5678'
|
205
|
+
assert_response 200, {name: 'heyman', splat: ['1234', '5678']}.to_json
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
sub_test_case 'capturing' do
|
210
|
+
setup do
|
211
|
+
mock_app do
|
212
|
+
get '/users/:name/articles/:article_id' do
|
213
|
+
[200, {}, [captures.join(' ')]]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
test 'gets captures correctly' do
|
219
|
+
get '/users/namusyaka/articles/1234'
|
220
|
+
assert_response 200, 'namusyaka 1234'
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
sub_test_case 'splat' do
|
225
|
+
sub_test_case 'multiple splats' do
|
226
|
+
setup do
|
227
|
+
mock_app do
|
228
|
+
get '/splatting/*/*/*' do |a, b, c|
|
229
|
+
[200, {}, ["captures: #{captures.join(' ')}, block: #{a} #{b} #{c}"]]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
test 'gets multiple splats correctly' do
|
235
|
+
omit_if disabled?(:multiple_splats)
|
236
|
+
get '/splatting/1234/5678/90'
|
237
|
+
assert_response 200, 'captures: 1234 5678 90, block: 1234 5678 90'
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
sub_test_case 'cascading' do
|
243
|
+
setup do
|
244
|
+
mock_app do
|
245
|
+
get '/cascading' do
|
246
|
+
[200, { 'X-Cascade' => 'pass' }, ['']]
|
247
|
+
end
|
248
|
+
|
249
|
+
get '/cascading' do
|
250
|
+
[200, {}, ['yay']]
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
test 'succeeds to cascading' do
|
256
|
+
omit_if disabled?(:cascading)
|
257
|
+
get '/cascading'
|
258
|
+
assert_response 200, 'yay'
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
sub_test_case 'halting' do
|
263
|
+
setup do
|
264
|
+
mock_app do
|
265
|
+
get ?/ do
|
266
|
+
throw :halt, [404, {}, ['not found']]
|
267
|
+
[200, {}, ['failed to halt']]
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
test 'succeeds to halting' do
|
273
|
+
get ?/
|
274
|
+
assert_response 404, 'not found'
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
data/test/test_router.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path('helper', __dir__)
|
2
|
+
|
3
|
+
class TestRouter < Test::Unit::TestCase
|
4
|
+
attr_accessor :router
|
5
|
+
|
6
|
+
setup { self.router = Pendragon::Router.new }
|
7
|
+
|
8
|
+
sub_test_case '#call' do
|
9
|
+
setup do
|
10
|
+
@mock_request = Rack::MockRequest.env_for(?/)
|
11
|
+
router.get(?/) { 'hello' }
|
12
|
+
end
|
13
|
+
|
14
|
+
test 'should recognize a route inside #with_block' do
|
15
|
+
router.expects(:with_optimization)
|
16
|
+
router.call(@mock_request)
|
17
|
+
end
|
18
|
+
|
19
|
+
test 'raises NotImplementedError' do
|
20
|
+
assert_raise NotImplementedError do
|
21
|
+
router.call(@mock_request)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
sub_test_case 'without matched route' do
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
%w[get post put delete head options].each do |request_method|
|
30
|
+
sub_test_case "##{request_method}" do
|
31
|
+
setup { @expected_block = Proc.new {} }
|
32
|
+
test "should append #{request_method} route correctly" do
|
33
|
+
router.public_send(request_method, ?/, &@expected_block)
|
34
|
+
actual = router.map[request_method.upcase].first
|
35
|
+
assert { actual.request_method == request_method.upcase }
|
36
|
+
assert { actual.path == ?/ }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|