rocketio 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -5
- data/.pryrc +2 -0
- data/.travis.yml +3 -0
- data/README.md +22 -5
- data/Rakefile +7 -1
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/rocketio.rb +131 -3
- data/lib/rocketio/application.rb +31 -0
- data/lib/rocketio/controller.rb +288 -0
- data/lib/rocketio/controller/authentication.rb +141 -0
- data/lib/rocketio/controller/authorization.rb +53 -0
- data/lib/rocketio/controller/cookies.rb +59 -0
- data/lib/rocketio/controller/error_handlers.rb +89 -0
- data/lib/rocketio/controller/filters.rb +119 -0
- data/lib/rocketio/controller/flash.rb +21 -0
- data/lib/rocketio/controller/helpers.rb +438 -0
- data/lib/rocketio/controller/middleware.rb +32 -0
- data/lib/rocketio/controller/render.rb +148 -0
- data/lib/rocketio/controller/render/engine.rb +76 -0
- data/lib/rocketio/controller/render/layout.rb +27 -0
- data/lib/rocketio/controller/render/layouts.rb +85 -0
- data/lib/rocketio/controller/render/templates.rb +83 -0
- data/lib/rocketio/controller/request.rb +115 -0
- data/lib/rocketio/controller/response.rb +84 -0
- data/lib/rocketio/controller/sessions.rb +64 -0
- data/lib/rocketio/controller/token_auth.rb +118 -0
- data/lib/rocketio/controller/websocket.rb +21 -0
- data/lib/rocketio/error_templates/404.html +3 -0
- data/lib/rocketio/error_templates/409.html +7 -0
- data/lib/rocketio/error_templates/500.html +3 -0
- data/lib/rocketio/error_templates/501.html +6 -0
- data/lib/rocketio/error_templates/layout.html +1 -0
- data/lib/rocketio/exceptions.rb +4 -0
- data/lib/rocketio/router.rb +65 -0
- data/lib/rocketio/util.rb +122 -0
- data/lib/rocketio/version.rb +2 -2
- data/rocketio.gemspec +21 -17
- data/test/aliases_test.rb +54 -0
- data/test/authentication_test.rb +307 -0
- data/test/authorization_test.rb +91 -0
- data/test/cache_control_test.rb +268 -0
- data/test/content_type_test.rb +124 -0
- data/test/cookies_test.rb +49 -0
- data/test/error_handlers_test.rb +125 -0
- data/test/etag_test.rb +445 -0
- data/test/filters_test.rb +177 -0
- data/test/halt_test.rb +73 -0
- data/test/helpers_test.rb +171 -0
- data/test/middleware_test.rb +57 -0
- data/test/redirect_test.rb +135 -0
- data/test/render/engine_test.rb +71 -0
- data/test/render/get.erb +1 -0
- data/test/render/items.erb +1 -0
- data/test/render/layout.erb +1 -0
- data/test/render/layout_test.rb +104 -0
- data/test/render/layouts/master.erb +1 -0
- data/test/render/layouts_test.rb +145 -0
- data/test/render/master.erb +1 -0
- data/test/render/post.erb +1 -0
- data/test/render/put.erb +1 -0
- data/test/render/render_test.rb +101 -0
- data/test/render/setup.rb +14 -0
- data/test/render/templates/a/get.erb +1 -0
- data/test/render/templates/master.erb +1 -0
- data/test/render/templates_test.rb +146 -0
- data/test/request_test.rb +105 -0
- data/test/response_test.rb +119 -0
- data/test/routes_test.rb +70 -0
- data/test/sendfile_test.rb +209 -0
- data/test/sessions_test.rb +176 -0
- data/test/setup.rb +59 -0
- metadata +144 -9
- data/LICENSE.txt +0 -22
data/test/routes_test.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'setup'
|
2
|
+
|
3
|
+
class A < RocketIO::Controller
|
4
|
+
def get(x); [:a, x]*':' end
|
5
|
+
def post; url end
|
6
|
+
|
7
|
+
class B <self
|
8
|
+
def get(x = nil); [:b, x]*':' end
|
9
|
+
|
10
|
+
class C < self
|
11
|
+
def get(x = nil); [:c, x]*':' end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class D < RocketIO::Controller
|
17
|
+
end
|
18
|
+
|
19
|
+
spec :Routes do
|
20
|
+
test 'paths' do
|
21
|
+
assert(A.url) == '/a'
|
22
|
+
assert(A::B.url) == '/a/b'
|
23
|
+
assert(A::B::C.url) == '/a/b/c'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'overrides inherited REST methods' do
|
27
|
+
app(A)
|
28
|
+
post
|
29
|
+
assert(last_response).is_ok
|
30
|
+
|
31
|
+
app(D)
|
32
|
+
post
|
33
|
+
assert(last_response).is_unimplemented
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'inherits REST methods from supercontroller' do
|
37
|
+
app(A::B)
|
38
|
+
get
|
39
|
+
assert(last_response).is_ok_with_body 'b:'
|
40
|
+
|
41
|
+
post
|
42
|
+
assert(last_response).is_ok_with_body '/a/b'
|
43
|
+
end
|
44
|
+
|
45
|
+
context :router do
|
46
|
+
before {app mock_app(A, A::B, A::B::C)}
|
47
|
+
|
48
|
+
test '"a" does not serve /a/b' do
|
49
|
+
get '/a/x'
|
50
|
+
assert(last_response).is_ok_with_body 'a:x'
|
51
|
+
|
52
|
+
get '/a/b'
|
53
|
+
assert(last_response).is_ok_with_body 'b:'
|
54
|
+
|
55
|
+
get '/a/b/x'
|
56
|
+
assert(last_response).is_ok_with_body 'b:x'
|
57
|
+
end
|
58
|
+
|
59
|
+
test '"b" does not serve /a/b/c' do
|
60
|
+
get '/a/b'
|
61
|
+
assert(last_response).is_ok_with_body 'b:'
|
62
|
+
|
63
|
+
get '/a/b/c'
|
64
|
+
assert(last_response).is_ok_with_body 'c:'
|
65
|
+
|
66
|
+
get '/a/b/c/x'
|
67
|
+
assert(last_response).is_ok_with_body 'c:x'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
# Copyright (c) 2007, 2008, 2009 Blake Mizerany
|
2
|
+
# Copyright (c) 2010, 2011, 2012, 2013, 2014 Konstantin Haase
|
3
|
+
# Copyright (c) 2015 Slee Woo
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person
|
6
|
+
# obtaining a copy of this software and associated documentation
|
7
|
+
# files (the "Software"), to deal in the Software without
|
8
|
+
# restriction, including without limitation the rights to use,
|
9
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the
|
11
|
+
# Software is furnished to do so, subject to the following
|
12
|
+
# conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
19
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
21
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
require 'setup'
|
27
|
+
|
28
|
+
spec 'attachment' do
|
29
|
+
|
30
|
+
def attachment_app(filename=nil)
|
31
|
+
app mock_controller('/attachment') {
|
32
|
+
define_method :get do
|
33
|
+
attachment filename
|
34
|
+
response.write("<html></html>")
|
35
|
+
end
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'sets the Content-Type response header' do
|
40
|
+
attachment_app('test.xml')
|
41
|
+
get '/attachment'
|
42
|
+
assert(last_response['Content-Type']) == 'application/xml'
|
43
|
+
assert(last_response.body) == '<html></html>'
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'sets the Content-Type response header without extname' do
|
47
|
+
attachment_app('test')
|
48
|
+
get '/attachment'
|
49
|
+
assert(last_response['Content-Type']) == 'text/html'
|
50
|
+
assert(last_response.body) == '<html></html>'
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'sets the Content-Type response header with extname' do
|
54
|
+
app mock_controller('/attachment') {
|
55
|
+
def get
|
56
|
+
content_type :atom
|
57
|
+
attachment 'test.xml'
|
58
|
+
response.write("<html></html>")
|
59
|
+
end
|
60
|
+
}
|
61
|
+
|
62
|
+
get '/attachment'
|
63
|
+
assert(last_response['Content-Type']) == 'application/atom+xml'
|
64
|
+
assert(last_response.body) == '<html></html>'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
spec 'send_file' do
|
69
|
+
before do
|
70
|
+
@file = File.dirname(__FILE__) + '/file.txt'
|
71
|
+
File.open(@file, 'wb') { |io| io.write('Hello World') }
|
72
|
+
end
|
73
|
+
|
74
|
+
after do
|
75
|
+
File.unlink @file
|
76
|
+
@file = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def send_file_app(opts={})
|
80
|
+
path = @file
|
81
|
+
app mock_controller('/file.txt') {
|
82
|
+
define_method(:get) {send_file(path, opts)}
|
83
|
+
}
|
84
|
+
get('/file.txt')
|
85
|
+
end
|
86
|
+
|
87
|
+
it "sends the contents of the file" do
|
88
|
+
send_file_app
|
89
|
+
assert(last_response).ok?
|
90
|
+
assert(last_response.body) == File.read(@file)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'sets the Content-Type response header if a mime-type can be located' do
|
94
|
+
send_file_app
|
95
|
+
assert(last_response['Content-Type']) == 'text/plain'
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'sets the Content-Type response header if type option is set to a file extension' do
|
99
|
+
send_file_app :type => 'html'
|
100
|
+
assert(last_response['Content-Type']) == 'text/html'
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'sets the Content-Type response header if type option is set to a mime type' do
|
104
|
+
send_file_app :type => 'application/octet-stream'
|
105
|
+
assert(last_response['Content-Type']) == 'application/octet-stream'
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'sets the Content-Length response header' do
|
109
|
+
send_file_app
|
110
|
+
assert(last_response['Content-Length']) == File.read(@file).length.to_s
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'sets the Last-Modified response header' do
|
114
|
+
send_file_app
|
115
|
+
assert(last_response['Last-Modified']) == File.mtime(@file).httpdate
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'allows passing in a different Last-Modified response header with :last_modified' do
|
119
|
+
time = Time.now
|
120
|
+
send_file_app :last_modified => time
|
121
|
+
assert(last_response['Last-Modified']) == time.httpdate
|
122
|
+
end
|
123
|
+
|
124
|
+
it "returns a 404 when not found" do
|
125
|
+
app mock_controller {
|
126
|
+
def get; send_file 'this-file-does-not-exist.txt'; end
|
127
|
+
}
|
128
|
+
get
|
129
|
+
assert(last_response).not_found?
|
130
|
+
end
|
131
|
+
|
132
|
+
it "does not set the Content-Disposition header by default" do
|
133
|
+
send_file_app
|
134
|
+
assert(last_response['Content-Disposition']).nil?
|
135
|
+
end
|
136
|
+
|
137
|
+
it "sets the Content-Disposition header when :disposition set to 'attachment'" do
|
138
|
+
send_file_app :disposition => 'attachment'
|
139
|
+
assert(last_response['Content-Disposition']) == 'attachment; filename="file.txt"'
|
140
|
+
end
|
141
|
+
|
142
|
+
it "does not set add a file name if filename is false" do
|
143
|
+
send_file_app :disposition => 'inline', :filename => false
|
144
|
+
assert(last_response['Content-Disposition']) == 'inline'
|
145
|
+
end
|
146
|
+
|
147
|
+
it "sets the Content-Disposition header when :disposition set to 'inline'" do
|
148
|
+
send_file_app :disposition => 'inline'
|
149
|
+
assert(last_response['Content-Disposition']) == 'inline; filename="file.txt"'
|
150
|
+
end
|
151
|
+
|
152
|
+
it "sets the Content-Disposition header when :filename provided" do
|
153
|
+
send_file_app :filename => 'foo.txt'
|
154
|
+
assert(last_response['Content-Disposition']) == 'attachment; filename="foo.txt"'
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'allows setting a custom status code' do
|
158
|
+
send_file_app :status => 201
|
159
|
+
assert(last_response.status) == 201
|
160
|
+
end
|
161
|
+
|
162
|
+
it "is able to send files with unknown mime type" do
|
163
|
+
file = File.dirname(__FILE__) + '/file.foobar'
|
164
|
+
File.open(file, 'wb') { |io| io.write('Hello World') }
|
165
|
+
|
166
|
+
app mock_controller('/file.txt') {
|
167
|
+
define_method(:get) {send_file(file)}
|
168
|
+
}
|
169
|
+
get('/file.txt')
|
170
|
+
File.unlink(file)
|
171
|
+
|
172
|
+
assert(last_response['Content-Type']) == 'application/octet-stream'
|
173
|
+
end
|
174
|
+
|
175
|
+
it "does not override Content-Type if already set and no explicit type is given" do
|
176
|
+
path = @file
|
177
|
+
app mock_controller {
|
178
|
+
define_method :get do
|
179
|
+
content_type :png
|
180
|
+
send_file path
|
181
|
+
end
|
182
|
+
}
|
183
|
+
get
|
184
|
+
assert(last_response['Content-Type']) == 'image/png'
|
185
|
+
end
|
186
|
+
|
187
|
+
it "does override Content-Type even if already set, if explicit type is given" do
|
188
|
+
path = @file
|
189
|
+
app mock_controller {
|
190
|
+
define_method :get do
|
191
|
+
content_type :png
|
192
|
+
send_file path, :type => :gif
|
193
|
+
end
|
194
|
+
}
|
195
|
+
get
|
196
|
+
assert(last_response['Content-Type']) == 'image/gif'
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'can have :status option as a string' do
|
200
|
+
path = @file
|
201
|
+
app mock_controller {
|
202
|
+
define_method :post do
|
203
|
+
send_file path, :status => '422'
|
204
|
+
end
|
205
|
+
}
|
206
|
+
post
|
207
|
+
assert(last_response.status) == 422
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# Copyright (c) 2007, 2008, 2009 Blake Mizerany
|
2
|
+
# Copyright (c) 2010, 2011, 2012, 2013, 2014 Konstantin Haase
|
3
|
+
# Copyright (c) 2015 Slee Woo
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person
|
6
|
+
# obtaining a copy of this software and associated documentation
|
7
|
+
# files (the "Software"), to deal in the Software without
|
8
|
+
# restriction, including without limitation the rights to use,
|
9
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the
|
11
|
+
# Software is furnished to do so, subject to the following
|
12
|
+
# conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
19
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
21
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
require 'setup'
|
27
|
+
|
28
|
+
spec :Sessions do
|
29
|
+
before do
|
30
|
+
env.clear
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'inherits sessions setup from superclass' do
|
34
|
+
a = mock_controller {
|
35
|
+
sessions :cookies
|
36
|
+
}
|
37
|
+
b = mock_controller(:b, a) {
|
38
|
+
def get(y); session[:x] = y end
|
39
|
+
def post; session[:x] end
|
40
|
+
}
|
41
|
+
app mock_app(a, b)
|
42
|
+
get :b, :y
|
43
|
+
post :b
|
44
|
+
assert(last_response.body) == 'y'
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'explicitly inherits sessions setup' do
|
48
|
+
a = mock_controller {
|
49
|
+
sessions :cookies
|
50
|
+
}
|
51
|
+
b = mock_controller(:b) {
|
52
|
+
inherit :sessions, from: a
|
53
|
+
def get(y); session[:x] = y end
|
54
|
+
def post; session[:x] end
|
55
|
+
}
|
56
|
+
app mock_app(a, b)
|
57
|
+
get :b, :y
|
58
|
+
post :b
|
59
|
+
assert(last_response.body) == 'y'
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'directly overrides sessions setup inherited from superclass' do
|
63
|
+
a = mock_controller {
|
64
|
+
sessions :cookies
|
65
|
+
}
|
66
|
+
b = mock_controller(:b, a) {
|
67
|
+
sessions nil
|
68
|
+
def get(y); session[:x] = y end
|
69
|
+
def post; session[:x] end
|
70
|
+
}
|
71
|
+
app mock_app(a, b)
|
72
|
+
get :b, :y
|
73
|
+
post :b
|
74
|
+
assert(last_response.body) == ''
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'uses `inherit` to override sessions setup inherited from superclass' do
|
78
|
+
a = mock_controller {
|
79
|
+
sessions :cookies
|
80
|
+
}
|
81
|
+
b = mock_controller(:b, a) {
|
82
|
+
sessions false
|
83
|
+
def get(y); session[:x] = y end
|
84
|
+
def post; session[:x] end
|
85
|
+
}
|
86
|
+
c = mock_controller(a) {
|
87
|
+
inherit :sessions, from: b
|
88
|
+
def get(y); session[:x] = y end
|
89
|
+
def post; session[:x] end
|
90
|
+
}
|
91
|
+
app mock_app(a, b)
|
92
|
+
get :b, :y
|
93
|
+
post :b
|
94
|
+
assert(last_response.body) == ''
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'uses the existing rack.session' do
|
98
|
+
app mock_app(mock_controller {
|
99
|
+
sessions :cookies
|
100
|
+
define_method(:get) {session[:foo]}
|
101
|
+
})
|
102
|
+
|
103
|
+
env['rack.session'] = { :foo => 'bar' }
|
104
|
+
get
|
105
|
+
assert(last_response.body) == 'bar'
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'creates a new session when none provided' do
|
109
|
+
a = mock_controller {
|
110
|
+
sessions :cookies, secret: 'svss'
|
111
|
+
|
112
|
+
define_method :get do
|
113
|
+
fail('session[:foo] already set') unless session[:foo].nil?
|
114
|
+
session[:foo] = 'bar'
|
115
|
+
redirect '/hi'
|
116
|
+
end
|
117
|
+
}
|
118
|
+
b = mock_controller('hi') {
|
119
|
+
sessions :cookies, secret: 'svss'
|
120
|
+
define_method(:get) {"hi #{session[:foo]}"}
|
121
|
+
}
|
122
|
+
app mock_app(a, b)
|
123
|
+
|
124
|
+
get
|
125
|
+
follow_redirect!
|
126
|
+
assert(last_response.body) == 'hi bar'
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'inserts session middleware' do
|
130
|
+
app mock_app(mock_controller {
|
131
|
+
sessions :cookies
|
132
|
+
|
133
|
+
define_method :get do
|
134
|
+
unless env['rack.session'] && env['rack.session.options']
|
135
|
+
fail('session middleware not inserted')
|
136
|
+
end
|
137
|
+
'ok'
|
138
|
+
end
|
139
|
+
})
|
140
|
+
|
141
|
+
get
|
142
|
+
assert(last_response.body) == 'ok'
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'sets a default session secret' do
|
146
|
+
app mock_app(mock_controller {
|
147
|
+
sessions :cookies
|
148
|
+
|
149
|
+
define_method :get do
|
150
|
+
unless env['rack.session.options'][:secret]
|
151
|
+
fail('default session secret not set')
|
152
|
+
end
|
153
|
+
'ok'
|
154
|
+
end
|
155
|
+
})
|
156
|
+
|
157
|
+
get
|
158
|
+
assert(last_response.body) == 'ok'
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'accepts an options hash' do
|
162
|
+
app mock_app(mock_controller {
|
163
|
+
sessions :cookies, secret: 'svss'
|
164
|
+
|
165
|
+
define_method :get do
|
166
|
+
unless env['rack.session.options'][:secret]
|
167
|
+
fail('looks like option hash not set/used')
|
168
|
+
end
|
169
|
+
'ok'
|
170
|
+
end
|
171
|
+
})
|
172
|
+
|
173
|
+
get
|
174
|
+
assert(last_response.body) == 'ok'
|
175
|
+
end
|
176
|
+
end
|