actionpack 5.2.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +429 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +57 -0
- data/lib/abstract_controller.rb +27 -0
- data/lib/abstract_controller/asset_paths.rb +12 -0
- data/lib/abstract_controller/base.rb +265 -0
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/caching/fragments.rb +166 -0
- data/lib/abstract_controller/callbacks.rb +212 -0
- data/lib/abstract_controller/collector.rb +43 -0
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +194 -0
- data/lib/abstract_controller/logger.rb +14 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +20 -0
- data/lib/abstract_controller/rendering.rb +127 -0
- data/lib/abstract_controller/translation.rb +31 -0
- data/lib/abstract_controller/url_for.rb +35 -0
- data/lib/action_controller.rb +66 -0
- data/lib/action_controller/api.rb +149 -0
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/base.rb +276 -0
- data/lib/action_controller/caching.rb +46 -0
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +78 -0
- data/lib/action_controller/metal.rb +256 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +274 -0
- data/lib/action_controller/metal/content_security_policy.rb +52 -0
- data/lib/action_controller/metal/cookies.rb +16 -0
- data/lib/action_controller/metal/data_streaming.rb +152 -0
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
- data/lib/action_controller/metal/exceptions.rb +53 -0
- data/lib/action_controller/metal/flash.rb +61 -0
- data/lib/action_controller/metal/force_ssl.rb +99 -0
- data/lib/action_controller/metal/head.rb +60 -0
- data/lib/action_controller/metal/helpers.rb +123 -0
- data/lib/action_controller/metal/http_authentication.rb +519 -0
- data/lib/action_controller/metal/implicit_render.rb +73 -0
- data/lib/action_controller/metal/instrumentation.rb +107 -0
- data/lib/action_controller/metal/live.rb +312 -0
- data/lib/action_controller/metal/mime_responds.rb +313 -0
- data/lib/action_controller/metal/parameter_encoding.rb +51 -0
- data/lib/action_controller/metal/params_wrapper.rb +293 -0
- data/lib/action_controller/metal/redirecting.rb +133 -0
- data/lib/action_controller/metal/renderers.rb +181 -0
- data/lib/action_controller/metal/rendering.rb +122 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +445 -0
- data/lib/action_controller/metal/rescue.rb +28 -0
- data/lib/action_controller/metal/streaming.rb +223 -0
- data/lib/action_controller/metal/strong_parameters.rb +1086 -0
- data/lib/action_controller/metal/testing.rb +16 -0
- data/lib/action_controller/metal/url_for.rb +58 -0
- data/lib/action_controller/railtie.rb +89 -0
- data/lib/action_controller/railties/helpers.rb +24 -0
- data/lib/action_controller/renderer.rb +117 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +629 -0
- data/lib/action_dispatch.rb +112 -0
- data/lib/action_dispatch/http/cache.rb +222 -0
- data/lib/action_dispatch/http/content_security_policy.rb +272 -0
- data/lib/action_dispatch/http/filter_parameters.rb +84 -0
- data/lib/action_dispatch/http/filter_redirect.rb +37 -0
- data/lib/action_dispatch/http/headers.rb +132 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +175 -0
- data/lib/action_dispatch/http/mime_type.rb +342 -0
- data/lib/action_dispatch/http/mime_types.rb +50 -0
- data/lib/action_dispatch/http/parameter_filter.rb +86 -0
- data/lib/action_dispatch/http/parameters.rb +126 -0
- data/lib/action_dispatch/http/rack_cache.rb +63 -0
- data/lib/action_dispatch/http/request.rb +430 -0
- data/lib/action_dispatch/http/response.rb +519 -0
- data/lib/action_dispatch/http/upload.rb +84 -0
- data/lib/action_dispatch/http/url.rb +350 -0
- data/lib/action_dispatch/journey.rb +7 -0
- data/lib/action_dispatch/journey/formatter.rb +189 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
- data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
- data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
- data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
- data/lib/action_dispatch/journey/nodes/node.rb +140 -0
- data/lib/action_dispatch/journey/parser.rb +199 -0
- data/lib/action_dispatch/journey/parser.y +50 -0
- data/lib/action_dispatch/journey/parser_extras.rb +31 -0
- data/lib/action_dispatch/journey/path/pattern.rb +198 -0
- data/lib/action_dispatch/journey/route.rb +203 -0
- data/lib/action_dispatch/journey/router.rb +156 -0
- data/lib/action_dispatch/journey/router/utils.rb +102 -0
- data/lib/action_dispatch/journey/routes.rb +82 -0
- data/lib/action_dispatch/journey/scanner.rb +64 -0
- data/lib/action_dispatch/journey/visitors.rb +268 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/middleware/callbacks.rb +36 -0
- data/lib/action_dispatch/middleware/cookies.rb +685 -0
- data/lib/action_dispatch/middleware/debug_exceptions.rb +205 -0
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +147 -0
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +300 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +57 -0
- data/lib/action_dispatch/middleware/reloader.rb +12 -0
- data/lib/action_dispatch/middleware/remote_ip.rb +183 -0
- data/lib/action_dispatch/middleware/request_id.rb +43 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +118 -0
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
- data/lib/action_dispatch/middleware/ssl.rb +150 -0
- data/lib/action_dispatch/middleware/stack.rb +116 -0
- data/lib/action_dispatch/middleware/static.rb +130 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +161 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
- data/lib/action_dispatch/railtie.rb +55 -0
- data/lib/action_dispatch/request/session.rb +234 -0
- data/lib/action_dispatch/request/utils.rb +78 -0
- data/lib/action_dispatch/routing.rb +260 -0
- data/lib/action_dispatch/routing/endpoint.rb +17 -0
- data/lib/action_dispatch/routing/inspector.rb +225 -0
- data/lib/action_dispatch/routing/mapper.rb +2267 -0
- data/lib/action_dispatch/routing/polymorphic_routes.rb +352 -0
- data/lib/action_dispatch/routing/redirection.rb +201 -0
- data/lib/action_dispatch/routing/route_set.rb +890 -0
- data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
- data/lib/action_dispatch/routing/url_for.rb +236 -0
- data/lib/action_dispatch/system_test_case.rb +147 -0
- data/lib/action_dispatch/system_testing/browser.rb +49 -0
- data/lib/action_dispatch/system_testing/driver.rb +59 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
- data/lib/action_dispatch/testing/assertion_response.rb +47 -0
- data/lib/action_dispatch/testing/assertions.rb +24 -0
- data/lib/action_dispatch/testing/assertions/response.rb +107 -0
- data/lib/action_dispatch/testing/assertions/routing.rb +222 -0
- data/lib/action_dispatch/testing/integration.rb +652 -0
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +50 -0
- data/lib/action_dispatch/testing/test_request.rb +71 -0
- data/lib/action_dispatch/testing/test_response.rb +53 -0
- data/lib/action_pack.rb +26 -0
- data/lib/action_pack/gem_version.rb +17 -0
- data/lib/action_pack/version.rb +10 -0
- metadata +318 -0
@@ -0,0 +1,352 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
module Routing
|
5
|
+
# Polymorphic URL helpers are methods for smart resolution to a named route call when
|
6
|
+
# given an Active Record model instance. They are to be used in combination with
|
7
|
+
# ActionController::Resources.
|
8
|
+
#
|
9
|
+
# These methods are useful when you want to generate the correct URL or path to a RESTful
|
10
|
+
# resource without having to know the exact type of the record in question.
|
11
|
+
#
|
12
|
+
# Nested resources and/or namespaces are also supported, as illustrated in the example:
|
13
|
+
#
|
14
|
+
# polymorphic_url([:admin, @article, @comment])
|
15
|
+
#
|
16
|
+
# results in:
|
17
|
+
#
|
18
|
+
# admin_article_comment_url(@article, @comment)
|
19
|
+
#
|
20
|
+
# == Usage within the framework
|
21
|
+
#
|
22
|
+
# Polymorphic URL helpers are used in a number of places throughout the \Rails framework:
|
23
|
+
#
|
24
|
+
# * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
|
25
|
+
# <tt>url_for(@article)</tt>;
|
26
|
+
# * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
|
27
|
+
# <tt>form_for(@article)</tt> without having to specify <tt>:url</tt> parameter for the form
|
28
|
+
# action;
|
29
|
+
# * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
|
30
|
+
# <tt>redirect_to(post)</tt> in your controllers;
|
31
|
+
# * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
|
32
|
+
# for feed entries.
|
33
|
+
#
|
34
|
+
# == Prefixed polymorphic helpers
|
35
|
+
#
|
36
|
+
# In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
|
37
|
+
# number of prefixed helpers are available as a shorthand to <tt>action: "..."</tt>
|
38
|
+
# in options. Those are:
|
39
|
+
#
|
40
|
+
# * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
|
41
|
+
# * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
|
42
|
+
#
|
43
|
+
# Example usage:
|
44
|
+
#
|
45
|
+
# edit_polymorphic_path(@post) # => "/posts/1/edit"
|
46
|
+
# polymorphic_path(@post, format: :pdf) # => "/posts/1.pdf"
|
47
|
+
#
|
48
|
+
# == Usage with mounted engines
|
49
|
+
#
|
50
|
+
# If you are using a mounted engine and you need to use a polymorphic_url
|
51
|
+
# pointing at the engine's routes, pass in the engine's route proxy as the first
|
52
|
+
# argument to the method. For example:
|
53
|
+
#
|
54
|
+
# polymorphic_url([blog, @post]) # calls blog.post_path(@post)
|
55
|
+
# form_for([blog, @post]) # => "/blog/posts/1"
|
56
|
+
#
|
57
|
+
module PolymorphicRoutes
|
58
|
+
# Constructs a call to a named RESTful route for the given record and returns the
|
59
|
+
# resulting URL string. For example:
|
60
|
+
#
|
61
|
+
# # calls post_url(post)
|
62
|
+
# polymorphic_url(post) # => "http://example.com/posts/1"
|
63
|
+
# polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
|
64
|
+
# polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
|
65
|
+
# polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
|
66
|
+
# polymorphic_url(Comment) # => "http://example.com/comments"
|
67
|
+
#
|
68
|
+
# ==== Options
|
69
|
+
#
|
70
|
+
# * <tt>:action</tt> - Specifies the action prefix for the named route:
|
71
|
+
# <tt>:new</tt> or <tt>:edit</tt>. Default is no prefix.
|
72
|
+
# * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
|
73
|
+
# Default is <tt>:url</tt>.
|
74
|
+
#
|
75
|
+
# Also includes all the options from <tt>url_for</tt>. These include such
|
76
|
+
# things as <tt>:anchor</tt> or <tt>:trailing_slash</tt>. Example usage
|
77
|
+
# is given below:
|
78
|
+
#
|
79
|
+
# polymorphic_url([blog, post], anchor: 'my_anchor')
|
80
|
+
# # => "http://example.com/blogs/1/posts/1#my_anchor"
|
81
|
+
# polymorphic_url([blog, post], anchor: 'my_anchor', script_name: "/my_app")
|
82
|
+
# # => "http://example.com/my_app/blogs/1/posts/1#my_anchor"
|
83
|
+
#
|
84
|
+
# For all of these options, see the documentation for {url_for}[rdoc-ref:ActionDispatch::Routing::UrlFor].
|
85
|
+
#
|
86
|
+
# ==== Functionality
|
87
|
+
#
|
88
|
+
# # an Article record
|
89
|
+
# polymorphic_url(record) # same as article_url(record)
|
90
|
+
#
|
91
|
+
# # a Comment record
|
92
|
+
# polymorphic_url(record) # same as comment_url(record)
|
93
|
+
#
|
94
|
+
# # it recognizes new records and maps to the collection
|
95
|
+
# record = Comment.new
|
96
|
+
# polymorphic_url(record) # same as comments_url()
|
97
|
+
#
|
98
|
+
# # the class of a record will also map to the collection
|
99
|
+
# polymorphic_url(Comment) # same as comments_url()
|
100
|
+
#
|
101
|
+
def polymorphic_url(record_or_hash_or_array, options = {})
|
102
|
+
if Hash === record_or_hash_or_array
|
103
|
+
options = record_or_hash_or_array.merge(options)
|
104
|
+
record = options.delete :id
|
105
|
+
return polymorphic_url record, options
|
106
|
+
end
|
107
|
+
|
108
|
+
if mapping = polymorphic_mapping(record_or_hash_or_array)
|
109
|
+
return mapping.call(self, [record_or_hash_or_array, options], false)
|
110
|
+
end
|
111
|
+
|
112
|
+
opts = options.dup
|
113
|
+
action = opts.delete :action
|
114
|
+
type = opts.delete(:routing_type) || :url
|
115
|
+
|
116
|
+
HelperMethodBuilder.polymorphic_method self,
|
117
|
+
record_or_hash_or_array,
|
118
|
+
action,
|
119
|
+
type,
|
120
|
+
opts
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns the path component of a URL for the given record. It uses
|
124
|
+
# <tt>polymorphic_url</tt> with <tt>routing_type: :path</tt>.
|
125
|
+
def polymorphic_path(record_or_hash_or_array, options = {})
|
126
|
+
if Hash === record_or_hash_or_array
|
127
|
+
options = record_or_hash_or_array.merge(options)
|
128
|
+
record = options.delete :id
|
129
|
+
return polymorphic_path record, options
|
130
|
+
end
|
131
|
+
|
132
|
+
if mapping = polymorphic_mapping(record_or_hash_or_array)
|
133
|
+
return mapping.call(self, [record_or_hash_or_array, options], true)
|
134
|
+
end
|
135
|
+
|
136
|
+
opts = options.dup
|
137
|
+
action = opts.delete :action
|
138
|
+
type = :path
|
139
|
+
|
140
|
+
HelperMethodBuilder.polymorphic_method self,
|
141
|
+
record_or_hash_or_array,
|
142
|
+
action,
|
143
|
+
type,
|
144
|
+
opts
|
145
|
+
end
|
146
|
+
|
147
|
+
%w(edit new).each do |action|
|
148
|
+
module_eval <<-EOT, __FILE__, __LINE__ + 1
|
149
|
+
def #{action}_polymorphic_url(record_or_hash, options = {})
|
150
|
+
polymorphic_url_for_action("#{action}", record_or_hash, options)
|
151
|
+
end
|
152
|
+
|
153
|
+
def #{action}_polymorphic_path(record_or_hash, options = {})
|
154
|
+
polymorphic_path_for_action("#{action}", record_or_hash, options)
|
155
|
+
end
|
156
|
+
EOT
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def polymorphic_url_for_action(action, record_or_hash, options)
|
162
|
+
polymorphic_url(record_or_hash, options.merge(action: action))
|
163
|
+
end
|
164
|
+
|
165
|
+
def polymorphic_path_for_action(action, record_or_hash, options)
|
166
|
+
polymorphic_path(record_or_hash, options.merge(action: action))
|
167
|
+
end
|
168
|
+
|
169
|
+
def polymorphic_mapping(record)
|
170
|
+
if record.respond_to?(:to_model)
|
171
|
+
_routes.polymorphic_mappings[record.to_model.model_name.name]
|
172
|
+
else
|
173
|
+
_routes.polymorphic_mappings[record.class.name]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class HelperMethodBuilder # :nodoc:
|
178
|
+
CACHE = { "path" => {}, "url" => {} }
|
179
|
+
|
180
|
+
def self.get(action, type)
|
181
|
+
type = type.to_s
|
182
|
+
CACHE[type].fetch(action) { build action, type }
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.url; CACHE["url".freeze][nil]; end
|
186
|
+
def self.path; CACHE["path".freeze][nil]; end
|
187
|
+
|
188
|
+
def self.build(action, type)
|
189
|
+
prefix = action ? "#{action}_" : ""
|
190
|
+
suffix = type
|
191
|
+
if action.to_s == "new"
|
192
|
+
HelperMethodBuilder.singular prefix, suffix
|
193
|
+
else
|
194
|
+
HelperMethodBuilder.plural prefix, suffix
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.singular(prefix, suffix)
|
199
|
+
new(->(name) { name.singular_route_key }, prefix, suffix)
|
200
|
+
end
|
201
|
+
|
202
|
+
def self.plural(prefix, suffix)
|
203
|
+
new(->(name) { name.route_key }, prefix, suffix)
|
204
|
+
end
|
205
|
+
|
206
|
+
def self.polymorphic_method(recipient, record_or_hash_or_array, action, type, options)
|
207
|
+
builder = get action, type
|
208
|
+
|
209
|
+
case record_or_hash_or_array
|
210
|
+
when Array
|
211
|
+
record_or_hash_or_array = record_or_hash_or_array.compact
|
212
|
+
if record_or_hash_or_array.empty?
|
213
|
+
raise ArgumentError, "Nil location provided. Can't build URI."
|
214
|
+
end
|
215
|
+
if record_or_hash_or_array.first.is_a?(ActionDispatch::Routing::RoutesProxy)
|
216
|
+
recipient = record_or_hash_or_array.shift
|
217
|
+
end
|
218
|
+
|
219
|
+
method, args = builder.handle_list record_or_hash_or_array
|
220
|
+
when String, Symbol
|
221
|
+
method, args = builder.handle_string record_or_hash_or_array
|
222
|
+
when Class
|
223
|
+
method, args = builder.handle_class record_or_hash_or_array
|
224
|
+
|
225
|
+
when nil
|
226
|
+
raise ArgumentError, "Nil location provided. Can't build URI."
|
227
|
+
else
|
228
|
+
method, args = builder.handle_model record_or_hash_or_array
|
229
|
+
end
|
230
|
+
|
231
|
+
if options.empty?
|
232
|
+
recipient.send(method, *args)
|
233
|
+
else
|
234
|
+
recipient.send(method, *args, options)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
attr_reader :suffix, :prefix
|
239
|
+
|
240
|
+
def initialize(key_strategy, prefix, suffix)
|
241
|
+
@key_strategy = key_strategy
|
242
|
+
@prefix = prefix
|
243
|
+
@suffix = suffix
|
244
|
+
end
|
245
|
+
|
246
|
+
def handle_string(record)
|
247
|
+
[get_method_for_string(record), []]
|
248
|
+
end
|
249
|
+
|
250
|
+
def handle_string_call(target, str)
|
251
|
+
target.send get_method_for_string str
|
252
|
+
end
|
253
|
+
|
254
|
+
def handle_class(klass)
|
255
|
+
[get_method_for_class(klass), []]
|
256
|
+
end
|
257
|
+
|
258
|
+
def handle_class_call(target, klass)
|
259
|
+
target.send get_method_for_class klass
|
260
|
+
end
|
261
|
+
|
262
|
+
def handle_model(record)
|
263
|
+
args = []
|
264
|
+
|
265
|
+
model = record.to_model
|
266
|
+
named_route = if model.persisted?
|
267
|
+
args << model
|
268
|
+
get_method_for_string model.model_name.singular_route_key
|
269
|
+
else
|
270
|
+
get_method_for_class model
|
271
|
+
end
|
272
|
+
|
273
|
+
[named_route, args]
|
274
|
+
end
|
275
|
+
|
276
|
+
def handle_model_call(target, record)
|
277
|
+
if mapping = polymorphic_mapping(target, record)
|
278
|
+
mapping.call(target, [record], suffix == "path")
|
279
|
+
else
|
280
|
+
method, args = handle_model(record)
|
281
|
+
target.send(method, *args)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def handle_list(list)
|
286
|
+
record_list = list.dup
|
287
|
+
record = record_list.pop
|
288
|
+
|
289
|
+
args = []
|
290
|
+
|
291
|
+
route = record_list.map { |parent|
|
292
|
+
case parent
|
293
|
+
when Symbol, String
|
294
|
+
parent.to_s
|
295
|
+
when Class
|
296
|
+
args << parent
|
297
|
+
parent.model_name.singular_route_key
|
298
|
+
else
|
299
|
+
args << parent.to_model
|
300
|
+
parent.to_model.model_name.singular_route_key
|
301
|
+
end
|
302
|
+
}
|
303
|
+
|
304
|
+
route <<
|
305
|
+
case record
|
306
|
+
when Symbol, String
|
307
|
+
record.to_s
|
308
|
+
when Class
|
309
|
+
@key_strategy.call record.model_name
|
310
|
+
else
|
311
|
+
model = record.to_model
|
312
|
+
if model.persisted?
|
313
|
+
args << model
|
314
|
+
model.model_name.singular_route_key
|
315
|
+
else
|
316
|
+
@key_strategy.call model.model_name
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
route << suffix
|
321
|
+
|
322
|
+
named_route = prefix + route.join("_")
|
323
|
+
[named_route, args]
|
324
|
+
end
|
325
|
+
|
326
|
+
private
|
327
|
+
|
328
|
+
def polymorphic_mapping(target, record)
|
329
|
+
if record.respond_to?(:to_model)
|
330
|
+
target._routes.polymorphic_mappings[record.to_model.model_name.name]
|
331
|
+
else
|
332
|
+
target._routes.polymorphic_mappings[record.class.name]
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def get_method_for_class(klass)
|
337
|
+
name = @key_strategy.call klass.model_name
|
338
|
+
get_method_for_string name
|
339
|
+
end
|
340
|
+
|
341
|
+
def get_method_for_string(str)
|
342
|
+
"#{prefix}#{str}_#{suffix}"
|
343
|
+
end
|
344
|
+
|
345
|
+
[nil, "new", "edit"].each do |action|
|
346
|
+
CACHE["url"][action] = build action, "url"
|
347
|
+
CACHE["path"][action] = build action, "path"
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_dispatch/http/request"
|
4
|
+
require "active_support/core_ext/uri"
|
5
|
+
require "active_support/core_ext/array/extract_options"
|
6
|
+
require "rack/utils"
|
7
|
+
require "action_controller/metal/exceptions"
|
8
|
+
require "action_dispatch/routing/endpoint"
|
9
|
+
|
10
|
+
module ActionDispatch
|
11
|
+
module Routing
|
12
|
+
class Redirect < Endpoint # :nodoc:
|
13
|
+
attr_reader :status, :block
|
14
|
+
|
15
|
+
def initialize(status, block)
|
16
|
+
@status = status
|
17
|
+
@block = block
|
18
|
+
end
|
19
|
+
|
20
|
+
def redirect?; true; end
|
21
|
+
|
22
|
+
def call(env)
|
23
|
+
serve Request.new env
|
24
|
+
end
|
25
|
+
|
26
|
+
def serve(req)
|
27
|
+
uri = URI.parse(path(req.path_parameters, req))
|
28
|
+
|
29
|
+
unless uri.host
|
30
|
+
if relative_path?(uri.path)
|
31
|
+
uri.path = "#{req.script_name}/#{uri.path}"
|
32
|
+
elsif uri.path.empty?
|
33
|
+
uri.path = req.script_name.empty? ? "/" : req.script_name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
uri.scheme ||= req.scheme
|
38
|
+
uri.host ||= req.host
|
39
|
+
uri.port ||= req.port unless req.standard_port?
|
40
|
+
|
41
|
+
req.commit_flash
|
42
|
+
|
43
|
+
body = %(<html><body>You are being <a href="#{ERB::Util.unwrapped_html_escape(uri.to_s)}">redirected</a>.</body></html>)
|
44
|
+
|
45
|
+
headers = {
|
46
|
+
"Location" => uri.to_s,
|
47
|
+
"Content-Type" => "text/html",
|
48
|
+
"Content-Length" => body.length.to_s
|
49
|
+
}
|
50
|
+
|
51
|
+
[ status, headers, [body] ]
|
52
|
+
end
|
53
|
+
|
54
|
+
def path(params, request)
|
55
|
+
block.call params, request
|
56
|
+
end
|
57
|
+
|
58
|
+
def inspect
|
59
|
+
"redirect(#{status})"
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def relative_path?(path)
|
64
|
+
path && !path.empty? && path[0] != "/"
|
65
|
+
end
|
66
|
+
|
67
|
+
def escape(params)
|
68
|
+
Hash[params.map { |k, v| [k, Rack::Utils.escape(v)] }]
|
69
|
+
end
|
70
|
+
|
71
|
+
def escape_fragment(params)
|
72
|
+
Hash[params.map { |k, v| [k, Journey::Router::Utils.escape_fragment(v)] }]
|
73
|
+
end
|
74
|
+
|
75
|
+
def escape_path(params)
|
76
|
+
Hash[params.map { |k, v| [k, Journey::Router::Utils.escape_path(v)] }]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class PathRedirect < Redirect
|
81
|
+
URL_PARTS = /\A([^?]+)?(\?[^#]+)?(#.+)?\z/
|
82
|
+
|
83
|
+
def path(params, request)
|
84
|
+
if block.match(URL_PARTS)
|
85
|
+
path = interpolation_required?($1, params) ? $1 % escape_path(params) : $1
|
86
|
+
query = interpolation_required?($2, params) ? $2 % escape(params) : $2
|
87
|
+
fragment = interpolation_required?($3, params) ? $3 % escape_fragment(params) : $3
|
88
|
+
|
89
|
+
"#{path}#{query}#{fragment}"
|
90
|
+
else
|
91
|
+
interpolation_required?(block, params) ? block % escape(params) : block
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def inspect
|
96
|
+
"redirect(#{status}, #{block})"
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
def interpolation_required?(string, params)
|
101
|
+
!params.empty? && string && string.match(/%\{\w*\}/)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class OptionRedirect < Redirect # :nodoc:
|
106
|
+
alias :options :block
|
107
|
+
|
108
|
+
def path(params, request)
|
109
|
+
url_options = {
|
110
|
+
protocol: request.protocol,
|
111
|
+
host: request.host,
|
112
|
+
port: request.optional_port,
|
113
|
+
path: request.path,
|
114
|
+
params: request.query_parameters
|
115
|
+
}.merge! options
|
116
|
+
|
117
|
+
if !params.empty? && url_options[:path].match(/%\{\w*\}/)
|
118
|
+
url_options[:path] = (url_options[:path] % escape_path(params))
|
119
|
+
end
|
120
|
+
|
121
|
+
unless options[:host] || options[:domain]
|
122
|
+
if relative_path?(url_options[:path])
|
123
|
+
url_options[:path] = "/#{url_options[:path]}"
|
124
|
+
url_options[:script_name] = request.script_name
|
125
|
+
elsif url_options[:path].empty?
|
126
|
+
url_options[:path] = request.script_name.empty? ? "/" : ""
|
127
|
+
url_options[:script_name] = request.script_name
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
ActionDispatch::Http::URL.url_for url_options
|
132
|
+
end
|
133
|
+
|
134
|
+
def inspect
|
135
|
+
"redirect(#{status}, #{options.map { |k, v| "#{k}: #{v}" }.join(', ')})"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
module Redirection
|
140
|
+
# Redirect any path to another path:
|
141
|
+
#
|
142
|
+
# get "/stories" => redirect("/posts")
|
143
|
+
#
|
144
|
+
# This will redirect the user, while ignoring certain parts of the request, including query string, etc.
|
145
|
+
# <tt>/stories</tt>, <tt>/stories?foo=bar</tt>, etc all redirect to <tt>/posts</tt>.
|
146
|
+
#
|
147
|
+
# You can also use interpolation in the supplied redirect argument:
|
148
|
+
#
|
149
|
+
# get 'docs/:article', to: redirect('/wiki/%{article}')
|
150
|
+
#
|
151
|
+
# Note that if you return a path without a leading slash then the URL is prefixed with the
|
152
|
+
# current SCRIPT_NAME environment variable. This is typically '/' but may be different in
|
153
|
+
# a mounted engine or where the application is deployed to a subdirectory of a website.
|
154
|
+
#
|
155
|
+
# Alternatively you can use one of the other syntaxes:
|
156
|
+
#
|
157
|
+
# The block version of redirect allows for the easy encapsulation of any logic associated with
|
158
|
+
# the redirect in question. Either the params and request are supplied as arguments, or just
|
159
|
+
# params, depending of how many arguments your block accepts. A string is required as a
|
160
|
+
# return value.
|
161
|
+
#
|
162
|
+
# get 'jokes/:number', to: redirect { |params, request|
|
163
|
+
# path = (params[:number].to_i.even? ? "wheres-the-beef" : "i-love-lamp")
|
164
|
+
# "http://#{request.host_with_port}/#{path}"
|
165
|
+
# }
|
166
|
+
#
|
167
|
+
# Note that the +do end+ syntax for the redirect block wouldn't work, as Ruby would pass
|
168
|
+
# the block to +get+ instead of +redirect+. Use <tt>{ ... }</tt> instead.
|
169
|
+
#
|
170
|
+
# The options version of redirect allows you to supply only the parts of the URL which need
|
171
|
+
# to change, it also supports interpolation of the path similar to the first example.
|
172
|
+
#
|
173
|
+
# get 'stores/:name', to: redirect(subdomain: 'stores', path: '/%{name}')
|
174
|
+
# get 'stores/:name(*all)', to: redirect(subdomain: 'stores', path: '/%{name}%{all}')
|
175
|
+
# get '/stories', to: redirect(path: '/posts')
|
176
|
+
#
|
177
|
+
# This will redirect the user, while changing only the specified parts of the request,
|
178
|
+
# for example the +path+ option in the last example.
|
179
|
+
# <tt>/stories</tt>, <tt>/stories?foo=bar</tt>, redirect to <tt>/posts</tt> and <tt>/posts?foo=bar</tt> respectively.
|
180
|
+
#
|
181
|
+
# Finally, an object which responds to call can be supplied to redirect, allowing you to reuse
|
182
|
+
# common redirect routes. The call method must accept two arguments, params and request, and return
|
183
|
+
# a string.
|
184
|
+
#
|
185
|
+
# get 'accounts/:name' => redirect(SubdomainRedirector.new('api'))
|
186
|
+
#
|
187
|
+
def redirect(*args, &block)
|
188
|
+
options = args.extract_options!
|
189
|
+
status = options.delete(:status) || 301
|
190
|
+
path = args.shift
|
191
|
+
|
192
|
+
return OptionRedirect.new(status, options) if options.any?
|
193
|
+
return PathRedirect.new(status, path) if String === path
|
194
|
+
|
195
|
+
block = path if path.respond_to? :call
|
196
|
+
raise ArgumentError, "redirection argument not supported" unless block
|
197
|
+
Redirect.new status, block
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|