gin 0.0.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +3 -3
- data/.gitignore +7 -0
- data/History.rdoc +3 -6
- data/Manifest.txt +36 -2
- data/README.rdoc +24 -14
- data/Rakefile +2 -9
- data/lib/gin.rb +122 -1
- data/lib/gin/app.rb +595 -0
- data/lib/gin/config.rb +50 -0
- data/lib/gin/controller.rb +602 -0
- data/lib/gin/core_ext/cgi.rb +15 -0
- data/lib/gin/core_ext/gin_class.rb +10 -0
- data/lib/gin/errorable.rb +113 -0
- data/lib/gin/filterable.rb +200 -0
- data/lib/gin/reloadable.rb +90 -0
- data/lib/gin/request.rb +76 -0
- data/lib/gin/response.rb +51 -0
- data/lib/gin/router.rb +222 -0
- data/lib/gin/stream.rb +56 -0
- data/public/400.html +14 -0
- data/public/404.html +13 -0
- data/public/500.html +14 -0
- data/public/error.html +38 -0
- data/public/favicon.ico +0 -0
- data/public/gin.css +61 -0
- data/public/gin_sm.png +0 -0
- data/test/app/app_foo.rb +15 -0
- data/test/app/controllers/app_controller.rb +16 -0
- data/test/app/controllers/foo_controller.rb +3 -0
- data/test/mock_config/backend.yml +7 -0
- data/test/mock_config/memcache.yml +10 -0
- data/test/mock_config/not_a_config.txt +0 -0
- data/test/test_app.rb +592 -0
- data/test/test_config.rb +33 -0
- data/test/test_controller.rb +808 -0
- data/test/test_errorable.rb +221 -0
- data/test/test_filterable.rb +126 -0
- data/test/test_gin.rb +59 -0
- data/test/test_helper.rb +5 -0
- data/test/test_request.rb +81 -0
- data/test/test_response.rb +68 -0
- data/test/test_router.rb +193 -0
- metadata +80 -15
- data/bin/gin +0 -3
- data/test/gin_test.rb +0 -8
data/test/test_config.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
require 'gin/config'
|
3
|
+
|
4
|
+
class ConfigTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def config_dir
|
7
|
+
File.expand_path("../mock_config", __FILE__)
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def test_config_dev
|
12
|
+
config = Gin::Config.new "development", config_dir
|
13
|
+
|
14
|
+
assert_equal "localhost", config.memcache['host']
|
15
|
+
assert_equal 1, config.memcache['connections']
|
16
|
+
|
17
|
+
assert_equal "dev.backend.example.com", config.backend['host']
|
18
|
+
|
19
|
+
assert_raises(NoMethodError){ config.not_a_config }
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def test_config_unknown_env
|
24
|
+
config = Gin::Config.new "foo", config_dir
|
25
|
+
|
26
|
+
assert_equal "example.com", config.memcache['host']
|
27
|
+
assert_equal 5, config.memcache['connections']
|
28
|
+
|
29
|
+
assert_equal "backend.example.com", config.backend['host']
|
30
|
+
|
31
|
+
assert_raises(NoMethodError){ config.not_a_config }
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,808 @@
|
|
1
|
+
require "test/test_helper"
|
2
|
+
|
3
|
+
unless defined? EventMachine
|
4
|
+
class EventMachine; end
|
5
|
+
end
|
6
|
+
|
7
|
+
class AppController < Gin::Controller
|
8
|
+
FILTERS_RUN = []
|
9
|
+
|
10
|
+
before_filter :f1 do
|
11
|
+
FILTERS_RUN << :f1
|
12
|
+
end
|
13
|
+
|
14
|
+
after_filter :f2 do
|
15
|
+
FILTERS_RUN << :f2
|
16
|
+
end
|
17
|
+
|
18
|
+
error Gin::BadRequest do
|
19
|
+
body "Bad Request"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class TestController < Gin::Controller
|
24
|
+
def show id
|
25
|
+
"SHOW #{id}!"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class BarController < AppController
|
30
|
+
before_filter :stop, :only => :delete do
|
31
|
+
FILTERS_RUN << :stop
|
32
|
+
halt 404, "Not Found"
|
33
|
+
end
|
34
|
+
|
35
|
+
error 404 do
|
36
|
+
content_type "text/plain"
|
37
|
+
body "This is not the page you are looking for."
|
38
|
+
end
|
39
|
+
|
40
|
+
def show id
|
41
|
+
"SHOW #{id}!"
|
42
|
+
end
|
43
|
+
|
44
|
+
def delete id, email=nil, name=nil
|
45
|
+
"DELETE!"
|
46
|
+
end
|
47
|
+
|
48
|
+
def index
|
49
|
+
raise "OH NOES"
|
50
|
+
end
|
51
|
+
|
52
|
+
def caught_error
|
53
|
+
raise Gin::BadRequest, "Something is wrong"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
class ControllerTest < Test::Unit::TestCase
|
59
|
+
class MockApp < Gin::App
|
60
|
+
mount BarController do
|
61
|
+
get :show, "/:id"
|
62
|
+
get :delete, :rm_bar
|
63
|
+
get :index, "/"
|
64
|
+
get :caught_error
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def setup
|
70
|
+
MockApp.instance_variable_set("@environment", nil)
|
71
|
+
MockApp.instance_variable_set("@asset_host", nil)
|
72
|
+
@app = MockApp.new
|
73
|
+
@ctrl = BarController.new(@app, rack_env)
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def teardown
|
78
|
+
BarController::FILTERS_RUN.clear
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def rack_env
|
83
|
+
@rack_env ||= {
|
84
|
+
'HTTP_HOST' => 'example.com',
|
85
|
+
'rack.input' => '',
|
86
|
+
'gin.path_query_hash' => {'id' => 123},
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def test_etag
|
92
|
+
@ctrl.etag("my-etag")
|
93
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
94
|
+
|
95
|
+
@ctrl.etag("my-etag", kind: :strong)
|
96
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
97
|
+
|
98
|
+
@ctrl.etag("my-etag", :strong)
|
99
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def test_etag_weak
|
104
|
+
@ctrl.etag("my-etag", kind: :weak)
|
105
|
+
assert_equal 'W/"my-etag"', @ctrl.response['ETag']
|
106
|
+
|
107
|
+
@ctrl.etag("my-etag", :weak)
|
108
|
+
assert_equal 'W/"my-etag"', @ctrl.response['ETag']
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
def test_etag_if_match
|
113
|
+
rack_env['HTTP_IF_MATCH'] = '"other-etag"'
|
114
|
+
resp = catch(:halt){ @ctrl.etag "my-etag" }
|
115
|
+
assert_equal 412, resp
|
116
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
117
|
+
|
118
|
+
rack_env['HTTP_IF_MATCH'] = '*'
|
119
|
+
resp = catch(:halt){ @ctrl.etag "my-etag" }
|
120
|
+
assert_equal nil, resp
|
121
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
122
|
+
|
123
|
+
rack_env['HTTP_IF_MATCH'] = '*'
|
124
|
+
resp = catch(:halt){ @ctrl.etag "my-etag", new_resource: true }
|
125
|
+
assert_equal 412, resp
|
126
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
127
|
+
|
128
|
+
rack_env['REQUEST_METHOD'] = 'POST'
|
129
|
+
rack_env['HTTP_IF_MATCH'] = '*'
|
130
|
+
resp = catch(:halt){ @ctrl.etag "my-etag" }
|
131
|
+
assert_equal 412, resp
|
132
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def test_etag_if_none_match
|
137
|
+
rack_env['REQUEST_METHOD'] = 'GET'
|
138
|
+
|
139
|
+
rack_env['HTTP_IF_NONE_MATCH'] = '"other-etag"'
|
140
|
+
resp = catch(:halt){ @ctrl.etag "my-etag" }
|
141
|
+
assert_equal nil, resp
|
142
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
143
|
+
|
144
|
+
rack_env['HTTP_IF_NONE_MATCH'] = '"other-etag", "my-etag"'
|
145
|
+
resp = catch(:halt){ @ctrl.etag "my-etag" }
|
146
|
+
assert_equal 304, resp
|
147
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
148
|
+
|
149
|
+
rack_env['HTTP_IF_NONE_MATCH'] = '*'
|
150
|
+
resp = catch(:halt){ @ctrl.etag "my-etag" }
|
151
|
+
assert_equal 304, resp
|
152
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
153
|
+
|
154
|
+
rack_env['REQUEST_METHOD'] = 'POST'
|
155
|
+
rack_env['HTTP_IF_NONE_MATCH'] = '*'
|
156
|
+
resp = catch(:halt){ @ctrl.etag "my-etag", new_resource: false }
|
157
|
+
assert_equal 412, resp
|
158
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
def test_etag_non_error_status
|
163
|
+
[200, 201, 202, 304].each do |code|
|
164
|
+
@ctrl.status code
|
165
|
+
rack_env['HTTP_IF_MATCH'] = '"other-etag"'
|
166
|
+
resp = catch(:halt){ @ctrl.etag "my-etag" }
|
167
|
+
assert_equal 412, resp
|
168
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
def test_etag_error_status
|
174
|
+
[301, 302, 303, 400, 404, 500, 502, 503].each do |code|
|
175
|
+
@ctrl.status code
|
176
|
+
rack_env['HTTP_IF_MATCH'] = '"other-etag"'
|
177
|
+
resp = catch(:halt){ @ctrl.etag "my-etag" }
|
178
|
+
assert_equal nil, resp
|
179
|
+
assert_equal "my-etag".inspect, @ctrl.response['ETag']
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
def test_cache_control
|
185
|
+
@ctrl.cache_control :public, :must_revalidate, max_age: 60
|
186
|
+
assert_equal "public, must-revalidate, max-age=60",
|
187
|
+
@ctrl.response['Cache-Control']
|
188
|
+
|
189
|
+
@ctrl.cache_control public:true, must_revalidate:false, max_age:'foo'
|
190
|
+
assert_equal "public, max-age=0", @ctrl.response['Cache-Control']
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
def test_expires_int
|
195
|
+
time = Time.now
|
196
|
+
@ctrl.expires 60, :public, :must_revalidate
|
197
|
+
|
198
|
+
assert_equal "public, must-revalidate, max-age=60",
|
199
|
+
@ctrl.response['Cache-Control']
|
200
|
+
|
201
|
+
assert_equal (time + 60).httpdate, @ctrl.response['Expires']
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
def test_expires_time
|
206
|
+
time = Time.now + 60
|
207
|
+
@ctrl.expires time, :public, must_revalidate:false, max_age:20
|
208
|
+
|
209
|
+
assert_equal "public, max-age=20",
|
210
|
+
@ctrl.response['Cache-Control']
|
211
|
+
|
212
|
+
assert_equal time.httpdate, @ctrl.response['Expires']
|
213
|
+
end
|
214
|
+
|
215
|
+
|
216
|
+
def test_expires_str
|
217
|
+
time = Time.now + 60
|
218
|
+
@ctrl.expires time.strftime("%Y/%m/%d %H:%M:%S"),
|
219
|
+
:public, must_revalidate:false, max_age:20
|
220
|
+
|
221
|
+
assert_equal "public, max-age=20",
|
222
|
+
@ctrl.response['Cache-Control']
|
223
|
+
|
224
|
+
assert_equal time.httpdate, @ctrl.response['Expires']
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
def test_expire_cache_control
|
229
|
+
@ctrl.expire_cache_control
|
230
|
+
assert_equal 'no-cache', @ctrl.response['Pragma']
|
231
|
+
assert_equal 'no-cache, no-store, must-revalidate, max-age=0',
|
232
|
+
@ctrl.response['Cache-Control']
|
233
|
+
assert_equal Time.new("1990","01","01").httpdate,
|
234
|
+
@ctrl.response['Expires']
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
def test_set_cookie
|
239
|
+
cookie = {:value => "user@example.com", :expires => Time.now + 360}
|
240
|
+
@ctrl.cookies['test'] = cookie
|
241
|
+
assert_equal cookie, @ctrl.env["rack.request.cookie_hash"]["test"]
|
242
|
+
assert_equal "user@example.com", @ctrl.cookies['test'][:value]
|
243
|
+
end
|
244
|
+
|
245
|
+
|
246
|
+
def test_set_session
|
247
|
+
@ctrl.session['test'] = 'user@example.com'
|
248
|
+
assert_equal({"test"=>"user@example.com"}, @ctrl.env['rack.session'])
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
def test_call_action
|
253
|
+
resp = @ctrl.call_action(:show)
|
254
|
+
assert_equal [:f1, :f2], BarController::FILTERS_RUN
|
255
|
+
assert_equal [200, {"Content-Type"=>"text/html;charset=UTF-8", "Content-Length"=>"9"},
|
256
|
+
["SHOW 123!"]], resp
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
def test_call_action_error
|
261
|
+
@ctrl.call_action(:index)
|
262
|
+
assert_equal [:f1, :f2], BarController::FILTERS_RUN
|
263
|
+
end
|
264
|
+
|
265
|
+
|
266
|
+
def test_call_action_caught_error
|
267
|
+
resp = @ctrl.call_action(:caught_error)
|
268
|
+
assert_equal [:f1, :f2], BarController::FILTERS_RUN
|
269
|
+
assert_equal [400, {"Content-Type"=>"text/html;charset=UTF-8", "Content-Length"=>"11"},
|
270
|
+
["Bad Request"]], resp
|
271
|
+
end
|
272
|
+
|
273
|
+
|
274
|
+
def test_call_action_halt
|
275
|
+
resp = @ctrl.call_action(:delete)
|
276
|
+
assert_equal [:f1, :stop, :f2], BarController::FILTERS_RUN
|
277
|
+
assert_equal [404, {"Content-Type"=>"text/plain;charset=UTF-8", "Content-Length"=>"41"},
|
278
|
+
["This is not the page you are looking for."]], resp
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
def test_dispatch
|
283
|
+
@ctrl.send(:dispatch, :show)
|
284
|
+
assert_equal [:f1, :f2], BarController::FILTERS_RUN
|
285
|
+
assert_equal 200, @ctrl.response.status
|
286
|
+
assert_equal ["SHOW 123!"], @ctrl.response.body
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
def test_dispatch_error
|
291
|
+
@ctrl.content_type :json
|
292
|
+
@ctrl.send(:dispatch, :index)
|
293
|
+
assert_equal [:f1, :f2], BarController::FILTERS_RUN
|
294
|
+
assert_equal 500, @ctrl.response.status
|
295
|
+
assert RuntimeError === @ctrl.env['gin.errors'].first
|
296
|
+
assert_equal String, @ctrl.response.body[0].class
|
297
|
+
assert_equal "text/html;charset=UTF-8", @ctrl.response['Content-Type']
|
298
|
+
assert @ctrl.response.body[0].include?('<h1>RuntimeError</h1>')
|
299
|
+
assert @ctrl.response.body[0].include?('<pre>')
|
300
|
+
end
|
301
|
+
|
302
|
+
|
303
|
+
def test_dispatch_error_nondev
|
304
|
+
MockApp.environment "prod"
|
305
|
+
@ctrl.send(:dispatch, :index)
|
306
|
+
|
307
|
+
assert_equal 500, @ctrl.response.status
|
308
|
+
assert RuntimeError === @ctrl.env['gin.errors'].first
|
309
|
+
|
310
|
+
assert_equal File.read(File.join(Gin::PUBLIC_DIR, "500.html")),
|
311
|
+
@ctrl.body.read
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
def test_dispatch_bad_request_nondev
|
316
|
+
MockApp.environment "prod"
|
317
|
+
@ctrl = TestController.new @app, rack_env
|
318
|
+
@ctrl.params.delete('id')
|
319
|
+
@ctrl.send(:dispatch, :show)
|
320
|
+
|
321
|
+
assert_equal 400, @ctrl.response.status
|
322
|
+
assert Gin::BadRequest === @ctrl.env['gin.errors'].first
|
323
|
+
assert_equal File.read(File.join(Gin::PUBLIC_DIR, "400.html")),
|
324
|
+
@ctrl.body.read
|
325
|
+
end
|
326
|
+
|
327
|
+
|
328
|
+
def test_dispatch_not_found_nondev
|
329
|
+
MockApp.environment "prod"
|
330
|
+
@ctrl = TestController.new @app, rack_env
|
331
|
+
@ctrl.params.delete('id')
|
332
|
+
@ctrl.send(:dispatch, :not_found)
|
333
|
+
|
334
|
+
assert_equal 404, @ctrl.response.status
|
335
|
+
assert Gin::NotFound === @ctrl.env['gin.errors'].first
|
336
|
+
assert_equal File.read(File.join(Gin::PUBLIC_DIR, "404.html")),
|
337
|
+
@ctrl.body.read
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
def test_dispatch_filter_halt
|
342
|
+
@ctrl.send(:dispatch, :delete)
|
343
|
+
assert_equal [:f1, :stop, :f2], BarController::FILTERS_RUN
|
344
|
+
assert_equal 404, @ctrl.response.status
|
345
|
+
assert_equal ["Not Found"], @ctrl.response.body
|
346
|
+
end
|
347
|
+
|
348
|
+
|
349
|
+
def test_invoke
|
350
|
+
@ctrl.send(:invoke){ "test body" }
|
351
|
+
assert_equal ["test body"], @ctrl.body
|
352
|
+
|
353
|
+
@ctrl.send(:invoke){ [401, "test body"] }
|
354
|
+
assert_equal 401, @ctrl.status
|
355
|
+
assert_equal ["test body"], @ctrl.body
|
356
|
+
|
357
|
+
@ctrl.send(:invoke){ [302, {'Location' => 'http://foo.com'}, "test body"] }
|
358
|
+
assert_equal 302, @ctrl.status
|
359
|
+
assert_equal 'http://foo.com', @ctrl.response['Location']
|
360
|
+
assert_equal ["test body"], @ctrl.body
|
361
|
+
|
362
|
+
@ctrl.send(:invoke){ 301 }
|
363
|
+
assert_equal 301, @ctrl.status
|
364
|
+
end
|
365
|
+
|
366
|
+
|
367
|
+
def test_action_arguments
|
368
|
+
assert_raises(Gin::NotFound){ @ctrl.send("action_arguments", "nonaction") }
|
369
|
+
assert_equal [], @ctrl.send("action_arguments", "index")
|
370
|
+
|
371
|
+
assert_equal [123], @ctrl.send("action_arguments", "show")
|
372
|
+
assert_equal [123], @ctrl.send("action_arguments", "delete")
|
373
|
+
|
374
|
+
@ctrl.params.update 'name' => 'bob'
|
375
|
+
assert_equal [123,nil,'bob'], @ctrl.send("action_arguments", "delete")
|
376
|
+
|
377
|
+
@ctrl.params.delete('id')
|
378
|
+
assert_raises(Gin::BadRequest){
|
379
|
+
@ctrl.send("action_arguments", "show")
|
380
|
+
}
|
381
|
+
end
|
382
|
+
|
383
|
+
|
384
|
+
if RUBY_VERSION =~ /^2.0/
|
385
|
+
eval <<-EVAL
|
386
|
+
class SpecialCtrl < Gin::Controller
|
387
|
+
def find(q, title_only:false, count: 10); end
|
388
|
+
end
|
389
|
+
EVAL
|
390
|
+
|
391
|
+
def test_action_arguments_key_parameters
|
392
|
+
@ctrl = SpecialCtrl.new nil, rack_env.merge('QUERY_STRING'=>'q=pizza&count=20')
|
393
|
+
assert_equal ["pizza", {count: 20}], @ctrl.send("action_arguments", "find")
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
|
398
|
+
def test_asset_url
|
399
|
+
old_public_dir = MockApp.public_dir
|
400
|
+
MockApp.public_dir File.dirname(__FILE__)
|
401
|
+
|
402
|
+
file_id = MockApp.md5 __FILE__
|
403
|
+
expected = "/test_controller.rb?#{file_id}"
|
404
|
+
|
405
|
+
assert_equal 8, file_id.length
|
406
|
+
assert_equal expected, @ctrl.asset_url(File.basename(__FILE__))
|
407
|
+
|
408
|
+
ensure
|
409
|
+
MockApp.public_dir old_public_dir
|
410
|
+
end
|
411
|
+
|
412
|
+
|
413
|
+
def test_asset_url_unknown
|
414
|
+
assert_equal "/foo.jpg", @ctrl.asset_url("foo.jpg")
|
415
|
+
end
|
416
|
+
|
417
|
+
|
418
|
+
def test_asset_url_w_host
|
419
|
+
MockApp.asset_host "http://example.com"
|
420
|
+
assert_equal "http://example.com/foo.jpg", @ctrl.asset_url("foo.jpg")
|
421
|
+
|
422
|
+
MockApp.asset_host do |file|
|
423
|
+
file =~ /\.js$/ ? "http://js.example.com" : "http://img.example.com"
|
424
|
+
end
|
425
|
+
assert_equal "http://js.example.com/foo.js", @ctrl.asset_url("foo.js")
|
426
|
+
assert_equal "http://img.example.com/foo.jpg", @ctrl.asset_url("foo.jpg")
|
427
|
+
end
|
428
|
+
|
429
|
+
|
430
|
+
def test_redirect_non_get
|
431
|
+
rack_env['REQUEST_METHOD'] = 'POST'
|
432
|
+
rack_env['HTTP_VERSION'] = 'HTTP/1.0'
|
433
|
+
catch(:halt){ @ctrl.redirect "/foo" }
|
434
|
+
assert_equal 302, @ctrl.status
|
435
|
+
assert_equal "http://example.com/foo", @ctrl.response['Location']
|
436
|
+
|
437
|
+
rack_env['HTTP_VERSION'] = 'HTTP/1.1'
|
438
|
+
catch(:halt){ @ctrl.redirect "/foo" }
|
439
|
+
assert_equal 303, @ctrl.status
|
440
|
+
assert_equal "http://example.com/foo", @ctrl.response['Location']
|
441
|
+
end
|
442
|
+
|
443
|
+
|
444
|
+
def test_redirect
|
445
|
+
catch(:halt){ @ctrl.redirect "/foo" }
|
446
|
+
assert_equal 302, @ctrl.status
|
447
|
+
assert_equal "http://example.com/foo", @ctrl.response['Location']
|
448
|
+
|
449
|
+
resp = catch(:halt){ @ctrl.redirect "https://google.com/foo", 301, "Move Along" }
|
450
|
+
assert_equal 302, @ctrl.status
|
451
|
+
assert_equal "https://google.com/foo", @ctrl.response['Location']
|
452
|
+
assert_equal [301, "Move Along"], resp
|
453
|
+
|
454
|
+
resp = catch(:halt){ @ctrl.redirect "https://google.com/foo", 301, {'X-LOC' => "test"}, "Move Along" }
|
455
|
+
assert_equal 302, @ctrl.status
|
456
|
+
assert_equal "https://google.com/foo", @ctrl.response['Location']
|
457
|
+
assert_equal [301, {'X-LOC' => "test"}, "Move Along"], resp
|
458
|
+
end
|
459
|
+
|
460
|
+
|
461
|
+
def test_url_to
|
462
|
+
assert_equal "http://example.com/bar/123",
|
463
|
+
@ctrl.url_to(BarController, :show, :id => 123)
|
464
|
+
assert_equal "http://example.com/bar/123", @ctrl.url_to(:show, :id => 123)
|
465
|
+
assert_equal "http://example.com/bar/delete?id=123",
|
466
|
+
@ctrl.url_to(:rm_bar, :id => 123)
|
467
|
+
assert_equal "https://foo.com/path?id=123",
|
468
|
+
@ctrl.url_to("https://foo.com/path", :id => 123)
|
469
|
+
assert_equal "http://example.com/bar/123",
|
470
|
+
@ctrl.to(BarController, :show, :id => 123)
|
471
|
+
end
|
472
|
+
|
473
|
+
|
474
|
+
def test_path_to
|
475
|
+
assert_equal "/bar/123", @ctrl.path_to(BarController, :show, id: 123)
|
476
|
+
assert_equal "/bar/123", @ctrl.path_to(:show, id: 123)
|
477
|
+
assert_equal "/bar/delete?id=123", @ctrl.path_to(:rm_bar, id: 123)
|
478
|
+
assert_equal "/bar/delete", @ctrl.path_to(:rm_bar)
|
479
|
+
assert_equal "/test?id=123", @ctrl.path_to("/test", id: 123)
|
480
|
+
assert_equal "/test", @ctrl.path_to("/test")
|
481
|
+
end
|
482
|
+
|
483
|
+
|
484
|
+
def test_session
|
485
|
+
assert_equal(@ctrl.request.session, @ctrl.session)
|
486
|
+
end
|
487
|
+
|
488
|
+
|
489
|
+
def test_params
|
490
|
+
assert_equal({'id' => 123}, @ctrl.params)
|
491
|
+
assert_equal(@ctrl.request.params, @ctrl.params)
|
492
|
+
end
|
493
|
+
|
494
|
+
|
495
|
+
def test_logger
|
496
|
+
assert_equal @app.logger, @ctrl.logger
|
497
|
+
end
|
498
|
+
|
499
|
+
|
500
|
+
def test_stream
|
501
|
+
@ctrl.stream{|out| out << "BODY"}
|
502
|
+
assert Gin::Stream === @ctrl.body
|
503
|
+
assert_equal Gin::Stream, @ctrl.body.instance_variable_get("@scheduler")
|
504
|
+
|
505
|
+
@ctrl.env.merge! "async.callback" => lambda{}
|
506
|
+
|
507
|
+
@ctrl.stream{|out| out << "BODY"}
|
508
|
+
assert Gin::Stream === @ctrl.body
|
509
|
+
assert_equal EventMachine, @ctrl.body.instance_variable_get("@scheduler")
|
510
|
+
end
|
511
|
+
|
512
|
+
|
513
|
+
def test_headers
|
514
|
+
@ctrl.headers "Content-Length" => 123
|
515
|
+
assert_equal @ctrl.response.headers, @ctrl.headers
|
516
|
+
assert_equal({"Content-Length"=>123}, @ctrl.headers)
|
517
|
+
end
|
518
|
+
|
519
|
+
|
520
|
+
def test_error_num_and_str
|
521
|
+
resp = catch :halt do
|
522
|
+
@ctrl.error 404, "Not Found"
|
523
|
+
"BAD RESP"
|
524
|
+
end
|
525
|
+
|
526
|
+
assert_equal 404, resp
|
527
|
+
assert_equal ["Not Found"], @ctrl.body
|
528
|
+
end
|
529
|
+
|
530
|
+
|
531
|
+
def test_error_num
|
532
|
+
resp = catch :halt do
|
533
|
+
@ctrl.error 404
|
534
|
+
"BAD RESP"
|
535
|
+
end
|
536
|
+
|
537
|
+
assert_equal 404, resp
|
538
|
+
assert_equal [], @ctrl.body
|
539
|
+
end
|
540
|
+
|
541
|
+
|
542
|
+
def test_error_str
|
543
|
+
resp = catch :halt do
|
544
|
+
@ctrl.error "OH NOES"
|
545
|
+
"BAD RESP"
|
546
|
+
end
|
547
|
+
|
548
|
+
assert_equal 500, resp
|
549
|
+
assert_equal ["OH NOES"], @ctrl.body
|
550
|
+
end
|
551
|
+
|
552
|
+
|
553
|
+
def test_halt_single
|
554
|
+
resp = catch :halt do
|
555
|
+
@ctrl.halt 400
|
556
|
+
"BAD RESP"
|
557
|
+
end
|
558
|
+
|
559
|
+
assert_equal 400, resp
|
560
|
+
end
|
561
|
+
|
562
|
+
|
563
|
+
def test_halt_ary
|
564
|
+
resp = catch :halt do
|
565
|
+
@ctrl.halt 201, "Accepted"
|
566
|
+
"BAD RESP"
|
567
|
+
end
|
568
|
+
|
569
|
+
assert_equal [201, "Accepted"], resp
|
570
|
+
end
|
571
|
+
|
572
|
+
|
573
|
+
def test_last_modified
|
574
|
+
time = Time.now
|
575
|
+
@ctrl.last_modified time
|
576
|
+
assert_equal time.httpdate, @ctrl.response['Last-Modified']
|
577
|
+
|
578
|
+
date = DateTime.parse time.to_s
|
579
|
+
@ctrl.last_modified date
|
580
|
+
assert_equal time.httpdate, @ctrl.response['Last-Modified']
|
581
|
+
|
582
|
+
date = Date.parse time.to_s
|
583
|
+
expected = Time.new(time.year, time.month, time.day)
|
584
|
+
@ctrl.last_modified date
|
585
|
+
assert_equal expected.httpdate, @ctrl.response['Last-Modified']
|
586
|
+
end
|
587
|
+
|
588
|
+
|
589
|
+
def test_last_modified_str
|
590
|
+
time = Time.now
|
591
|
+
@ctrl.last_modified time.to_s
|
592
|
+
assert_equal time.httpdate, @ctrl.response['Last-Modified']
|
593
|
+
end
|
594
|
+
|
595
|
+
|
596
|
+
def test_last_modified_int
|
597
|
+
time = Time.now
|
598
|
+
@ctrl.last_modified time.to_i
|
599
|
+
assert_equal time.httpdate, @ctrl.response['Last-Modified']
|
600
|
+
end
|
601
|
+
|
602
|
+
|
603
|
+
def test_last_modified_false
|
604
|
+
@ctrl.last_modified false
|
605
|
+
assert_nil @ctrl.response['Last-Modified']
|
606
|
+
end
|
607
|
+
|
608
|
+
|
609
|
+
def test_last_modified_if_modified_since
|
610
|
+
rack_env['HTTP_IF_MODIFIED_SINCE'] = Time.parse("10 Feb 3012").httpdate
|
611
|
+
time = Time.now
|
612
|
+
@ctrl.status 200
|
613
|
+
res = catch(:halt){ @ctrl.last_modified time }
|
614
|
+
|
615
|
+
assert_equal 304, res
|
616
|
+
assert_equal time.httpdate, @ctrl.response['Last-Modified']
|
617
|
+
|
618
|
+
rack_env['HTTP_IF_MODIFIED_SINCE'] = Time.parse("10 Feb 2012").httpdate
|
619
|
+
time = Time.now
|
620
|
+
@ctrl.status 200
|
621
|
+
@ctrl.last_modified time
|
622
|
+
assert_equal time.httpdate, @ctrl.response['Last-Modified']
|
623
|
+
end
|
624
|
+
|
625
|
+
|
626
|
+
def test_last_modified_if_modified_since_non_200
|
627
|
+
rack_env['HTTP_IF_MODIFIED_SINCE'] = Time.parse("10 Feb 3012").httpdate
|
628
|
+
time = Time.now
|
629
|
+
@ctrl.status 404
|
630
|
+
@ctrl.last_modified time
|
631
|
+
assert_equal time.httpdate, @ctrl.response['Last-Modified']
|
632
|
+
end
|
633
|
+
|
634
|
+
|
635
|
+
def test_last_modified_if_unmodified_since
|
636
|
+
[200, 299, 412].each do |code|
|
637
|
+
rack_env['HTTP_IF_UNMODIFIED_SINCE'] = Time.parse("10 Feb 2012").httpdate
|
638
|
+
time = Time.now
|
639
|
+
@ctrl.status code
|
640
|
+
res = catch(:halt){ @ctrl.last_modified time }
|
641
|
+
|
642
|
+
assert_equal 412, res
|
643
|
+
assert_equal time.httpdate, @ctrl.response['Last-Modified']
|
644
|
+
|
645
|
+
rack_env['HTTP_IF_MODIFIED_SINCE'] = Time.parse("10 Feb 3012").httpdate
|
646
|
+
time = Time.now
|
647
|
+
@ctrl.status code
|
648
|
+
@ctrl.last_modified time
|
649
|
+
assert_equal time.httpdate, @ctrl.response['Last-Modified']
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
|
654
|
+
def test_last_modified_if_unmodified_since_404
|
655
|
+
rack_env['HTTP_IF_UNMODIFIED_SINCE'] = Time.parse("10 Feb 2012").httpdate
|
656
|
+
time = Time.now
|
657
|
+
@ctrl.status 404
|
658
|
+
@ctrl.last_modified time
|
659
|
+
|
660
|
+
assert_equal 404, @ctrl.status
|
661
|
+
assert_equal time.httpdate, @ctrl.response['Last-Modified']
|
662
|
+
end
|
663
|
+
|
664
|
+
|
665
|
+
def test_send_file
|
666
|
+
res = catch(:halt){ @ctrl.send_file "./Manifest.txt" }
|
667
|
+
assert_equal 200, res[0]
|
668
|
+
assert File === res[1]
|
669
|
+
|
670
|
+
@ctrl.status 404
|
671
|
+
@ctrl.send(:invoke){ @ctrl.send_file "./Manifest.txt" }
|
672
|
+
assert_equal 200, @ctrl.status
|
673
|
+
assert_equal "text/plain;charset=UTF-8", @ctrl.headers["Content-Type"]
|
674
|
+
assert_equal File.size("./Manifest.txt").to_s, @ctrl.headers["Content-Length"]
|
675
|
+
assert(Time.parse(@ctrl.headers["Last-Modified"]) >
|
676
|
+
Time.parse("Fri, 22 Feb 2012 18:51:31 GMT"))
|
677
|
+
assert File === @ctrl.body
|
678
|
+
|
679
|
+
read_body = ""
|
680
|
+
@ctrl.body.each{|data| read_body << data}
|
681
|
+
assert_equal File.read("./Manifest.txt"), read_body
|
682
|
+
end
|
683
|
+
|
684
|
+
|
685
|
+
def test_send_file_not_found
|
686
|
+
res = catch(:halt){ @ctrl.send_file "./no-such-file" }
|
687
|
+
assert_equal 404, res
|
688
|
+
end
|
689
|
+
|
690
|
+
|
691
|
+
def test_send_file_last_modified
|
692
|
+
mod_date = Time.now
|
693
|
+
catch(:halt){ @ctrl.send_file "./Manifest.txt", last_modified: mod_date }
|
694
|
+
assert_equal mod_date.httpdate, @ctrl.headers["Last-Modified"]
|
695
|
+
assert_nil @ctrl.headers['Content-Disposition']
|
696
|
+
end
|
697
|
+
|
698
|
+
|
699
|
+
def test_send_file_attachment
|
700
|
+
res = catch(:halt){ @ctrl.send_file "./Manifest.txt", :disposition => 'attachment' }
|
701
|
+
expected = "attachment; filename=\"Manifest.txt\""
|
702
|
+
assert_equal expected, @ctrl.headers['Content-Disposition']
|
703
|
+
|
704
|
+
res = catch(:halt){
|
705
|
+
@ctrl.send_file "./Manifest.txt", :disposition => 'attachment', :filename => "foo.txt"
|
706
|
+
}
|
707
|
+
expected = "attachment; filename=\"foo.txt\""
|
708
|
+
assert_equal expected, @ctrl.headers['Content-Disposition']
|
709
|
+
end
|
710
|
+
|
711
|
+
|
712
|
+
def test_mime_type
|
713
|
+
assert_equal "text/html", @ctrl.mime_type(:html)
|
714
|
+
assert_equal @ctrl.app.mime_type(:html), @ctrl.mime_type(:html)
|
715
|
+
end
|
716
|
+
|
717
|
+
|
718
|
+
def test_content_type
|
719
|
+
assert_nil @ctrl.content_type
|
720
|
+
assert_nil @ctrl.response['Content-Type']
|
721
|
+
|
722
|
+
@ctrl.content_type 'text/json'
|
723
|
+
assert_equal "text/json;charset=UTF-8", @ctrl.content_type
|
724
|
+
assert_equal "text/json;charset=UTF-8", @ctrl.response['Content-Type']
|
725
|
+
|
726
|
+
assert_equal "application/json;charset=UTF-8", @ctrl.content_type(".json")
|
727
|
+
end
|
728
|
+
|
729
|
+
|
730
|
+
def test_content_type_params
|
731
|
+
assert_equal "application/json;charset=ASCII-8BIT",
|
732
|
+
@ctrl.content_type(".json", charset: "ASCII-8BIT")
|
733
|
+
|
734
|
+
assert_equal "application/json;foo=bar, charset=UTF-8",
|
735
|
+
@ctrl.content_type(".json", foo: "bar")
|
736
|
+
end
|
737
|
+
|
738
|
+
|
739
|
+
def test_content_type_unknown
|
740
|
+
assert_raises RuntimeError do
|
741
|
+
@ctrl.content_type 'fhwbghd'
|
742
|
+
end
|
743
|
+
|
744
|
+
assert_equal "text/html;charset=UTF-8",
|
745
|
+
@ctrl.content_type('fhwbghd', default: 'text/html')
|
746
|
+
end
|
747
|
+
|
748
|
+
|
749
|
+
def test_body
|
750
|
+
assert_equal [], @ctrl.body
|
751
|
+
assert_equal [], @ctrl.response.body
|
752
|
+
|
753
|
+
@ctrl.body("<HTML></HTML>")
|
754
|
+
assert_equal ["<HTML></HTML>"], @ctrl.body
|
755
|
+
assert_equal ["<HTML></HTML>"], @ctrl.response.body
|
756
|
+
end
|
757
|
+
|
758
|
+
|
759
|
+
def test_status
|
760
|
+
assert_equal 200, @ctrl.status
|
761
|
+
assert_equal 200, @ctrl.response.status
|
762
|
+
|
763
|
+
@ctrl.status(404)
|
764
|
+
assert_equal 404, @ctrl.status
|
765
|
+
assert_equal 404, @ctrl.response.status
|
766
|
+
end
|
767
|
+
|
768
|
+
|
769
|
+
def test_init
|
770
|
+
assert Gin::Request === @ctrl.request
|
771
|
+
assert Gin::Response === @ctrl.response
|
772
|
+
assert_nil @ctrl.response['Content-Type']
|
773
|
+
assert_equal @app, @ctrl.app
|
774
|
+
assert_equal rack_env, @ctrl.env
|
775
|
+
end
|
776
|
+
|
777
|
+
|
778
|
+
def test_supports_features
|
779
|
+
assert Gin::Controller.ancestors.include?(Gin::Filterable)
|
780
|
+
assert Gin::Controller.ancestors.include?(Gin::Errorable)
|
781
|
+
end
|
782
|
+
|
783
|
+
|
784
|
+
def test_class_controller_name
|
785
|
+
assert_equal "app", AppController.controller_name
|
786
|
+
assert_equal "bar", BarController.controller_name
|
787
|
+
|
788
|
+
assert_equal "app", AppController.new(nil,rack_env).controller_name
|
789
|
+
end
|
790
|
+
|
791
|
+
|
792
|
+
def test_class_content_type
|
793
|
+
assert_equal "text/html", AppController.content_type
|
794
|
+
assert_equal "text/html", BarController.content_type
|
795
|
+
|
796
|
+
AppController.content_type "application/json"
|
797
|
+
assert_equal "application/json", AppController.content_type
|
798
|
+
assert_equal "application/json", BarController.content_type
|
799
|
+
|
800
|
+
BarController.content_type "text/plain"
|
801
|
+
assert_equal "application/json", AppController.content_type
|
802
|
+
assert_equal "text/plain", BarController.content_type
|
803
|
+
|
804
|
+
ensure
|
805
|
+
AppController.instance_variable_set("@content_type",nil)
|
806
|
+
BarController.instance_variable_set("@content_type",nil)
|
807
|
+
end
|
808
|
+
end
|