strelka 0.0.1pre4 → 0.0.1.pre129
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/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
|
+
|