halorgium-actionpack 3.0.pre
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/CHANGELOG +5179 -0
- data/MIT-LICENSE +21 -0
- data/README +409 -0
- data/lib/abstract_controller.rb +16 -0
- data/lib/abstract_controller/base.rb +158 -0
- data/lib/abstract_controller/callbacks.rb +113 -0
- data/lib/abstract_controller/exceptions.rb +12 -0
- data/lib/abstract_controller/helpers.rb +151 -0
- data/lib/abstract_controller/layouts.rb +250 -0
- data/lib/abstract_controller/localized_cache.rb +49 -0
- data/lib/abstract_controller/logger.rb +61 -0
- data/lib/abstract_controller/rendering_controller.rb +188 -0
- data/lib/action_controller.rb +72 -0
- data/lib/action_controller/base.rb +168 -0
- data/lib/action_controller/caching.rb +80 -0
- data/lib/action_controller/caching/actions.rb +163 -0
- data/lib/action_controller/caching/fragments.rb +116 -0
- data/lib/action_controller/caching/pages.rb +154 -0
- data/lib/action_controller/caching/sweeping.rb +97 -0
- data/lib/action_controller/deprecated.rb +4 -0
- data/lib/action_controller/deprecated/integration_test.rb +2 -0
- data/lib/action_controller/deprecated/performance_test.rb +1 -0
- data/lib/action_controller/dispatch/dispatcher.rb +57 -0
- data/lib/action_controller/metal.rb +129 -0
- data/lib/action_controller/metal/benchmarking.rb +73 -0
- data/lib/action_controller/metal/compatibility.rb +145 -0
- data/lib/action_controller/metal/conditional_get.rb +86 -0
- data/lib/action_controller/metal/configuration.rb +28 -0
- data/lib/action_controller/metal/cookies.rb +105 -0
- data/lib/action_controller/metal/exceptions.rb +55 -0
- data/lib/action_controller/metal/filter_parameter_logging.rb +77 -0
- data/lib/action_controller/metal/flash.rb +162 -0
- data/lib/action_controller/metal/head.rb +27 -0
- data/lib/action_controller/metal/helpers.rb +115 -0
- data/lib/action_controller/metal/hide_actions.rb +47 -0
- data/lib/action_controller/metal/http_authentication.rb +312 -0
- data/lib/action_controller/metal/layouts.rb +171 -0
- data/lib/action_controller/metal/mime_responds.rb +317 -0
- data/lib/action_controller/metal/rack_convenience.rb +27 -0
- data/lib/action_controller/metal/redirector.rb +22 -0
- data/lib/action_controller/metal/render_options.rb +103 -0
- data/lib/action_controller/metal/rendering_controller.rb +57 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +108 -0
- data/lib/action_controller/metal/rescuable.rb +13 -0
- data/lib/action_controller/metal/responder.rb +200 -0
- data/lib/action_controller/metal/session.rb +15 -0
- data/lib/action_controller/metal/session_management.rb +45 -0
- data/lib/action_controller/metal/streaming.rb +188 -0
- data/lib/action_controller/metal/testing.rb +39 -0
- data/lib/action_controller/metal/url_for.rb +41 -0
- data/lib/action_controller/metal/verification.rb +130 -0
- data/lib/action_controller/middleware.rb +38 -0
- data/lib/action_controller/notifications.rb +10 -0
- data/lib/action_controller/polymorphic_routes.rb +183 -0
- data/lib/action_controller/record_identifier.rb +91 -0
- data/lib/action_controller/testing/process.rb +111 -0
- data/lib/action_controller/testing/test_case.rb +345 -0
- data/lib/action_controller/translation.rb +13 -0
- data/lib/action_controller/url_rewriter.rb +204 -0
- data/lib/action_controller/vendor/html-scanner.rb +16 -0
- data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
- data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +176 -0
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
- data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
- data/lib/action_dispatch.rb +70 -0
- data/lib/action_dispatch/http/headers.rb +33 -0
- data/lib/action_dispatch/http/mime_type.rb +231 -0
- data/lib/action_dispatch/http/mime_types.rb +23 -0
- data/lib/action_dispatch/http/request.rb +539 -0
- data/lib/action_dispatch/http/response.rb +290 -0
- data/lib/action_dispatch/http/status_codes.rb +42 -0
- data/lib/action_dispatch/http/utils.rb +20 -0
- data/lib/action_dispatch/middleware/callbacks.rb +50 -0
- data/lib/action_dispatch/middleware/params_parser.rb +79 -0
- data/lib/action_dispatch/middleware/rescue.rb +26 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +208 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +235 -0
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +47 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +143 -0
- data/lib/action_dispatch/middleware/stack.rb +116 -0
- data/lib/action_dispatch/middleware/static.rb +44 -0
- data/lib/action_dispatch/middleware/string_coercion.rb +29 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +26 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +10 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +29 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +2 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +10 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +21 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +2 -0
- data/lib/action_dispatch/routing.rb +381 -0
- data/lib/action_dispatch/routing/deprecated_mapper.rb +878 -0
- data/lib/action_dispatch/routing/mapper.rb +327 -0
- data/lib/action_dispatch/routing/route.rb +49 -0
- data/lib/action_dispatch/routing/route_set.rb +497 -0
- data/lib/action_dispatch/testing/assertions.rb +8 -0
- data/lib/action_dispatch/testing/assertions/dom.rb +35 -0
- data/lib/action_dispatch/testing/assertions/model.rb +19 -0
- data/lib/action_dispatch/testing/assertions/response.rb +145 -0
- data/lib/action_dispatch/testing/assertions/routing.rb +144 -0
- data/lib/action_dispatch/testing/assertions/selector.rb +639 -0
- data/lib/action_dispatch/testing/assertions/tag.rb +123 -0
- data/lib/action_dispatch/testing/integration.rb +504 -0
- data/lib/action_dispatch/testing/performance_test.rb +15 -0
- data/lib/action_dispatch/testing/test_request.rb +83 -0
- data/lib/action_dispatch/testing/test_response.rb +131 -0
- data/lib/action_pack.rb +24 -0
- data/lib/action_pack/version.rb +9 -0
- data/lib/action_view.rb +58 -0
- data/lib/action_view/base.rb +308 -0
- data/lib/action_view/context.rb +44 -0
- data/lib/action_view/erb/util.rb +48 -0
- data/lib/action_view/helpers.rb +62 -0
- data/lib/action_view/helpers/active_model_helper.rb +306 -0
- data/lib/action_view/helpers/ajax_helper.rb +68 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +830 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
- data/lib/action_view/helpers/cache_helper.rb +39 -0
- data/lib/action_view/helpers/capture_helper.rb +168 -0
- data/lib/action_view/helpers/date_helper.rb +988 -0
- data/lib/action_view/helpers/debug_helper.rb +38 -0
- data/lib/action_view/helpers/form_helper.rb +1102 -0
- data/lib/action_view/helpers/form_options_helper.rb +600 -0
- data/lib/action_view/helpers/form_tag_helper.rb +495 -0
- data/lib/action_view/helpers/javascript_helper.rb +208 -0
- data/lib/action_view/helpers/number_helper.rb +311 -0
- data/lib/action_view/helpers/prototype_helper.rb +1309 -0
- data/lib/action_view/helpers/raw_output_helper.rb +9 -0
- data/lib/action_view/helpers/record_identification_helper.rb +20 -0
- data/lib/action_view/helpers/record_tag_helper.rb +58 -0
- data/lib/action_view/helpers/sanitize_helper.rb +259 -0
- data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
- data/lib/action_view/helpers/tag_helper.rb +151 -0
- data/lib/action_view/helpers/text_helper.rb +594 -0
- data/lib/action_view/helpers/translation_helper.rb +39 -0
- data/lib/action_view/helpers/url_helper.rb +639 -0
- data/lib/action_view/locale/en.yml +117 -0
- data/lib/action_view/paths.rb +80 -0
- data/lib/action_view/render/partials.rb +342 -0
- data/lib/action_view/render/rendering.rb +134 -0
- data/lib/action_view/safe_buffer.rb +28 -0
- data/lib/action_view/template/error.rb +101 -0
- data/lib/action_view/template/handler.rb +36 -0
- data/lib/action_view/template/handlers.rb +52 -0
- data/lib/action_view/template/handlers/builder.rb +17 -0
- data/lib/action_view/template/handlers/erb.rb +53 -0
- data/lib/action_view/template/handlers/rjs.rb +18 -0
- data/lib/action_view/template/resolver.rb +165 -0
- data/lib/action_view/template/template.rb +131 -0
- data/lib/action_view/template/text.rb +38 -0
- data/lib/action_view/test_case.rb +163 -0
- metadata +236 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
module ActionDispatch
|
|
2
|
+
module Routing
|
|
3
|
+
class Mapper
|
|
4
|
+
module Resources
|
|
5
|
+
def resource(*resources, &block)
|
|
6
|
+
options = resources.last.is_a?(Hash) ? resources.pop : {}
|
|
7
|
+
|
|
8
|
+
if resources.length > 1
|
|
9
|
+
raise ArgumentError if block_given?
|
|
10
|
+
resources.each { |r| resource(r, options) }
|
|
11
|
+
return self
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
resource = resources.pop
|
|
15
|
+
|
|
16
|
+
if @scope[:scope_level] == :resources
|
|
17
|
+
member do
|
|
18
|
+
resource(resource, options, &block)
|
|
19
|
+
end
|
|
20
|
+
return self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
singular = resource.to_s
|
|
24
|
+
plural = singular.pluralize
|
|
25
|
+
|
|
26
|
+
controller(plural) do
|
|
27
|
+
namespace(resource) do
|
|
28
|
+
with_scope_level(:resource) do
|
|
29
|
+
yield if block_given?
|
|
30
|
+
|
|
31
|
+
get "", :to => :show, :as => "#{singular}"
|
|
32
|
+
post "", :to => :create
|
|
33
|
+
put "", :to => :update
|
|
34
|
+
delete "", :to => :destroy
|
|
35
|
+
get "new", :to => :new, :as => "new_#{singular}"
|
|
36
|
+
get "edit", :to => :edit, :as => "edit_#{singular}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
self
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def resources(*resources, &block)
|
|
45
|
+
options = resources.last.is_a?(Hash) ? resources.pop : {}
|
|
46
|
+
|
|
47
|
+
if resources.length > 1
|
|
48
|
+
raise ArgumentError if block_given?
|
|
49
|
+
resources.each { |r| resources(r, options) }
|
|
50
|
+
return self
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
resource = resources.pop
|
|
54
|
+
|
|
55
|
+
if @scope[:scope_level] == :resources
|
|
56
|
+
member do
|
|
57
|
+
resources(resource, options, &block)
|
|
58
|
+
end
|
|
59
|
+
return self
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
plural = resource.to_s
|
|
63
|
+
singular = plural.singularize
|
|
64
|
+
|
|
65
|
+
controller(resource) do
|
|
66
|
+
namespace(resource) do
|
|
67
|
+
with_scope_level(:resources) do
|
|
68
|
+
yield if block_given?
|
|
69
|
+
|
|
70
|
+
member do
|
|
71
|
+
get "", :to => :show, :as => "#{singular}"
|
|
72
|
+
put "", :to => :update
|
|
73
|
+
delete "", :to => :destroy
|
|
74
|
+
get "edit", :to => :edit, :as => "edit_#{singular}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
collection do
|
|
78
|
+
get "", :to => :index, :as => "#{plural}"
|
|
79
|
+
post "", :to => :create
|
|
80
|
+
get "new", :to => :new, :as => "new_#{singular}"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
self
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def collection
|
|
90
|
+
unless @scope[:scope_level] == :resources
|
|
91
|
+
raise ArgumentError, "can't use collection outside resources scope"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
with_scope_level(:collection) do
|
|
95
|
+
yield
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def member
|
|
100
|
+
unless @scope[:scope_level] == :resources
|
|
101
|
+
raise ArgumentError, "can't use member outside resources scope"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
with_scope_level(:member) do
|
|
105
|
+
scope(":id") do
|
|
106
|
+
yield
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def match(*args)
|
|
112
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
113
|
+
args.push(options)
|
|
114
|
+
|
|
115
|
+
case options.delete(:on)
|
|
116
|
+
when :collection
|
|
117
|
+
return collection { match(*args) }
|
|
118
|
+
when :member
|
|
119
|
+
return member { match(*args) }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
if @scope[:scope_level] == :resources
|
|
123
|
+
raise ArgumentError, "can't define route directly in resources scope"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
super
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
def with_scope_level(kind)
|
|
131
|
+
old, @scope[:scope_level] = @scope[:scope_level], kind
|
|
132
|
+
yield
|
|
133
|
+
ensure
|
|
134
|
+
@scope[:scope_level] = old
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
module Scoping
|
|
139
|
+
def scope(*args)
|
|
140
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
141
|
+
|
|
142
|
+
constraints = options.delete(:constraints) || {}
|
|
143
|
+
unless constraints.is_a?(Hash)
|
|
144
|
+
block, constraints = constraints, {}
|
|
145
|
+
end
|
|
146
|
+
constraints, @scope[:constraints] = @scope[:constraints], (@scope[:constraints] || {}).merge(constraints)
|
|
147
|
+
blocks, @scope[:blocks] = @scope[:blocks], (@scope[:blocks] || []) + [block]
|
|
148
|
+
|
|
149
|
+
options, @scope[:options] = @scope[:options], (@scope[:options] || {}).merge(options)
|
|
150
|
+
|
|
151
|
+
path_set = controller_set = false
|
|
152
|
+
|
|
153
|
+
case args.first
|
|
154
|
+
when String
|
|
155
|
+
path_set = true
|
|
156
|
+
path = args.first
|
|
157
|
+
path, @scope[:path] = @scope[:path], "#{@scope[:path]}#{Rack::Mount::Utils.normalize_path(path)}"
|
|
158
|
+
when Symbol
|
|
159
|
+
controller_set = true
|
|
160
|
+
controller = args.first
|
|
161
|
+
controller, @scope[:controller] = @scope[:controller], controller
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
yield
|
|
165
|
+
|
|
166
|
+
self
|
|
167
|
+
ensure
|
|
168
|
+
@scope[:path] = path if path_set
|
|
169
|
+
@scope[:controller] = controller if controller_set
|
|
170
|
+
@scope[:options] = options
|
|
171
|
+
@scope[:blocks] = blocks
|
|
172
|
+
@scope[:constraints] = constraints
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def controller(controller)
|
|
176
|
+
scope(controller.to_sym) { yield }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def namespace(path)
|
|
180
|
+
scope(path.to_s) { yield }
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def constraints(constraints = {})
|
|
184
|
+
scope(:constraints => constraints) { yield }
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
class Constraints
|
|
189
|
+
def initialize(app, constraints = [])
|
|
190
|
+
@app, @constraints = app, constraints
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def call(env)
|
|
194
|
+
req = Rack::Request.new(env)
|
|
195
|
+
|
|
196
|
+
@constraints.each { |constraint|
|
|
197
|
+
if constraint.respond_to?(:matches?) && !constraint.matches?(req)
|
|
198
|
+
return Rack::Mount::Const::EXPECTATION_FAILED_RESPONSE
|
|
199
|
+
elsif constraint.respond_to?(:call) && !constraint.call(req)
|
|
200
|
+
return Rack::Mount::Const::EXPECTATION_FAILED_RESPONSE
|
|
201
|
+
end
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@app.call(env)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def initialize(set)
|
|
209
|
+
@set = set
|
|
210
|
+
@scope = {}
|
|
211
|
+
|
|
212
|
+
extend Scoping
|
|
213
|
+
extend Resources
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def get(*args, &block)
|
|
217
|
+
map_method(:get, *args, &block)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def post(*args, &block)
|
|
221
|
+
map_method(:post, *args, &block)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def put(*args, &block)
|
|
225
|
+
map_method(:put, *args, &block)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def delete(*args, &block)
|
|
229
|
+
map_method(:delete, *args, &block)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def root(options = {})
|
|
233
|
+
match '/', options.merge(:as => :root)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def match(*args)
|
|
237
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
238
|
+
|
|
239
|
+
if args.length > 1
|
|
240
|
+
args.each { |path| match(path, options) }
|
|
241
|
+
return self
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
if args.first.is_a?(Symbol)
|
|
245
|
+
return match(args.first.to_s, options.merge(:to => args.first.to_sym))
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
path = args.first
|
|
249
|
+
|
|
250
|
+
options = (@scope[:options] || {}).merge(options)
|
|
251
|
+
conditions, defaults = {}, {}
|
|
252
|
+
|
|
253
|
+
path = nil if path == ""
|
|
254
|
+
path = Rack::Mount::Utils.normalize_path(path) if path
|
|
255
|
+
path = "#{@scope[:path]}#{path}" if @scope[:path]
|
|
256
|
+
|
|
257
|
+
raise ArgumentError, "path is required" unless path
|
|
258
|
+
|
|
259
|
+
constraints = options[:constraints] || {}
|
|
260
|
+
unless constraints.is_a?(Hash)
|
|
261
|
+
block, constraints = constraints, {}
|
|
262
|
+
end
|
|
263
|
+
blocks = ((@scope[:blocks] || []) + [block]).compact
|
|
264
|
+
constraints = (@scope[:constraints] || {}).merge(constraints)
|
|
265
|
+
options.each { |k, v| constraints[k] = v if v.is_a?(Regexp) }
|
|
266
|
+
|
|
267
|
+
conditions[:path_info] = path
|
|
268
|
+
requirements = constraints.dup
|
|
269
|
+
|
|
270
|
+
path_regexp = Rack::Mount::Strexp.compile(path, constraints, SEPARATORS)
|
|
271
|
+
segment_keys = Rack::Mount::RegexpWithNamedGroups.new(path_regexp).names
|
|
272
|
+
constraints.reject! { |k, v| segment_keys.include?(k.to_s) }
|
|
273
|
+
conditions.merge!(constraints)
|
|
274
|
+
|
|
275
|
+
if via = options[:via]
|
|
276
|
+
via = Array(via).map { |m| m.to_s.upcase }
|
|
277
|
+
conditions[:request_method] = Regexp.union(*via)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
defaults[:controller] = @scope[:controller].to_s if @scope[:controller]
|
|
281
|
+
|
|
282
|
+
if options[:to].respond_to?(:call)
|
|
283
|
+
app = options[:to]
|
|
284
|
+
defaults.delete(:controller)
|
|
285
|
+
defaults.delete(:action)
|
|
286
|
+
elsif options[:to].is_a?(String)
|
|
287
|
+
defaults[:controller], defaults[:action] = options[:to].split('#')
|
|
288
|
+
elsif options[:to].is_a?(Symbol)
|
|
289
|
+
defaults[:action] = options[:to].to_s
|
|
290
|
+
end
|
|
291
|
+
app ||= Routing::RouteSet::Dispatcher.new(:defaults => defaults)
|
|
292
|
+
|
|
293
|
+
if app.is_a?(Routing::RouteSet::Dispatcher)
|
|
294
|
+
unless defaults.include?(:controller) || segment_keys.include?("controller")
|
|
295
|
+
raise ArgumentError, "missing :controller"
|
|
296
|
+
end
|
|
297
|
+
unless defaults.include?(:action) || segment_keys.include?("action")
|
|
298
|
+
raise ArgumentError, "missing :action"
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
app = Constraints.new(app, blocks) if blocks.any?
|
|
303
|
+
@set.add_route(app, conditions, requirements, defaults, options[:as])
|
|
304
|
+
|
|
305
|
+
self
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def redirect(path, options = {})
|
|
309
|
+
status = options[:status] || 301
|
|
310
|
+
lambda { |env|
|
|
311
|
+
req = Rack::Request.new(env)
|
|
312
|
+
url = req.scheme + '://' + req.host + path
|
|
313
|
+
[status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']]
|
|
314
|
+
}
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
private
|
|
318
|
+
def map_method(method, *args, &block)
|
|
319
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
320
|
+
options[:via] = method
|
|
321
|
+
args.push(options)
|
|
322
|
+
match(*args, &block)
|
|
323
|
+
self
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module ActionDispatch
|
|
2
|
+
module Routing
|
|
3
|
+
class Route #:nodoc:
|
|
4
|
+
attr_reader :app, :conditions, :defaults, :name
|
|
5
|
+
attr_reader :path, :requirements
|
|
6
|
+
|
|
7
|
+
def initialize(app, conditions = {}, requirements = {}, defaults = {}, name = nil)
|
|
8
|
+
@app = app
|
|
9
|
+
@defaults = defaults
|
|
10
|
+
@name = name
|
|
11
|
+
|
|
12
|
+
@requirements = requirements.merge(defaults)
|
|
13
|
+
@requirements.delete(:controller) if @requirements[:controller].is_a?(Regexp)
|
|
14
|
+
@requirements.delete_if { |k, v|
|
|
15
|
+
v == Regexp.compile("[^#{SEPARATORS.join}]+")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if path = conditions[:path_info]
|
|
19
|
+
@path = path
|
|
20
|
+
conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
@conditions = conditions.inject({}) { |h, (k, v)|
|
|
24
|
+
h[k] = Rack::Mount::RegexpWithNamedGroups.new(v)
|
|
25
|
+
h
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def verb
|
|
30
|
+
if method = conditions[:request_method]
|
|
31
|
+
case method
|
|
32
|
+
when Regexp
|
|
33
|
+
method.source.upcase
|
|
34
|
+
else
|
|
35
|
+
method.to_s.upcase
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def segment_keys
|
|
41
|
+
@segment_keys ||= conditions[:path_info].names.compact.map { |key| key.to_sym }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def to_a
|
|
45
|
+
[@app, @conditions, @defaults, @name]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
require 'rack/mount'
|
|
2
|
+
require 'forwardable'
|
|
3
|
+
|
|
4
|
+
module ActionDispatch
|
|
5
|
+
module Routing
|
|
6
|
+
class RouteSet #:nodoc:
|
|
7
|
+
NotFound = lambda { |env|
|
|
8
|
+
raise ActionController::RoutingError, "No route matches #{env[::Rack::Mount::Const::PATH_INFO].inspect} with #{env.inspect}"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
|
|
12
|
+
|
|
13
|
+
class Dispatcher
|
|
14
|
+
def initialize(options = {})
|
|
15
|
+
defaults = options[:defaults]
|
|
16
|
+
@glob_param = options.delete(:glob)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call(env)
|
|
20
|
+
params = env[PARAMETERS_KEY]
|
|
21
|
+
merge_default_action!(params)
|
|
22
|
+
split_glob_param!(params) if @glob_param
|
|
23
|
+
params.each do |key, value|
|
|
24
|
+
if value.is_a?(String)
|
|
25
|
+
value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding)
|
|
26
|
+
params[key] = URI.unescape(value)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if env['action_controller.recognize']
|
|
31
|
+
[200, {}, params]
|
|
32
|
+
else
|
|
33
|
+
controller = controller(params)
|
|
34
|
+
controller.action(params[:action]).call(env)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
def controller(params)
|
|
40
|
+
if params && params.has_key?(:controller)
|
|
41
|
+
controller = "#{params[:controller].camelize}Controller"
|
|
42
|
+
ActiveSupport::Inflector.constantize(controller)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def merge_default_action!(params)
|
|
47
|
+
params[:action] ||= 'index'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def split_glob_param!(params)
|
|
51
|
+
params[@glob_param] = params[@glob_param].split('/').map { |v| URI.unescape(v) }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# A NamedRouteCollection instance is a collection of named routes, and also
|
|
57
|
+
# maintains an anonymous module that can be used to install helpers for the
|
|
58
|
+
# named routes.
|
|
59
|
+
class NamedRouteCollection #:nodoc:
|
|
60
|
+
include Enumerable
|
|
61
|
+
attr_reader :routes, :helpers
|
|
62
|
+
|
|
63
|
+
def initialize
|
|
64
|
+
clear!
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def clear!
|
|
68
|
+
@routes = {}
|
|
69
|
+
@helpers = []
|
|
70
|
+
|
|
71
|
+
@module ||= Module.new
|
|
72
|
+
@module.instance_methods.each do |selector|
|
|
73
|
+
@module.class_eval { remove_method selector }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def add(name, route)
|
|
78
|
+
routes[name.to_sym] = route
|
|
79
|
+
define_named_route_methods(name, route)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def get(name)
|
|
83
|
+
routes[name.to_sym]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
alias []= add
|
|
87
|
+
alias [] get
|
|
88
|
+
alias clear clear!
|
|
89
|
+
|
|
90
|
+
def each
|
|
91
|
+
routes.each { |name, route| yield name, route }
|
|
92
|
+
self
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def names
|
|
96
|
+
routes.keys
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def length
|
|
100
|
+
routes.length
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def reset!
|
|
104
|
+
old_routes = routes.dup
|
|
105
|
+
clear!
|
|
106
|
+
old_routes.each do |name, route|
|
|
107
|
+
add(name, route)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def install(destinations = [ActionController::Base, ActionView::Base], regenerate = false)
|
|
112
|
+
reset! if regenerate
|
|
113
|
+
Array(destinations).each do |dest|
|
|
114
|
+
dest.__send__(:include, @module)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
def url_helper_name(name, kind = :url)
|
|
120
|
+
:"#{name}_#{kind}"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def hash_access_name(name, kind = :url)
|
|
124
|
+
:"hash_for_#{name}_#{kind}"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def define_named_route_methods(name, route)
|
|
128
|
+
{:url => {:only_path => false}, :path => {:only_path => true}}.each do |kind, opts|
|
|
129
|
+
hash = route.defaults.merge(:use_route => name).merge(opts)
|
|
130
|
+
define_hash_access route, name, kind, hash
|
|
131
|
+
define_url_helper route, name, kind, hash
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def named_helper_module_eval(code, *args)
|
|
136
|
+
@module.module_eval(code, *args)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def define_hash_access(route, name, kind, options)
|
|
140
|
+
selector = hash_access_name(name, kind)
|
|
141
|
+
named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks
|
|
142
|
+
def #{selector}(options = nil) # def hash_for_users_url(options = nil)
|
|
143
|
+
options ? #{options.inspect}.merge(options) : #{options.inspect} # options ? {:only_path=>false}.merge(options) : {:only_path=>false}
|
|
144
|
+
end # end
|
|
145
|
+
protected :#{selector} # protected :hash_for_users_url
|
|
146
|
+
end_eval
|
|
147
|
+
helpers << selector
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def define_url_helper(route, name, kind, options)
|
|
151
|
+
selector = url_helper_name(name, kind)
|
|
152
|
+
# The segment keys used for positional parameters
|
|
153
|
+
|
|
154
|
+
hash_access_method = hash_access_name(name, kind)
|
|
155
|
+
|
|
156
|
+
# allow ordered parameters to be associated with corresponding
|
|
157
|
+
# dynamic segments, so you can do
|
|
158
|
+
#
|
|
159
|
+
# foo_url(bar, baz, bang)
|
|
160
|
+
#
|
|
161
|
+
# instead of
|
|
162
|
+
#
|
|
163
|
+
# foo_url(:bar => bar, :baz => baz, :bang => bang)
|
|
164
|
+
#
|
|
165
|
+
# Also allow options hash, so you can do
|
|
166
|
+
#
|
|
167
|
+
# foo_url(bar, baz, bang, :sort_by => 'baz')
|
|
168
|
+
#
|
|
169
|
+
named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks
|
|
170
|
+
def #{selector}(*args) # def users_url(*args)
|
|
171
|
+
#
|
|
172
|
+
opts = if args.empty? || Hash === args.first # opts = if args.empty? || Hash === args.first
|
|
173
|
+
args.first || {} # args.first || {}
|
|
174
|
+
else # else
|
|
175
|
+
options = args.extract_options! # options = args.extract_options!
|
|
176
|
+
args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)| # args = args.zip([]).inject({}) do |h, (v, k)|
|
|
177
|
+
h[k] = v # h[k] = v
|
|
178
|
+
h # h
|
|
179
|
+
end # end
|
|
180
|
+
options.merge(args) # options.merge(args)
|
|
181
|
+
end # end
|
|
182
|
+
#
|
|
183
|
+
url_for(#{hash_access_method}(opts)) # url_for(hash_for_users_url(opts))
|
|
184
|
+
#
|
|
185
|
+
end # end
|
|
186
|
+
#Add an alias to support the now deprecated formatted_* URL. # #Add an alias to support the now deprecated formatted_* URL.
|
|
187
|
+
def formatted_#{selector}(*args) # def formatted_users_url(*args)
|
|
188
|
+
ActiveSupport::Deprecation.warn( # ActiveSupport::Deprecation.warn(
|
|
189
|
+
"formatted_#{selector}() has been deprecated. " + # "formatted_users_url() has been deprecated. " +
|
|
190
|
+
"Please pass format to the standard " + # "Please pass format to the standard " +
|
|
191
|
+
"#{selector} method instead.", caller) # "users_url method instead.", caller)
|
|
192
|
+
#{selector}(*args) # users_url(*args)
|
|
193
|
+
end # end
|
|
194
|
+
protected :#{selector} # protected :users_url
|
|
195
|
+
end_eval
|
|
196
|
+
helpers << selector
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
attr_accessor :routes, :named_routes, :configuration_files
|
|
201
|
+
|
|
202
|
+
def initialize
|
|
203
|
+
self.configuration_files = []
|
|
204
|
+
|
|
205
|
+
self.routes = []
|
|
206
|
+
self.named_routes = NamedRouteCollection.new
|
|
207
|
+
|
|
208
|
+
clear!
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def draw(&block)
|
|
212
|
+
clear!
|
|
213
|
+
Mapper.new(self).instance_exec(DeprecatedMapper.new(self), &block)
|
|
214
|
+
@set.add_route(NotFound)
|
|
215
|
+
install_helpers
|
|
216
|
+
@set.freeze
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def clear!
|
|
220
|
+
routes.clear
|
|
221
|
+
named_routes.clear
|
|
222
|
+
@set = ::Rack::Mount::RouteSet.new(:parameters_key => PARAMETERS_KEY)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)
|
|
226
|
+
Array(destinations).each { |d| d.module_eval { include Helpers } }
|
|
227
|
+
named_routes.install(destinations, regenerate_code)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def empty?
|
|
231
|
+
routes.empty?
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def add_configuration_file(path)
|
|
235
|
+
self.configuration_files << path
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Deprecated accessor
|
|
239
|
+
def configuration_file=(path)
|
|
240
|
+
add_configuration_file(path)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Deprecated accessor
|
|
244
|
+
def configuration_file
|
|
245
|
+
configuration_files
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def load!
|
|
249
|
+
Routing.use_controllers!(nil) # Clear the controller cache so we may discover new ones
|
|
250
|
+
load_routes!
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# reload! will always force a reload whereas load checks the timestamp first
|
|
254
|
+
alias reload! load!
|
|
255
|
+
|
|
256
|
+
def reload
|
|
257
|
+
if configuration_files.any? && @routes_last_modified
|
|
258
|
+
if routes_changed_at == @routes_last_modified
|
|
259
|
+
return # routes didn't change, don't reload
|
|
260
|
+
else
|
|
261
|
+
@routes_last_modified = routes_changed_at
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
load!
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def load_routes!
|
|
269
|
+
if configuration_files.any?
|
|
270
|
+
configuration_files.each { |config| load(config) }
|
|
271
|
+
@routes_last_modified = routes_changed_at
|
|
272
|
+
else
|
|
273
|
+
draw do |map|
|
|
274
|
+
map.connect ":controller/:action/:id"
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def routes_changed_at
|
|
280
|
+
routes_changed_at = nil
|
|
281
|
+
|
|
282
|
+
configuration_files.each do |config|
|
|
283
|
+
config_changed_at = File.stat(config).mtime
|
|
284
|
+
|
|
285
|
+
if routes_changed_at.nil? || config_changed_at > routes_changed_at
|
|
286
|
+
routes_changed_at = config_changed_at
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
routes_changed_at
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil)
|
|
294
|
+
route = Route.new(app, conditions, requirements, defaults, name)
|
|
295
|
+
@set.add_route(*route)
|
|
296
|
+
named_routes[name] = route if name
|
|
297
|
+
routes << route
|
|
298
|
+
route
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def options_as_params(options)
|
|
302
|
+
# If an explicit :controller was given, always make :action explicit
|
|
303
|
+
# too, so that action expiry works as expected for things like
|
|
304
|
+
#
|
|
305
|
+
# generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
|
|
306
|
+
#
|
|
307
|
+
# (the above is from the unit tests). In the above case, because the
|
|
308
|
+
# controller was explicitly given, but no action, the action is implied to
|
|
309
|
+
# be "index", not the recalled action of "show".
|
|
310
|
+
#
|
|
311
|
+
# great fun, eh?
|
|
312
|
+
|
|
313
|
+
options_as_params = options.clone
|
|
314
|
+
options_as_params[:action] ||= 'index' if options[:controller]
|
|
315
|
+
options_as_params[:action] = options_as_params[:action].to_s if options_as_params[:action]
|
|
316
|
+
options_as_params
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def build_expiry(options, recall)
|
|
320
|
+
recall.inject({}) do |expiry, (key, recalled_value)|
|
|
321
|
+
expiry[key] = (options.key?(key) && options[key].to_param != recalled_value.to_param)
|
|
322
|
+
expiry
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Generate the path indicated by the arguments, and return an array of
|
|
327
|
+
# the keys that were not used to generate it.
|
|
328
|
+
def extra_keys(options, recall={})
|
|
329
|
+
generate_extras(options, recall).last
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def generate_extras(options, recall={})
|
|
333
|
+
generate(options, recall, :generate_extras)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def generate(options, recall = {}, method = :generate)
|
|
337
|
+
options, recall = options.dup, recall.dup
|
|
338
|
+
named_route = options.delete(:use_route)
|
|
339
|
+
|
|
340
|
+
options = options_as_params(options)
|
|
341
|
+
expire_on = build_expiry(options, recall)
|
|
342
|
+
|
|
343
|
+
recall[:action] ||= 'index' if options[:controller] || recall[:controller]
|
|
344
|
+
|
|
345
|
+
if recall[:controller] && (!options.has_key?(:controller) || options[:controller] == recall[:controller])
|
|
346
|
+
options[:controller] = recall.delete(:controller)
|
|
347
|
+
|
|
348
|
+
if recall[:action] && (!options.has_key?(:action) || options[:action] == recall[:action])
|
|
349
|
+
options[:action] = recall.delete(:action)
|
|
350
|
+
|
|
351
|
+
if recall[:id] && (!options.has_key?(:id) || options[:id] == recall[:id])
|
|
352
|
+
options[:id] = recall.delete(:id)
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
options[:controller] = options[:controller].to_s if options[:controller]
|
|
358
|
+
|
|
359
|
+
if !named_route && expire_on[:controller] && options[:controller] && options[:controller][0] != ?/
|
|
360
|
+
old_parts = recall[:controller].split('/')
|
|
361
|
+
new_parts = options[:controller].split('/')
|
|
362
|
+
parts = old_parts[0..-(new_parts.length + 1)] + new_parts
|
|
363
|
+
options[:controller] = parts.join('/')
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
options[:controller] = options[:controller][1..-1] if options[:controller] && options[:controller][0] == ?/
|
|
367
|
+
|
|
368
|
+
merged = options.merge(recall)
|
|
369
|
+
if options.has_key?(:action) && options[:action].nil?
|
|
370
|
+
options.delete(:action)
|
|
371
|
+
recall[:action] = 'index'
|
|
372
|
+
end
|
|
373
|
+
recall[:action] = options.delete(:action) if options[:action] == 'index'
|
|
374
|
+
|
|
375
|
+
path = _uri(named_route, options, recall)
|
|
376
|
+
if path && method == :generate_extras
|
|
377
|
+
uri = URI(path)
|
|
378
|
+
extras = uri.query ?
|
|
379
|
+
Rack::Utils.parse_nested_query(uri.query).keys.map { |k| k.to_sym } :
|
|
380
|
+
[]
|
|
381
|
+
[uri.path, extras]
|
|
382
|
+
elsif path
|
|
383
|
+
path
|
|
384
|
+
else
|
|
385
|
+
raise ActionController::RoutingError, "No route matches #{options.inspect}"
|
|
386
|
+
end
|
|
387
|
+
rescue Rack::Mount::RoutingError
|
|
388
|
+
raise ActionController::RoutingError, "No route matches #{options.inspect}"
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def call(env)
|
|
392
|
+
@set.call(env)
|
|
393
|
+
rescue ActionController::RoutingError => e
|
|
394
|
+
raise e if env['action_controller.rescue_error'] == false
|
|
395
|
+
|
|
396
|
+
method, path = env['REQUEST_METHOD'].downcase.to_sym, env['PATH_INFO']
|
|
397
|
+
|
|
398
|
+
# Route was not recognized. Try to find out why (maybe wrong verb).
|
|
399
|
+
allows = HTTP_METHODS.select { |verb|
|
|
400
|
+
begin
|
|
401
|
+
recognize_path(path, {:method => verb}, false)
|
|
402
|
+
rescue ActionController::RoutingError
|
|
403
|
+
nil
|
|
404
|
+
end
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if !HTTP_METHODS.include?(method)
|
|
408
|
+
raise ActionController::NotImplemented.new(*allows)
|
|
409
|
+
elsif !allows.empty?
|
|
410
|
+
raise ActionController::MethodNotAllowed.new(*allows)
|
|
411
|
+
else
|
|
412
|
+
raise e
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def recognize(request)
|
|
417
|
+
params = recognize_path(request.path, extract_request_environment(request))
|
|
418
|
+
request.path_parameters = params.with_indifferent_access
|
|
419
|
+
"#{params[:controller].to_s.camelize}Controller".constantize
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def recognize_path(path, environment = {}, rescue_error = true)
|
|
423
|
+
method = (environment[:method] || "GET").to_s.upcase
|
|
424
|
+
|
|
425
|
+
begin
|
|
426
|
+
env = Rack::MockRequest.env_for(path, {:method => method})
|
|
427
|
+
rescue URI::InvalidURIError => e
|
|
428
|
+
raise ActionController::RoutingError, e.message
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
env['action_controller.recognize'] = true
|
|
432
|
+
env['action_controller.rescue_error'] = rescue_error
|
|
433
|
+
status, headers, body = call(env)
|
|
434
|
+
body
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
# Subclasses and plugins may override this method to extract further attributes
|
|
438
|
+
# from the request, for use by route conditions and such.
|
|
439
|
+
def extract_request_environment(request)
|
|
440
|
+
{ :method => request.method }
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
private
|
|
444
|
+
def _uri(named_route, params, recall)
|
|
445
|
+
params = URISegment.wrap_values(params)
|
|
446
|
+
recall = URISegment.wrap_values(recall)
|
|
447
|
+
|
|
448
|
+
unless result = @set.generate(:path_info, named_route, params, recall)
|
|
449
|
+
return
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
uri, params = result
|
|
453
|
+
params.each do |k, v|
|
|
454
|
+
if v._value
|
|
455
|
+
params[k] = v._value
|
|
456
|
+
else
|
|
457
|
+
params.delete(k)
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
uri << "?#{Rack::Mount::Utils.build_nested_query(params)}" if uri && params.any?
|
|
462
|
+
uri
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
class URISegment < Struct.new(:_value, :_escape)
|
|
466
|
+
EXCLUDED = [:controller]
|
|
467
|
+
|
|
468
|
+
def self.wrap_values(hash)
|
|
469
|
+
hash.inject({}) { |h, (k, v)|
|
|
470
|
+
h[k] = new(v, !EXCLUDED.include?(k.to_sym))
|
|
471
|
+
h
|
|
472
|
+
}
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
extend Forwardable
|
|
476
|
+
def_delegators :_value, :==, :eql?, :hash
|
|
477
|
+
|
|
478
|
+
def to_param
|
|
479
|
+
@to_param ||= begin
|
|
480
|
+
if _value.is_a?(Array)
|
|
481
|
+
_value.map { |v| _escaped(v) }.join('/')
|
|
482
|
+
else
|
|
483
|
+
_escaped(_value)
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
end
|
|
487
|
+
alias_method :to_s, :to_param
|
|
488
|
+
|
|
489
|
+
private
|
|
490
|
+
def _escaped(value)
|
|
491
|
+
v = value.respond_to?(:to_param) ? value.to_param : value
|
|
492
|
+
_escape ? Rack::Mount::Utils.escape_uri(v) : v.to_s
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
end
|