actionpack 3.2.22.5 → 4.0.0.beta1

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 (265) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +641 -418
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -288
  5. data/lib/abstract_controller.rb +1 -8
  6. data/lib/abstract_controller/asset_paths.rb +2 -2
  7. data/lib/abstract_controller/base.rb +39 -37
  8. data/lib/abstract_controller/callbacks.rb +101 -82
  9. data/lib/abstract_controller/collector.rb +7 -3
  10. data/lib/abstract_controller/helpers.rb +23 -11
  11. data/lib/abstract_controller/layouts.rb +68 -73
  12. data/lib/abstract_controller/logger.rb +1 -2
  13. data/lib/abstract_controller/rendering.rb +22 -13
  14. data/lib/abstract_controller/translation.rb +16 -1
  15. data/lib/abstract_controller/url_for.rb +6 -6
  16. data/lib/abstract_controller/view_paths.rb +1 -1
  17. data/lib/action_controller.rb +15 -6
  18. data/lib/action_controller/base.rb +46 -22
  19. data/lib/action_controller/caching.rb +46 -33
  20. data/lib/action_controller/caching/fragments.rb +23 -53
  21. data/lib/action_controller/deprecated.rb +5 -1
  22. data/lib/action_controller/deprecated/integration_test.rb +3 -0
  23. data/lib/action_controller/log_subscriber.rb +11 -8
  24. data/lib/action_controller/metal.rb +16 -30
  25. data/lib/action_controller/metal/conditional_get.rb +76 -32
  26. data/lib/action_controller/metal/data_streaming.rb +20 -26
  27. data/lib/action_controller/metal/exceptions.rb +19 -6
  28. data/lib/action_controller/metal/flash.rb +24 -9
  29. data/lib/action_controller/metal/force_ssl.rb +32 -9
  30. data/lib/action_controller/metal/head.rb +25 -4
  31. data/lib/action_controller/metal/helpers.rb +6 -9
  32. data/lib/action_controller/metal/hide_actions.rb +1 -2
  33. data/lib/action_controller/metal/http_authentication.rb +105 -87
  34. data/lib/action_controller/metal/implicit_render.rb +1 -1
  35. data/lib/action_controller/metal/instrumentation.rb +2 -1
  36. data/lib/action_controller/metal/live.rb +141 -0
  37. data/lib/action_controller/metal/mime_responds.rb +161 -47
  38. data/lib/action_controller/metal/params_wrapper.rb +112 -74
  39. data/lib/action_controller/metal/rack_delegation.rb +9 -3
  40. data/lib/action_controller/metal/redirecting.rb +15 -20
  41. data/lib/action_controller/metal/renderers.rb +11 -9
  42. data/lib/action_controller/metal/rendering.rb +8 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +112 -19
  44. data/lib/action_controller/metal/responder.rb +20 -19
  45. data/lib/action_controller/metal/streaming.rb +12 -18
  46. data/lib/action_controller/metal/strong_parameters.rb +516 -0
  47. data/lib/action_controller/metal/testing.rb +13 -18
  48. data/lib/action_controller/metal/url_for.rb +27 -25
  49. data/lib/action_controller/model_naming.rb +12 -0
  50. data/lib/action_controller/railtie.rb +33 -17
  51. data/lib/action_controller/railties/helpers.rb +22 -0
  52. data/lib/action_controller/record_identifier.rb +18 -72
  53. data/lib/action_controller/test_case.rb +215 -123
  54. data/lib/action_controller/vendor/html-scanner.rb +4 -19
  55. data/lib/action_dispatch.rb +27 -19
  56. data/lib/action_dispatch/http/cache.rb +63 -11
  57. data/lib/action_dispatch/http/filter_parameters.rb +18 -8
  58. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  59. data/lib/action_dispatch/http/headers.rb +27 -19
  60. data/lib/action_dispatch/http/mime_negotiation.rb +25 -2
  61. data/lib/action_dispatch/http/mime_type.rb +145 -113
  62. data/lib/action_dispatch/http/mime_types.rb +1 -1
  63. data/lib/action_dispatch/http/parameter_filter.rb +44 -46
  64. data/lib/action_dispatch/http/parameters.rb +12 -5
  65. data/lib/action_dispatch/http/rack_cache.rb +2 -3
  66. data/lib/action_dispatch/http/request.rb +49 -18
  67. data/lib/action_dispatch/http/response.rb +129 -35
  68. data/lib/action_dispatch/http/upload.rb +60 -17
  69. data/lib/action_dispatch/http/url.rb +53 -31
  70. data/lib/action_dispatch/journey.rb +5 -0
  71. data/lib/action_dispatch/journey/backwards.rb +5 -0
  72. data/lib/action_dispatch/journey/formatter.rb +146 -0
  73. data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
  74. data/lib/action_dispatch/journey/gtg/simulator.rb +44 -0
  75. data/lib/action_dispatch/journey/gtg/transition_table.rb +156 -0
  76. data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
  77. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  78. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  79. data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
  80. data/lib/action_dispatch/journey/nodes/node.rb +124 -0
  81. data/lib/action_dispatch/journey/parser.rb +206 -0
  82. data/lib/action_dispatch/journey/parser.y +47 -0
  83. data/lib/action_dispatch/journey/parser_extras.rb +23 -0
  84. data/lib/action_dispatch/journey/path/pattern.rb +196 -0
  85. data/lib/action_dispatch/journey/route.rb +116 -0
  86. data/lib/action_dispatch/journey/router.rb +164 -0
  87. data/lib/action_dispatch/journey/router/strexp.rb +24 -0
  88. data/lib/action_dispatch/journey/router/utils.rb +54 -0
  89. data/lib/action_dispatch/journey/routes.rb +75 -0
  90. data/lib/action_dispatch/journey/scanner.rb +61 -0
  91. data/lib/action_dispatch/journey/visitors.rb +189 -0
  92. data/lib/action_dispatch/journey/visualizer/fsm.css +34 -0
  93. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  94. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  95. data/lib/action_dispatch/middleware/callbacks.rb +9 -4
  96. data/lib/action_dispatch/middleware/cookies.rb +168 -57
  97. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -17
  98. data/lib/action_dispatch/middleware/exception_wrapper.rb +27 -3
  99. data/lib/action_dispatch/middleware/flash.rb +58 -58
  100. data/lib/action_dispatch/middleware/params_parser.rb +14 -29
  101. data/lib/action_dispatch/middleware/public_exceptions.rb +31 -14
  102. data/lib/action_dispatch/middleware/reloader.rb +6 -6
  103. data/lib/action_dispatch/middleware/remote_ip.rb +145 -39
  104. data/lib/action_dispatch/middleware/request_id.rb +2 -6
  105. data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
  106. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  107. data/lib/action_dispatch/middleware/session/cookie_store.rb +81 -7
  108. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
  109. data/lib/action_dispatch/middleware/show_exceptions.rb +12 -45
  110. data/lib/action_dispatch/middleware/ssl.rb +70 -0
  111. data/lib/action_dispatch/middleware/stack.rb +6 -1
  112. data/lib/action_dispatch/middleware/static.rb +5 -24
  113. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +14 -11
  114. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +25 -0
  115. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
  116. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +15 -9
  117. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -5
  118. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +7 -2
  119. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +30 -15
  120. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +39 -13
  121. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +6 -2
  122. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  123. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +144 -0
  124. data/lib/action_dispatch/railtie.rb +16 -6
  125. data/lib/action_dispatch/request/session.rb +181 -0
  126. data/lib/action_dispatch/routing.rb +41 -40
  127. data/lib/action_dispatch/routing/inspector.rb +240 -0
  128. data/lib/action_dispatch/routing/mapper.rb +501 -273
  129. data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -20
  130. data/lib/action_dispatch/routing/redirection.rb +46 -29
  131. data/lib/action_dispatch/routing/route_set.rb +203 -164
  132. data/lib/action_dispatch/routing/routes_proxy.rb +2 -0
  133. data/lib/action_dispatch/routing/url_for.rb +48 -33
  134. data/lib/action_dispatch/testing/assertions/dom.rb +3 -13
  135. data/lib/action_dispatch/testing/assertions/response.rb +32 -40
  136. data/lib/action_dispatch/testing/assertions/routing.rb +40 -39
  137. data/lib/action_dispatch/testing/assertions/selector.rb +15 -20
  138. data/lib/action_dispatch/testing/assertions/tag.rb +20 -23
  139. data/lib/action_dispatch/testing/integration.rb +41 -22
  140. data/lib/action_dispatch/testing/test_process.rb +9 -6
  141. data/lib/action_dispatch/testing/test_request.rb +7 -3
  142. data/lib/action_pack.rb +1 -1
  143. data/lib/action_pack/version.rb +4 -4
  144. data/lib/action_view.rb +17 -8
  145. data/lib/action_view/base.rb +15 -34
  146. data/lib/action_view/buffers.rb +1 -1
  147. data/lib/action_view/context.rb +4 -4
  148. data/lib/action_view/dependency_tracker.rb +91 -0
  149. data/lib/action_view/digestor.rb +85 -0
  150. data/lib/action_view/flows.rb +1 -4
  151. data/lib/action_view/helpers.rb +2 -4
  152. data/lib/action_view/helpers/active_model_helper.rb +3 -4
  153. data/lib/action_view/helpers/asset_tag_helper.rb +211 -353
  154. data/lib/action_view/helpers/asset_url_helper.rb +354 -0
  155. data/lib/action_view/helpers/atom_feed_helper.rb +13 -10
  156. data/lib/action_view/helpers/cache_helper.rb +150 -18
  157. data/lib/action_view/helpers/capture_helper.rb +42 -29
  158. data/lib/action_view/helpers/csrf_helper.rb +0 -2
  159. data/lib/action_view/helpers/date_helper.rb +268 -247
  160. data/lib/action_view/helpers/debug_helper.rb +10 -11
  161. data/lib/action_view/helpers/form_helper.rb +904 -547
  162. data/lib/action_view/helpers/form_options_helper.rb +341 -166
  163. data/lib/action_view/helpers/form_tag_helper.rb +188 -88
  164. data/lib/action_view/helpers/javascript_helper.rb +23 -16
  165. data/lib/action_view/helpers/number_helper.rb +148 -354
  166. data/lib/action_view/helpers/output_safety_helper.rb +3 -3
  167. data/lib/action_view/helpers/record_tag_helper.rb +17 -22
  168. data/lib/action_view/helpers/rendering_helper.rb +2 -4
  169. data/lib/action_view/helpers/sanitize_helper.rb +3 -6
  170. data/lib/action_view/helpers/tag_helper.rb +43 -37
  171. data/lib/action_view/helpers/tags.rb +39 -0
  172. data/lib/action_view/helpers/tags/base.rb +148 -0
  173. data/lib/action_view/helpers/tags/check_box.rb +64 -0
  174. data/lib/action_view/helpers/tags/checkable.rb +16 -0
  175. data/lib/action_view/helpers/tags/collection_check_boxes.rb +43 -0
  176. data/lib/action_view/helpers/tags/collection_helpers.rb +83 -0
  177. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
  178. data/lib/action_view/helpers/tags/collection_select.rb +28 -0
  179. data/lib/action_view/helpers/tags/color_field.rb +25 -0
  180. data/lib/action_view/helpers/tags/date_field.rb +13 -0
  181. data/lib/action_view/helpers/tags/date_select.rb +72 -0
  182. data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
  183. data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
  184. data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
  185. data/lib/action_view/helpers/tags/email_field.rb +8 -0
  186. data/lib/action_view/helpers/tags/file_field.rb +8 -0
  187. data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
  188. data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
  189. data/lib/action_view/helpers/tags/label.rb +65 -0
  190. data/lib/action_view/helpers/tags/month_field.rb +13 -0
  191. data/lib/action_view/helpers/tags/number_field.rb +18 -0
  192. data/lib/action_view/helpers/tags/password_field.rb +12 -0
  193. data/lib/action_view/helpers/tags/radio_button.rb +31 -0
  194. data/lib/action_view/helpers/tags/range_field.rb +8 -0
  195. data/lib/action_view/helpers/tags/search_field.rb +24 -0
  196. data/lib/action_view/helpers/tags/select.rb +41 -0
  197. data/lib/action_view/helpers/tags/tel_field.rb +8 -0
  198. data/lib/action_view/helpers/tags/text_area.rb +18 -0
  199. data/lib/action_view/helpers/tags/text_field.rb +29 -0
  200. data/lib/action_view/helpers/tags/time_field.rb +13 -0
  201. data/lib/action_view/helpers/tags/time_select.rb +8 -0
  202. data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
  203. data/lib/action_view/helpers/tags/url_field.rb +8 -0
  204. data/lib/action_view/helpers/tags/week_field.rb +13 -0
  205. data/lib/action_view/helpers/text_helper.rb +126 -113
  206. data/lib/action_view/helpers/translation_helper.rb +32 -16
  207. data/lib/action_view/helpers/url_helper.rb +200 -271
  208. data/lib/action_view/locale/en.yml +1 -105
  209. data/lib/action_view/log_subscriber.rb +6 -4
  210. data/lib/action_view/lookup_context.rb +15 -39
  211. data/lib/action_view/model_naming.rb +12 -0
  212. data/lib/action_view/path_set.rb +9 -39
  213. data/lib/action_view/railtie.rb +6 -22
  214. data/lib/action_view/record_identifier.rb +84 -0
  215. data/lib/action_view/renderer/abstract_renderer.rb +10 -19
  216. data/lib/action_view/renderer/partial_renderer.rb +144 -81
  217. data/lib/action_view/renderer/renderer.rb +2 -19
  218. data/lib/action_view/renderer/streaming_template_renderer.rb +2 -5
  219. data/lib/action_view/renderer/template_renderer.rb +14 -13
  220. data/lib/action_view/routing_url_for.rb +107 -0
  221. data/lib/action_view/template.rb +22 -21
  222. data/lib/action_view/template/error.rb +22 -12
  223. data/lib/action_view/template/handlers.rb +12 -9
  224. data/lib/action_view/template/handlers/builder.rb +1 -1
  225. data/lib/action_view/template/handlers/erb.rb +11 -16
  226. data/lib/action_view/template/handlers/raw.rb +11 -0
  227. data/lib/action_view/template/resolver.rb +111 -83
  228. data/lib/action_view/template/text.rb +12 -8
  229. data/lib/action_view/template/types.rb +57 -0
  230. data/lib/action_view/test_case.rb +66 -43
  231. data/lib/action_view/testing/resolvers.rb +3 -2
  232. data/lib/action_view/vendor/html-scanner.rb +20 -0
  233. data/lib/{action_controller → action_view}/vendor/html-scanner/html/document.rb +0 -0
  234. data/lib/{action_controller → action_view}/vendor/html-scanner/html/node.rb +12 -12
  235. data/lib/{action_controller → action_view}/vendor/html-scanner/html/sanitizer.rb +18 -7
  236. data/lib/{action_controller → action_view}/vendor/html-scanner/html/selector.rb +1 -1
  237. data/lib/{action_controller → action_view}/vendor/html-scanner/html/tokenizer.rb +1 -1
  238. data/lib/{action_controller → action_view}/vendor/html-scanner/html/version.rb +0 -0
  239. metadata +135 -125
  240. data/lib/action_controller/caching/actions.rb +0 -185
  241. data/lib/action_controller/caching/pages.rb +0 -187
  242. data/lib/action_controller/caching/sweeping.rb +0 -97
  243. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  244. data/lib/action_controller/metal/compatibility.rb +0 -65
  245. data/lib/action_controller/metal/session_management.rb +0 -14
  246. data/lib/action_controller/railties/paths.rb +0 -25
  247. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  248. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  249. data/lib/action_dispatch/middleware/head.rb +0 -18
  250. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  251. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  252. data/lib/action_view/asset_paths.rb +0 -142
  253. data/lib/action_view/helpers/asset_paths.rb +0 -7
  254. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  255. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  256. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  257. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  258. data/lib/sprockets/assets.rake +0 -99
  259. data/lib/sprockets/bootstrap.rb +0 -37
  260. data/lib/sprockets/compressors.rb +0 -83
  261. data/lib/sprockets/helpers.rb +0 -6
  262. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  263. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  264. data/lib/sprockets/railtie.rb +0 -62
  265. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,14 +1,24 @@
