omg-actionpack 8.0.0.alpha1

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