renee-url-generation 0.4.0.pre1
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/lib/renee/core/chaining.rb +66 -0
- data/lib/renee/core/env_accessors.rb +72 -0
- data/lib/renee/core/exceptions.rb +15 -0
- data/lib/renee/core/matcher.rb +61 -0
- data/lib/renee/core/plugins.rb +31 -0
- data/lib/renee/core/rack_interaction.rb +50 -0
- data/lib/renee/core/request_context.rb +56 -0
- data/lib/renee/core/responding.rb +112 -0
- data/lib/renee/core/response.rb +78 -0
- data/lib/renee/core/routing.rb +319 -0
- data/lib/renee/core/transform.rb +18 -0
- data/lib/renee/core.rb +98 -0
- data/test/renee-core/chaining_test.rb +33 -0
- data/test/renee-core/env_accessors_test.rb +43 -0
- data/test/renee-core/include_test.rb +14 -0
- data/test/renee-core/request_context_test.rb +70 -0
- data/test/renee-core/responding_test.rb +128 -0
- data/test/renee-core/routing_test.rb +443 -0
- data/test/renee-core/test_helper.rb +4 -0
- data/test/renee-core/variable_type_test.rb +57 -0
- data/test/test_helper.rb +70 -0
- metadata +127 -0
@@ -0,0 +1,319 @@
|
|
1
|
+
module Renee
|
2
|
+
class Core
|
3
|
+
# Collection of useful methods for routing within a {Renee::Core} app.
|
4
|
+
module Routing
|
5
|
+
include Chaining
|
6
|
+
|
7
|
+
# Allow continued routing if a routing block fails to match
|
8
|
+
#
|
9
|
+
# @param [Boolean] val
|
10
|
+
# indicate if continued routing should be allowed.
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
def continue_routing
|
14
|
+
if block_given?
|
15
|
+
original_env = @env.dup
|
16
|
+
begin
|
17
|
+
yield
|
18
|
+
rescue NotMatchedError
|
19
|
+
@env = original_env
|
20
|
+
end
|
21
|
+
else
|
22
|
+
create_chain_proxy(:continue_routing)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
chainable :continue_routing
|
26
|
+
|
27
|
+
# Match a path to respond to.
|
28
|
+
#
|
29
|
+
# @param [String] p
|
30
|
+
# path to match.
|
31
|
+
# @param [Proc] blk
|
32
|
+
# block to yield
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# path('/') { ... } #=> '/'
|
36
|
+
# path('test') { ... } #=> '/test'
|
37
|
+
#
|
38
|
+
# path 'foo' do
|
39
|
+
# path('bar') { ... } #=> '/foo/bar'
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# @api public
|
43
|
+
def path(p, &blk)
|
44
|
+
if blk
|
45
|
+
p = p[1, p.size] if p[0] == ?/
|
46
|
+
extension_part = detected_extension ? "|\\.#{Regexp.quote(detected_extension)}" : ""
|
47
|
+
part(/^\/#{Regexp.quote(p)}(?=\/|$#{extension_part})/, &blk)
|
48
|
+
else
|
49
|
+
create_chain_proxy(:path, p)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
chainable :path
|
53
|
+
|
54
|
+
# Like #path, but doesn't look for leading slashes.
|
55
|
+
def part(p)
|
56
|
+
if block_given?
|
57
|
+
p = /^\/?#{Regexp.quote(p)}/ if p.is_a?(String)
|
58
|
+
if match = env['PATH_INFO'][p]
|
59
|
+
with_path_part(match) { yield }
|
60
|
+
end
|
61
|
+
else
|
62
|
+
create_chain_proxy(:part, p)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Match parts off the path as variables. The parts matcher can conform to either a regular expression, or be an Integer, or
|
67
|
+
# simply a String.
|
68
|
+
# @param[Object] type the type of object to match for. If you supply Integer, this will only match integers in addition to casting your variable for you.
|
69
|
+
# @param[Object] default the default value to use if your param cannot be successfully matched.
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
# path '/' do
|
73
|
+
# variable { |id| halt [200, {}, id] }
|
74
|
+
# end
|
75
|
+
# GET /hey #=> [200, {}, 'hey']
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# path '/' do
|
79
|
+
# variable(:integer) { |id| halt [200, {}, "This is a numeric id: #{id}"] }
|
80
|
+
# end
|
81
|
+
# GET /123 #=> [200, {}, 'This is a numeric id: 123']
|
82
|
+
#
|
83
|
+
# @example
|
84
|
+
# path '/test' do
|
85
|
+
# variable { |foo, bar| halt [200, {}, "#{foo}-#{bar}"] }
|
86
|
+
# end
|
87
|
+
# GET /test/hey/there #=> [200, {}, 'hey-there']
|
88
|
+
#
|
89
|
+
# @api public
|
90
|
+
def variable(type = nil, &blk)
|
91
|
+
blk ? complex_variable(type, '/', 1, &blk) : create_chain_proxy(:variable, type)
|
92
|
+
end
|
93
|
+
alias_method :var, :variable
|
94
|
+
chainable :variable, :var
|
95
|
+
|
96
|
+
def optional_variable(type = nil, &blk)
|
97
|
+
blk ? complex_variable(type, '/', 0..1) { |vars| blk[vars.first] } : create_chain_proxy(:variable, type)
|
98
|
+
end
|
99
|
+
alias_method :optional, :optional_variable
|
100
|
+
chainable :optional, :optional_variable
|
101
|
+
|
102
|
+
# Same as variable except you can match multiple variables with the same type.
|
103
|
+
# @param [Range, Integer] count The number of parameters to capture.
|
104
|
+
# @param [Symbol] type The type to use for match.
|
105
|
+
def multi_variable(count, type = nil, &blk)
|
106
|
+
blk ? complex_variable(type, '/', count, &blk) : create_chain_proxy(:multi_variable, count, type)
|
107
|
+
end
|
108
|
+
alias_method :multi_var, :multi_variable
|
109
|
+
alias_method :mvar, :multi_variable
|
110
|
+
chainable :multi_variable, :multi_var, :mvar
|
111
|
+
|
112
|
+
# Same as variable except it matches indefinitely.
|
113
|
+
# @param [Symbol] type The type to use for match.
|
114
|
+
def repeating_variable(type = nil, &blk)
|
115
|
+
blk ? complex_variable(type, '/', nil, &blk) : create_chain_proxy(:repeating_variable, type)
|
116
|
+
end
|
117
|
+
alias_method :glob, :repeating_variable
|
118
|
+
chainable :repeating_variable, :glob
|
119
|
+
|
120
|
+
# Match parts off the path as variables without a leading slash.
|
121
|
+
# @see #variable
|
122
|
+
# @api public
|
123
|
+
def partial_variable(type = nil, &blk)
|
124
|
+
blk ? complex_variable(type, nil, 1, &blk) : create_chain_proxy(:partial_variable, type)
|
125
|
+
end
|
126
|
+
alias_method :part_var, :partial_variable
|
127
|
+
chainable :partial_variable, :part_var
|
128
|
+
|
129
|
+
# Returns the matched extension. If no extension is present, returns `nil`.
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
# halt [200, {}, path] if extension == 'html'
|
133
|
+
#
|
134
|
+
# @api public
|
135
|
+
def extension
|
136
|
+
detected_extension
|
137
|
+
end
|
138
|
+
alias_method :ext, :extension
|
139
|
+
|
140
|
+
# Match no extension.
|
141
|
+
#
|
142
|
+
# @example
|
143
|
+
# no_extension { |path| halt [200, {}, path] }
|
144
|
+
#
|
145
|
+
# @api public
|
146
|
+
def no_extension(&blk)
|
147
|
+
blk.call unless detected_extension
|
148
|
+
end
|
149
|
+
|
150
|
+
# Match any remaining path.
|
151
|
+
#
|
152
|
+
# @example
|
153
|
+
# remainder { |path| halt [200, {}, path] }
|
154
|
+
#
|
155
|
+
# @api public
|
156
|
+
def remainder(&blk)
|
157
|
+
blk ? with_path_part(env['PATH_INFO']) { |var| blk.call(var) } : create_chain_proxy(:remainder)
|
158
|
+
end
|
159
|
+
alias_method :catchall, :remainder
|
160
|
+
chainable :remainder, :catchall
|
161
|
+
|
162
|
+
# Respond to a GET request and yield the block.
|
163
|
+
#
|
164
|
+
# @example
|
165
|
+
# get { halt [200, {}, "hello world"] }
|
166
|
+
#
|
167
|
+
# @api public
|
168
|
+
def get(&blk)
|
169
|
+
blk ? request_method('GET', &blk) : create_chain_proxy(:get)
|
170
|
+
end
|
171
|
+
chainable :get
|
172
|
+
|
173
|
+
# Respond to a POST request and yield the block.
|
174
|
+
#
|
175
|
+
# @example
|
176
|
+
# post { halt [200, {}, "hello world"] }
|
177
|
+
#
|
178
|
+
# @api public
|
179
|
+
def post(&blk)
|
180
|
+
blk ? request_method('POST', &blk) : create_chain_proxy(:post)
|
181
|
+
end
|
182
|
+
chainable :post
|
183
|
+
|
184
|
+
# Respond to a PUT request and yield the block.
|
185
|
+
#
|
186
|
+
# @example
|
187
|
+
# put { halt [200, {}, "hello world"] }
|
188
|
+
#
|
189
|
+
# @api public
|
190
|
+
def put(&blk)
|
191
|
+
blk ? request_method('PUT', &blk) : create_chain_proxy(:put)
|
192
|
+
end
|
193
|
+
chainable :put
|
194
|
+
|
195
|
+
# Respond to a DELETE request and yield the block.
|
196
|
+
#
|
197
|
+
# @example
|
198
|
+
# delete { halt [200, {}, "hello world"] }
|
199
|
+
#
|
200
|
+
# @api public
|
201
|
+
def delete(&blk)
|
202
|
+
blk ? request_method('DELETE', &blk) : create_chain_proxy(:delete)
|
203
|
+
end
|
204
|
+
chainable :delete
|
205
|
+
|
206
|
+
# Match only when the path is either '' or '/'.
|
207
|
+
#
|
208
|
+
# @example
|
209
|
+
# complete { halt [200, {}, "hello world"] }
|
210
|
+
#
|
211
|
+
# @api public
|
212
|
+
def complete(&blk)
|
213
|
+
if blk
|
214
|
+
with_path_part(env['PATH_INFO']) { blk.call } if complete?
|
215
|
+
else
|
216
|
+
create_chain_proxy(:complete)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
chainable :complete
|
220
|
+
|
221
|
+
# Test if the path has been consumed
|
222
|
+
#
|
223
|
+
# @example
|
224
|
+
# if complete?
|
225
|
+
# halt "Hey, the path is done"
|
226
|
+
# end
|
227
|
+
#
|
228
|
+
# @api public
|
229
|
+
def complete?
|
230
|
+
(detected_extension and env['PATH_INFO'] =~ /^\/?(\.#{Regexp.quote(detected_extension)}\/?)?$/) || (detected_extension.nil? and env['PATH_INFO'] =~ /^\/?$/)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Match only when the path is ''.
|
234
|
+
#
|
235
|
+
# @example
|
236
|
+
# empty { halt [200, {}, "hello world"] }
|
237
|
+
#
|
238
|
+
# @api public
|
239
|
+
def empty(&blk)
|
240
|
+
if blk
|
241
|
+
if env['PATH_INFO'] == ''
|
242
|
+
with_path_part(env['PATH_INFO']) { blk.call }
|
243
|
+
end
|
244
|
+
else
|
245
|
+
create_chain_proxy(:empty)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
chainable :empty
|
249
|
+
|
250
|
+
private
|
251
|
+
def complex_variable(type, prefix, count)
|
252
|
+
matcher = variable_matcher_for_type(type)
|
253
|
+
path = env['PATH_INFO'].dup
|
254
|
+
vals = []
|
255
|
+
var_index = 0
|
256
|
+
variable_matching_loop(count) do
|
257
|
+
path.start_with?(prefix) ? path.slice!(0, prefix.size) : break if prefix
|
258
|
+
if match = matcher[path]
|
259
|
+
path.slice!(0, match.first.size)
|
260
|
+
vals << match.last
|
261
|
+
end
|
262
|
+
end
|
263
|
+
return unless count.nil? || count === vals.size
|
264
|
+
with_path_part(env['PATH_INFO'][0, env['PATH_INFO'].size - path.size]) do
|
265
|
+
if count == 1
|
266
|
+
yield(vals.first)
|
267
|
+
else
|
268
|
+
yield(vals)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def variable_matching_loop(count)
|
274
|
+
case count
|
275
|
+
when Range then count.max.times { break unless yield }
|
276
|
+
when nil then loop { break unless yield }
|
277
|
+
else count.times { break unless yield }
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def variable_matcher_for_type(type)
|
282
|
+
if self.class.variable_types.key?(type)
|
283
|
+
self.class.variable_types[type]
|
284
|
+
else
|
285
|
+
regexp = case type
|
286
|
+
when nil, String
|
287
|
+
detected_extension ?
|
288
|
+
/(([^\/](?!#{Regexp.quote(detected_extension)}$))+)(?=$|\/|\.#{Regexp.quote(detected_extension)})/ :
|
289
|
+
/([^\/]+)(?=$|\/)/
|
290
|
+
when Regexp
|
291
|
+
type
|
292
|
+
else
|
293
|
+
raise "Unexpected variable type #{type.inspect}"
|
294
|
+
end
|
295
|
+
proc do |path|
|
296
|
+
if match = /^#{regexp.to_s}/.match(path)
|
297
|
+
[match[0]]
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def with_path_part(part)
|
304
|
+
script_part = env['PATH_INFO'][0, part.size]
|
305
|
+
env['PATH_INFO'] = env['PATH_INFO'].slice(part.size, env['PATH_INFO'].size)
|
306
|
+
env['SCRIPT_NAME'] += script_part
|
307
|
+
yield script_part
|
308
|
+
raise NotMatchedError
|
309
|
+
end
|
310
|
+
|
311
|
+
def request_method(method)
|
312
|
+
if env['REQUEST_METHOD'] == method && complete?
|
313
|
+
yield
|
314
|
+
raise NotMatchedError
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Renee
|
2
|
+
class Core
|
3
|
+
# Module used for transforming arbitrary values using the registerd variable types.
|
4
|
+
# @see #register_variable_name.
|
5
|
+
#
|
6
|
+
module Transform
|
7
|
+
# Transforms a value according to the rules specified by #register_variable_name.
|
8
|
+
# @param [Symbol] name The name of the variable type.
|
9
|
+
# @param [String] value The value to transform.
|
10
|
+
# @return The transformed value or nil.
|
11
|
+
def transform(type, value)
|
12
|
+
if self.class.variable_types.key?(type) and m = self.class.variable_types[type][value]
|
13
|
+
m.first == value ? m.last : nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/renee/core.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
require 'renee/version'
|
4
|
+
require 'renee/core/matcher'
|
5
|
+
require 'renee/core/chaining'
|
6
|
+
require 'renee/core/response'
|
7
|
+
require 'renee/core/exceptions'
|
8
|
+
require 'renee/core/rack_interaction'
|
9
|
+
require 'renee/core/request_context'
|
10
|
+
require 'renee/core/transform'
|
11
|
+
require 'renee/core/routing'
|
12
|
+
require 'renee/core/responding'
|
13
|
+
require 'renee/core/env_accessors'
|
14
|
+
require 'renee/core/plugins'
|
15
|
+
|
16
|
+
# Top-level Renee constant
|
17
|
+
module Renee
|
18
|
+
# @example
|
19
|
+
# Renee.core { path('/hello') { halt :ok } }
|
20
|
+
def self.core(&blk)
|
21
|
+
cls = Class.new(Renee::Core)
|
22
|
+
cls.app(&blk) if blk
|
23
|
+
cls
|
24
|
+
end
|
25
|
+
|
26
|
+
# The top-level class for creating core application.
|
27
|
+
# For convience you can also used a method named #Renee
|
28
|
+
# for decalaring new instances.
|
29
|
+
class Core
|
30
|
+
# Current version of Renee::Core
|
31
|
+
VERSION = Renee::VERSION
|
32
|
+
|
33
|
+
# Error raised if routing fails. Use #continue_routing to continue routing.
|
34
|
+
NotMatchedError = Class.new(RuntimeError)
|
35
|
+
|
36
|
+
# Class methods that are included in new instances of {Core}
|
37
|
+
module ClassMethods
|
38
|
+
include Plugins
|
39
|
+
|
40
|
+
# The application block used to create your application.
|
41
|
+
attr_reader :application_block
|
42
|
+
|
43
|
+
# Provides a rack interface compliant call method. This method creates a new instance of your class and calls
|
44
|
+
# #call on it.
|
45
|
+
def call(env)
|
46
|
+
new.call(env)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Allows you to set the #application_block on your class.
|
50
|
+
# @yield The application block
|
51
|
+
def app(&app)
|
52
|
+
@application_block = app
|
53
|
+
setup do
|
54
|
+
register_variable_type :integer, IntegerMatcher
|
55
|
+
register_variable_type :int, :integer
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Runs class methods on your application.
|
60
|
+
def setup(&blk)
|
61
|
+
instance_eval(&blk)
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
# The currently available variable types you've defined.
|
66
|
+
def variable_types
|
67
|
+
@variable_types ||= {}
|
68
|
+
end
|
69
|
+
|
70
|
+
# Registers a new variable type for use within {Renee::Core::Routing#variable} and others.
|
71
|
+
# @param [Symbol] name The name of the variable.
|
72
|
+
# @param [Regexp] matcher A regexp describing what part of an arbitrary string to capture.
|
73
|
+
# @return [Renee::Core::Matcher] A matcher
|
74
|
+
def register_variable_type(name, matcher)
|
75
|
+
matcher = case matcher
|
76
|
+
when Matcher then matcher
|
77
|
+
when Array then Matcher.new(matcher.map{|m| variable_types[m]})
|
78
|
+
when Symbol then variable_types[matcher]
|
79
|
+
else Matcher.new(matcher)
|
80
|
+
end
|
81
|
+
matcher.name = name
|
82
|
+
variable_types[name] = matcher
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
include Chaining
|
87
|
+
include RequestContext
|
88
|
+
include Routing
|
89
|
+
include Responding
|
90
|
+
include RackInteraction
|
91
|
+
include Transform
|
92
|
+
include EnvAccessors
|
93
|
+
|
94
|
+
class << self
|
95
|
+
include ClassMethods
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
describe "Route chaining" do
|
4
|
+
|
5
|
+
it "should chaining" do
|
6
|
+
type = { 'Content-Type' => 'text/plain' }
|
7
|
+
mock_app do
|
8
|
+
path('/').get { halt [200,type,['foo']] }
|
9
|
+
continue_routing.path('bar').put { halt [200,type,['bar']] }
|
10
|
+
continue_routing.path('bar').var.put { |id| halt [200,type,[id]] }
|
11
|
+
continue_routing.path('bar').var.get.halt { |id| "wow, nice to meet you " }
|
12
|
+
end
|
13
|
+
get '/'
|
14
|
+
assert_equal 200, response.status
|
15
|
+
assert_equal 'foo', response.body
|
16
|
+
put '/bar'
|
17
|
+
assert_equal 200, response.status
|
18
|
+
assert_equal 'bar', response.body
|
19
|
+
put '/bar/asd'
|
20
|
+
assert_equal 200, response.status
|
21
|
+
assert_equal 'asd', response.body
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should chain and halt with a non-routing method" do
|
25
|
+
type = { 'Content-Type' => 'text/plain' }
|
26
|
+
mock_app do
|
27
|
+
path('/').get.halt "hi"
|
28
|
+
end
|
29
|
+
get '/'
|
30
|
+
assert_equal 200, response.status
|
31
|
+
assert_equal 'hi', response.body
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path('../test_helper', __FILE__)
|
4
|
+
|
5
|
+
describe Renee::Core::EnvAccessors do
|
6
|
+
it "should allow accessing the env" do
|
7
|
+
@app = Renee.core {
|
8
|
+
self.test = 'hello'
|
9
|
+
path('test').get do
|
10
|
+
halt "test is #{test}"
|
11
|
+
end
|
12
|
+
}.setup {
|
13
|
+
env_accessor :test
|
14
|
+
}
|
15
|
+
get '/test'
|
16
|
+
assert_equal 200, response.status
|
17
|
+
assert_equal 'test is hello', response.body
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should raise when you try to access weird env keys" do
|
21
|
+
assert_raises(Renee::Core::EnvAccessors::InvalidEnvNameError) {
|
22
|
+
@app = Renee.core {
|
23
|
+
self.test_test = 'hello'
|
24
|
+
}.setup {
|
25
|
+
env_accessor "test.test"
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should allow weird env keys if you map them" do
|
31
|
+
@app = Renee.core {
|
32
|
+
self.test_test = 'hello'
|
33
|
+
path('test').get do
|
34
|
+
halt "test is #{test_test}"
|
35
|
+
end
|
36
|
+
}.setup {
|
37
|
+
env_accessor "test.test" => :test_test
|
38
|
+
}
|
39
|
+
get '/test'
|
40
|
+
assert_equal 200, response.status
|
41
|
+
assert_equal 'test is hello', response.body
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
describe "Route::Settings#include" do
|
4
|
+
it "should allow the inclusion of arbitrary modules" do
|
5
|
+
type = { 'Content-Type' => 'text/plain' }
|
6
|
+
@app = Renee.core {
|
7
|
+
halt :ok if respond_to?(:hi)
|
8
|
+
}.setup {
|
9
|
+
include Module.new { def hi; end }
|
10
|
+
}
|
11
|
+
get '/'
|
12
|
+
assert_equal 200, response.status
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class AddHelloThereMiddleware
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
if env['hello']
|
10
|
+
env['hello'] << "there"
|
11
|
+
else
|
12
|
+
env['hello'] = 'hello'
|
13
|
+
end
|
14
|
+
@app.call(env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class AddWhatsThatMiddleware
|
19
|
+
def initialize(app)
|
20
|
+
@app = app
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(env)
|
24
|
+
if env['hello']
|
25
|
+
env['hello'] << "that"
|
26
|
+
else
|
27
|
+
env['hello'] = 'whats'
|
28
|
+
end
|
29
|
+
@app.call(env)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "Route::Core::RequestContext#use" do
|
34
|
+
it "should allow the inclusion of arbitrary middlewares" do
|
35
|
+
type = { 'Content-Type' => 'text/plain' }
|
36
|
+
@app = Renee.core {
|
37
|
+
halt env['hello']
|
38
|
+
}.setup {
|
39
|
+
use AddHelloThereMiddleware
|
40
|
+
}
|
41
|
+
get '/'
|
42
|
+
assert_equal 200, response.status
|
43
|
+
assert_equal 'hello', response.body
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should call middlewares in sequence (1)" do
|
47
|
+
type = { 'Content-Type' => 'text/plain' }
|
48
|
+
@app = Renee.core {
|
49
|
+
halt env['hello']
|
50
|
+
}.setup {
|
51
|
+
use AddHelloThereMiddleware
|
52
|
+
use AddWhatsThatMiddleware
|
53
|
+
}
|
54
|
+
get '/'
|
55
|
+
assert_equal 200, response.status
|
56
|
+
assert_equal 'hellothat', response.body
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should call middlewares in sequence (2)" do
|
60
|
+
@app = Renee.core {
|
61
|
+
halt env['hello']
|
62
|
+
}.setup {
|
63
|
+
use AddWhatsThatMiddleware
|
64
|
+
use AddHelloThereMiddleware
|
65
|
+
}
|
66
|
+
get '/'
|
67
|
+
assert_equal 200, response.status
|
68
|
+
assert_equal 'whatsthere', response.body
|
69
|
+
end
|
70
|
+
end
|