1
1
  require 'action_view/helpers/tag_helper'
2
2
  require 'i18n/exceptions'
3
3
 
4
+ module I18n
5
+ class ExceptionHandler
6
+ include Module.new {
7
+ def call(exception, locale, key, options)
8
+ exception.is_a?(MissingTranslation) && options[:rescue_format] == :html ? super.html_safe : super
9
+ end
10
+ }
11
+ end
12
+ end
13
+
4
14
  module ActionView
5
15
  # = Action View Translation Helpers
6
16
  module Helpers
7
17
  module TranslationHelper
8
18
  # Delegates to <tt>I18n#translate</tt> but also performs three additional functions.
9
19
  #
10
- # First, it will ensure that any thrown +MissingTranslation+ messages will be turned
11
- # into inline spans that:
20
+ # First, it'll pass the <tt>rescue_format: :html</tt> option to I18n so that any
21
+ # thrown +MissingTranslation+ messages will be turned into inline spans that
12
22
  #
13
23
  # * have a "translation-missing" class set,
14
24
  # * contain the missing key as a title attribute and
@@ -34,15 +44,8 @@ module ActionView
34
44
  # naming convention helps to identify translations that include HTML tags so that
35
45
  # you know what kind of output to expect when you call translate in a template.
36
46
  def translate(key, options = {})
