gin 0.0.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class ErrorableTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
class Foo
|
6
|
+
include Gin::Errorable
|
7
|
+
|
8
|
+
def self.delete_all_handlers!
|
9
|
+
@err_handlers = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :env
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@status = 200
|
16
|
+
@env = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def status val=nil
|
20
|
+
@status = val if val
|
21
|
+
@status
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Bar < Foo; end
|
26
|
+
|
27
|
+
|
28
|
+
def setup
|
29
|
+
Foo.delete_all_handlers!
|
30
|
+
Bar.delete_all_handlers!
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def test_handler_lookup
|
35
|
+
block_404 = lambda{|err| "404" }
|
36
|
+
block_err = lambda{|err| "ERROR'D!" }
|
37
|
+
|
38
|
+
Foo.error(404, &block_404)
|
39
|
+
Foo.error(Exception, &block_err)
|
40
|
+
|
41
|
+
assert_equal block_404, Foo.error_handler_for(404)
|
42
|
+
assert_equal block_err, Foo.error_handler_for(Exception.new)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def test_handler_local_fallback
|
47
|
+
block_def = lambda{|err| "default" }
|
48
|
+
Foo.error(&block_def)
|
49
|
+
assert_equal block_def, Foo.error_handler_for(404)
|
50
|
+
assert_equal block_def, Foo.error_handler_for(ArgumentError.new)
|
51
|
+
|
52
|
+
block_err = lambda{|err| "ERROR'D!" }
|
53
|
+
Foo.error(StandardError, &block_err)
|
54
|
+
assert_equal block_err, Foo.error_handler_for(ArgumentError.new)
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def test_handler_inheritance
|
59
|
+
block_404 = lambda{|err| "404" }
|
60
|
+
block_err = lambda{|err| "ERROR'D!" }
|
61
|
+
Foo.error(StandardError, &block_err)
|
62
|
+
Foo.error(404, &block_404)
|
63
|
+
|
64
|
+
assert_equal block_404, Bar.error_handler_for(404)
|
65
|
+
assert_equal block_err, Bar.error_handler_for(ArgumentError.new)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def test_handler_inheritance_fallback
|
70
|
+
block_404 = lambda{|err| "404" }
|
71
|
+
block_def = lambda{|err| "default" }
|
72
|
+
Foo.error(&block_def)
|
73
|
+
Bar.error(404, &block_404)
|
74
|
+
|
75
|
+
assert_equal block_404, Bar.error_handler_for(404)
|
76
|
+
assert_equal block_def, Bar.error_handler_for(ArgumentError.new)
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def test_handler_err_inheritance_order
|
81
|
+
block_err = lambda{|err| "ERROR'D!" }
|
82
|
+
block_def = lambda{|err| "default" }
|
83
|
+
Foo.error(ArgumentError, &block_err)
|
84
|
+
Bar.error(&block_def)
|
85
|
+
|
86
|
+
assert_equal block_def, Bar.error_handler_for(ArgumentError.new)
|
87
|
+
|
88
|
+
block_err2 = lambda{|err| "ERROR'D!" }
|
89
|
+
Bar.error(Exception, &block_err2)
|
90
|
+
|
91
|
+
assert_equal block_err2, Bar.error_handler_for(ArgumentError.new)
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
def test_handler_status_inheritance_order
|
96
|
+
block_404 = lambda{|err| "404" }
|
97
|
+
block_def = lambda{|err| "default" }
|
98
|
+
Foo.error(404, &block_404)
|
99
|
+
Bar.error(&block_def)
|
100
|
+
|
101
|
+
assert_equal block_def, Bar.error_handler_for(ArgumentError.new)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def test_handle_status
|
106
|
+
handlers = []
|
107
|
+
Foo.error{ handlers << :default }
|
108
|
+
Foo.all_errors{ handlers << :all }
|
109
|
+
Foo.error(404){ handlers << :s404 }
|
110
|
+
|
111
|
+
Foo.new.handle_status(404)
|
112
|
+
assert_equal [:s404], handlers
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
def test_handle_status_missing
|
117
|
+
assert_nil Foo.new.handle_status(404)
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
def test_handle_error
|
122
|
+
handlers = []
|
123
|
+
Foo.error{ handlers << :default }
|
124
|
+
|
125
|
+
err = ArgumentError.new
|
126
|
+
foo = Foo.new
|
127
|
+
foo.handle_error(err)
|
128
|
+
|
129
|
+
assert_equal [err], foo.env['gin.errors']
|
130
|
+
assert_equal [:default], handlers
|
131
|
+
assert_equal 500, foo.status
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
def test_all_errors_no_rescue_handler
|
136
|
+
handlers = []
|
137
|
+
Foo.all_errors{ handlers << :all }
|
138
|
+
|
139
|
+
err = ArgumentError.new
|
140
|
+
bar = Bar.new
|
141
|
+
|
142
|
+
assert_raises ArgumentError do
|
143
|
+
bar.handle_error err
|
144
|
+
end
|
145
|
+
|
146
|
+
assert_equal [err], bar.env['gin.errors']
|
147
|
+
assert_equal [:all], handlers
|
148
|
+
assert_equal 500, bar.status
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
def test_handle_error_all_errors
|
153
|
+
handlers = []
|
154
|
+
err = ArgumentError.new
|
155
|
+
|
156
|
+
Foo.error{|e| raise "Unexpected Error" unless err == e; handlers << :default }
|
157
|
+
Foo.all_errors{|e| raise "Unexpected Error" unless err == e; handlers << :all }
|
158
|
+
|
159
|
+
foo = Foo.new
|
160
|
+
foo.handle_error(err)
|
161
|
+
|
162
|
+
assert_equal [err], foo.env['gin.errors']
|
163
|
+
assert_equal [:default, :all], handlers
|
164
|
+
assert_equal 500, foo.status
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
def test_handle_error_with_status
|
169
|
+
Foo.error(Gin::NotFound){ "OOPS" }
|
170
|
+
foo = Foo.new
|
171
|
+
|
172
|
+
foo.status 200
|
173
|
+
foo.handle_error(Gin::NotFound.new)
|
174
|
+
assert_equal 404, foo.status
|
175
|
+
|
176
|
+
foo.status 302
|
177
|
+
foo.handle_error(Gin::NotFound.new)
|
178
|
+
assert_equal 404, foo.status
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
def test_handle_error_preset_status
|
183
|
+
Foo.error{ "OOPS" }
|
184
|
+
foo = Foo.new
|
185
|
+
|
186
|
+
foo.status(400)
|
187
|
+
foo.handle_error(ArgumentError.new)
|
188
|
+
assert_equal 400, foo.status
|
189
|
+
|
190
|
+
foo.status(302)
|
191
|
+
foo.handle_error(ArgumentError.new)
|
192
|
+
assert_equal 500, foo.status
|
193
|
+
|
194
|
+
foo.status(200)
|
195
|
+
foo.handle_error(ArgumentError.new)
|
196
|
+
assert_equal 500, foo.status
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
def test_handle_error_preset_status_with_status
|
201
|
+
Foo.error(Gin::NotFound){ "OOPS" }
|
202
|
+
foo = Foo.new
|
203
|
+
foo.status(400)
|
204
|
+
foo.handle_error(Gin::NotFound.new)
|
205
|
+
|
206
|
+
assert_equal 404, foo.status
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
def test_handle_error_missing
|
211
|
+
foo = Foo.new
|
212
|
+
err = ArgumentError.new
|
213
|
+
|
214
|
+
assert_raises ArgumentError do
|
215
|
+
foo.handle_error(err)
|
216
|
+
end
|
217
|
+
|
218
|
+
assert_equal [err], foo.env['gin.errors']
|
219
|
+
assert_equal 500, foo.status
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class FilterableTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
FILTER_CALLS = []
|
6
|
+
|
7
|
+
class AppCtrl
|
8
|
+
include Gin::Filterable
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :is_logged_in
|
12
|
+
end
|
13
|
+
|
14
|
+
self.is_logged_in = true
|
15
|
+
|
16
|
+
filter :logged_in do
|
17
|
+
FILTER_CALLS << :logged_in
|
18
|
+
self.class.is_logged_in
|
19
|
+
end
|
20
|
+
|
21
|
+
filter :find_device do
|
22
|
+
FILTER_CALLS << :find_device
|
23
|
+
"iPhone"
|
24
|
+
end
|
25
|
+
|
26
|
+
filter :log_action do
|
27
|
+
FILTER_CALLS << :log_action
|
28
|
+
"foo"
|
29
|
+
end
|
30
|
+
|
31
|
+
filter :set_login_cookie do
|
32
|
+
FILTER_CALLS << :set_login_cookie
|
33
|
+
"COOKIES"
|
34
|
+
end
|
35
|
+
|
36
|
+
filter :other_filter do
|
37
|
+
FILTER_CALLS << :other_filter
|
38
|
+
"other_filter"
|
39
|
+
end
|
40
|
+
|
41
|
+
before_filter :logged_in
|
42
|
+
before_filter :find_device
|
43
|
+
|
44
|
+
after_filter :log_action
|
45
|
+
after_filter :set_login_cookie, :other_filter, :except => :foo
|
46
|
+
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def __call_filters__ type, action #:nodoc:
|
51
|
+
filter(*__send__(:"#{type}_filters_for", action))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
class SessionCtrl < AppCtrl
|
57
|
+
class << self
|
58
|
+
attr_accessor :is_custom_thing
|
59
|
+
end
|
60
|
+
|
61
|
+
self.is_logged_in = true
|
62
|
+
self.is_custom_thing = true
|
63
|
+
|
64
|
+
filter :custom_thing do
|
65
|
+
FILTER_CALLS << :custom_thing
|
66
|
+
self.class.is_custom_thing
|
67
|
+
end
|
68
|
+
|
69
|
+
before_filter :custom_thing
|
70
|
+
|
71
|
+
skip_before_filter :find_device
|
72
|
+
skip_before_filter :logged_in, :only => [:create, :new]
|
73
|
+
skip_after_filter :set_login_cookie, :except => [:logout]
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
def setup
|
79
|
+
FILTER_CALLS.clear
|
80
|
+
@app_ctrl = AppCtrl.new
|
81
|
+
@session_ctrl = SessionCtrl.new
|
82
|
+
SessionCtrl.is_logged_in = true
|
83
|
+
SessionCtrl.is_custom_thing = true
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def test_filter_chain_inheritance
|
88
|
+
assert_equal [:logged_in, :find_device],
|
89
|
+
AppCtrl.before_filters[nil]
|
90
|
+
|
91
|
+
assert_equal [:logged_in, :custom_thing],
|
92
|
+
SessionCtrl.before_filters[nil]
|
93
|
+
|
94
|
+
assert_equal [:log_action, :set_login_cookie, :other_filter],
|
95
|
+
AppCtrl.after_filters[nil]
|
96
|
+
|
97
|
+
assert_equal [:log_action, :other_filter],
|
98
|
+
SessionCtrl.after_filters[nil]
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def test_call_filters
|
103
|
+
@app_ctrl.send(:__call_filters__, :before, :action)
|
104
|
+
assert_equal [:logged_in, :find_device], FILTER_CALLS
|
105
|
+
|
106
|
+
FILTER_CALLS.clear
|
107
|
+
@session_ctrl.send(:__call_filters__, :before, :action)
|
108
|
+
assert_equal [:logged_in, :custom_thing], FILTER_CALLS
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
def test_call_filters_with_restrictions
|
113
|
+
@app_ctrl.send(:__call_filters__, :before, :create)
|
114
|
+
assert_equal [:logged_in, :find_device], FILTER_CALLS
|
115
|
+
|
116
|
+
FILTER_CALLS.clear
|
117
|
+
@session_ctrl.send(:__call_filters__, :before, :create)
|
118
|
+
assert_equal [:custom_thing], FILTER_CALLS
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
def test_filter_calls
|
123
|
+
@session_ctrl.filter :logged_in, :custom_thing, :other_filter
|
124
|
+
assert_equal [:logged_in, :custom_thing, :other_filter], FILTER_CALLS
|
125
|
+
end
|
126
|
+
end
|
data/test/test_gin.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class GinTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_underscore
|
6
|
+
assert_equal "foo_bar", Gin.underscore("FooBar")
|
7
|
+
assert_equal "foo_bar", Gin.underscore("fooBar")
|
8
|
+
assert_equal "foo_bar", Gin.underscore("foo_Bar")
|
9
|
+
assert_equal "foo_http", Gin.underscore("fooHTTP")
|
10
|
+
assert_equal "foo_http_thing", Gin.underscore("fooHTTPThing")
|
11
|
+
assert_equal "foo/bar", Gin.underscore("Foo::Bar")
|
12
|
+
assert_equal "foo/http", Gin.underscore("Foo::HTTP")
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def test_build_query
|
17
|
+
hash = {a: "bob", b: [1,2.2,-3,{ba:"test"}], c:true, d:false}
|
18
|
+
expected = "a=bob&b[]=1&b[]=2.2&b[]=-3&b[][ba]=test&c=true&d=false"
|
19
|
+
assert_equal expected, Gin.build_query(hash)
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def test_build_query_non_hash
|
24
|
+
[[1,2,3], 1, 1.2, "str"].each do |obj|
|
25
|
+
assert_raises(ArgumentError, "#{obj.class} did not raise ArgumentError") do
|
26
|
+
Gin.build_query obj
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def test_find_loadpath
|
33
|
+
assert_equal __FILE__, Gin.find_loadpath("test/test_gin")
|
34
|
+
assert_equal __FILE__, Gin.find_loadpath("test/test_gin.rb")
|
35
|
+
assert_equal __FILE__, Gin.find_loadpath(__FILE__)
|
36
|
+
assert_nil Gin.find_loadpath("FUUUUU")
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def test_const_find
|
41
|
+
assert_equal Test::Unit, Gin.const_find("Test::Unit")
|
42
|
+
assert_equal Test::Unit, Gin.const_find("Unit", Test)
|
43
|
+
assert_raises(NameError){ Gin.const_find("Unit", Gin) }
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def test_app_trace
|
48
|
+
trace = [
|
49
|
+
Gin::LIB_DIR + "/thing",
|
50
|
+
"/path/to/app/thing",
|
51
|
+
Gin::LIB_DIR + "/gin/app.rb:123:in `dispatch'",
|
52
|
+
Gem.path[0] + "/foo",
|
53
|
+
"/stuff/to/ignore"
|
54
|
+
]
|
55
|
+
|
56
|
+
assert_equal [Gin::LIB_DIR + "/thing", "/path/to/app/thing"],
|
57
|
+
Gin.app_trace(trace)
|
58
|
+
end
|
59
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class RequestTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@env = {
|
7
|
+
'HTTP_HOST' => 'example.com',
|
8
|
+
'rack.input' => '',
|
9
|
+
'QUERY_STRING' => 'id=456&foo=bar&bar=5&bool=true',
|
10
|
+
'gin.path_query_hash' => {'id' => 123},
|
11
|
+
}
|
12
|
+
@req = Gin::Request.new @env
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def test_query_hash_as_param
|
17
|
+
assert_equal 123, @req.params['id']
|
18
|
+
assert_equal 'bar', @req.params['foo']
|
19
|
+
assert_equal 5, @req.params['bar']
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def test_params_symbol_accessible
|
24
|
+
[:id, :foo, :bar].each do |key|
|
25
|
+
assert @req.params[key]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def test_forwarded
|
31
|
+
assert !@req.forwarded?
|
32
|
+
@env["HTTP_X_FORWARDED_HOST"] = "example.com"
|
33
|
+
assert @req.forwarded?
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def test_ssl
|
38
|
+
assert !@req.ssl?
|
39
|
+
|
40
|
+
@env['HTTPS'] = 'on'
|
41
|
+
assert @req.ssl?
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def test_safe
|
46
|
+
@env['REQUEST_METHOD'] = 'POST'
|
47
|
+
assert !@req.safe?, "Verb POST should NOT be safe"
|
48
|
+
|
49
|
+
%w{GET HEAD OPTIONS TRACE}.each do |verb|
|
50
|
+
@env['REQUEST_METHOD'] = verb
|
51
|
+
assert @req.safe?, "Verb #{verb} should be safe"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def test_idempotent
|
57
|
+
@env['REQUEST_METHOD'] = 'POST'
|
58
|
+
assert !@req.idempotent?, "Verb POST should NOT be idempotent"
|
59
|
+
|
60
|
+
%w{GET HEAD OPTIONS TRACE PUT DELETE}.each do |verb|
|
61
|
+
@env['REQUEST_METHOD'] = verb
|
62
|
+
assert @req.idempotent?, "Verb #{verb} should be idempotent"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def test_process_params
|
68
|
+
assert_equal true, @req.send(:process_params, "true")
|
69
|
+
assert_equal false, @req.send(:process_params, "false")
|
70
|
+
assert_equal 1, @req.send(:process_params, "1")
|
71
|
+
assert_equal 1.1, @req.send(:process_params, "1.1")
|
72
|
+
assert_equal "not_true", @req.send(:process_params, "not_true")
|
73
|
+
|
74
|
+
ary = @req.send(:process_params, ["true", "1", "foo"])
|
75
|
+
assert_equal [true, 1, "foo"], ary
|
76
|
+
|
77
|
+
hash = @req.send(:process_params, {'key' => ["true", "1", "foo"]})
|
78
|
+
assert_equal [true, 1, "foo"], hash['key']
|
79
|
+
assert_equal hash['key'].object_id, hash[:key].object_id
|
80
|
+
end
|
81
|
+
end
|