rack-action 0.0.1
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/.gitignore +17 -0
- data/.rvmrc +1 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +37 -0
- data/Rakefile +11 -0
- data/config.ru +9 -0
- data/lib/rack-action.rb +1 -0
- data/lib/rack/action.rb +291 -0
- data/lib/rack/filters.rb +40 -0
- data/lib/rack_action.rb +1 -0
- data/rack-action.gemspec +18 -0
- data/test/rack/action_test.rb +192 -0
- data/test/rack_test.rb +35 -0
- metadata +79 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.9.3@rack-action --create
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Paul Barry
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Rack::Action
|
2
|
+
|
3
|
+
Rack action is a small, simple framework for generating Rack responses. A basic rack action looks like this:
|
4
|
+
|
5
|
+
require 'rack/action'
|
6
|
+
|
7
|
+
class MyAction < Rack::Action
|
8
|
+
def respond
|
9
|
+
"Hello, World!"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
run MyAction
|
14
|
+
|
15
|
+
See the docs for Rack::Action for more details
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
gem 'rack-action'
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle
|
26
|
+
|
27
|
+
Or install it yourself as:
|
28
|
+
|
29
|
+
$ gem install rack-action
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
1. Fork it
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
37
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/config.ru
ADDED
data/lib/rack-action.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rack/action'
|
data/lib/rack/action.rb
ADDED
@@ -0,0 +1,291 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'json'
|
3
|
+
require 'rack'
|
4
|
+
require 'rack/filters'
|
5
|
+
|
6
|
+
module Rack
|
7
|
+
# Rack::Action provides functionality to generate a Rack response.
|
8
|
+
# To use Rack::Action, you should subclass Rack::Action and provide
|
9
|
+
# your own implementation of {#respond}. The simplest Rack action is
|
10
|
+
# one that just returns a string from respond:
|
11
|
+
#
|
12
|
+
# require 'rack/action'
|
13
|
+
#
|
14
|
+
# class MyAction < Rack::Action
|
15
|
+
# def respond
|
16
|
+
# "Hello, World!"
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# run MyAction
|
21
|
+
#
|
22
|
+
# The class itself is a rack app, so the previous code example is a valid
|
23
|
+
# rackup file. Rack::Action is meant to be used with one action per
|
24
|
+
# page/endpoint in your application, so it is typically used in conjuction
|
25
|
+
# with something like Rack::Router, which would look something like this:
|
26
|
+
#
|
27
|
+
# require 'rack/action'
|
28
|
+
# require 'rack/router'
|
29
|
+
#
|
30
|
+
# class FooAction < Rack::Action
|
31
|
+
# def respond
|
32
|
+
# "foo"
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# class BarAction < Rack::Action
|
37
|
+
# def respond
|
38
|
+
# "bar"
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# router = Rack::Router.new do
|
43
|
+
# get "/foo" => FooAction
|
44
|
+
# get "/bar" => BarAction
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# run router
|
48
|
+
#
|
49
|
+
# Rack::Action makes an instance of Rack::Request and Rack::Response available
|
50
|
+
# which can be used to set headers, cookies, etc.
|
51
|
+
#
|
52
|
+
# class ArticleAction < Rack::Action
|
53
|
+
# def respond
|
54
|
+
# article = Article.find(params["id"])
|
55
|
+
# response['Content-Type'] = "text/xml"
|
56
|
+
# article.to_xml
|
57
|
+
# end
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# You can use before filters to do things before respond is called:
|
61
|
+
#
|
62
|
+
# class AccountAction < Rack::Action
|
63
|
+
# before_filter :load_current_user
|
64
|
+
#
|
65
|
+
# def load_current_user
|
66
|
+
# @current_user = User.find(params["id"])
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# def respond
|
70
|
+
# "Welcome Back, #{@current_user.name}"
|
71
|
+
# end
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# and you can of course share functionality across actions with inheritance:
|
75
|
+
#
|
76
|
+
# class ApplicationAction < Rack::Action
|
77
|
+
# before_filter :login_required
|
78
|
+
#
|
79
|
+
# def login_required
|
80
|
+
# redirect_to "/login" unless logged_in?
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# class PublicAction < ApplicationAction
|
85
|
+
# skip_before_filter :login_required
|
86
|
+
#
|
87
|
+
# def respond
|
88
|
+
# "Hello"
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# class PrivateAction < ApplicationAction
|
93
|
+
# def respond
|
94
|
+
# "It's A Secret To Everybody."
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# Before filters will execute in the order they are defined. If a before
|
99
|
+
# filter writes to the response, subsequent filters will not be executed
|
100
|
+
# and the respond method will not be executed. As long as no before filters
|
101
|
+
# write to the response, execution of subsequent filters and the respond
|
102
|
+
# method will be called.
|
103
|
+
class Action
|
104
|
+
extend Filters
|
105
|
+
|
106
|
+
# @private
|
107
|
+
RACK_ROUTE_PARAMS = 'route.route_params'.freeze
|
108
|
+
# @private
|
109
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
110
|
+
# @private
|
111
|
+
APPLICATION_JSON = 'application/json'.freeze
|
112
|
+
# @private
|
113
|
+
LOCATION = 'Location'.freeze
|
114
|
+
# @private
|
115
|
+
DEFAULT_RESPONSE = "Default Rack::Action Response"
|
116
|
+
|
117
|
+
# This implements the Rack interface
|
118
|
+
#
|
119
|
+
# @param [Hash] env The Rack environment
|
120
|
+
# @return [Rack::Response] A Rack response
|
121
|
+
def self.call(env)
|
122
|
+
new(env).call
|
123
|
+
end
|
124
|
+
|
125
|
+
attr_accessor :env, :request, :response, :params
|
126
|
+
|
127
|
+
def initialize(env)
|
128
|
+
@env = env
|
129
|
+
end
|
130
|
+
|
131
|
+
def request
|
132
|
+
@request ||= Rack::Request.new(env)
|
133
|
+
end
|
134
|
+
|
135
|
+
def response
|
136
|
+
@response ||= Rack::Response.new
|
137
|
+
end
|
138
|
+
|
139
|
+
def params
|
140
|
+
@params ||= request.params.merge(env[RACK_ROUTE_PARAMS] || {})
|
141
|
+
end
|
142
|
+
|
143
|
+
# This is the main method responsible for generating a Rack response.
|
144
|
+
# You typically won't need to override this method of call it directly.
|
145
|
+
# First this will run the before filters for this action.
|
146
|
+
# If none of the before filters generate a response, this will call
|
147
|
+
# {#respond} to generate a response.
|
148
|
+
# All after filters for this action are called once the response
|
149
|
+
# is genenated. Finally the response is returned.
|
150
|
+
#
|
151
|
+
# @return [Array<Numeric, Hash, #each>] A Rack response
|
152
|
+
def call
|
153
|
+
run_before_filters
|
154
|
+
run_respond
|
155
|
+
run_after_filters
|
156
|
+
finish_response
|
157
|
+
end
|
158
|
+
|
159
|
+
# This is the main method that you should override in your action.
|
160
|
+
# You can either write to the response during this method, or simply
|
161
|
+
# return a string, which will be written to the response if the
|
162
|
+
# response is still empty after this is called.
|
163
|
+
#
|
164
|
+
# @return [String] The Rack response or a String
|
165
|
+
def respond
|
166
|
+
DEFAULT_RESPONSE
|
167
|
+
end
|
168
|
+
|
169
|
+
# This is a convenience method that sets the Content-Type headers
|
170
|
+
# and writes the JSON String to the response.
|
171
|
+
#
|
172
|
+
# @param [Hash] data The data
|
173
|
+
# @return [String] The JSON
|
174
|
+
def json(data)
|
175
|
+
response[CONTENT_TYPE] = APPLICATION_JSON
|
176
|
+
response.write JSON.generate(data)
|
177
|
+
end
|
178
|
+
|
179
|
+
# This is a convenience method that sets the Content-Type headers
|
180
|
+
# and writes the pretty-formatted JSON String to the response.
|
181
|
+
#
|
182
|
+
# @param [Hash] data The data
|
183
|
+
# @return [String] The JSON
|
184
|
+
def pretty_json(data)
|
185
|
+
response[CONTENT_TYPE] = APPLICATION_JSON
|
186
|
+
response.write JSON.pretty_generate(data)
|
187
|
+
end
|
188
|
+
|
189
|
+
# This is a convenience method that forms an absolute URL based on the
|
190
|
+
# url parameter, which can be a relative or absolute URL, and then
|
191
|
+
# sets the headers and the body appropriately to do a 302 redirect.
|
192
|
+
#
|
193
|
+
# @see #absolute_url
|
194
|
+
# @return [String] The absolute url
|
195
|
+
def redirect_to(url, options={})
|
196
|
+
full_url = absolute_url(url, options)
|
197
|
+
response[LOCATION] = full_url
|
198
|
+
response.status = 302
|
199
|
+
response.write ''
|
200
|
+
full_url
|
201
|
+
end
|
202
|
+
|
203
|
+
# Generate an absolute url from the url. If the url is already
|
204
|
+
# an absolute url, this will return it unchanged.
|
205
|
+
#
|
206
|
+
# @param [String] url The URL
|
207
|
+
# @param [Hash] options The options to use to generate the absolute URL
|
208
|
+
# @option options [true, false] :https If https should be used,
|
209
|
+
# uses rack.url_scheme from the Rack env to determine the default
|
210
|
+
# @option options [String] :host The host to use,
|
211
|
+
# uses SERVER_NAME form the Rack env for the default
|
212
|
+
# @option options [String, Numeric] :port The port to use,
|
213
|
+
# users SERVER_PORT from the Rack env for the default
|
214
|
+
# @return [String] The absolute url
|
215
|
+
def absolute_url(url, options={})
|
216
|
+
URL.new(env, url, options).to_absolute
|
217
|
+
end
|
218
|
+
|
219
|
+
protected
|
220
|
+
def run_before_filters
|
221
|
+
self.class.before_filters.each do |filter|
|
222
|
+
send(filter)
|
223
|
+
return unless response.empty?
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def run_respond
|
228
|
+
return if !response.empty?
|
229
|
+
body = respond
|
230
|
+
if response.empty?
|
231
|
+
response.write body
|
232
|
+
else
|
233
|
+
body
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def run_after_filters
|
238
|
+
self.class.after_filters.each do |filter|
|
239
|
+
send(filter)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def finish_response
|
244
|
+
response.finish
|
245
|
+
end
|
246
|
+
|
247
|
+
# @private
|
248
|
+
class URL
|
249
|
+
HTTPS = 'https'.freeze
|
250
|
+
HTTP_PREFIX = 'http://'.freeze
|
251
|
+
HTTPS_PREFIX = 'https://'.freeze
|
252
|
+
ABSOLUTE_URL_REGEX = /\Ahttps?:\/\//
|
253
|
+
RACK_URL_SCHEME = 'rack.url_scheme'
|
254
|
+
SERVER_NAME = 'SERVER_NAME'.freeze
|
255
|
+
SERVER_PORT = 'SERVER_PORT'.freeze
|
256
|
+
DEFAULT_HTTP_PORT = 80
|
257
|
+
DEFAULT_HTTPS_PORT = 443
|
258
|
+
|
259
|
+
attr_accessor :env, :url, :https, :host, :port
|
260
|
+
|
261
|
+
def initialize(env, url, options={})
|
262
|
+
@env = env
|
263
|
+
@url = url.to_s
|
264
|
+
@options = options || {}
|
265
|
+
@https = options.fetch(:https, env[RACK_URL_SCHEME] == HTTPS)
|
266
|
+
@host = options.fetch(:host, env[SERVER_NAME])
|
267
|
+
@port = Integer(options.fetch(:port, env[SERVER_PORT]))
|
268
|
+
end
|
269
|
+
|
270
|
+
def to_absolute
|
271
|
+
if url =~ ABSOLUTE_URL_REGEX
|
272
|
+
url
|
273
|
+
else
|
274
|
+
absolute_url = [prefix]
|
275
|
+
absolute_url << (port == default_port ? host : "#{host}:#{port}")
|
276
|
+
absolute_url << url
|
277
|
+
::File.join(*absolute_url)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def prefix
|
282
|
+
https ? HTTPS_PREFIX : HTTP_PREFIX
|
283
|
+
end
|
284
|
+
|
285
|
+
def default_port
|
286
|
+
https ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
end
|
data/lib/rack/filters.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Rack
|
2
|
+
module Filters
|
3
|
+
def before_filters
|
4
|
+
@before_filters ||= if superclass.respond_to?(:before_filters)
|
5
|
+
superclass.before_filters.dup
|
6
|
+
else
|
7
|
+
[]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def before_filter(method)
|
12
|
+
unless before_filters.include?(method)
|
13
|
+
before_filters << method
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def skip_before_filter(method)
|
18
|
+
before_filters.delete(method)
|
19
|
+
end
|
20
|
+
|
21
|
+
def after_filters
|
22
|
+
@after_filters ||= if superclass.respond_to?(:after_filters)
|
23
|
+
superclass.after_filters.dup
|
24
|
+
else
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def after_filter(method)
|
30
|
+
unless after_filters.include?(method)
|
31
|
+
after_filters << method
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def skip_after_filter(method)
|
36
|
+
after_filters.delete(method)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
data/lib/rack_action.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rack/action'
|
data/rack-action.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.authors = ["Paul Barry"]
|
5
|
+
gem.email = ["mail@paulbarry.com"]
|
6
|
+
gem.description = %q{a small, simple framework for generating Rack responses}
|
7
|
+
gem.summary = %q{a small, simple framework for generating Rack responses}
|
8
|
+
gem.homepage = "http://github.com/pjb3/rack-action"
|
9
|
+
|
10
|
+
gem.files = `git ls-files`.split($\)
|
11
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
12
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
13
|
+
gem.name = "rack-action"
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
gem.version = "0.0.1"
|
16
|
+
|
17
|
+
gem.add_runtime_dependency "rack"
|
18
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
require 'rack_action'
|
2
|
+
require 'rack_test'
|
3
|
+
|
4
|
+
class Rack::ActionTest < RackTest
|
5
|
+
|
6
|
+
def test_default_respond
|
7
|
+
app = Class.new(Rack::Action)
|
8
|
+
|
9
|
+
response = get app, "/"
|
10
|
+
|
11
|
+
assert_equal 200, response.status
|
12
|
+
assert_equal Rack::Action::DEFAULT_RESPONSE.length, response.length
|
13
|
+
assert_equal 'text/html', response["Content-Type"]
|
14
|
+
assert_equal [Rack::Action::DEFAULT_RESPONSE], response.body
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_custom_respond
|
18
|
+
app = Class.new(Rack::Action) do
|
19
|
+
def respond
|
20
|
+
"bananas"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
response = get app, "/"
|
25
|
+
|
26
|
+
assert_equal 200, response.status
|
27
|
+
assert_equal 7, response.length
|
28
|
+
assert_equal 'text/html', response["Content-Type"]
|
29
|
+
assert_equal ['bananas'], response.body
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_json_respond
|
33
|
+
app = Class.new(Rack::Action) do
|
34
|
+
def respond
|
35
|
+
json :hello => "world"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
expected = %{{"hello":"world"}}
|
39
|
+
|
40
|
+
response = get app, "/"
|
41
|
+
|
42
|
+
assert_equal 200, response.status
|
43
|
+
assert_equal expected.length, response.length
|
44
|
+
assert_equal 'application/json', response["Content-Type"]
|
45
|
+
assert_equal [expected], response.body
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_pretty_json_respond
|
49
|
+
app = Class.new(Rack::Action) do
|
50
|
+
def respond
|
51
|
+
pretty_json :hello => "world"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
expected = %{{
|
55
|
+
"hello": "world"
|
56
|
+
}}
|
57
|
+
|
58
|
+
response = get app, "/"
|
59
|
+
|
60
|
+
assert_equal 200, response.status
|
61
|
+
assert_equal expected.length.to_s, response["Content-Length"]
|
62
|
+
assert_equal 'application/json', response["Content-Type"]
|
63
|
+
assert_equal [expected], response.body
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_before_filter_set_instance_variable
|
67
|
+
app = Class.new(Rack::Action) do
|
68
|
+
def respond
|
69
|
+
@message
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_message
|
73
|
+
@message = "Hello, World!"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
app.before_filter :set_message
|
77
|
+
|
78
|
+
response = get app, "/"
|
79
|
+
|
80
|
+
assert_equal 200, response.status
|
81
|
+
assert_equal ["Hello, World!"], response.body
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_before_filter_set_response
|
85
|
+
app = Class.new(Rack::Action) do
|
86
|
+
def respond
|
87
|
+
fail "respond should not be called if a before filter sets the response"
|
88
|
+
end
|
89
|
+
|
90
|
+
def set_response
|
91
|
+
response.write "test"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
app.before_filter :set_response
|
95
|
+
|
96
|
+
response = get app, "/"
|
97
|
+
|
98
|
+
assert_equal 200, response.status
|
99
|
+
assert_equal ["test"], response.body
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_redirect
|
103
|
+
app = Class.new(Rack::Action) do
|
104
|
+
def respond
|
105
|
+
fail "respond should not be called if a before filter sets the response"
|
106
|
+
end
|
107
|
+
|
108
|
+
def login_required
|
109
|
+
redirect_to "/login"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
app.before_filter :login_required
|
113
|
+
|
114
|
+
response = get app, "/"
|
115
|
+
|
116
|
+
assert_equal 302, response.status
|
117
|
+
assert_equal "http://example.com/login", response["Location"]
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_redirect_non_default_port
|
121
|
+
app = Class.new(Rack::Action) do
|
122
|
+
def respond
|
123
|
+
fail "respond should not be called if a before filter sets the response"
|
124
|
+
end
|
125
|
+
|
126
|
+
def login_required
|
127
|
+
redirect_to "/login"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
app.before_filter :login_required
|
131
|
+
|
132
|
+
response = get app, "/", "SERVER_PORT" => "3000"
|
133
|
+
|
134
|
+
assert_equal 302, response.status
|
135
|
+
assert_equal "http://example.com:3000/login", response["Location"]
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_redirect_non_default_port_option
|
139
|
+
app = Class.new(Rack::Action) do
|
140
|
+
def respond
|
141
|
+
fail "respond should not be called if a before filter sets the response"
|
142
|
+
end
|
143
|
+
|
144
|
+
def login_required
|
145
|
+
redirect_to "/login", :port => 3000
|
146
|
+
end
|
147
|
+
end
|
148
|
+
app.before_filter :login_required
|
149
|
+
|
150
|
+
response = get app, "/"
|
151
|
+
|
152
|
+
assert_equal 302, response.status
|
153
|
+
assert_equal "http://example.com:3000/login", response["Location"]
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_secure_redirect
|
157
|
+
app = Class.new(Rack::Action) do
|
158
|
+
def respond
|
159
|
+
fail "respond should not be called if a before filter sets the response"
|
160
|
+
end
|
161
|
+
|
162
|
+
def login_required
|
163
|
+
redirect_to "/login"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
app.before_filter :login_required
|
167
|
+
|
168
|
+
response = get app, "/", "SERVER_PORT" => "443", "rack.url_scheme" => "https"
|
169
|
+
|
170
|
+
assert_equal 302, response.status
|
171
|
+
assert_equal "https://example.com/login", response["Location"]
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_redirect_absolute_url
|
175
|
+
app = Class.new(Rack::Action) do
|
176
|
+
def respond
|
177
|
+
fail "respond should not be called if a before filter sets the response"
|
178
|
+
end
|
179
|
+
|
180
|
+
def login_required
|
181
|
+
redirect_to "http://test.com/login"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
app.before_filter :login_required
|
185
|
+
|
186
|
+
response = get app, "/"
|
187
|
+
|
188
|
+
assert_equal 302, response.status
|
189
|
+
assert_equal "http://test.com/login", response["Location"]
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
data/test/rack_test.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
class RackTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
DEFAULT_HOST = "example.com".freeze
|
7
|
+
DEFAULT_PORT = 80
|
8
|
+
|
9
|
+
DEFAULT_ENV = {
|
10
|
+
"SCRIPT_NAME" => "",
|
11
|
+
"QUERY_STRING" => "",
|
12
|
+
"SERVER_NAME" => DEFAULT_HOST,
|
13
|
+
"SERVER_PORT" => DEFAULT_PORT.to_s,
|
14
|
+
"HTTP_HOST" => DEFAULT_HOST,
|
15
|
+
"HTTP_ACCEPT" => "*/*",
|
16
|
+
"rack.input" => StringIO.new,
|
17
|
+
"rack.url_scheme" => "http"
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
def default_env
|
21
|
+
DEFAULT_ENV
|
22
|
+
end
|
23
|
+
|
24
|
+
def request(app, method, path, env={})
|
25
|
+
resp = app.call(DEFAULT_ENV.merge({
|
26
|
+
"REQUEST_METHOD" => method.to_s.upcase,
|
27
|
+
"PATH_INFO" => path
|
28
|
+
}.merge(env)))
|
29
|
+
resp.is_a?(Rack::Response) ? resp : Rack::Response.new(resp[2], resp[0], resp[1])
|
30
|
+
end
|
31
|
+
|
32
|
+
def get(app, path, env={})
|
33
|
+
request(app, :get, path, env)
|
34
|
+
end
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-action
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Paul Barry
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rack
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: a small, simple framework for generating Rack responses
|
31
|
+
email:
|
32
|
+
- mail@paulbarry.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- .rvmrc
|
39
|
+
- .yardopts
|
40
|
+
- Gemfile
|
41
|
+
- LICENSE
|
42
|
+
- README.md
|
43
|
+
- Rakefile
|
44
|
+
- config.ru
|
45
|
+
- lib/rack-action.rb
|
46
|
+
- lib/rack/action.rb
|
47
|
+
- lib/rack/filters.rb
|
48
|
+
- lib/rack_action.rb
|
49
|
+
- rack-action.gemspec
|
50
|
+
- test/rack/action_test.rb
|
51
|
+
- test/rack_test.rb
|
52
|
+
homepage: http://github.com/pjb3/rack-action
|
53
|
+
licenses: []
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 1.8.23
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: a small, simple framework for generating Rack responses
|
76
|
+
test_files:
|
77
|
+
- test/rack/action_test.rb
|
78
|
+
- test/rack_test.rb
|
79
|
+
has_rdoc:
|