37
- # If the user has specified rescue_format then pass it all through, otherwise use
38
- # raise and do the work ourselves
39
- if options.key?(:raise) || options.key?(:rescue_format)
40
- raise_error = options[:raise] || options[:rescue_format]
41
- else
42
- raise_error = false
43
- options[:raise] = true
44
- end
45
-
47
+ options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
48
+ options[:default] = wrap_translate_defaults(options[:default]) if options[:default]
46
49
  if html_safe_translation_key?(key)
47
50
  html_safe_options = options.dup
48
51
  options.except(*I18n::RESERVED_KEYS).each do |name, value|
@@ -56,15 +59,13 @@ module ActionView
56
59
  else
57
60
  I18n.translate(scope_key_by_partial(key), options)
58
61
  end
59
- rescue I18n::MissingTranslationData => e
60
- raise e if raise_error
61
-
62
- keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
63
- content_tag('span', keys.last.to_s.titleize, :class => 'translation_missing', :title => "translation missing: #{keys.join('.')}")
64
62
  end
65
63
  alias :t :translate
66
64
 
67
65
  # Delegates to <tt>I18n.localize</tt> with no additional functionality.
66
+ #
67
+ # See http://rubydoc.info/github/svenfuchs/i18n/master/I18n/Backend/Base:localize
68
+ # for more information.
68
69
  def localize(*args)
69
70
  I18n.localize(*args)
70
71
  end
@@ -86,6 +87,21 @@ module ActionView
86
87
  def html_safe_translation_key?(key)
87
88
  key.to_s =~ /(\b|_|\.)html$/
88
89
  end
90
+
91
+ def wrap_translate_defaults(defaults)
92
+ new_defaults = []
93
+ defaults = Array(defaults)
94
+ while key = defaults.shift
95
+ if key.is_a?(Symbol)
96
+ new_defaults << lambda { |_, options| translate key, options.merge(:default => defaults) }
97
+ break
98
+ else
99
+ new_defaults << key
100
+ end
101
+ end
102
+
103
+ new_defaults
104
+ end
89
105
  end
90
106
  end
91
107
  end
