actionpack 6.0.0

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 (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +311 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +58 -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 +267 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/caching/fragments.rb +150 -0
  10. data/lib/abstract_controller/callbacks.rb +224 -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 +32 -0
  18. data/lib/abstract_controller/url_for.rb +35 -0
  19. data/lib/action_controller.rb +67 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +271 -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 +81 -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 +280 -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 +151 -0
  32. data/lib/action_controller/metal/default_headers.rb +17 -0
  33. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  34. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  35. data/lib/action_controller/metal/exceptions.rb +74 -0
  36. data/lib/action_controller/metal/flash.rb +61 -0
  37. data/lib/action_controller/metal/force_ssl.rb +58 -0
  38. data/lib/action_controller/metal/head.rb +60 -0
  39. data/lib/action_controller/metal/helpers.rb +122 -0
  40. data/lib/action_controller/metal/http_authentication.rb +518 -0
  41. data/lib/action_controller/metal/implicit_render.rb +63 -0
  42. data/lib/action_controller/metal/instrumentation.rb +105 -0
  43. data/lib/action_controller/metal/live.rb +314 -0
  44. data/lib/action_controller/metal/mime_responds.rb +324 -0
  45. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  46. data/lib/action_controller/metal/params_wrapper.rb +297 -0
  47. data/lib/action_controller/metal/redirecting.rb +133 -0
  48. data/lib/action_controller/metal/renderers.rb +181 -0
  49. data/lib/action_controller/metal/rendering.rb +122 -0
  50. data/lib/action_controller/metal/request_forgery_protection.rb +456 -0
  51. data/lib/action_controller/metal/rescue.rb +28 -0
  52. data/lib/action_controller/metal/streaming.rb +223 -0
  53. data/lib/action_controller/metal/strong_parameters.rb +1105 -0
  54. data/lib/action_controller/metal/testing.rb +16 -0
  55. data/lib/action_controller/metal/url_for.rb +58 -0
  56. data/lib/action_controller/railtie.rb +89 -0
  57. data/lib/action_controller/railties/helpers.rb +24 -0
  58. data/lib/action_controller/renderer.rb +130 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +626 -0
  61. data/lib/action_dispatch.rb +114 -0
  62. data/lib/action_dispatch/http/cache.rb +226 -0
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +284 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +86 -0
  66. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  67. data/lib/action_dispatch/http/headers.rb +132 -0
  68. data/lib/action_dispatch/http/mime_negotiation.rb +177 -0
  69. data/lib/action_dispatch/http/mime_type.rb +350 -0
  70. data/lib/action_dispatch/http/mime_types.rb +50 -0
  71. data/lib/action_dispatch/http/parameter_filter.rb +12 -0
  72. data/lib/action_dispatch/http/parameters.rb +136 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +63 -0
  74. data/lib/action_dispatch/http/request.rb +427 -0
  75. data/lib/action_dispatch/http/response.rb +534 -0
  76. data/lib/action_dispatch/http/upload.rb +92 -0
  77. data/lib/action_dispatch/http/url.rb +350 -0
  78. data/lib/action_dispatch/journey.rb +7 -0
  79. data/lib/action_dispatch/journey/formatter.rb +189 -0
  80. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  81. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  82. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  83. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  84. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  85. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  86. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  87. data/lib/action_dispatch/journey/nodes/node.rb +141 -0
  88. data/lib/action_dispatch/journey/parser.rb +199 -0
  89. data/lib/action_dispatch/journey/parser.y +50 -0
  90. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +203 -0
  92. data/lib/action_dispatch/journey/route.rb +204 -0
  93. data/lib/action_dispatch/journey/router.rb +153 -0
  94. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  95. data/lib/action_dispatch/journey/routes.rb +81 -0
  96. data/lib/action_dispatch/journey/scanner.rb +71 -0
  97. data/lib/action_dispatch/journey/visitors.rb +268 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/middleware/actionable_exceptions.rb +39 -0
  102. data/lib/action_dispatch/middleware/callbacks.rb +34 -0
  103. data/lib/action_dispatch/middleware/cookies.rb +663 -0
  104. data/lib/action_dispatch/middleware/debug_exceptions.rb +185 -0
  105. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  106. data/lib/action_dispatch/middleware/debug_view.rb +68 -0
  107. data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -0
  108. data/lib/action_dispatch/middleware/executor.rb +21 -0
  109. data/lib/action_dispatch/middleware/flash.rb +300 -0
  110. data/lib/action_dispatch/middleware/host_authorization.rb +103 -0
  111. data/lib/action_dispatch/middleware/public_exceptions.rb +61 -0
  112. data/lib/action_dispatch/middleware/reloader.rb +12 -0
  113. data/lib/action_dispatch/middleware/remote_ip.rb +181 -0
  114. data/lib/action_dispatch/middleware/request_id.rb +43 -0
  115. data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
  116. data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
  117. data/lib/action_dispatch/middleware/session/cookie_store.rb +113 -0
  118. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
  119. data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
  120. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  121. data/lib/action_dispatch/middleware/stack.rb +148 -0
  122. data/lib/action_dispatch/middleware/static.rb +129 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +24 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +29 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +38 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +165 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  143. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  148. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  149. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +203 -0
  150. data/lib/action_dispatch/railtie.rb +58 -0
  151. data/lib/action_dispatch/request/session.rb +242 -0
  152. data/lib/action_dispatch/request/utils.rb +78 -0
  153. data/lib/action_dispatch/routing.rb +261 -0
  154. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  155. data/lib/action_dispatch/routing/inspector.rb +274 -0
  156. data/lib/action_dispatch/routing/mapper.rb +2289 -0
  157. data/lib/action_dispatch/routing/polymorphic_routes.rb +351 -0
  158. data/lib/action_dispatch/routing/redirection.rb +201 -0
  159. data/lib/action_dispatch/routing/route_set.rb +887 -0
  160. data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
  161. data/lib/action_dispatch/routing/url_for.rb +237 -0
  162. data/lib/action_dispatch/system_test_case.rb +168 -0
  163. data/lib/action_dispatch/system_testing/browser.rb +80 -0
  164. data/lib/action_dispatch/system_testing/driver.rb +68 -0
  165. data/lib/action_dispatch/system_testing/server.rb +31 -0
  166. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +97 -0
  167. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +33 -0
  168. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  169. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  170. data/lib/action_dispatch/testing/assertions.rb +24 -0
  171. data/lib/action_dispatch/testing/assertions/response.rb +106 -0
  172. data/lib/action_dispatch/testing/assertions/routing.rb +234 -0
  173. data/lib/action_dispatch/testing/integration.rb +659 -0
  174. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  175. data/lib/action_dispatch/testing/test_process.rb +50 -0
  176. data/lib/action_dispatch/testing/test_request.rb +71 -0
  177. data/lib/action_dispatch/testing/test_response.rb +25 -0
  178. data/lib/action_pack.rb +26 -0
  179. data/lib/action_pack/gem_version.rb +17 -0
  180. data/lib/action_pack/version.rb +10 -0
  181. metadata +329 -0
@@ -0,0 +1,351 @@
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.
124
+ def polymorphic_path(record_or_hash_or_array, options = {})
125
+ if Hash === record_or_hash_or_array
126
+ options = record_or_hash_or_array.merge(options)
127
+ record = options.delete :id
128
+ return polymorphic_path record, options
129
+ end
130
+
131
+ if mapping = polymorphic_mapping(record_or_hash_or_array)
132
+ return mapping.call(self, [record_or_hash_or_array, options], true)
133
+ end
134
+
135
+ opts = options.dup
136
+ action = opts.delete :action
137
+ type = :path
138
+
139
+ HelperMethodBuilder.polymorphic_method self,
140
+ record_or_hash_or_array,
141
+ action,
142
+ type,
143
+ opts
144
+ end
145
+
146
+ %w(edit new).each do |action|
147
+ module_eval <<-EOT, __FILE__, __LINE__ + 1
148
+ def #{action}_polymorphic_url(record_or_hash, options = {})
149
+ polymorphic_url_for_action("#{action}", record_or_hash, options)
150
+ end
151
+
152
+ def #{action}_polymorphic_path(record_or_hash, options = {})
153
+ polymorphic_path_for_action("#{action}", record_or_hash, options)
154
+ end
155
+ EOT
156
+ end
157
+
158
+ private
159
+
160
+ def polymorphic_url_for_action(action, record_or_hash, options)
161
+ polymorphic_url(record_or_hash, options.merge(action: action))
162
+ end
163
+
164
+ def polymorphic_path_for_action(action, record_or_hash, options)
165
+ polymorphic_path(record_or_hash, options.merge(action: action))
166
+ end
167
+
168
+ def polymorphic_mapping(record)
169
+ if record.respond_to?(:to_model)
170
+ _routes.polymorphic_mappings[record.to_model.model_name.name]
171
+ else
172
+ _routes.polymorphic_mappings[record.class.name]
173
+ end
174
+ end
175
+
176
+ class HelperMethodBuilder # :nodoc:
177
+ CACHE = { "path" => {}, "url" => {} }
178
+
179
+ def self.get(action, type)
180
+ type = type.to_s
181
+ CACHE[type].fetch(action) { build action, type }
182
+ end
183
+
184
+ def self.url; CACHE["url"][nil]; end
185
+ def self.path; CACHE["path"][nil]; end
186
+
187
+ def self.build(action, type)
188
+ prefix = action ? "#{action}_" : ""
189
+ suffix = type
190
+ if action.to_s == "new"
191
+ HelperMethodBuilder.singular prefix, suffix
192
+ else
193
+ HelperMethodBuilder.plural prefix, suffix
194
+ end
195
+ end
196
+
197
+ def self.singular(prefix, suffix)
198
+ new(->(name) { name.singular_route_key }, prefix, suffix)
199
+ end
200
+
201
+ def self.plural(prefix, suffix)
202
+ new(->(name) { name.route_key }, prefix, suffix)
203
+ end
204
+
205
+ def self.polymorphic_method(recipient, record_or_hash_or_array, action, type, options)
206
+ builder = get action, type
207
+
208
+ case record_or_hash_or_array
209
+ when Array
210
+ record_or_hash_or_array = record_or_hash_or_array.compact
211
+ if record_or_hash_or_array.empty?
212
+ raise ArgumentError, "Nil location provided. Can't build URI."
213
+ end
214
+ if record_or_hash_or_array.first.is_a?(ActionDispatch::Routing::RoutesProxy)
215
+ recipient = record_or_hash_or_array.shift
216
+ end
217
+
218
+ method, args = builder.handle_list record_or_hash_or_array
219
+ when String, Symbol
220
+ method, args = builder.handle_string record_or_hash_or_array
221
+ when Class
222
+ method, args = builder.handle_class record_or_hash_or_array
223
+
224
+ when nil
225
+ raise ArgumentError, "Nil location provided. Can't build URI."
226
+ else
227
+ method, args = builder.handle_model record_or_hash_or_array
228
+ end
229
+
230
+ if options.empty?
231
+ recipient.send(method, *args)
232
+ else
233
+ recipient.send(method, *args, options)
234
+ end
235
+ end
236
+
237
+ attr_reader :suffix, :prefix
238
+
239
+ def initialize(key_strategy, prefix, suffix)
240
+ @key_strategy = key_strategy
241
+ @prefix = prefix
242
+ @suffix = suffix
243
+ end
244
+
245
+ def handle_string(record)
246
+ [get_method_for_string(record), []]
247
+ end
248
+
249
+ def handle_string_call(target, str)
250
+ target.send get_method_for_string str
251
+ end
252
+
253
+ def handle_class(klass)
254
+ [get_method_for_class(klass), []]
255
+ end
256
+
257
+ def handle_class_call(target, klass)
258
+ target.send get_method_for_class klass
259
+ end
260
+
261
+ def handle_model(record)
262
+ args = []
263
+
264
+ model = record.to_model
265
+ named_route = if model.persisted?
266
+ args << model
267
+ get_method_for_string model.model_name.singular_route_key
268
+ else
269
+ get_method_for_class model
270
+ end
271
+
272
+ [named_route, args]
273
+ end
274
+
275
+ def handle_model_call(target, record)
276
+ if mapping = polymorphic_mapping(target, record)
277
+ mapping.call(target, [record], suffix == "path")
278
+ else
279
+ method, args = handle_model(record)
280
+ target.send(method, *args)
281
+ end
282
+ end
283
+
284
+ def handle_list(list)
285
+ record_list = list.dup
286
+ record = record_list.pop
287
+
288
+ args = []
289
+
290
+ route = record_list.map { |parent|
291
+ case parent
292
+ when Symbol, String
293
+ parent.to_s
294
+ when Class
295
+ args << parent
296
+ parent.model_name.singular_route_key
297
+ else
298
+ args << parent.to_model
299
+ parent.to_model.model_name.singular_route_key
300
+ end
301
+ }
302
+
303
+ route <<
304
+ case record
305
+ when Symbol, String
306
+ record.to_s
307
+ when Class
308
+ @key_strategy.call record.model_name
309
+ else
310
+ model = record.to_model
311
+ if model.persisted?
312
+ args << model
313
+ model.model_name.singular_route_key
314
+ else
315
+ @key_strategy.call model.model_name
316
+ end
317
+ end
318
+
319
+ route << suffix
320
+
321
+ named_route = prefix + route.join("_")
322
+ [named_route, args]
323
+ end
324
+
325
+ private
326
+
327
+ def polymorphic_mapping(target, record)
328
+ if record.respond_to?(:to_model)
329
+ target._routes.polymorphic_mappings[record.to_model.model_name.name]
330
+ else
331
+ target._routes.polymorphic_mappings[record.class.name]
332
+ end
333
+ end
334
+
335
+ def get_method_for_class(klass)
336
+ name = @key_strategy.call klass.model_name
337
+ get_method_for_string name
338
+ end
339
+
340
+ def get_method_for_string(str)
341
+ "#{prefix}#{str}_#{suffix}"
342
+ end
343
+
344
+ [nil, "new", "edit"].each do |action|
345
+ CACHE["url"][action] = build action, "url"
346
+ CACHE["path"][action] = build action, "path"
347
+ end
348
+ end
349
+ end
350
+ end
351
+ 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