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.

Files changed (170) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +429 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller.rb +27 -0
  6. data/lib/abstract_controller/asset_paths.rb +12 -0
  7. data/lib/abstract_controller/base.rb +265 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/caching/fragments.rb +166 -0
  10. data/lib/abstract_controller/callbacks.rb +212 -0
  11. data/lib/abstract_controller/collector.rb +43 -0
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +194 -0
  14. data/lib/abstract_controller/logger.rb +14 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +20 -0
  16. data/lib/abstract_controller/rendering.rb +127 -0
  17. data/lib/abstract_controller/translation.rb +31 -0
  18. data/lib/abstract_controller/url_for.rb +35 -0
  19. data/lib/action_controller.rb +66 -0
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +276 -0
  23. data/lib/action_controller/caching.rb +46 -0
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +78 -0
  26. data/lib/action_controller/metal.rb +256 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +274 -0
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +16 -0
  31. data/lib/action_controller/metal/data_streaming.rb +152 -0
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  34. data/lib/action_controller/metal/exceptions.rb +53 -0
  35. data/lib/action_controller/metal/flash.rb +61 -0
  36. data/lib/action_controller/metal/force_ssl.rb +99 -0
  37. data/lib/action_controller/metal/head.rb +60 -0
  38. data/lib/action_controller/metal/helpers.rb +123 -0
  39. data/lib/action_controller/metal/http_authentication.rb +519 -0
  40. data/lib/action_controller/metal/implicit_render.rb +73 -0
  41. data/lib/action_controller/metal/instrumentation.rb +107 -0
  42. data/lib/action_controller/metal/live.rb +312 -0
  43. data/lib/action_controller/metal/mime_responds.rb +313 -0
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +293 -0
  46. data/lib/action_controller/metal/redirecting.rb +133 -0
  47. data/lib/action_controller/metal/renderers.rb +181 -0
  48. data/lib/action_controller/metal/rendering.rb +122 -0
  49. data/lib/action_controller/metal/request_forgery_protection.rb +445 -0
  50. data/lib/action_controller/metal/rescue.rb +28 -0
  51. data/lib/action_controller/metal/streaming.rb +223 -0
  52. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  53. data/lib/action_controller/metal/testing.rb +16 -0
  54. data/lib/action_controller/metal/url_for.rb +58 -0
  55. data/lib/action_controller/railtie.rb +89 -0
  56. data/lib/action_controller/railties/helpers.rb +24 -0
  57. data/lib/action_controller/renderer.rb +117 -0
  58. data/lib/action_controller/template_assertions.rb +11 -0
  59. data/lib/action_controller/test_case.rb +629 -0
  60. data/lib/action_dispatch.rb +112 -0
  61. data/lib/action_dispatch/http/cache.rb +222 -0
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +84 -0
  64. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  65. data/lib/action_dispatch/http/headers.rb +132 -0
  66. data/lib/action_dispatch/http/mime_negotiation.rb +175 -0
  67. data/lib/action_dispatch/http/mime_type.rb +342 -0
  68. data/lib/action_dispatch/http/mime_types.rb +50 -0
  69. data/lib/action_dispatch/http/parameter_filter.rb +86 -0
  70. data/lib/action_dispatch/http/parameters.rb +126 -0
  71. data/lib/action_dispatch/http/rack_cache.rb +63 -0
  72. data/lib/action_dispatch/http/request.rb +430 -0
  73. data/lib/action_dispatch/http/response.rb +519 -0
  74. data/lib/action_dispatch/http/upload.rb +84 -0
  75. data/lib/action_dispatch/http/url.rb +350 -0
  76. data/lib/action_dispatch/journey.rb +7 -0
  77. data/lib/action_dispatch/journey/formatter.rb +189 -0
  78. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  81. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  82. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  85. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  86. data/lib/action_dispatch/journey/parser.rb +199 -0
  87. data/lib/action_dispatch/journey/parser.y +50 -0
  88. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  89. data/lib/action_dispatch/journey/path/pattern.rb +198 -0
  90. data/lib/action_dispatch/journey/route.rb +203 -0
  91. data/lib/action_dispatch/journey/router.rb +156 -0
  92. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  93. data/lib/action_dispatch/journey/routes.rb +82 -0
  94. data/lib/action_dispatch/journey/scanner.rb +64 -0
  95. data/lib/action_dispatch/journey/visitors.rb +268 -0
  96. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  97. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  98. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  99. data/lib/action_dispatch/middleware/callbacks.rb +36 -0
  100. data/lib/action_dispatch/middleware/cookies.rb +685 -0
  101. data/lib/action_dispatch/middleware/debug_exceptions.rb +205 -0
  102. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  103. data/lib/action_dispatch/middleware/exception_wrapper.rb +147 -0
  104. data/lib/action_dispatch/middleware/executor.rb +21 -0
  105. data/lib/action_dispatch/middleware/flash.rb +300 -0
  106. data/lib/action_dispatch/middleware/public_exceptions.rb +57 -0
  107. data/lib/action_dispatch/middleware/reloader.rb +12 -0
  108. data/lib/action_dispatch/middleware/remote_ip.rb +183 -0
  109. data/lib/action_dispatch/middleware/request_id.rb +43 -0
  110. data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
  111. data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
  112. data/lib/action_dispatch/middleware/session/cookie_store.rb +118 -0
  113. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
  114. data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
  115. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  116. data/lib/action_dispatch/middleware/stack.rb +116 -0
  117. data/lib/action_dispatch/middleware/static.rb +130 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +161 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  138. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  139. data/lib/action_dispatch/railtie.rb +55 -0
  140. data/lib/action_dispatch/request/session.rb +234 -0
  141. data/lib/action_dispatch/request/utils.rb +78 -0
  142. data/lib/action_dispatch/routing.rb +260 -0
  143. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  144. data/lib/action_dispatch/routing/inspector.rb +225 -0
  145. data/lib/action_dispatch/routing/mapper.rb +2267 -0
  146. data/lib/action_dispatch/routing/polymorphic_routes.rb +352 -0
  147. data/lib/action_dispatch/routing/redirection.rb +201 -0
  148. data/lib/action_dispatch/routing/route_set.rb +890 -0
  149. data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
  150. data/lib/action_dispatch/routing/url_for.rb +236 -0
  151. data/lib/action_dispatch/system_test_case.rb +147 -0
  152. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  153. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  154. data/lib/action_dispatch/system_testing/server.rb +31 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  158. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  159. data/lib/action_dispatch/testing/assertions.rb +24 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +107 -0
  161. data/lib/action_dispatch/testing/assertions/routing.rb +222 -0
  162. data/lib/action_dispatch/testing/integration.rb +652 -0
  163. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  164. data/lib/action_dispatch/testing/test_process.rb +50 -0
  165. data/lib/action_dispatch/testing/test_request.rb +71 -0
  166. data/lib/action_dispatch/testing/test_response.rb +53 -0
  167. data/lib/action_pack.rb +26 -0
  168. data/lib/action_pack/gem_version.rb +17 -0
  169. data/lib/action_pack/version.rb +10 -0
  170. 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