@@ -2,7 +2,6 @@ require 'action_view/helpers/javascript_helper'
2
2
  require 'active_support/core_ext/array/access'
3
3
  require 'active_support/core_ext/hash/keys'
4
4
  require 'active_support/core_ext/string/output_safety'
5
- require 'action_dispatch'
6
5
 
7
6
  module ActionView
8
7
  # = Action View URL Helpers
@@ -20,98 +19,33 @@ module ActionView
20
19
 
21
20
  extend ActiveSupport::Concern
22
21
 
23
- include ActionDispatch::Routing::UrlFor
24
22
  include TagHelper
25
23
 
26
- def _routes_context
27
- controller
28
- end
29
-
30
- # Need to map default url options to controller one.
31
- # def default_url_options(*args) #:nodoc:
32
- # controller.send(:default_url_options, *args)
33
- # end
34
- #
35
- def url_options
36
- return super unless controller.respond_to?(:url_options)
37
- controller.url_options
24
+ module ClassMethods
25
+ def _url_for_modules
26
+ ActionView::RoutingUrlFor
27
+ end
38
28
  end
39
29
 
40
- # Returns the URL for the set of +options+ provided. This takes the
41
- # same options as +url_for+ in Action Controller (see the
42
- # documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
43
- # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action"
44
- # instead of the fully qualified URL like "http://example.com/controller/action".
45
- #
46
- # ==== Options
47
- # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
48
- # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
49
- # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
50
- # is currently not recommended since it breaks caching.
51
- # * <tt>:host</tt> - Overrides the default (current) host if provided.
52
- # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
53
- # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
54
- # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
55
- #
56
- # ==== Relying on named routes
57
- #
58
- # Passing a record (like an Active Record or Active Resource) instead of a Hash as the options parameter will
59
- # trigger the named route for that record. The lookup will happen on the name of the class. So passing a
60
- # Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
61
- # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
62
- #
63
- # ==== Examples
64
- # <%= url_for(:action => 'index') %>
65
- # # => /blog/
66
- #
67
- # <%= url_for(:action => 'find', :controller => 'books') %>
68
- # # => /books/find
69
- #
70
- # <%= url_for(:action => 'login', :controller => 'members', :only_path => false, :protocol => 'https') %>
71
- # # => https://www.example.com/members/login/
72
- #
73
- # <%= url_for(:action => 'play', :anchor => 'player') %>
74
- # # => /messages/play/#player
75
- #
76
- # <%= url_for(:action => 'jump', :anchor => 'tax&ship') %>
77
- # # => /testing/jump/#tax&ship
78
- #
79
- # <%= url_for(Workshop.new) %>
80
- # # relies on Workshop answering a persisted? call (and in this case returning false)
81
- # # => /workshops
82
- #
83
- # <%= url_for(@workshop) %>
84
- # # calls @workshop.to_param which by default returns the id
85
- # # => /workshops/5
86
- #
87
- # # to_param can be re-defined in a model to provide different URL names:
88
- # # => /workshops/1-workshop-name
89
- #
90
- # <%= url_for("http://www.example.com") %>
91
- # # => http://www.example.com
92
- #
93
- # <%= url_for(:back) %>
94
- # # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
95
- # # => http://www.example.com
96
- #
97
- # <%= url_for(:back) %>
98
- # # if request.env["HTTP_REFERER"] is not set or is blank
99
- # # => javascript:history.back()
100
- def url_for(options = {})
101
- options ||= {}
30
+ # Basic implementation of url_for to allow use helpers without routes existence
31
+ def url_for(options = nil) # :nodoc:
102
32
  case options
103
33
  when String
104
34
  options
105
- when Hash
106
- options = options.symbolize_keys.reverse_merge!(:only_path => options[:host].nil?)
107
- super
108
35
  when :back
109
- controller.request.env["HTTP_REFERER"] || 'javascript:history.back()'
36
+ _back_url
110
37
  else
111
- polymorphic_path(options)
38
+ raise ArgumentError, "arguments passed to url_for can't be handled. Please require " +
39
+ "routes or provide your own implementation"
112
40
  end
113
41
  end
114
42
 
43
+ def _back_url # :nodoc:
44
+ referrer = controller.respond_to?(:request) && controller.request.env["HTTP_REFERER"]
45
+ referrer || 'javascript:history.back()'
46
+ end
47
+ protected :_back_url
48
+
115
49
  # Creates a link tag of the given +name+ using a URL created by the set of +options+.
116
50
  # See the valid options in the documentation for +url_for+. It's also possible to
117
51
  # pass a String instead of an options hash, which generates a link tag that uses the
@@ -127,8 +61,7 @@ module ActionView
127
61
  # # posts_path
128
62
  #
129
63
  # link_to(body, url_options = {}, html_options = {})
130
- # # url_options, except :confirm or :method,
131
- # # is passed to url_for
64
+ # # url_options, except :method, is passed to url_for
132
65
  #
133
66
  # link_to(options = {}, html_options = {}) do
134
67
  # # name
@@ -139,25 +72,33 @@ module ActionView
139
72
  # end
140
73
  #
141
74
  # ==== Options
142
- # * <tt>:confirm => 'question?'</tt> - This will allow the unobtrusive JavaScript
143
- # driver to prompt with the question specified. If the user accepts, the link is
144
- # processed normally, otherwise no action is taken.
145
- # * <tt>:method => symbol of HTTP verb</tt> - This modifier will dynamically
75
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
76
+ # * <tt>method: symbol of HTTP verb</tt> - This modifier will dynamically
146
77
  # create an HTML form and immediately submit the form for processing using
147
78
  # the HTTP verb specified. Useful for having links perform a POST operation
148
79
  # in dangerous actions like deleting a record (which search bots can follow
149
- # while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt> and <tt>:put</tt>.
80
+ # while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>.
150
81
  # Note that if the user has JavaScript disabled, the request will fall back
151
- # to using GET. If <tt>:href => '#'</tt> is used and the user has JavaScript
82
+ # to using GET. If <tt>href: '#'</tt> is used and the user has JavaScript
152
83
  # disabled clicking the link will have no effect. If you are relying on the
153
84
  # POST behavior, you should check for it in your controller's action by using
154
- # the request object's methods for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
155
- # * <tt>:remote => true</tt> - This will allow the unobtrusive JavaScript
85
+ # the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>:patch</tt>, or <tt>put?</tt>.
86
+ # * <tt>remote: true</tt> - This will allow the unobtrusive JavaScript
156
87
  # driver to make an Ajax request to the URL in question instead of following
157
88
  # the link. The drivers each provide mechanisms for listening for the
158
89
  # completion of the Ajax request and performing JavaScript operations once
159
90
  # they're complete
160
91
  #
