jellyfish 0.6.0 → 0.8.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 +7 -0
- data/.gitignore +0 -1
- data/.travis.yml +1 -0
- data/CHANGES.md +71 -0
- data/README.md +488 -20
- data/jellyfish.gemspec +25 -10
- data/lib/jellyfish.rb +84 -40
- data/lib/jellyfish/chunked_body.rb +20 -0
- data/lib/jellyfish/multi_actions.rb +35 -0
- data/lib/jellyfish/newrelic.rb +0 -1
- data/lib/jellyfish/normalized_params.rb +55 -0
- data/lib/jellyfish/normalized_path.rb +13 -0
- data/lib/jellyfish/sinatra.rb +6 -47
- data/lib/jellyfish/test.rb +7 -2
- data/lib/jellyfish/version.rb +1 -1
- data/task/gemgem.rb +7 -6
- data/test/sinatra/test_base.rb +110 -0
- data/test/sinatra/test_chunked_body.rb +43 -0
- data/test/sinatra/test_error.rb +145 -0
- data/test/sinatra/test_multi_actions.rb +217 -0
- data/test/sinatra/test_routing.rb +425 -0
- data/test/test_from_readme.rb +39 -0
- data/test/test_inheritance.rb +88 -0
- metadata +32 -25
- data/example/config.ru +0 -118
- data/example/rainbows.rb +0 -4
- data/example/server.sh +0 -3
data/lib/jellyfish/test.rb
CHANGED
@@ -8,8 +8,13 @@ Bacon.summary_on_exit
|
|
8
8
|
shared :jellyfish do
|
9
9
|
%w[options get head post put delete patch].each do |method|
|
10
10
|
instance_eval <<-RUBY
|
11
|
-
def #{method} path='/', app=app
|
12
|
-
|
11
|
+
def #{method} path='/', app=app, env={}
|
12
|
+
File.open(File::NULL) do |input|
|
13
|
+
app.call({'PATH_INFO' => path ,
|
14
|
+
'REQUEST_METHOD' => '#{method}'.upcase,
|
15
|
+
'SCRIPT_NAME' => '' ,
|
16
|
+
'rack.input' => input }.merge(env))
|
17
|
+
end
|
13
18
|
end
|
14
19
|
RUBY
|
15
20
|
end
|
data/lib/jellyfish/version.rb
CHANGED
data/task/gemgem.rb
CHANGED
@@ -14,6 +14,7 @@ module Gemgem
|
|
14
14
|
|
15
15
|
s.description = description.join
|
16
16
|
s.summary = description.first
|
17
|
+
s.license = readme['LICENSE'].sub(/.+\n\n/, '').lines.first.strip
|
17
18
|
|
18
19
|
s.rubygems_version = Gem::VERSION
|
19
20
|
s.date = Time.now.strftime('%Y-%m-%d')
|
@@ -33,8 +34,8 @@ module Gemgem
|
|
33
34
|
@readme ||=
|
34
35
|
if path
|
35
36
|
ps = "##{File.read(path)}".
|
36
|
-
scan(/((#+)[^\n]+\n\n.+?(
|
37
|
-
ps.inject(
|
37
|
+
scan(/((#+)[^\n]+\n\n.+?(?=(\n\n\2[^#\n]+\n)|\Z))/m).map(&:first)
|
38
|
+
ps.inject('HEADER' => ps.first){ |r, s, i|
|
38
39
|
r[s[/\w+/]] = s
|
39
40
|
r
|
40
41
|
}
|
@@ -44,7 +45,7 @@ module Gemgem
|
|
44
45
|
end
|
45
46
|
|
46
47
|
def description
|
47
|
-
@description ||= (readme['DESCRIPTION']||'').sub(/.+\n\n/, '').lines
|
48
|
+
@description ||= (readme['DESCRIPTION']||'').sub(/.+\n\n/, '').lines
|
48
49
|
end
|
49
50
|
|
50
51
|
def changes
|
@@ -104,9 +105,9 @@ module Gemgem
|
|
104
105
|
end
|
105
106
|
|
106
107
|
def split_lines ruby
|
107
|
-
ruby.gsub(/(.+?)\[(.+?)\]/){ |s|
|
108
|
+
ruby.gsub(/(.+?)\s*=\s*\[(.+?)\]/){ |s|
|
108
109
|
if $2.index(',')
|
109
|
-
"#{$1}[\n #{$2.split(',').map(&:strip).join(",\n ")}]"
|
110
|
+
"#{$1} = [\n #{$2.split(',').map(&:strip).join(",\n ")}]"
|
110
111
|
else
|
111
112
|
s
|
112
113
|
end
|
@@ -179,7 +180,7 @@ namespace :gem do
|
|
179
180
|
|
180
181
|
desc 'Install gem'
|
181
182
|
task :install => [:build] do
|
182
|
-
sh("#{Gem.ruby} -S gem install pkg/#{Gemgem.gem_tag}")
|
183
|
+
sh("#{Gem.ruby} -S gem install pkg/#{Gemgem.gem_tag}.gem")
|
183
184
|
end
|
184
185
|
|
185
186
|
desc 'Build gem'
|
@@ -0,0 +1,110 @@
|
|
1
|
+
|
2
|
+
require 'jellyfish/test'
|
3
|
+
|
4
|
+
# stolen from sinatra
|
5
|
+
describe 'Sinatra base_test.rb' do
|
6
|
+
behaves_like :jellyfish
|
7
|
+
|
8
|
+
should 'process requests with #call' do
|
9
|
+
app = Class.new{
|
10
|
+
include Jellyfish
|
11
|
+
get '/' do
|
12
|
+
'Hello World'
|
13
|
+
end
|
14
|
+
}.new
|
15
|
+
app.respond_to?(:call).should.eq true
|
16
|
+
status, _, body = get('/', app)
|
17
|
+
status.should.eq 200
|
18
|
+
body .should.eq ['Hello World']
|
19
|
+
end
|
20
|
+
|
21
|
+
should 'not maintain state between requests' do
|
22
|
+
app = Class.new{
|
23
|
+
include Jellyfish
|
24
|
+
get '/state' do
|
25
|
+
@foo ||= 'new'
|
26
|
+
body = "Foo: #{@foo}"
|
27
|
+
@foo = 'discard'
|
28
|
+
body
|
29
|
+
end
|
30
|
+
}.new
|
31
|
+
|
32
|
+
2.times do
|
33
|
+
status, _, body = get('/state', app)
|
34
|
+
status.should.eq 200
|
35
|
+
body .should.eq ['Foo: new']
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'Jellyfish as a Rack middleware' do
|
40
|
+
behaves_like :jellyfish
|
41
|
+
|
42
|
+
def app
|
43
|
+
@app ||= Class.new{
|
44
|
+
include Jellyfish
|
45
|
+
get '/' do
|
46
|
+
'Hello from middleware'
|
47
|
+
end
|
48
|
+
|
49
|
+
get '/low-level-forward' do
|
50
|
+
status, headers, body = jellyfish.app.call(env)
|
51
|
+
self.status status
|
52
|
+
self.headers headers
|
53
|
+
body
|
54
|
+
end
|
55
|
+
|
56
|
+
get '/explicit-forward' do
|
57
|
+
headers_merge 'X-Middleware' => 'true'
|
58
|
+
status, headers, _ = jellyfish.app.call(env)
|
59
|
+
self.status status
|
60
|
+
self.headers headers
|
61
|
+
'Hello after explicit forward'
|
62
|
+
end
|
63
|
+
}.new(inner_app)
|
64
|
+
end
|
65
|
+
|
66
|
+
def inner_app
|
67
|
+
@inner_app ||= lambda{ |env|
|
68
|
+
[210, {'X-Downstream' => 'true'}, ['Hello from downstream']]
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
should 'create a middleware that responds to #call with .new' do
|
73
|
+
app.respond_to?(:call).should.eq true
|
74
|
+
end
|
75
|
+
|
76
|
+
should 'expose the downstream app' do
|
77
|
+
app.app.object_id.should.eq inner_app.object_id
|
78
|
+
end
|
79
|
+
|
80
|
+
should 'intercept requests' do
|
81
|
+
status, _, body = get('/')
|
82
|
+
status.should.eq 200
|
83
|
+
body .should.eq ['Hello from middleware']
|
84
|
+
end
|
85
|
+
|
86
|
+
should 'forward requests downstream when no matching route found' do
|
87
|
+
status, headers, body = get('/missing')
|
88
|
+
status .should.eq 210
|
89
|
+
headers['X-Downstream'].should.eq 'true'
|
90
|
+
body .should.eq ['Hello from downstream']
|
91
|
+
end
|
92
|
+
|
93
|
+
should 'call the downstream app directly and return result' do
|
94
|
+
status, headers, body = get('/low-level-forward')
|
95
|
+
status .should.eq 210
|
96
|
+
headers['X-Downstream'].should.eq 'true'
|
97
|
+
body .should.eq ['Hello from downstream']
|
98
|
+
end
|
99
|
+
|
100
|
+
should 'forward the request and integrate the response' do
|
101
|
+
status, headers, body =
|
102
|
+
get('/explicit-forward', Rack::ContentLength.new(app))
|
103
|
+
|
104
|
+
status .should.eq 210
|
105
|
+
headers['X-Downstream'] .should.eq 'true'
|
106
|
+
headers['Content-Length'].should.eq '28'
|
107
|
+
body .should.eq ['Hello after explicit forward']
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
require 'jellyfish/test'
|
3
|
+
|
4
|
+
# stolen from sinatra
|
5
|
+
describe 'Sinatra streaming_test.rb' do
|
6
|
+
behaves_like :jellyfish
|
7
|
+
|
8
|
+
should 'return the concatinated body' do
|
9
|
+
app = Class.new{
|
10
|
+
include Jellyfish
|
11
|
+
get '/' do
|
12
|
+
Jellyfish::ChunkedBody.new{ |out|
|
13
|
+
out['Hello']
|
14
|
+
out[' ']
|
15
|
+
out['World!']
|
16
|
+
}
|
17
|
+
end
|
18
|
+
}.new
|
19
|
+
_, _, body = get('/', app)
|
20
|
+
body.to_a.join.should.eq 'Hello World!'
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'postpone body generation' do
|
24
|
+
stream = Jellyfish::ChunkedBody.new{ |out|
|
25
|
+
10.times{ |i| out[i] }
|
26
|
+
}
|
27
|
+
|
28
|
+
stream.each.with_index do |s, i|
|
29
|
+
s.should.eq i
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
should 'give access to route specific params' do
|
34
|
+
app = Class.new{
|
35
|
+
include Jellyfish
|
36
|
+
get(%r{/(?<name>\w+)}){ |m|
|
37
|
+
Jellyfish::ChunkedBody.new{ |o| o[m[:name]] }
|
38
|
+
}
|
39
|
+
}.new
|
40
|
+
_, _, body = get('/foo', app)
|
41
|
+
body.to_a.join.should.eq 'foo'
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
|
2
|
+
require 'jellyfish/test'
|
3
|
+
|
4
|
+
describe 'Sinatra mapped_error_test.rb' do
|
5
|
+
behaves_like :jellyfish
|
6
|
+
|
7
|
+
exp = Class.new(RuntimeError)
|
8
|
+
|
9
|
+
should 'invoke handlers registered with handle when raised' do
|
10
|
+
app = Class.new{
|
11
|
+
include Jellyfish
|
12
|
+
handle(exp){ 'Foo!' }
|
13
|
+
get '/' do
|
14
|
+
raise exp
|
15
|
+
end
|
16
|
+
}.new
|
17
|
+
|
18
|
+
status, _, body = get('/', app)
|
19
|
+
status.should.eq 200
|
20
|
+
body .should.eq ['Foo!']
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'pass the exception object to the error handler' do
|
24
|
+
app = Class.new{
|
25
|
+
include Jellyfish
|
26
|
+
handle(exp){ |e| e.should.kind_of?(exp) }
|
27
|
+
get('/'){ raise exp }
|
28
|
+
}.new
|
29
|
+
get('/', app)
|
30
|
+
end
|
31
|
+
|
32
|
+
should 'use the Exception handler if no matching handler found' do
|
33
|
+
app = Class.new{
|
34
|
+
include Jellyfish
|
35
|
+
handle(Exception){ 'Exception!' }
|
36
|
+
get('/'){ raise exp }
|
37
|
+
}.new
|
38
|
+
|
39
|
+
status, _, body = get('/', app)
|
40
|
+
status.should.eq 200
|
41
|
+
body .should.eq ['Exception!']
|
42
|
+
end
|
43
|
+
|
44
|
+
should 'favour subclass handler over superclass handler if available' do
|
45
|
+
app = Class.new{
|
46
|
+
include Jellyfish
|
47
|
+
handle(Exception) { 'Exception!' }
|
48
|
+
handle(RuntimeError){ 'RuntimeError!' }
|
49
|
+
get('/'){ raise exp }
|
50
|
+
}.new
|
51
|
+
|
52
|
+
status, _, body = get('/', app)
|
53
|
+
status.should.eq 200
|
54
|
+
body .should.eq ['RuntimeError!']
|
55
|
+
|
56
|
+
handlers = app.class.handlers
|
57
|
+
handlers.size.should.eq 3
|
58
|
+
handlers[exp].should.eq handlers[RuntimeError]
|
59
|
+
end
|
60
|
+
|
61
|
+
should 'pass the exception to the handler' do
|
62
|
+
app = Class.new{
|
63
|
+
include Jellyfish
|
64
|
+
handle(exp){ |e|
|
65
|
+
e.should.kind_of?(exp)
|
66
|
+
'looks good'
|
67
|
+
}
|
68
|
+
get('/'){ raise exp }
|
69
|
+
}.new
|
70
|
+
|
71
|
+
_, _, body = get('/', app)
|
72
|
+
body.should.eq ['looks good']
|
73
|
+
end
|
74
|
+
|
75
|
+
should 'raise errors from the app when handle_exceptions is false' do
|
76
|
+
app = Class.new{
|
77
|
+
include Jellyfish
|
78
|
+
handle_exceptions false
|
79
|
+
get('/'){ raise exp }
|
80
|
+
}.new
|
81
|
+
|
82
|
+
lambda{ get('/', app) }.should.raise(exp)
|
83
|
+
end
|
84
|
+
|
85
|
+
should 'call error handlers even when handle_exceptions is false' do
|
86
|
+
app = Class.new{
|
87
|
+
include Jellyfish
|
88
|
+
handle_exceptions false
|
89
|
+
handle(exp){ "she's there." }
|
90
|
+
get('/'){ raise exp }
|
91
|
+
}.new
|
92
|
+
|
93
|
+
_, _, body = get('/', app)
|
94
|
+
body.should.eq ["she's there."]
|
95
|
+
end
|
96
|
+
|
97
|
+
should 'never raises Jellyfish::NotFound beyond the application' do
|
98
|
+
app = Class.new{
|
99
|
+
include Jellyfish
|
100
|
+
get('/'){ raise Jellyfish::NotFound }
|
101
|
+
}.new
|
102
|
+
|
103
|
+
status, _, _ = get('/', app)
|
104
|
+
status.should.eq 404
|
105
|
+
end
|
106
|
+
|
107
|
+
should 'cascade for subclasses of Jellyfish::NotFound' do
|
108
|
+
e = Class.new(Jellyfish::NotFound)
|
109
|
+
app = Class.new{
|
110
|
+
include Jellyfish
|
111
|
+
get('/'){ raise e }
|
112
|
+
}.new
|
113
|
+
|
114
|
+
status, _, body = get('/', app)
|
115
|
+
status.should.eq 404
|
116
|
+
end
|
117
|
+
|
118
|
+
should 'inherit error mappings from base class' do
|
119
|
+
sup = Class.new{
|
120
|
+
include Jellyfish
|
121
|
+
handle(exp){ 'sup' }
|
122
|
+
}
|
123
|
+
app = Class.new(sup){
|
124
|
+
get('/'){ raise exp }
|
125
|
+
}.new
|
126
|
+
|
127
|
+
_, _, body = get('/', app)
|
128
|
+
body.should.eq ['sup']
|
129
|
+
end
|
130
|
+
|
131
|
+
should 'override error mappings in base class' do
|
132
|
+
sup = Class.new{
|
133
|
+
include Jellyfish
|
134
|
+
handle(exp){ 'sup' }
|
135
|
+
}
|
136
|
+
app = Class.new(sup){
|
137
|
+
handle(exp){ 'sub' }
|
138
|
+
get('/'){ raise exp }
|
139
|
+
}.new
|
140
|
+
|
141
|
+
|
142
|
+
_, _, body = get('/', app)
|
143
|
+
body.should.eq ['sub']
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
|
2
|
+
require 'jellyfish/test'
|
3
|
+
|
4
|
+
# stolen from sinatra
|
5
|
+
describe 'Sinatra filter_test.rb' do
|
6
|
+
behaves_like :jellyfish
|
7
|
+
|
8
|
+
def new_app base=Object, &block
|
9
|
+
Class.new(base){
|
10
|
+
include Jellyfish
|
11
|
+
controller_include(Jellyfish::MultiActions)
|
12
|
+
instance_eval(&block)
|
13
|
+
}.new
|
14
|
+
end
|
15
|
+
|
16
|
+
should 'executes filters in the order defined' do
|
17
|
+
count = 0
|
18
|
+
app = new_app{
|
19
|
+
get { count.should.eq 0; count = 1 }
|
20
|
+
get { count.should.eq 1; count = 2 }
|
21
|
+
get('/'){ 'Hello World' }
|
22
|
+
}
|
23
|
+
|
24
|
+
status, _, body = get('/', app)
|
25
|
+
status.should.eq 200
|
26
|
+
count .should.eq 2
|
27
|
+
body .should.eq ['Hello World']
|
28
|
+
end
|
29
|
+
|
30
|
+
should 'modify env' do
|
31
|
+
app = new_app{
|
32
|
+
get{ env['BOO'] = 'MOO' }
|
33
|
+
get('/foo'){ env['BOO'] }
|
34
|
+
}
|
35
|
+
|
36
|
+
status, _, body = get('/foo', app)
|
37
|
+
status.should.eq 200
|
38
|
+
body .should.eq ['MOO']
|
39
|
+
end
|
40
|
+
|
41
|
+
should 'modify instance variables available to routes' do
|
42
|
+
app = new_app{
|
43
|
+
get{ @foo = 'bar' }
|
44
|
+
get('/foo') { @foo }
|
45
|
+
}
|
46
|
+
|
47
|
+
status, _, body = get('/foo', app)
|
48
|
+
status.should.eq 200
|
49
|
+
body .should.eq ['bar']
|
50
|
+
end
|
51
|
+
|
52
|
+
should 'allows redirects' do
|
53
|
+
app = new_app{
|
54
|
+
get{ found '/bar' }
|
55
|
+
get('/foo') do
|
56
|
+
fail 'before block should have halted processing'
|
57
|
+
'ORLY?!'
|
58
|
+
end
|
59
|
+
}
|
60
|
+
|
61
|
+
status, headers, body = get('/foo', app)
|
62
|
+
status .should.eq 302
|
63
|
+
headers['Location'].should.eq '/bar'
|
64
|
+
body.join .should =~ %r{<h1>Jellyfish found: /bar</h1>}
|
65
|
+
end
|
66
|
+
|
67
|
+
should 'not modify the response with its return value' do
|
68
|
+
app = new_app{
|
69
|
+
get{ 'Hello World!' }
|
70
|
+
get '/foo' do
|
71
|
+
body.should.eq nil
|
72
|
+
'cool'
|
73
|
+
end
|
74
|
+
}
|
75
|
+
|
76
|
+
status, _, body = get('/foo', app)
|
77
|
+
status.should.eq 200
|
78
|
+
body .should.eq ['cool']
|
79
|
+
end
|
80
|
+
|
81
|
+
should 'modify the response with halt' do
|
82
|
+
app = new_app{
|
83
|
+
get('/foo'){ halt [302, {}, ['Hi']] }
|
84
|
+
get('/foo'){ 'should not happen' }
|
85
|
+
get('/bar'){ status 402; body 'Ho'; halt }
|
86
|
+
get('/bar'){ 'should not happen' }
|
87
|
+
}
|
88
|
+
|
89
|
+
get('/foo', app).should.eq [302, {}, ['Hi']]
|
90
|
+
get('/bar', app).should.eq [402, {}, ['Ho']]
|
91
|
+
end
|
92
|
+
|
93
|
+
should 'give you access to params' do
|
94
|
+
app = new_app{
|
95
|
+
get{ @foo = Rack::Request.new(env).params['foo'] }
|
96
|
+
get('/foo'){ @foo.reverse }
|
97
|
+
}
|
98
|
+
|
99
|
+
status, _, body = get('/foo', app, 'QUERY_STRING' => 'foo=cool')
|
100
|
+
status.should.eq 200
|
101
|
+
body .should.eq ['looc']
|
102
|
+
end
|
103
|
+
|
104
|
+
should 'run filters defined in superclasses' do
|
105
|
+
sup = new_app{ get{ @foo = 'hello from superclass' } }.class
|
106
|
+
app = new_app(sup){ get('/foo'){ @foo } }
|
107
|
+
|
108
|
+
_, _, body = get('/foo', app)
|
109
|
+
body.should.eq ['hello from superclass']
|
110
|
+
|
111
|
+
sup .routes['get'].size.should.eq 1
|
112
|
+
app.class.routes['get'].size.should.eq 2
|
113
|
+
end
|
114
|
+
|
115
|
+
should 'take an optional route pattern' do
|
116
|
+
ran_filter = false
|
117
|
+
app = new_app{
|
118
|
+
get(%r{^/b}){ ran_filter = true }
|
119
|
+
get('/foo') {}
|
120
|
+
get('/bar') {}
|
121
|
+
}
|
122
|
+
get('/foo', app)
|
123
|
+
ran_filter.should.eq false
|
124
|
+
get('/bar', app)
|
125
|
+
ran_filter.should.eq true
|
126
|
+
end
|
127
|
+
|
128
|
+
should 'generate block arguments from route pattern' do
|
129
|
+
subpath = nil
|
130
|
+
app = new_app{
|
131
|
+
get(%r{^/foo/(\w+)}){ |m| subpath = m[1] }
|
132
|
+
}
|
133
|
+
get('/foo/bar', app)
|
134
|
+
subpath.should.eq 'bar'
|
135
|
+
end
|
136
|
+
|
137
|
+
should 'execute before and after filters in correct order' do
|
138
|
+
invoked = 0
|
139
|
+
app = new_app{
|
140
|
+
get { invoked = 2 }
|
141
|
+
get('/'){ invoked += 2; body 'hello' }
|
142
|
+
get { invoked *= 2 }
|
143
|
+
}
|
144
|
+
|
145
|
+
status, _, body = get('/', app)
|
146
|
+
status .should.eq 200
|
147
|
+
body .should.eq ['hello']
|
148
|
+
invoked.should.eq 8
|
149
|
+
end
|
150
|
+
|
151
|
+
should 'execute filters in the order defined' do
|
152
|
+
count = 0
|
153
|
+
app = new_app{
|
154
|
+
get('/'){ body 'Hello World' }
|
155
|
+
get{
|
156
|
+
count.should.eq 0
|
157
|
+
count = 1
|
158
|
+
}
|
159
|
+
get{
|
160
|
+
count.should.eq 1
|
161
|
+
count = 2
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
status, _, body = get('/', app)
|
166
|
+
status.should.eq 200
|
167
|
+
count .should.eq 2
|
168
|
+
body .should.eq ['Hello World']
|
169
|
+
end
|
170
|
+
|
171
|
+
should 'allow redirects' do
|
172
|
+
app = new_app{
|
173
|
+
get('/foo'){ 'ORLY' }
|
174
|
+
get { found '/bar' }
|
175
|
+
}
|
176
|
+
|
177
|
+
status, headers, body = get('/foo', app)
|
178
|
+
status .should.eq 302
|
179
|
+
headers['Location'].should.eq '/bar'
|
180
|
+
body.join .should =~ %r{<h1>Jellyfish found: /bar</h1>}
|
181
|
+
end
|
182
|
+
|
183
|
+
should 'not modify the response with its return value' do
|
184
|
+
app = new_app{
|
185
|
+
get('/foo'){ body 'cool' }
|
186
|
+
get { 'Hello World!' }
|
187
|
+
}
|
188
|
+
|
189
|
+
status, _, body = get('/foo', app)
|
190
|
+
status.should.eq 200
|
191
|
+
body .should.eq ['cool']
|
192
|
+
end
|
193
|
+
|
194
|
+
should 'modify the response with halt' do
|
195
|
+
app = new_app{
|
196
|
+
get('/foo'){ 'should not be returned' }
|
197
|
+
get{ halt [302, {}, ['Hi']] }
|
198
|
+
}
|
199
|
+
|
200
|
+
status, _, body = get('/foo', app)
|
201
|
+
status.should.eq 302
|
202
|
+
body .should.eq ['Hi']
|
203
|
+
end
|
204
|
+
|
205
|
+
should 'take an optional route pattern' do
|
206
|
+
ran_filter = false
|
207
|
+
app = new_app{
|
208
|
+
get('/foo') {}
|
209
|
+
get('/bar') {}
|
210
|
+
get(%r{^/b}){ ran_filter = true }
|
211
|
+
}
|
212
|
+
get('/foo', app)
|
213
|
+
ran_filter.should.eq false
|
214
|
+
get('/bar', app)
|
215
|
+
ran_filter.should.eq true
|
216
|
+
end
|
217
|
+
end
|