strelka 0.0.1pre4 → 0.0.1.pre129
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +1 -1
- data/IDEAS.rdoc +62 -0
- data/Manifest.txt +38 -7
- data/README.rdoc +124 -5
- data/Rakefile +22 -6
- data/bin/leash +102 -157
- data/contrib/hoetemplate/.autotest.erb +23 -0
- data/contrib/hoetemplate/History.rdoc.erb +4 -0
- data/contrib/hoetemplate/Manifest.txt.erb +8 -0
- data/contrib/hoetemplate/README.rdoc.erb +17 -0
- data/contrib/hoetemplate/Rakefile.erb +24 -0
- data/contrib/hoetemplate/data/file_name/apps/file_name_app +36 -0
- data/contrib/hoetemplate/data/file_name/templates/layout.tmpl.erb +13 -0
- data/contrib/hoetemplate/data/file_name/templates/top.tmpl.erb +8 -0
- data/contrib/hoetemplate/lib/file_name.rb.erb +18 -0
- data/contrib/hoetemplate/spec/file_name_spec.rb.erb +21 -0
- data/data/strelka/apps/hello-world +30 -0
- data/lib/strelka/app/defaultrouter.rb +49 -30
- data/lib/strelka/app/errors.rb +121 -0
- data/lib/strelka/app/exclusiverouter.rb +40 -0
- data/lib/strelka/app/filters.rb +18 -7
- data/lib/strelka/app/negotiation.rb +122 -0
- data/lib/strelka/app/parameters.rb +171 -14
- data/lib/strelka/app/paramvalidator.rb +751 -0
- data/lib/strelka/app/plugins.rb +66 -46
- data/lib/strelka/app/restresources.rb +499 -0
- data/lib/strelka/app/router.rb +73 -0
- data/lib/strelka/app/routing.rb +140 -18
- data/lib/strelka/app/templating.rb +12 -3
- data/lib/strelka/app.rb +174 -24
- data/lib/strelka/constants.rb +0 -20
- data/lib/strelka/exceptions.rb +29 -0
- data/lib/strelka/httprequest/acceptparams.rb +377 -0
- data/lib/strelka/httprequest/negotiation.rb +257 -0
- data/lib/strelka/httprequest.rb +155 -7
- data/lib/strelka/httpresponse/negotiation.rb +579 -0
- data/lib/strelka/httpresponse.rb +140 -0
- data/lib/strelka/logging.rb +4 -1
- data/lib/strelka/mixins.rb +53 -0
- data/lib/strelka.rb +22 -1
- data/spec/data/error.tmpl +1 -0
- data/spec/lib/constants.rb +0 -1
- data/spec/lib/helpers.rb +21 -0
- data/spec/strelka/app/defaultrouter_spec.rb +41 -35
- data/spec/strelka/app/errors_spec.rb +212 -0
- data/spec/strelka/app/exclusiverouter_spec.rb +220 -0
- data/spec/strelka/app/filters_spec.rb +196 -0
- data/spec/strelka/app/negotiation_spec.rb +73 -0
- data/spec/strelka/app/parameters_spec.rb +149 -0
- data/spec/strelka/app/paramvalidator_spec.rb +1059 -0
- data/spec/strelka/app/plugins_spec.rb +26 -19
- data/spec/strelka/app/restresources_spec.rb +393 -0
- data/spec/strelka/app/router_spec.rb +63 -0
- data/spec/strelka/app/routing_spec.rb +183 -9
- data/spec/strelka/app/templating_spec.rb +1 -2
- data/spec/strelka/app_spec.rb +265 -32
- data/spec/strelka/exceptions_spec.rb +53 -0
- data/spec/strelka/httprequest/acceptparams_spec.rb +282 -0
- data/spec/strelka/httprequest/negotiation_spec.rb +246 -0
- data/spec/strelka/httprequest_spec.rb +204 -14
- data/spec/strelka/httpresponse/negotiation_spec.rb +464 -0
- data/spec/strelka/httpresponse_spec.rb +114 -0
- data/spec/strelka/mixins_spec.rb +99 -0
- data.tar.gz.sig +1 -0
- metadata +175 -79
- metadata.gz.sig +2 -0
- data/IDEAS.textile +0 -174
- data/data/strelka/apps/strelka-admin +0 -65
- data/data/strelka/apps/strelka-setup +0 -26
- data/data/strelka/bootstrap-config.rb +0 -34
- data/data/strelka/templates/admin/console.tmpl +0 -21
- data/data/strelka/templates/layout.tmpl +0 -30
- data/lib/strelka/process.rb +0 -19
@@ -25,6 +25,7 @@ describe Strelka::App::Routing do
|
|
25
25
|
|
26
26
|
before( :all ) do
|
27
27
|
setup_logging( :fatal )
|
28
|
+
@request_factory = Mongrel2::RequestFactory.new( route: '' )
|
28
29
|
end
|
29
30
|
|
30
31
|
after( :all ) do
|
@@ -41,6 +42,9 @@ describe Strelka::App::Routing do
|
|
41
42
|
Strelka.log.debug "Creating a new Strelka::App"
|
42
43
|
@app = Class.new( Strelka::App ) do
|
43
44
|
plugin :routing
|
45
|
+
def initialize( appid='params-test', sspec=TEST_SEND_SPEC, rspec=TEST_RECV_SPEC )
|
46
|
+
super
|
47
|
+
end
|
44
48
|
end
|
45
49
|
Strelka.log.debug " new instance is: %p, routes array: 0x%016x" %
|
46
50
|
[ @app, @app.routes.object_id * 2 ]
|
@@ -51,6 +55,32 @@ describe Strelka::App::Routing do
|
|
51
55
|
@app.routes.should be_a( Array )
|
52
56
|
end
|
53
57
|
|
58
|
+
it "knows what its route methods are" do
|
59
|
+
@app.route_methods.should == []
|
60
|
+
@app.class_eval do
|
61
|
+
get() {}
|
62
|
+
post( '/clowns' ) {}
|
63
|
+
options( '/clowns' ) {}
|
64
|
+
end
|
65
|
+
|
66
|
+
@app.route_methods.should == [ :GET, :POST_clowns, :OPTIONS_clowns ]
|
67
|
+
end
|
68
|
+
|
69
|
+
# OPTIONS GET/HEAD POST PUT DELETE TRACE CONNECT
|
70
|
+
|
71
|
+
it "can declare a OPTIONS route" do
|
72
|
+
@app.routes.should be_empty()
|
73
|
+
|
74
|
+
@app.class_eval do
|
75
|
+
options do |req|
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
@app.routes.should == [
|
80
|
+
[ :OPTIONS, [], {action: @app.instance_method(:OPTIONS), options: {}} ]
|
81
|
+
]
|
82
|
+
end
|
83
|
+
|
54
84
|
it "can declare a GET route" do
|
55
85
|
@app.routes.should be_empty()
|
56
86
|
|
@@ -59,9 +89,76 @@ describe Strelka::App::Routing do
|
|
59
89
|
end
|
60
90
|
end
|
61
91
|
|
62
|
-
@app.routes.should == [
|
92
|
+
@app.routes.should == [
|
93
|
+
[ :GET, [], {action: @app.instance_method(:GET), options: {}} ]
|
94
|
+
]
|
95
|
+
end
|
96
|
+
|
97
|
+
it "can declare a POST route" do
|
98
|
+
@app.routes.should be_empty()
|
99
|
+
|
100
|
+
@app.class_eval do
|
101
|
+
post do |req|
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
@app.routes.should == [
|
106
|
+
[ :POST, [], {action: @app.instance_method(:POST), options: {}} ]
|
107
|
+
]
|
108
|
+
end
|
109
|
+
|
110
|
+
it "can declare a PUT route" do
|
111
|
+
@app.routes.should be_empty()
|
112
|
+
|
113
|
+
@app.class_eval do
|
114
|
+
put do |req|
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
@app.routes.should == [[ :PUT, [], {action: @app.instance_method(:PUT), options: {}} ]]
|
119
|
+
end
|
120
|
+
|
121
|
+
it "can declare a DELETE route" do
|
122
|
+
@app.routes.should be_empty()
|
123
|
+
|
124
|
+
@app.class_eval do
|
125
|
+
delete do |req|
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
@app.routes.should == [
|
130
|
+
[ :DELETE, [], {action: @app.instance_method(:DELETE), options: {}} ]
|
131
|
+
]
|
132
|
+
end
|
133
|
+
|
134
|
+
it "can declare a TRACE route" do
|
135
|
+
@app.routes.should be_empty()
|
136
|
+
|
137
|
+
@app.class_eval do
|
138
|
+
trace do |req|
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
@app.routes.should == [
|
143
|
+
[ :TRACE, [], {action: @app.instance_method(:TRACE), options: {}} ]
|
144
|
+
]
|
145
|
+
end
|
146
|
+
|
147
|
+
it "can declare a CONNECT route" do
|
148
|
+
@app.routes.should be_empty()
|
149
|
+
|
150
|
+
@app.class_eval do
|
151
|
+
connect do |req|
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
@app.routes.should == [
|
156
|
+
[ :CONNECT, [], {action: @app.instance_method(:CONNECT), options: {}} ]
|
157
|
+
]
|
63
158
|
end
|
64
159
|
|
160
|
+
|
161
|
+
|
65
162
|
it "allows a route to specify a path" do
|
66
163
|
@app.routes.should be_empty()
|
67
164
|
|
@@ -70,7 +167,9 @@ describe Strelka::App::Routing do
|
|
70
167
|
end
|
71
168
|
end
|
72
169
|
|
73
|
-
@app.routes.should == [
|
170
|
+
@app.routes.should == [
|
171
|
+
[ :GET, ['info'], {action: @app.instance_method(:GET_info), options: {}} ]
|
172
|
+
]
|
74
173
|
end
|
75
174
|
|
76
175
|
it "allows a route to omit the leading '/' when specifying a path" do
|
@@ -79,23 +178,48 @@ describe Strelka::App::Routing do
|
|
79
178
|
end
|
80
179
|
end
|
81
180
|
|
82
|
-
@app.routes.should == [
|
181
|
+
@app.routes.should == [
|
182
|
+
[ :GET, ['info'], {action: @app.instance_method(:GET_info), options: {}} ]
|
183
|
+
]
|
83
184
|
end
|
84
185
|
|
85
186
|
|
86
187
|
it "uses the Strelka::App::DefaultRouter as it's router by default" do
|
87
|
-
@app.routerclass.should
|
188
|
+
@app.routerclass.should == :default
|
189
|
+
@app.new.router.should be_a( Strelka::App::DefaultRouter )
|
88
190
|
end
|
89
191
|
|
90
192
|
it "can specify a different Router class than the default" do
|
91
|
-
class MyRouter < Strelka::App::
|
193
|
+
class MyRouter < Strelka::App::Router; end
|
92
194
|
@app.class_eval do
|
93
195
|
router MyRouter
|
94
196
|
end
|
95
197
|
@app.routerclass.should equal( MyRouter )
|
198
|
+
@app.new.router.should be_a( MyRouter )
|
96
199
|
end
|
97
200
|
|
98
201
|
|
202
|
+
it "has its routes inherited by subclasses" do
|
203
|
+
@app.class_eval do
|
204
|
+
get( '/info' ) {}
|
205
|
+
get( '/about' ) {}
|
206
|
+
get( '/origami' ) {}
|
207
|
+
end
|
208
|
+
subclass = Class.new( @app ) do
|
209
|
+
get( '/origami' ) {}
|
210
|
+
end
|
211
|
+
|
212
|
+
subclass.routes.should have( 3 ).members
|
213
|
+
|
214
|
+
subclass.routes.
|
215
|
+
should include([ :GET, ['info'], {action: @app.instance_method(:GET_info), options: {}} ])
|
216
|
+
subclass.routes.
|
217
|
+
should include([ :GET, ['about'], {action: @app.instance_method(:GET_about), options: {}} ])
|
218
|
+
subclass.routes.should include(
|
219
|
+
[ :GET, ['origami'], {action: subclass.instance_method(:GET_origami), options: {}} ]
|
220
|
+
)
|
221
|
+
end
|
222
|
+
|
99
223
|
describe "that also uses the :parameters plugin" do
|
100
224
|
|
101
225
|
before( :each ) do
|
@@ -109,15 +233,65 @@ describe Strelka::App::Routing do
|
|
109
233
|
end
|
110
234
|
end
|
111
235
|
|
112
|
-
@app.routes.should ==
|
113
|
-
[[ :POST, ['userinfo', /(?<username>(?i-mx:[a-z]\w+))/],
|
114
|
-
@app.instance_method(:POST_userinfo__username), {} ]]
|
236
|
+
@app.routes.should ==
|
237
|
+
[[ :POST, ['userinfo', /(?<username>(?i-mx:[a-z]\w+))/],
|
238
|
+
{action: @app.instance_method(:POST_userinfo__username), options: {}} ]]
|
239
|
+
end
|
240
|
+
|
241
|
+
it "unbinds parameter patterns bound with ^ and $ for the route" do
|
242
|
+
@app.class_eval do
|
243
|
+
param :username, /^[a-z]\w+$/i
|
244
|
+
post '/userinfo/:username' do |req|
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
@app.routes.should ==
|
249
|
+
[[ :POST, ['userinfo', /(?<username>(?i-mx:[a-z]\w+))/],
|
250
|
+
{action: @app.instance_method(:POST_userinfo__username), options: {}} ]]
|
251
|
+
end
|
252
|
+
|
253
|
+
it "unbinds parameter patterns bound with \\A and \\z for the route" do
|
254
|
+
@app.class_eval do
|
255
|
+
param :username, /\A[a-z]\w+\z/i
|
256
|
+
post '/userinfo/:username' do |req|
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
@app.routes.should ==
|
261
|
+
[[ :POST, ['userinfo', /(?<username>(?i-mx:[a-z]\w+))/],
|
262
|
+
{action: @app.instance_method(:POST_userinfo__username), options: {}} ]]
|
263
|
+
end
|
264
|
+
|
265
|
+
it "unbinds parameter patterns bound with \\Z for the route" do
|
266
|
+
@app.class_eval do
|
267
|
+
param :username, /\A[a-z]\w+\Z/i
|
268
|
+
post '/userinfo/:username' do |req|
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
@app.routes.should ==
|
273
|
+
[[ :POST, ['userinfo', /(?<username>(?i-mx:[a-z]\w+))/],
|
274
|
+
{action: @app.instance_method(:POST_userinfo__username), options: {}} ]]
|
275
|
+
end
|
276
|
+
|
277
|
+
it "merges parameters from the route path into the request's param validator" do
|
278
|
+
@app.class_eval do
|
279
|
+
param :username, /\A[a-z]\w+\Z/i
|
280
|
+
get '/userinfo/:username' do |req|
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
req = @request_factory.get( '/userinfo/benthik' )
|
285
|
+
@app.new.handle( req )
|
286
|
+
|
287
|
+
req.params[:username].should == 'benthik'
|
115
288
|
end
|
116
289
|
|
117
290
|
|
118
291
|
it "raises a ScriptError if a route is defined with a param without it having first " +
|
119
292
|
"been set up" do
|
120
|
-
# RSpec's expect {}
|
293
|
+
# RSpec's "expect {}.to" construct only rescues RuntimeErrors, so we have to do
|
294
|
+
# this ourselves.
|
121
295
|
begin
|
122
296
|
@app.get( '/userinfo/:username' ) {}
|
123
297
|
rescue ScriptError => err
|
@@ -87,12 +87,11 @@ describe Strelka::App::Templating do
|
|
87
87
|
@app.class_eval do
|
88
88
|
templates :main => 'main.tmpl'
|
89
89
|
end
|
90
|
-
@instance = @app.new( 'template-test', 'tcp://127.0.0.1:9999', 'tcp://127.0.0.1:9998' )
|
91
90
|
end
|
92
91
|
|
93
92
|
|
94
93
|
it "can load declared templates by mentioning the symbol" do
|
95
|
-
@
|
94
|
+
@app.new.template( :main ).should be_a( Inversion::Template )
|
96
95
|
end
|
97
96
|
|
98
97
|
it "can respond with just a template name" do
|
data/spec/strelka/app_spec.rb
CHANGED
@@ -25,6 +25,14 @@ describe Strelka::App do
|
|
25
25
|
before( :all ) do
|
26
26
|
setup_logging( :fatal )
|
27
27
|
@request_factory = Mongrel2::RequestFactory.new( route: '/mail' )
|
28
|
+
Mongrel2::Config.db = Mongrel2::Config.in_memory_db
|
29
|
+
Mongrel2::Config.init_database
|
30
|
+
|
31
|
+
# Skip loading the 'strelka' gem, which probably doesn't exist in the right version
|
32
|
+
strelkaspec = make_gemspec( 'strelka', '0.0.1', false )
|
33
|
+
loaded_specs = Gem.instance_variable_get( :@loaded_specs )
|
34
|
+
loaded_specs['strelka'] = strelkaspec
|
35
|
+
|
28
36
|
end
|
29
37
|
|
30
38
|
before( :each ) do
|
@@ -32,17 +40,119 @@ describe Strelka::App do
|
|
32
40
|
def initialize( appid=TEST_APPID, sspec=TEST_SEND_SPEC, rspec=TEST_RECV_SPEC )
|
33
41
|
super
|
34
42
|
end
|
43
|
+
def set_signal_handlers; end
|
44
|
+
def start_accepting_requests; end
|
45
|
+
def restore_signal_handlers; end
|
35
46
|
end
|
36
47
|
@req = @request_factory.get( '/mail/inbox' )
|
37
48
|
end
|
38
49
|
|
50
|
+
after( :each ) do
|
51
|
+
@app = nil
|
52
|
+
end
|
53
|
+
|
39
54
|
after( :all ) do
|
40
55
|
reset_logging()
|
41
56
|
end
|
42
57
|
|
43
58
|
|
44
|
-
|
59
|
+
#
|
60
|
+
# Helpers
|
61
|
+
#
|
62
|
+
|
63
|
+
def make_gemspec( name, version, strelka_dep=true )
|
64
|
+
spec = Gem::Specification.new( name, version )
|
65
|
+
spec.add_runtime_dependency( 'strelka', '~> 0.0' ) if strelka_dep
|
66
|
+
return spec
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
#
|
71
|
+
# Examples
|
72
|
+
#
|
73
|
+
|
74
|
+
it "has a method for loading app class/es from a file" do
|
75
|
+
app_file = 'an_app.rb'
|
76
|
+
app_path = Pathname( 'an_app.rb' ).expand_path
|
77
|
+
app_class = nil
|
78
|
+
|
79
|
+
Kernel.should_receive( :load ).with( app_path.to_s ).and_return do
|
80
|
+
app_class = Class.new( Strelka::App )
|
81
|
+
end
|
82
|
+
Strelka::App.load( app_file ).should == [ app_class ]
|
83
|
+
end
|
84
|
+
|
85
|
+
it "has a method for discovering installed Strelka app files" do
|
86
|
+
specs = {}
|
87
|
+
specs[:donkey] = make_gemspec( 'donkey', '1.0.0' )
|
88
|
+
specs[:rabbit_old] = make_gemspec( 'rabbit', '1.0.0' )
|
89
|
+
specs[:rabbit_new] = make_gemspec( 'rabbit', '1.0.8' )
|
90
|
+
specs[:bear] = make_gemspec( 'bear', '1.0.0', false )
|
91
|
+
specs[:giraffe] = make_gemspec( 'giraffe', '1.0.0' )
|
92
|
+
|
93
|
+
expectation = Gem::Specification.should_receive( :each )
|
94
|
+
specs.values.each {|spec| expectation.and_yield(spec) }
|
95
|
+
|
96
|
+
donkey_path = specs[:donkey].full_gem_path
|
97
|
+
rabbit_path = specs[:rabbit_new].full_gem_path
|
98
|
+
giraffe_path = specs[:giraffe].full_gem_path
|
99
|
+
|
100
|
+
Dir.should_receive( :glob ).with( "#{giraffe_path}/data/giraffe/{apps,handlers}/**/*" ).
|
101
|
+
and_return([ "#{giraffe_path}/data/giraffe/apps/app" ])
|
102
|
+
Dir.should_receive( :glob ).with( "#{rabbit_path}/data/rabbit/{apps,handlers}/**/*" ).
|
103
|
+
and_return([ "#{rabbit_path}/data/rabbit/apps/subdir/app1.rb",
|
104
|
+
"#{rabbit_path}/data/rabbit/apps/subdir/app2.rb" ])
|
105
|
+
Dir.should_receive( :glob ).with( "#{donkey_path}/data/donkey/{apps,handlers}/**/*" ).
|
106
|
+
and_return([ "#{donkey_path}/data/donkey/apps/app.rb" ])
|
107
|
+
|
108
|
+
app_paths = Strelka::App.discover_paths
|
109
|
+
|
110
|
+
# app_paths.should have( 4 ).members
|
111
|
+
app_paths.should include(
|
112
|
+
'donkey' => [Pathname("#{donkey_path}/data/donkey/apps/app.rb")],
|
113
|
+
'rabbit' => [Pathname("#{rabbit_path}/data/rabbit/apps/subdir/app1.rb"),
|
114
|
+
Pathname("#{rabbit_path}/data/rabbit/apps/subdir/app2.rb")],
|
115
|
+
'giraffe' => [Pathname("#{giraffe_path}/data/giraffe/apps/app")]
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "has a method for loading discovered app classes from installed Strelka app files" do
|
120
|
+
gemspec = make_gemspec( 'blood-orgy', '0.0.3' )
|
121
|
+
Gem::Specification.should_receive( :each ).and_yield( gemspec ).at_least( :once )
|
122
|
+
|
123
|
+
Dir.should_receive( :glob ).with( "#{gemspec.full_gem_path}/data/blood-orgy/{apps,handlers}/**/*" ).
|
124
|
+
and_return([ "#{gemspec.full_gem_path}/data/blood-orgy/apps/kurzweil" ])
|
125
|
+
|
126
|
+
Kernel.stub( :load ).
|
127
|
+
with( "#{gemspec.full_gem_path}/data/blood-orgy/apps/kurzweil" ).
|
128
|
+
and_return do
|
129
|
+
Class.new( Strelka::App )
|
130
|
+
true
|
131
|
+
end
|
132
|
+
|
133
|
+
app_classes = Strelka::App.discover
|
134
|
+
app_classes.should have( 1 ).member
|
135
|
+
app_classes.first.should be_a( Class )
|
136
|
+
app_classes.first.should < Strelka::App
|
137
|
+
end
|
138
|
+
|
139
|
+
it "handles exceptions while loading discovered apps" do
|
140
|
+
gemspec = make_gemspec( 'blood-orgy', '0.0.3' )
|
141
|
+
Gem::Specification.should_receive( :each ).and_yield( gemspec ).at_least( :once )
|
142
|
+
|
143
|
+
Dir.should_receive( :glob ).with( "#{gemspec.full_gem_path}/data/blood-orgy/{apps,handlers}/**/*" ).
|
144
|
+
and_return([ "#{gemspec.full_gem_path}/data/blood-orgy/apps/kurzweil" ])
|
145
|
+
|
146
|
+
Kernel.stub( :load ).
|
147
|
+
with( "#{gemspec.full_gem_path}/data/blood-orgy/apps/kurzweil" ).
|
148
|
+
and_raise( SyntaxError.new("kurzweil:1: syntax error, unexpected coffeeshop philosopher") )
|
149
|
+
|
150
|
+
app_classes = Strelka::App.discover
|
151
|
+
app_classes.should be_empty()
|
152
|
+
end
|
45
153
|
|
154
|
+
|
155
|
+
it "returns a No Content response by default" do
|
46
156
|
res = @app.new.handle( @req )
|
47
157
|
|
48
158
|
res.should be_a( Mongrel2::HTTPResponse )
|
@@ -91,9 +201,30 @@ describe Strelka::App do
|
|
91
201
|
end
|
92
202
|
|
93
203
|
|
204
|
+
it "uses the specified content type for error responses" do
|
205
|
+
# make an auth plugin that always denies requests
|
206
|
+
forbidden_plugin = Module.new do
|
207
|
+
extend Strelka::App::Plugin
|
208
|
+
def handle_request( r )
|
209
|
+
finish_with( HTTP::FORBIDDEN, "You aren't allowed to look at that.",
|
210
|
+
:content_type => 'text/html' )
|
211
|
+
fail "Shouldn't be reached."
|
212
|
+
end
|
213
|
+
end
|
214
|
+
@app.plugin( forbidden_plugin )
|
215
|
+
|
216
|
+
res = @app.new.handle( @req )
|
217
|
+
|
218
|
+
res.should be_a( Mongrel2::HTTPResponse )
|
219
|
+
res.status_line.should == 'HTTP/1.1 403 Forbidden'
|
220
|
+
res.content_type.should == 'text/html'
|
221
|
+
res.body.should == "You aren't allowed to look at that.\n"
|
222
|
+
end
|
223
|
+
|
224
|
+
|
94
225
|
it "provides a declarative for setting the default content type of responses" do
|
95
226
|
@app.class_eval do
|
96
|
-
|
227
|
+
default_type 'text/css'
|
97
228
|
def handle_request( r )
|
98
229
|
r.response.puts( "body { font-family: monospace }" )
|
99
230
|
r.response
|
@@ -106,54 +237,156 @@ describe Strelka::App do
|
|
106
237
|
res.content_type.should == 'text/css'
|
107
238
|
end
|
108
239
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
header_fixup_plugin = Module.new do
|
113
|
-
extend Strelka::App::Plugin
|
114
|
-
def fixup_request( r )
|
115
|
-
r.headers[:x_funted_by] = 'Cragnux/1.1.3'
|
116
|
-
super
|
117
|
-
end
|
240
|
+
it "doesn't override an explicitly-set content-type header with the default" do
|
241
|
+
@app.class_eval do
|
242
|
+
default_type 'text/css'
|
118
243
|
def handle_request( r )
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
return res
|
244
|
+
r.response.puts( "I lied, I'm actually returning text." )
|
245
|
+
r.response.content_type = 'text/plain'
|
246
|
+
r.response
|
123
247
|
end
|
124
248
|
end
|
125
|
-
@app.plugin( header_fixup_plugin )
|
126
249
|
|
127
250
|
res = @app.new.handle( @req )
|
128
251
|
|
129
252
|
res.should be_a( Mongrel2::HTTPResponse )
|
130
|
-
res.
|
131
|
-
res.body.should == "Request was funted by Cragnux/1.1.3!\n"
|
253
|
+
res.content_type.should == 'text/plain'
|
132
254
|
end
|
133
255
|
|
134
256
|
|
135
|
-
it "
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
super
|
257
|
+
it "automatically truncates HEAD responses" do
|
258
|
+
@app.class_eval do
|
259
|
+
default_type 'text/plain'
|
260
|
+
def handle_request( r )
|
261
|
+
r.response.puts( "Rendered output." )
|
262
|
+
r.response
|
142
263
|
end
|
264
|
+
end
|
265
|
+
|
266
|
+
req = @request_factory.head( '/mail/inbox' )
|
267
|
+
res = @app.new.handle( req )
|
268
|
+
|
269
|
+
res.should be_a( Mongrel2::HTTPResponse )
|
270
|
+
res.content_type.should == 'text/plain'
|
271
|
+
res.body.should be_empty()
|
272
|
+
res.headers.content_length.should == "Rendered output.\n".bytesize
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
it "uses the app's ID constant for the appid if .run is called without one" do
|
277
|
+
@app.const_set( :ID, 'testing-app' )
|
278
|
+
|
279
|
+
Mongrel2::Handler.should_receive( :connection_info_for ).with( 'testing-app' ).
|
280
|
+
and_return([ TEST_SEND_SPEC, TEST_RECV_SPEC ])
|
281
|
+
Mongrel2::Connection.should_receive( :new ).
|
282
|
+
with( 'testing-app', TEST_SEND_SPEC, TEST_RECV_SPEC ).
|
283
|
+
and_return( :a_connection )
|
284
|
+
|
285
|
+
@app.run
|
286
|
+
end
|
287
|
+
|
288
|
+
|
289
|
+
it "uses the app's name for the appid if .run is called without one and it has no ID constant" do
|
290
|
+
@app.class_eval do
|
291
|
+
def self::name; "My::First::Blog" ; end
|
292
|
+
end
|
293
|
+
|
294
|
+
Mongrel2::Handler.should_receive( :connection_info_for ).with( 'my-first-blog' ).
|
295
|
+
and_return([ TEST_SEND_SPEC, TEST_RECV_SPEC ])
|
296
|
+
Mongrel2::Connection.should_receive( :new ).
|
297
|
+
with( 'my-first-blog', TEST_SEND_SPEC, TEST_RECV_SPEC ).
|
298
|
+
and_return( :a_connection )
|
299
|
+
|
300
|
+
@app.run
|
301
|
+
end
|
302
|
+
|
303
|
+
|
304
|
+
it "handles uncaught exceptions with a SERVER_ERROR response" do
|
305
|
+
@app.class_eval do
|
143
306
|
def handle_request( r )
|
144
|
-
|
145
|
-
res.puts( "Funt this" )
|
146
|
-
res.status = HTTP::OK
|
147
|
-
return res
|
307
|
+
raise "Something went wrong."
|
148
308
|
end
|
149
309
|
end
|
150
|
-
@app.plugin( header_fixup_plugin )
|
151
310
|
|
152
311
|
res = @app.new.handle( @req )
|
153
312
|
|
154
313
|
res.should be_a( Mongrel2::HTTPResponse )
|
155
|
-
res.
|
156
|
-
res.
|
314
|
+
res.status.should == HTTP::SERVER_ERROR
|
315
|
+
res.content_type = 'text/plain'
|
316
|
+
res.body.should =~ /internal server error/i
|
317
|
+
end
|
318
|
+
|
319
|
+
|
320
|
+
describe "process name" do
|
321
|
+
|
322
|
+
before( :all ) do
|
323
|
+
$old_0 = $0
|
324
|
+
end
|
325
|
+
|
326
|
+
after( :all ) do
|
327
|
+
$0 = $old_0
|
328
|
+
end
|
329
|
+
|
330
|
+
it "sets the process name to something more interesting than the command line" do
|
331
|
+
@app.new.run
|
332
|
+
|
333
|
+
$0.should =~ /#{@app.inspect}/
|
334
|
+
$0.should =~ %r|\{\S+\} tcp://\S+ <-> \S+|
|
335
|
+
end
|
336
|
+
|
337
|
+
end
|
338
|
+
|
339
|
+
describe "plugin hooks" do
|
340
|
+
|
341
|
+
it "provides a plugin hook for plugins to manipulate the request before handling it" do
|
342
|
+
# make a fixup plugin that adds a custom x- header to the request
|
343
|
+
header_fixup_plugin = Module.new do
|
344
|
+
extend Strelka::App::Plugin
|
345
|
+
def fixup_request( r )
|
346
|
+
r.headers[:x_funted_by] = 'Cragnux/1.1.3'
|
347
|
+
super
|
348
|
+
end
|
349
|
+
def handle_request( r )
|
350
|
+
res = r.response
|
351
|
+
res.puts( "Request was funted by %s!" % [r.headers.x_funted_by] )
|
352
|
+
res.status = HTTP::OK
|
353
|
+
return res
|
354
|
+
end
|
355
|
+
end
|
356
|
+
@app.plugin( header_fixup_plugin )
|
357
|
+
|
358
|
+
res = @app.new.handle( @req )
|
359
|
+
|
360
|
+
res.should be_a( Mongrel2::HTTPResponse )
|
361
|
+
res.status_line.should == 'HTTP/1.1 200 OK'
|
362
|
+
res.body.should == "Request was funted by Cragnux/1.1.3!\n"
|
363
|
+
end
|
364
|
+
|
365
|
+
|
366
|
+
it "provides a plugin hook for plugins to manipulate the response before it's returned to Mongrel2" do
|
367
|
+
# make a fixup plugin that adds a custom x- header to the response
|
368
|
+
header_fixup_plugin = Module.new do
|
369
|
+
extend Strelka::App::Plugin
|
370
|
+
def fixup_response( res )
|
371
|
+
res.headers.x_funted_by = 'Cragnux/1.1.3'
|
372
|
+
super
|
373
|
+
end
|
374
|
+
def handle_request( r )
|
375
|
+
res = r.response
|
376
|
+
res.puts( "Funt this" )
|
377
|
+
res.status = HTTP::OK
|
378
|
+
return res
|
379
|
+
end
|
380
|
+
end
|
381
|
+
@app.plugin( header_fixup_plugin )
|
382
|
+
|
383
|
+
res = @app.new.handle( @req )
|
384
|
+
|
385
|
+
res.should be_a( Mongrel2::HTTPResponse )
|
386
|
+
res.status_line.should == 'HTTP/1.1 200 OK'
|
387
|
+
res.header_data.should =~ %r{X-Funted-By: Cragnux/1.1.3}
|
388
|
+
end
|
389
|
+
|
157
390
|
end
|
158
391
|
|
159
392
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
BEGIN {
|
4
|
+
require 'pathname'
|
5
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent
|
6
|
+
$LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
|
7
|
+
}
|
8
|
+
|
9
|
+
require 'rspec'
|
10
|
+
require 'zmq'
|
11
|
+
require 'mongrel2'
|
12
|
+
|
13
|
+
require 'spec/lib/helpers'
|
14
|
+
|
15
|
+
require 'strelka'
|
16
|
+
require 'strelka/exceptions'
|
17
|
+
|
18
|
+
|
19
|
+
#####################################################################
|
20
|
+
### C O N T E X T S
|
21
|
+
#####################################################################
|
22
|
+
|
23
|
+
describe Strelka, "exception classes" do
|
24
|
+
|
25
|
+
before( :all ) do
|
26
|
+
setup_logging( :fatal )
|
27
|
+
@request_factory = Mongrel2::RequestFactory.new( route: '/exceptions' )
|
28
|
+
end
|
29
|
+
|
30
|
+
after( :all ) do
|
31
|
+
reset_logging()
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
describe Strelka::RequestError do
|
36
|
+
|
37
|
+
it "keeps track of the request that had the error" do
|
38
|
+
req = @request_factory.get( '/exceptions/spec' )
|
39
|
+
exception = nil
|
40
|
+
|
41
|
+
begin
|
42
|
+
raise Strelka::RequestError.new( req, "invalid request" )
|
43
|
+
rescue Strelka::RequestError => err
|
44
|
+
exception = err
|
45
|
+
end
|
46
|
+
|
47
|
+
exception.request.should == req
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|