92
+ # ==== Data attributes
93
+ #
94
+ # * <tt>confirm: 'question?'</tt> - This will allow the unobtrusive JavaScript
95
+ # driver to prompt with the question specified. If the user accepts, the link is
96
+ # processed normally, otherwise no action is taken.
97
+ # * <tt>:disable_with</tt> - Value of this parameter will be
98
+ # used as the value for a disabled version of the submit
99
+ # button when the form is submitted. This feature is provided
100
+ # by the unobtrusive JavaScript driver.
101
+ #
161
102
  # ==== Examples
162
103
  # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
163
104
  # and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
@@ -173,7 +114,7 @@ module ActionView
173
114
  #
174
115
  # in place of the older more verbose, non-resource-oriented
175
116
  #
176
- # link_to "Profile", :controller => "profiles", :action => "show", :id => @profile
117
+ # link_to "Profile", controller: "profiles", action: "show", id: @profile
177
118
  # # => <a href="/profiles/show/1">Profile</a>
178
119
  #
179
120
  # Similarly,
@@ -183,7 +124,7 @@ module ActionView
183
124
  #
184
125
  # is better than
185
126
  #
186
- # link_to "Profiles", :controller => "profiles"
127
+ # link_to "Profiles", controller: "profiles"
187
128
  # # => <a href="/profiles">Profiles</a>
188
129
  #
189
130
  # You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
@@ -197,56 +138,49 @@ module ActionView
197
138
  #
198
139
  # Classes and ids for CSS are easy to produce:
199
140
  #
200
- # link_to "Articles", articles_path, :id => "news", :class => "article"
141
+ # link_to "Articles", articles_path, id: "news", class: "article"
201
142
  # # => <a href="/articles" class="article" id="news">Articles</a>
202
143
  #
203
144
  # Be careful when using the older argument style, as an extra literal hash is needed:
204
145
  #
205
- # link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
146
+ # link_to "Articles", { controller: "articles" }, id: "news", class: "article"
206
147
  # # => <a href="/articles" class="article" id="news">Articles</a>
207
148
  #
208
149
  # Leaving the hash off gives the wrong link:
209
150
  #
210
- # link_to "WRONG!", :controller => "articles", :id => "news", :class => "article"
151
+ # link_to "WRONG!", controller: "articles", id: "news", class: "article"
211
152
  # # => <a href="/articles/index/news?class=article">WRONG!</a>
212
153
  #
213
154
  # +link_to+ can also produce links with anchors or query strings:
214
155
  #
215
- # link_to "Comment wall", profile_path(@profile, :anchor => "wall")
156
+ # link_to "Comment wall", profile_path(@profile, anchor: "wall")
216
157
  # # => <a href="/profiles/1#wall">Comment wall</a>
217
158
  #
218
- # link_to "Ruby on Rails search", :controller => "searches", :query => "ruby on rails"
159
+ # link_to "Ruby on Rails search", controller: "searches", query: "ruby on rails"
219
160
  # # => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
220
161
  #
221
- # link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
162
+ # link_to "Nonsense search", searches_path(foo: "bar", baz: "quux")
222
163
  # # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
223
164
  #
224
- # The two options specific to +link_to+ (<tt>:confirm</tt> and <tt>:method</tt>) are used as follows:
165
+ # The only option specific to +link_to+ (<tt>:method</tt>) is used as follows:
225
166
  #
226
- # link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
227
- # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?"">Visit Other Site</a>
167
+ # link_to("Destroy", "http://www.example.com", method: :delete)
168
+ # # => <a href='http://www.example.com' rel="nofollow" data-method="delete">Destroy</a>
228
169
  #
229
- # link_to("Destroy", "http://www.example.com", :method => :delete, :confirm => "Are you sure?")
230
- # # => <a href='http://www.example.com' rel="nofollow" data-method="delete" data-confirm="Are you sure?">Destroy</a>
231
- def link_to(*args, &block)
232
- if block_given?
233
- options = args.first || {}
234
- html_options = args.second
235
- link_to(capture(&block), options, html_options)
236
- else
237
- name = args[0]
238
- options = args[1] || {}
239
- html_options = args[2]
170
+ # You can also use custom data attributes using the <tt>:data</tt> option:
171
+ #
172
+ # link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
173
+ # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a>
174
+ def link_to(name = nil, options = nil, html_options = nil, &block)
175
+ html_options, options = options, name if block_given?
176
+ options ||= {}
240
177
 
241
- html_options = convert_options_to_data_attributes(options, html_options)
242
- url = url_for(options)
178
+ html_options = convert_options_to_data_attributes(options, html_options)
243
179
 
244
- href = html_options['href']
245
- tag_options = tag_options(html_options)
180
+ url = url_for(options)
181
+ html_options['href'] ||= url
246
182
 
247
- href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" unless href
248
- "<a #{href_attr}#{tag_options}>#{ERB::Util.html_escape(name || url)}</a>".html_safe
249
- end
183
+ content_tag(:a, name || url, html_options, &block)
250
184
  end
251
185
 
252
186
  # Generates a form containing a single button that submits to the URL created
@@ -260,10 +194,9 @@ module ActionView
260
194
  # to allow styling of the form itself and its children. This can be changed
261
195
  # using the <tt>:form_class</tt> modifier within +html_options+. You can control
262
196
  # the form submission and input element behavior using +html_options+.
263
- # This method accepts the <tt>:method</tt> and <tt>:confirm</tt> modifiers
264
- # described in the +link_to+ documentation. If no <tt>:method</tt> modifier
265
- # is given, it will default to performing a POST operation. You can also
266
- # disable the button by passing <tt>:disabled => true</tt> in +html_options+.
197
+ # This method accepts the <tt>:method</tt> modifier described in the +link_to+ documentation.
198
+ # If no <tt>:method</tt> modifier is given, it will default to performing a POST operation.
199
+ # You can also disable the button by passing <tt>disabled: true</tt> in +html_options+.
267
200
  # If you are using RESTful routes, you can pass the <tt>:method</tt>
268
201
  # to change the HTTP verb used to submit the form.
269
202
  #
@@ -272,89 +205,114 @@ module ActionView
272
205
  #
273
206
  # There are a few special +html_options+:
274
207
  # * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
275
- # <tt>:delete</tt> and <tt>:put</tt>. By default it will be <tt>:post</tt>.
208
+ # <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>.
276
209
  # * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
277
- # * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
278
- # prompt with the question specified. If the user accepts, the link is
279
- # processed normally, otherwise no action is taken.
210
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
280
211
  # * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
281
212
  # submit behavior. By default this behavior is an ajax submit.
282
213
  # * <tt>:form</tt> - This hash will be form attributes
