omg-actionpack 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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