283
214
  # * <tt>:form_class</tt> - This controls the class of the form within which the submit button will
284
215
  # be placed
285
216
  #
217
+ # ==== Data attributes
218
+ #
219
+ # * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
220
+ # prompt with the question specified. If the user accepts, the link is
221
+ # processed normally, otherwise no action is taken.
222
+ # * <tt>:disable_with</tt> - Value of this parameter will be
223
+ # used as the value for a disabled version of the submit
224
+ # button when the form is submitted. This feature is provided
225
+ # by the unobtrusive JavaScript driver.
226
+ #
286
227
  # ==== Examples
287
- # <%= button_to "New", :action => "new" %>
228
+ # <%= button_to "New", action: "new" %>
288
229
  # # => "<form method="post" action="/controller/new" class="button_to">
289
230
  # # <div><input value="New" type="submit" /></div>
290
231
  # # </form>"
291
232
  #
233
+ # <%= button_to [:make_happy, @user] do %>
234
+ # Make happy <strong><%= @user.name %></strong>
235
+ # <% end %>
236
+ # # => "<form method="post" action="/users/1/make_happy" class="button_to">
237
+ # # <div>
238
+ # # <button type="submit">
239
+ # # Make happy <strong><%= @user.name %></strong>
240
+ # # </button>
241
+ # # </div>
242
+ # # </form>"
292
243
  #
293
- # <%= button_to "New", :action => "new", :form_class => "new-thing" %>
244
+ # <%= button_to "New", { action: "new" }, form_class: "new-thing" %>
294
245
  # # => "<form method="post" action="/controller/new" class="new-thing">
295
246
  # # <div><input value="New" type="submit" /></div>
296
247
  # # </form>"
297
248
  #
298
249
  #
299
- # <%= button_to "Create", :action => "create", :remote => true, :form => { "data-type" => "json" } %>
250
+ # <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %>
300
251
  # # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
301
- # # <div><input value="Create" type="submit" /></div>
252
+ # # <div>
253
+ # # <input value="Create" type="submit" />
254
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
255
+ # # </div>
302
256
  # # </form>"
303
257
  #
304
- #
305
- # <%= button_to "Delete Image", { :action => "delete", :id => @image.id },
306
- # :confirm => "Are you sure?", :method => :delete %>
258
+ #
259
+ # <%= button_to "Delete Image", { action: "delete", id: @image.id },
260
+ # method: :delete, data: { confirm: "Are you sure?" } %>
307
261
  # # => "<form method="post" action="/images/delete/1" class="button_to">
308
262
  # # <div>
309
263
  # # <input type="hidden" name="_method" value="delete" />
310
- # # <input data-confirm='Are you sure?' value="Delete" type="submit" />
264
+ # # <input data-confirm='Are you sure?' value="Delete Image" type="submit" />
265
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
311
266
  # # </div>
312
267
  # # </form>"
313
268
  #
314
269
  #
315
- # <%= button_to('Destroy', 'http://www.example.com', :confirm => 'Are you sure?',
316
- # :method => "delete", :remote => true, :disable_with => 'loading...') %>
270
+ # <%= button_to('Destroy', 'http://www.example.com',
271
+ # method: "delete", remote: true, data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
317
272
  # # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
318
273
  # # <div>
319
274
  # # <input name='_method' value='delete' type='hidden' />
320
- # # <input value='Destroy' type='submit' disable_with='loading...' data-confirm='Are you sure?' />
275
+ # # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' />
276
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
321
277
  # # </div>
322
278
  # # </form>"
323
279
  # #
324
- def button_to(name, options = {}, html_options = {})
280
+ def button_to(name = nil, options = nil, html_options = nil, &block)
281
+ html_options, options = options, name if block_given?
282
+ options ||= {}
283
+ html_options ||= {}
284
+
325
285
  html_options = html_options.stringify_keys
326
- convert_boolean_attributes!(html_options, %w( disabled ))
286
+ convert_boolean_attributes!(html_options, %w(disabled))
327
287
 
328
- method_tag = ''
329
- if (method = html_options.delete('method')) && %w{put delete}.include?(method.to_s)
330
- method_tag = tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
331
- end
288
+ url = options.is_a?(String) ? options : url_for(options)
289
+ remote = html_options.delete('remote')
332
290
 
333
- form_method = method.to_s == 'get' ? 'get' : 'post'
291
+ method = html_options.delete('method').to_s
292
+ method_tag = %w{patch put delete}.include?(method) ? method_tag(method) : ''.html_safe
293
+
294
+ form_method = method == 'get' ? 'get' : 'post'
334
295
  form_options = html_options.delete('form') || {}
335
296
  form_options[:class] ||= html_options.delete('form_class') || 'button_to'
336
-
337
- remote = html_options.delete('remote')
338
-
339
- request_token_tag = ''
340
- if form_method == 'post' && protect_against_forgery?
341
- request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
342
- end
297
+ form_options.merge!(method: form_method, action: url)
298
+ form_options.merge!("data-remote" => "true") if remote
343
299
 
344
- url = options.is_a?(String) ? options : self.url_for(options)
345
- name ||= url
300
+ request_token_tag = form_method == 'post' ? token_tag : ''
346
301
 
347
302
  html_options = convert_options_to_data_attributes(options, html_options)
303
+ html_options['type'] = 'submit'
348
304
 
349
- html_options.merge!("type" => "submit", "value" => name)
305
+ button = if block_given?
306
+ content_tag('button', html_options, &block)
307
+ else
308
+ html_options['value'] = name || url
309
+ tag('input', html_options)
310
+ end
350
311
 
351
- form_options.merge!(:method => form_method, :action => url)
352
- form_options.merge!("data-remote" => "true") if remote
353
-
354
- "#{tag(:form, form_options, true)}<div>#{method_tag}#{tag("input", html_options)}#{request_token_tag}</div></form>".html_safe
312
+ inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
313
+ content_tag('form', content_tag('div', inner_tags), form_options)
355
314
  end
356
315
 
357
-
358
316
  # Creates a link tag of the given +name+ using a URL created by the set of
359
317
  # +options+ unless the current request URI is the same as the links, in
360
318
  # which case only the name is returned (or the given block is yielded, if
@@ -366,8 +324,8 @@ module ActionView
366
324
  # Let's say you have a navigation menu...
367
325
  #
368
326
  # <ul id="navbar">
369
- # <li><%= link_to_unless_current("Home", { :action => "index" }) %></li>
370
- # <li><%= link_to_unless_current("About Us", { :action => "about" }) %></li>
327
+ # <li><%= link_to_unless_current("Home", { action: "index" }) %></li>
328
+ # <li><%= link_to_unless_current("About Us", { action: "about" }) %></li>
371
329
  # </ul>
372
330
  #
373
331
  # If in the "about" action, it will render...
@@ -389,8 +347,8 @@ module ActionView
389
347
  # "Go Back" link instead of a link to the comments page, we could do something like this...
390
348
  #
391
349
  # <%=
392
- # link_to_unless_current("Comment", { :controller => "comments", :action => "new" }) do
393
- # link_to("Go back", { :controller => "posts", :action => "index" })
350
+ # link_to_unless_current("Comment", { controller: "comments", action: "new" }) do
351
+ # link_to("Go back", { controller: "posts", action: "index" })
394
352
  # end
395
353
  # %>
396
354
  def link_to_unless_current(name, options = {}, html_options = {}, &block)
@@ -404,13 +362,13 @@ module ActionView
404
362
  # accepts the name or the full argument list for +link_to_unless+.
405
363
  #
406
364
  # ==== Examples
407
- # <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) %>
365
+ # <%= link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) %>
408
366
  # # If the user is logged in...
409
367
  # # => <a href="/controller/reply/">Reply</a>
410
368
  #
411
369
  # <%=
412
- # link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) do |name|
413
- # link_to(name, { :controller => "accounts", :action => "signup" })
370
+ # link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) do |name|
371
+ # link_to(name, { controller: "accounts", action: "signup" })
414
372
  # end
415
373
  # %>
416
374
  # # If the user is logged in...
@@ -422,7 +380,7 @@ module ActionView
422
380
  if block_given?
423
381
  block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
424
382
  else
425
- ERB::Util.html_escape(name)
383
+ name
426
384
  end
427
385
  else
428
386
  link_to(name, options, html_options)
@@ -436,13 +394,13 @@ module ActionView
436
394
  # in +link_to_unless+).
437
395
  #
438
396
  # ==== Examples
439
- # <%= link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) %>
397
+ # <%= link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) %>
440
398
  # # If the user isn't logged in...
441
399
  # # => <a href="/sessions/new/">Login</a>
442
400
  #
443
401
  # <%=
444
- # link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) do
445
- # link_to(@current_user.login, { :controller => "accounts", :action => "show", :id => @current_user })
402
+ # link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) do
403
+ # link_to(@current_user.login, { controller: "accounts", action: "show", id: @current_user })
446
404
  # end
447
405
  # %>
448
406
  # # If the user isn't logged in...
@@ -457,132 +415,96 @@ module ActionView
457
415
  # also used as the name of the link unless +name+ is specified. Additional
458
416
  # HTML attributes for the link can be passed in +html_options+.
459
417
  #
460
- # +mail_to+ has several methods for hindering email harvesters and customizing
461
- # the email itself by passing special keys to +html_options+.
418
+ # +mail_to+ has several methods for customizing the email itself by
419
+ # passing special keys to +html_options+.
462
420
  #
463
421
  # ==== Options
464
- # * <tt>:encode</tt> - This key will accept the strings "javascript" or "hex".
465
- # Passing "javascript" will dynamically create and encode the mailto link then
466
- # eval it into the DOM of the page. This method will not show the link on
467
- # the page if the user has JavaScript disabled. Passing "hex" will hex
468
- # encode the +email_address+ before outputting the mailto link.
469
- # * <tt>:replace_at</tt> - When the link +name+ isn't provided, the
470
- # +email_address+ is used for the link label. You can use this option to
471
- # obfuscate the +email_address+ by substituting the @ sign with the string
472
- # given as the value.
473
- # * <tt>:replace_dot</tt> - When the link +name+ isn't provided, the
474
- # +email_address+ is used for the link label. You can use this option to
475
- # obfuscate the +email_address+ by substituting the . in the email with the
476
- # string given as the value.
477
422
  # * <tt>:subject</tt> - Preset the subject line of the email.
478
423
  # * <tt>:body</tt> - Preset the body of the email.
479
424
  # * <tt>:cc</tt> - Carbon Copy additional recipients on the email.
480
425
  # * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
481
426
  #
427
+ # ==== Obfuscation
428
+ # Prior to Rails 4.0, +mail_to+ provided options for encoding the address
429
+ # in order to hinder email harvesters. To take advantage of these options,
430
+ # install the +actionview-encoded_mail_to+ gem.
431
+ #
482
432
  # ==== Examples
483
433
  # mail_to "me@domain.com"
484
434
  # # => <a href="mailto:me@domain.com">me@domain.com</a>
485
435
  #
486
- # mail_to "me@domain.com", "My email", :encode => "javascript"
487
- # # => <script type="text/javascript">eval(decodeURIComponent('%64%6f%63...%27%29%3b'))</script>
436
+ # mail_to "me@domain.com", "My email"
437
+ # # => <a href="mailto:me@domain.com">My email</a>
488
438
  #
489
- # mail_to "me@domain.com", "My email", :encode => "hex"
490
- # # => <a href="mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>
491
- #
492
- # mail_to "me@domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email"
493
- # # => <a href="mailto:me@domain.com" class="email">me_at_domain_dot_com</a>
494
- #
495
- # mail_to "me@domain.com", "My email", :cc => "ccaddress@domain.com",
496
- # :subject => "This is an example email"
439
+ # mail_to "me@domain.com", "My email", cc: "ccaddress@domain.com",
440
+ # subject: "This is an example email"
497
441
  # # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
498
442
  def mail_to(email_address, name = nil, html_options = {})
499
443
  email_address = ERB::Util.html_escape(email_address)
500
444
 
501
- html_options = html_options.stringify_keys
502
- encode = html_options.delete("encode").to_s
445
+ html_options.stringify_keys!
503
446
 
504
447
  extras = %w{ cc bcc body subject }.map { |item|
505
448
  option = html_options.delete(item) || next
506
- "#{item}=#{Rack::Utils.escape(option).gsub("+", "%20")}"
449
+ "#{item}=#{Rack::Utils.escape_path(option)}"
507
450
  }.compact
508
451
  extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&'))
509
-
510
- email_address_obfuscated = email_address.to_str
511
- email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.key?("replace_at")
512
- email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.key?("replace_dot")
513
- case encode
514
- when "javascript"
515
- string = ''
516
- html = content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))
517
- html = escape_javascript(html.to_str)
518
- "document.write('#{html}');".each_byte do |c|
519
- string << sprintf("%%%x", c)
520
- end
521
- "<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>".html_safe
522
- when "hex"
523
- email_address_encoded = email_address_obfuscated.unpack('C*').map {|c|
524
- sprintf("&#%d;", c)
525
- }.join
526
-
527
- string = 'mailto:'.unpack('C*').map { |c|
528
- sprintf("&#%d;", c)
529
- }.join + email_address.unpack('C*').map { |c|
530
- char = c.chr
531
- char =~ /\w/ ? sprintf("%%%x", c) : char
532
- }.join
533
-
534
- content_tag "a", name || email_address_encoded.html_safe, html_options.merge("href" => "#{string}#{extras}".html_safe)
535
- else
536
- content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)
537
- end
452
+
453
+ content_tag "a", name || email_address.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)
538
454
  end
539
455
 
540
456
  # True if the current request URI was generated by the given +options+.
541
457
  #
542
458
  # ==== Examples
543
- # Let's say we're in the <tt>/shop/checkout?order=desc</tt> action.
459
+ # Let's say we're in the <tt>http://www.example.com/shop/checkout?order=desc</tt> action.
544
460
  #
545
- # current_page?(:action => 'process')
461
+ # current_page?(action: 'process')
546
462
  # # => false
547
463
  #
548
- # current_page?(:controller => 'shop', :action => 'checkout')
464
+ # current_page?(controller: 'shop', action: 'checkout')
549
465
  # # => true
550
466
  #
551
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'asc')
467
+ # current_page?(controller: 'shop', action: 'checkout', order: 'asc')
552
468
  # # => false
553
469
  #
554
- # current_page?(:action => 'checkout')
470
+ # current_page?(action: 'checkout')
555
471
  # # => true
556
472
  #
557
- # current_page?(:controller => 'library', :action => 'checkout')
473
+ # current_page?(controller: 'library', action: 'checkout')
558
474
  # # => false
559
475
  #
560
- # Let's say we're in the <tt>/shop/checkout?order=desc&page=1</tt> action.
476
+ # current_page?('http://www.example.com/shop/checkout')
477
+ # # => true
478
+ #
479
+ # current_page?('/shop/checkout')
480
+ # # => true
561
481
  #
562
- # current_page?(:action => 'process')
482
+ # Let's say we're in the <tt>http://www.example.com/shop/checkout?order=desc&page=1</tt> action.
483
+ #
484
+ # current_page?(action: 'process')
563
485
  # # => false
564
486
  #
565
- # current_page?(:controller => 'shop', :action => 'checkout')
487
+ # current_page?(controller: 'shop', action: 'checkout')
566
488
  # # => true
567
489
  #
568
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '1')
490
+ # current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '1')
569
491
  # # => true
570
492
  #
571
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '2')
493
+ # current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '2')
572
494
  # # => false
573
495
  #
574
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc')
496
+ # current_page?(controller: 'shop', action: 'checkout', order: 'desc')
575
497
  # # => false
576
498
  #
577
- # current_page?(:action => 'checkout')
499
+ # current_page?(action: 'checkout')
578
500
  # # => true
579
501
  #
580
- # current_page?(:controller => 'library', :action => 'checkout')
502
+ # current_page?(controller: 'library', action: 'checkout')
581
503
  # # => false
582
504
  #
583
- # Let's say we're in the <tt>/products</tt> action with method POST in case of invalid product.
505
+ # Let's say we're in the <tt>http://www.example.com/products</tt> action with method POST in case of invalid product.
584
506
  #
585
- # current_page?(:controller => 'product', :action => 'index')
507
+ # current_page?(controller: 'product', action: 'index')
586
508
  # # => false
587
509
  #
588
510
  def current_page?(options)
@@ -592,18 +514,14 @@ module ActionView
592
514
  "in a #request method"
593
515
  end
594
516
 
595
- return false unless request.get?
517
+ return false unless request.get? || request.head?
596
518
 
597
519
  url_string = url_for(options)
598
520
 
599
521
  # We ignore any extra parameters in the request_uri if the
600
522
  # submitted url doesn't have any either. This lets the function
601
523
  # work with things like ?order=asc
602
- if url_string.index("?")
603
- request_uri = request.fullpath
604
- else
605
- request_uri = request.path
606
- end
524
+ request_uri = url_string.index("?") ? request.fullpath : request.path
607
525
 
608
526
  if url_string =~ /^\w+:\/\//
609
527
  url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
@@ -622,9 +540,23 @@ module ActionView
622
540
  confirm = html_options.delete('confirm')
623
541
  method = html_options.delete('method')
624
542
 
625
- html_options["data-disable-with"] = disable_with if disable_with
626
- html_options["data-confirm"] = confirm if confirm
627
- add_method_to_attributes!(html_options, method) if method
543
+ if confirm
544
+ message = ":confirm option is deprecated and will be removed from Rails 4.1. " \
545
+ "Use 'data: { confirm: \'Text\' }' instead."
546
+ ActiveSupport::Deprecation.warn message
547
+
548
+ html_options["data-confirm"] = confirm
549
+ end
550
+
551
+ add_method_to_attributes!(html_options, method) if method
552
+
553
+ if disable_with
554
+ message = ":disable_with option is deprecated and will be removed from Rails 4.1. " \
555
+ "Use 'data: { disable_with: \'Text\' }' instead."
556
+ ActiveSupport::Deprecation.warn message
557
+
558
+ html_options["data-disable-with"] = disable_with
559
+ end
628
560
 
629
561
  html_options
630
562
  else
@@ -640,27 +572,11 @@ module ActionView
640
572
 
641
573
  def add_method_to_attributes!(html_options, method)
642
574
  if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/
643
- html_options["rel"] = "#{html_options["rel"]} nofollow".strip
575
+ html_options["rel"] = "#{html_options["rel"]} nofollow".lstrip
644
576
  end
645
577
  html_options["data-method"] = method
646
578
  end
647
579
 
648
- def options_for_javascript(options)
649
- if options.empty?
650
- '{}'
651
- else
652
- "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
653
- end
654
- end
655
-
656
- def array_or_string_for_javascript(option)
657
- if option.kind_of?(Array)
658
- "['#{option.join('\',\'')}']"
659
- elsif !option.nil?
660
- "'#{option}'"
661
- end
662
- end
663
-
664
580
  # Processes the +html_options+ hash, converting the boolean
665
581
  # attributes from true/false form into the form required by
666
582
  # HTML/XHTML. (An attribute is considered to be boolean if
@@ -688,6 +604,19 @@ module ActionView
688
604
  bool_attrs.each { |x| html_options[x] = x if html_options.delete(x) }
689
605
  html_options
690
606
  end
607
+
608
+ def token_tag(token=nil)
609
+ if token != false && protect_against_forgery?
610
+ token ||= form_authenticity_token
611
+ tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
612
+ else
613
+ ''
614
+ end
615
+ end
616
+
617
+ def method_tag(method)
618
+ tag('input', type: 'hidden', name: '_method', value: method.to_s)
619
+ end
691
620
  end
692
621
  end
693
622
  end