actionpack 3.0.20 → 3.1.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 (161) hide show
  1. data/CHANGELOG +88 -142
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +5 -6
  4. data/lib/abstract_controller.rb +1 -0
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +24 -19
  7. data/lib/abstract_controller/callbacks.rb +19 -19
  8. data/lib/abstract_controller/helpers.rb +11 -13
  9. data/lib/abstract_controller/layouts.rb +4 -5
  10. data/lib/abstract_controller/railties/routes_helpers.rb +18 -0
  11. data/lib/abstract_controller/rendering.rb +34 -31
  12. data/lib/abstract_controller/url_for.rb +27 -0
  13. data/lib/abstract_controller/view_paths.rb +31 -6
  14. data/lib/action_controller.rb +5 -3
  15. data/lib/action_controller/base.rb +15 -16
  16. data/lib/action_controller/caching.rb +2 -2
  17. data/lib/action_controller/caching/actions.rb +11 -12
  18. data/lib/action_controller/caching/fragments.rb +41 -19
  19. data/lib/action_controller/caching/pages.rb +3 -9
  20. data/lib/action_controller/caching/sweeping.rb +0 -1
  21. data/lib/action_controller/deprecated.rb +1 -1
  22. data/lib/action_controller/log_subscriber.rb +1 -1
  23. data/lib/action_controller/metal.rb +78 -20
  24. data/lib/action_controller/metal/compatibility.rb +0 -9
  25. data/lib/action_controller/metal/conditional_get.rb +9 -9
  26. data/lib/action_controller/metal/data_streaming.rb +145 -0
  27. data/lib/action_controller/metal/force_ssl.rb +35 -0
  28. data/lib/action_controller/metal/head.rb +1 -1
  29. data/lib/action_controller/metal/helpers.rb +37 -44
  30. data/lib/action_controller/metal/hide_actions.rb +2 -3
  31. data/lib/action_controller/metal/http_authentication.rb +41 -38
  32. data/lib/action_controller/metal/implicit_render.rb +13 -13
  33. data/lib/action_controller/metal/instrumentation.rb +2 -2
  34. data/lib/action_controller/metal/mime_responds.rb +25 -19
  35. data/lib/action_controller/metal/params_wrapper.rb +224 -0
  36. data/lib/action_controller/metal/redirecting.rb +6 -2
  37. data/lib/action_controller/metal/renderers.rb +50 -36
  38. data/lib/action_controller/metal/rendering.rb +34 -25
  39. data/lib/action_controller/metal/request_forgery_protection.rb +18 -36
  40. data/lib/action_controller/metal/responder.rb +47 -12
  41. data/lib/action_controller/metal/streaming.rb +244 -138
  42. data/lib/action_controller/metal/testing.rb +0 -9
  43. data/lib/action_controller/metal/url_for.rb +12 -14
  44. data/lib/action_controller/railtie.rb +19 -37
  45. data/lib/action_controller/railties/paths.rb +24 -0
  46. data/lib/action_controller/record_identifier.rb +4 -10
  47. data/lib/action_controller/test_case.rb +36 -19
  48. data/lib/action_controller/vendor/html-scanner/html/node.rb +5 -5
  49. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +3 -3
  50. data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -0
  51. data/lib/action_dispatch.rb +4 -1
  52. data/lib/action_dispatch/http/cache.rb +5 -32
  53. data/lib/action_dispatch/http/filter_parameters.rb +3 -1
  54. data/lib/action_dispatch/http/mime_negotiation.rb +22 -3
  55. data/lib/action_dispatch/http/mime_type.rb +45 -5
  56. data/lib/action_dispatch/http/rack_cache.rb +58 -0
  57. data/lib/action_dispatch/http/request.rb +27 -41
  58. data/lib/action_dispatch/http/response.rb +56 -54
  59. data/lib/action_dispatch/http/upload.rb +1 -11
  60. data/lib/action_dispatch/http/url.rb +102 -42
  61. data/lib/action_dispatch/middleware/callbacks.rb +8 -25
  62. data/lib/action_dispatch/middleware/closed_error.rb +7 -0
  63. data/lib/action_dispatch/middleware/cookies.rb +37 -15
  64. data/lib/action_dispatch/middleware/flash.rb +80 -11
  65. data/lib/action_dispatch/middleware/params_parser.rb +2 -2
  66. data/lib/action_dispatch/middleware/reloader.rb +76 -0
  67. data/lib/action_dispatch/middleware/session/abstract_store.rb +56 -226
  68. data/lib/action_dispatch/middleware/session/cookie_store.rb +20 -44
  69. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -46
  70. data/lib/action_dispatch/middleware/show_exceptions.rb +15 -2
  71. data/lib/action_dispatch/middleware/stack.rb +50 -17
  72. data/lib/action_dispatch/middleware/static.rb +41 -29
  73. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +3 -3
  74. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
  75. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +3 -3
  76. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -2
  77. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +2 -6
  78. data/lib/action_dispatch/railtie.rb +8 -0
  79. data/lib/action_dispatch/routing.rb +13 -1
  80. data/lib/action_dispatch/routing/mapper.rb +345 -227
  81. data/lib/action_dispatch/routing/polymorphic_routes.rb +33 -13
  82. data/lib/action_dispatch/routing/redirection.rb +110 -0
  83. data/lib/action_dispatch/routing/route.rb +15 -13
  84. data/lib/action_dispatch/routing/route_set.rb +116 -90
  85. data/lib/action_dispatch/routing/routes_proxy.rb +35 -0
  86. data/lib/action_dispatch/routing/url_for.rb +25 -1
  87. data/lib/action_dispatch/testing/assertions/response.rb +8 -10
  88. data/lib/action_dispatch/testing/assertions/routing.rb +15 -15
  89. data/lib/action_dispatch/testing/assertions/selector.rb +13 -220
  90. data/lib/action_dispatch/testing/integration.rb +37 -28
  91. data/lib/action_dispatch/testing/performance_test.rb +1 -3
  92. data/lib/action_dispatch/testing/test_process.rb +1 -1
  93. data/lib/action_dispatch/testing/test_request.rb +9 -3
  94. data/lib/action_dispatch/testing/test_response.rb +4 -111
  95. data/lib/action_pack.rb +1 -1
  96. data/lib/action_pack/version.rb +3 -3
  97. data/lib/action_view.rb +39 -24
  98. data/lib/action_view/base.rb +61 -86
  99. data/lib/action_view/buffers.rb +43 -0
  100. data/lib/action_view/context.rb +21 -24
  101. data/lib/action_view/flows.rb +79 -0
  102. data/lib/action_view/helpers.rb +8 -6
  103. data/lib/action_view/helpers/active_model_helper.rb +0 -23
  104. data/lib/action_view/helpers/asset_paths.rb +79 -0
  105. data/lib/action_view/helpers/asset_tag_helper.rb +30 -500
  106. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +147 -0
  107. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +101 -0
  108. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +200 -0
  109. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +152 -0
  110. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  111. data/lib/action_view/helpers/cache_helper.rb +11 -19
  112. data/lib/action_view/helpers/capture_helper.rb +19 -8
  113. data/lib/action_view/helpers/controller_helper.rb +21 -0
  114. data/lib/action_view/helpers/csrf_helper.rb +22 -4
  115. data/lib/action_view/helpers/date_helper.rb +36 -22
  116. data/lib/action_view/helpers/form_helper.rb +199 -113
  117. data/lib/action_view/helpers/form_options_helper.rb +10 -11
  118. data/lib/action_view/helpers/form_tag_helper.rb +94 -22
  119. data/lib/action_view/helpers/javascript_helper.rb +24 -107
  120. data/lib/action_view/helpers/number_helper.rb +36 -33
  121. data/lib/action_view/helpers/output_safety_helper.rb +38 -0
  122. data/lib/action_view/helpers/record_tag_helper.rb +6 -6
  123. data/lib/action_view/helpers/rendering_helper.rb +90 -0
  124. data/lib/action_view/helpers/sanitize_helper.rb +2 -2
  125. data/lib/action_view/helpers/sprockets_helper.rb +69 -0
  126. data/lib/action_view/helpers/tag_helper.rb +34 -12
  127. data/lib/action_view/helpers/text_helper.rb +30 -145
  128. data/lib/action_view/helpers/translation_helper.rb +10 -17
  129. data/lib/action_view/helpers/url_helper.rb +70 -67
  130. data/lib/action_view/locale/en.yml +1 -1
  131. data/lib/action_view/lookup_context.rb +36 -14
  132. data/lib/action_view/{paths.rb → path_set.rb} +9 -8
  133. data/lib/action_view/railtie.rb +12 -4
  134. data/lib/action_view/renderer/abstract_renderer.rb +36 -0
  135. data/lib/action_view/{render/partials.rb → renderer/partial_renderer.rb} +147 -146
  136. data/lib/action_view/renderer/renderer.rb +54 -0
  137. data/lib/action_view/renderer/streaming_template_renderer.rb +106 -0
  138. data/lib/action_view/renderer/template_renderer.rb +74 -0
  139. data/lib/action_view/template.rb +91 -54
  140. data/lib/action_view/template/error.rb +11 -8
  141. data/lib/action_view/template/handler.rb +9 -1
  142. data/lib/action_view/template/handlers.rb +9 -9
  143. data/lib/action_view/template/handlers/builder.rb +4 -4
  144. data/lib/action_view/template/handlers/erb.rb +21 -41
  145. data/lib/action_view/template/resolver.rb +171 -57
  146. data/lib/action_view/template/text.rb +0 -4
  147. data/lib/action_view/test_case.rb +32 -16
  148. data/lib/action_view/testing/resolvers.rb +16 -10
  149. data/lib/sprockets/railtie.rb +100 -0
  150. metadata +162 -140
  151. checksums.yaml +0 -7
  152. data/lib/action_controller/deprecated/base.rb +0 -143
  153. data/lib/action_controller/deprecated/dispatcher.rb +0 -28
  154. data/lib/action_controller/deprecated/url_writer.rb +0 -14
  155. data/lib/action_dispatch/routing/deprecated_mapper.rb +0 -525
  156. data/lib/action_view/helpers/prototype_helper.rb +0 -851
  157. data/lib/action_view/helpers/raw_output_helper.rb +0 -18
  158. data/lib/action_view/helpers/scriptaculous_helper.rb +0 -263
  159. data/lib/action_view/render/layouts.rb +0 -83
  160. data/lib/action_view/render/rendering.rb +0 -67
  161. data/lib/action_view/template/handlers/rjs.rb +0 -17
@@ -19,7 +19,7 @@ module ActionView
19
19
  # simple_format('<a href="http://example.com/">Example</a>')
20
20
  # # => "<p><a href=\"http://example.com/\">Example</a></p>"
21
21
  #
22
- # simple_format('<a href="javascript:alert('no!')">Example</a>')
22
+ # simple_format('<a href="javascript:alert(\'no!\')">Example</a>')
23
23
  # # => "<p><a>Example</a></p>"
24
24
  #
25
25
  # If you want to escape all content, you should invoke the +h+ method before
@@ -115,12 +115,13 @@ module ActionView
115
115
  end
116
116
  options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
117
117
 
118
- if text.present? && phrases.present?
119
- match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
120
- text = text.to_str.gsub(/(#{match})(?!(?:[^<]*?)(?:["'])[^<>]*>)/i, options[:highlighter])
121
- end
122
118
  text = sanitize(text) unless options[:sanitize] == false
123
- text
119
+ if text.blank? || phrases.blank?
120
+ text
121
+ else
122
+ match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
123
+ text.gsub(/(#{match})(?!(?:[^<]*?)(?:["'])[^<>]*>)/i, options[:highlighter])
124
+ end.html_safe
124
125
  end
125
126
 
126
127
  # Extracts an excerpt from +text+ that matches the first instance of +phrase+.
@@ -151,6 +152,8 @@ module ActionView
151
152
  # excerpt('This is an example', 'an', 5) # => ...s is an exam...
152
153
  # excerpt('This is also an example', 'an', 8, '<chop> ') # => <chop> is also an example
153
154
  def excerpt(text, phrase, *args)
155
+ return unless text && phrase
156
+
154
157
  options = args.extract_options!
155
158
  unless args.empty?
156
159
  options[:radius] = args[0] || 100
@@ -158,21 +161,16 @@ module ActionView
158
161
  end
159
162
  options.reverse_merge!(:radius => 100, :omission => "...")
160
163
 
161
- if text && phrase
162
- phrase = Regexp.escape(phrase)
164
+ phrase = Regexp.escape(phrase)
165
+ return unless found_pos = text.mb_chars =~ /(#{phrase})/i
163
166
 
164
- if found_pos = text.mb_chars =~ /(#{phrase})/i
165
- start_pos = [ found_pos - options[:radius], 0 ].max
166
- end_pos = [ [ found_pos + phrase.mb_chars.length + options[:radius] - 1, 0].max, text.mb_chars.length ].min
167
+ start_pos = [ found_pos - options[:radius], 0 ].max
168
+ end_pos = [ [ found_pos + phrase.mb_chars.length + options[:radius] - 1, 0].max, text.mb_chars.length ].min
167
169
 
168
- prefix = start_pos > 0 ? options[:omission] : ""
169
- postfix = end_pos < text.mb_chars.length - 1 ? options[:omission] : ""
170
+ prefix = start_pos > 0 ? options[:omission] : ""
171
+ postfix = end_pos < text.mb_chars.length - 1 ? options[:omission] : ""
170
172
 
171
- prefix + text.mb_chars[start_pos..end_pos].strip + postfix
172
- else
173
- nil
174
- end
175
- end
173
+ prefix + text.mb_chars[start_pos..end_pos].strip + postfix
176
174
  end
177
175
 
178
176
  # Attempts to pluralize the +singular+ word unless +count+ is 1. If
@@ -236,6 +234,10 @@ module ActionView
236
234
  #
237
235
  # You can pass any HTML attributes into <tt>html_options</tt>. These
238
236
  # will be added to all created paragraphs.
237
+ #
238
+ # ==== Options
239
+ # * <tt>:sanitize</tt> - If +false+, does not sanitize +text+.
240
+ #
239
241
  # ==== Examples
240
242
  # my_text = "Here is some basic text...\n...with a line break."
241
243
  #
@@ -249,71 +251,18 @@ module ActionView
249
251
  #
250
252
  # simple_format("Look ma! A class!", :class => 'description')
251
253
  # # => "<p class='description'>Look ma! A class!</p>"
254
+ #
255
+ # simple_format("<span>I'm allowed!</span> It's true.", {}, :sanitize => false)
256
+ # # => "<p><span>I'm allowed!</span> It's true.</p>"
252
257
  def simple_format(text, html_options={}, options={})
253
- text = text ? text.to_str : ''
254
- text = text.dup if text.frozen?
258
+ text = ''.html_safe if text.nil?
255
259
  start_tag = tag('p', html_options, true)
260
+ text = sanitize(text) unless options[:sanitize] == false
256
261
  text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n
257
262
  text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph
258
263
  text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
259
264
  text.insert 0, start_tag
260
- text.concat("</p>")
261
- text = sanitize(text) unless options[:sanitize] == false
262
- text
263
- end
264
-
265
- # Turns all URLs and e-mail addresses into clickable links. The <tt>:link</tt> option
266
- # will limit what should be linked. You can add HTML attributes to the links using
267
- # <tt>:html</tt>. Possible values for <tt>:link</tt> are <tt>:all</tt> (default),
268
- # <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
269
- # e-mail address is yielded and the result is used as the link text.
270
- #
271
- # ==== Examples
272
- # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
273
- # # => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
274
- # # say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
275
- #
276
- # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :urls)
277
- # # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
278
- # # or e-mail david@loudthinking.com"
279
- #
280
- # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :email_addresses)
281
- # # => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
282
- #
283
- # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
284
- # auto_link(post_body, :html => { :target => '_blank' }) do |text|
285
- # truncate(text, 15)
286
- # end
287
- # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
288
- # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
289
- #
290
- #
291
- # You can still use <tt>auto_link</tt> with the old API that accepts the
292
- # +link+ as its optional second parameter and the +html_options+ hash
293
- # as its optional third parameter:
294
- # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
295
- # auto_link(post_body, :urls) # => Once upon\na time
296
- # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\">http://www.myblog.com</a>.
297
- # Please e-mail me at me@email.com."
298
- #
299
- # auto_link(post_body, :all, :target => "_blank") # => Once upon\na time
300
- # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
301
- # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
302
- def auto_link(text, *args, &block)#link = :all, html = {}, &block)
303
- return '' if text.blank?
304
-
305
- options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
306
- unless args.empty?
307
- options[:link] = args[0] || :all
308
- options[:html] = args[1] || {}
309
- end
310
- options.reverse_merge!(:link => :all, :html => {})
311
-
312
- case options[:link].to_sym
313
- when :all then auto_link_email_addresses(auto_link_urls(text, options[:html], options, &block), options[:html], &block)
314
- when :email_addresses then auto_link_email_addresses(text, options[:html], &block)
315
- when :urls then auto_link_urls(text, options[:html], options, &block)
316
- end
265
+ text.html_safe.safe_concat("</p>")
317
266
  end
318
267
 
319
268
  # Creates a Cycle object whose _to_s_ method cycles through elements of an
@@ -364,10 +313,10 @@ module ActionView
364
313
  values.unshift(first_value)
365
314
 
366
315
  cycle = get_cycle(name)
367
- if (cycle.nil? || cycle.values != values)
316
+ unless cycle && cycle.values == values
368
317
  cycle = set_cycle(name, Cycle.new(*values))
369
318
  end
370
- return cycle.to_s
319
+ cycle.to_s
371
320
  end
372
321
 
373
322
  # Returns the current cycle string after a cycle has been started. Useful
@@ -384,7 +333,7 @@ module ActionView
384
333
  # <% end %>
385
334
  def current_cycle(name = "default")
386
335
  cycle = get_cycle(name)
387
- cycle.current_value unless cycle.nil?
336
+ cycle.current_value if cycle
388
337
  end
389
338
 
390
339
  # Resets a cycle so that it starts from the first element the next time
@@ -408,7 +357,7 @@ module ActionView
408
357
  # </table>
409
358
  def reset_cycle(name = "default")
410
359
  cycle = get_cycle(name)
411
- cycle.reset unless cycle.nil?
360
+ cycle.reset if cycle
412
361
  end
413
362
 
414
363
  class Cycle #:nodoc:
@@ -461,70 +410,6 @@ module ActionView
461
410
  @_cycles = Hash.new unless defined?(@_cycles)
462
411
  @_cycles[name] = cycle_object
463
412
  end
464
-
465
- AUTO_LINK_RE = %r{
466
- (?: ((?:ed2k|ftp|http|https|irc|mailto|news|gopher|nntp|telnet|webcal|xmpp|callto|feed|svn|urn|aim|rsync|tag|ssh|sftp|rtsp|afs):)// | www\. )
467
- [^\s<]+
468
- }x
469
-
470
- # regexps for determining context, used high-volume
471
- AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, /<a\b.*?>/i, /<\/a>/i]
472
-
473
- AUTO_EMAIL_RE = /[\w.!#\$%+-]+@[\w-]+(?:\.[\w-]+)+/
474
-
475
- BRACKETS = { ']' => '[', ')' => '(', '}' => '{' }
476
-
477
- # Turns all urls into clickable links. If a block is given, each url
478
- # is yielded and the result is used as the link text.
479
- def auto_link_urls(text, html_options = {}, options = {})
480
- link_attributes = html_options.stringify_keys
481
- text.to_str.gsub(AUTO_LINK_RE) do
482
- scheme, href = $1, $&
483
- punctuation = []
484
-
485
- if auto_linked?($`, $')
486
- # do not change string; URL is already linked
487
- href
488
- else
489
- # don't include trailing punctuation character as part of the URL
490
- while href.sub!(/[^\w\/-]$/, '')
491
- punctuation.push $&
492
- if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size
493
- href << punctuation.pop
494
- break
495
- end
496
- end
497
-
498
- link_text = block_given? ? yield(href) : href
499
- href = 'http://' + href unless scheme
500
-
501
- sanitize_link = options[:sanitize] != false
502
- content_tag(:a, link_text, link_attributes.merge('href' => href), sanitize_link) + punctuation.reverse.join('')
503
- end
504
- end
505
- end
506
-
507
- # Turns all email addresses into clickable links. If a block is given,
508
- # each email is yielded and the result is used as the link text.
509
- def auto_link_email_addresses(text, html_options = {}, options = {})
510
- text.to_str.gsub(AUTO_EMAIL_RE) do
511
- text = $&
512
-
513
- if auto_linked?($`, $')
514
- text.html_safe
515
- else
516
- display_text = block_given? ? yield(text) : text
517
- display_text = sanitize(display_text) unless options[:sanitize] == false
518
- mail_to text, display_text, html_options
519
- end
520
- end
521
- end
522
-
523
- # Detects already linked context or position in the middle of a tag
524
- def auto_linked?(left, right)
525
- (left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or
526
- (left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3])
527
- end
528
413
  end
529
414
  end
530
415
  end
@@ -5,7 +5,7 @@ module I18n
5
5
  class ExceptionHandler
6
6
  include Module.new {
7
7
  def call(exception, locale, key, options)
8
- exception.is_a?(MissingTranslationData) && options[:rescue_format] == :html ? super.html_safe : super
8
+ exception.is_a?(MissingTranslation) ? super.html_safe : super
9
9
  end
10
10
  }
11
11
  end
@@ -17,8 +17,8 @@ module ActionView
17
17
  module TranslationHelper
18
18
  # Delegates to I18n#translate but also performs three additional functions.
19
19
  #
20
- # First, it'll pass the :rescue_format => :html option to I18n so that any caught
21
- # MissingTranslationData exceptions will be turned into inline spans that
20
+ # First, it'll pass the :rescue_format => :html option to I18n so that any
21
+ # thrown MissingTranslation messages will be turned into inline spans that
22
22
  #
23
23
  # * have a "translation-missing" class set,
24
24
  # * contain the missing key as a title attribute and
@@ -26,7 +26,7 @@ module ActionView
26
26
  #
27
27
  # E.g. the value returned for a missing translation key :"blog.post.title" will be
28
28
  # <span class="translation_missing" title="translation missing: blog.post.title">Title</span>.
29
- # This way your views will display rather reasonableful strings but it will still
29
+ # This way your views will display rather reasonable strings but it will still
30
30
  # be easy to spot missing translations.
31
31
  #
32
32
  # Second, it'll scope the key by the current partial if the key starts
@@ -45,18 +45,11 @@ module ActionView
45
45
  # you know what kind of output to expect when you call translate in a template.
46
46
  def translate(key, options = {})
47
47
  options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
48
- if html_safe_translation_key?(key)
49
- html_safe_options = options.dup
50
- options.except(*I18n::RESERVED_KEYS).each do |name, value|
51
- unless name == :count && value.is_a?(Numeric)
52
- html_safe_options[name] = ERB::Util.html_escape(value.to_s)
53
- end
54
- end
55
- translation = I18n.translate(scope_key_by_partial(key), html_safe_options)
56
-
57
- translation.respond_to?(:html_safe) ? translation.html_safe : translation
48
+ translation = I18n.translate(scope_key_by_partial(key), options)
49
+ if html_safe_translation_key?(key) && translation.respond_to?(:html_safe)
50
+ translation.html_safe
58
51
  else
59
- I18n.translate(scope_key_by_partial(key), options)
52
+ translation
60
53
  end
61
54
  end
62
55
  alias :t :translate
@@ -70,8 +63,8 @@ module ActionView
70
63
  private
71
64
  def scope_key_by_partial(key)
72
65
  if key.to_s.first == "."
73
- if @_virtual_path
74
- @_virtual_path.gsub(%r{/_?}, ".") + key.to_s
66
+ if @virtual_path
67
+ @virtual_path.gsub(%r{/_?}, ".") + key.to_s
75
68
  else
76
69
  raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
77
70
  end
@@ -1,6 +1,7 @@
1
1
  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
+ require 'active_support/core_ext/string/output_safety'
4
5
  require 'action_dispatch'
5
6
 
6
7
  module ActionView
@@ -22,6 +23,10 @@ module ActionView
22
23
  include ActionDispatch::Routing::UrlFor
23
24
  include TagHelper
24
25
 
26
+ def _routes_context
27
+ controller
28
+ end
29
+
25
30
  # Need to map default url options to controller one.
26
31
  # def default_url_options(*args) #:nodoc:
27
32
  # controller.send(:default_url_options, *args)
@@ -63,7 +68,7 @@ module ActionView
63
68
  # # => /books/find
64
69
  #
65
70
  # <%= url_for(:action => 'login', :controller => 'members', :only_path => false, :protocol => 'https') %>
66
- # # => https://www.railsapplication.com/members/login/
71
+ # # => https://www.example.com/members/login/
67
72
  #
68
73
  # <%= url_for(:action => 'play', :anchor => 'player') %>
69
74
  # # => /messages/play/#player
@@ -76,9 +81,12 @@ module ActionView
76
81
  # # => /workshops
77
82
  #
78
83
  # <%= url_for(@workshop) %>
79
- # # calls @workshop.to_s
84
+ # # calls @workshop.to_param which by default returns the id
80
85
  # # => /workshops/5
81
86
  #
87
+ # # to_param can be re-defined in a model to provide different URL names:
88
+ # # => /workshops/1-workshop-name
89
+ #
82
90
  # <%= url_for("http://www.example.com") %>
83
91
  # # => http://www.example.com
84
92
  #
@@ -91,7 +99,7 @@ module ActionView
91
99
  # # => javascript:history.back()
92
100
  def url_for(options = {})
93
101
  options ||= {}
94
- url = case options
102
+ case options
95
103
  when String
96
104
  options
97
105
  when Hash
@@ -102,8 +110,6 @@ module ActionView
102
110
  else
103
111
  polymorphic_path(options)
104
112
  end
105
-
106
- url
107
113
  end
108
114
 
109
115
  # Creates a link tag of the given +name+ using a URL created by the set
@@ -180,7 +186,7 @@ module ActionView
180
186
  # link_to "Profiles", :controller => "profiles"
181
187
  # # => <a href="/profiles">Profiles</a>
182
188
  #
183
- # You can use a block as well if your link target is hard to fit into the name parameter. ERb example:
189
+ # You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
184
190
  #
185
191
  # <%= link_to(@profile) do %>
186
192
  # <strong><%= @profile.name %></strong> -- <span>Check it out!</span>
@@ -238,8 +244,8 @@ module ActionView
238
244
  href = html_options['href']
239
245
  tag_options = tag_options(html_options)
240
246
 
241
- href_attr = "href=\"#{html_escape(url)}\"" unless href
242
- "<a #{href_attr}#{tag_options}>#{html_escape(name || url)}</a>".html_safe
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
243
249
  end
244
250
  end
245
251
 
@@ -250,8 +256,9 @@ module ActionView
250
256
  # using the +link_to+ method with the <tt>:method</tt> modifier as described in
251
257
  # the +link_to+ documentation.
252
258
  #
253
- # The generated form element has a class name of <tt>button_to</tt>
254
- # to allow styling of the form itself and its children. You can control
259
+ # By default, the generated form element has a class name of <tt>button_to</tt>
260
+ # to allow styling of the form itself and its children. This can be changed
261
+ # using the <tt>:form_class</tt> modifier within +html_options+. You can control
255
262
  # the form submission and input element behavior using +html_options+.
256
263
  # This method accepts the <tt>:method</tt> and <tt>:confirm</tt> modifiers
257
264
  # described in the +link_to+ documentation. If no <tt>:method</tt> modifier
@@ -264,7 +271,7 @@ module ActionView
264
271
  # The +options+ hash accepts the same options as url_for.
265
272
  #
266
273
  # There are a few special +html_options+:
267
- # * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
274
+ # * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
268
275
  # <tt>:delete</tt> and <tt>:put</tt>. By default it will be <tt>:post</tt>.
269
276
  # * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
270
277
  # * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
@@ -272,6 +279,8 @@ module ActionView
272
279
  # processed normally, otherwise no action is taken.
273
280
  # * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
274
281
  # submit behaviour. By default this behaviour is an ajax submit.
282
+ # * <tt>:form_class</tt> - This controls the class of the form within which the submit button will
283
+ # be placed
275
284
  #
276
285
  # ==== Examples
277
286
  # <%= button_to "New", :action => "new" %>
@@ -280,6 +289,12 @@ module ActionView
280
289
  # # </form>"
281
290
  #
282
291
  #
292
+ # <%= button_to "New", :action => "new", :form_class => "new-thing" %>
293
+ # # => "<form method="post" action="/controller/new" class="new-thing">
294
+ # # <div><input value="New" type="submit" /></div>
295
+ # # </form>"
296
+ #
297
+ #
283
298
  # <%= button_to "Delete Image", { :action => "delete", :id => @image.id },
284
299
  # :confirm => "Are you sure?", :method => :delete %>
285
300
  # # => "<form method="post" action="/images/delete/1" class="button_to">
@@ -309,6 +324,7 @@ module ActionView
309
324
  end
310
325
 
311
326
  form_method = method.to_s == 'get' ? 'get' : 'post'
327
+ form_class = html_options.delete('form_class') || 'button_to'
312
328
 
313
329
  remote = html_options.delete('remote')
314
330
 
@@ -324,7 +340,7 @@ module ActionView
324
340
 
325
341
  html_options.merge!("type" => "submit", "value" => name)
326
342
 
327
- ("<form method=\"#{form_method}\" action=\"#{html_escape(url)}\" #{"data-remote=\"true\"" if remote} class=\"button_to\"><div>" +
343
+ ("<form method=\"#{form_method}\" action=\"#{ERB::Util.html_escape(url)}\" #{"data-remote=\"true\"" if remote} class=\"#{ERB::Util.html_escape(form_class)}\"><div>" +
328
344
  method_tag + tag("input", html_options) + request_token_tag + "</div></form>").html_safe
329
345
  end
330
346
 
@@ -470,45 +486,41 @@ module ActionView
470
486
  # :subject => "This is an example email"
471
487
  # # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
472
488
  def mail_to(email_address, name = nil, html_options = {})
473
- email_address = html_escape(email_address)
489
+ email_address = ERB::Util.html_escape(email_address)
474
490
 
475
491
  html_options = html_options.stringify_keys
476
492
  encode = html_options.delete("encode").to_s
477
- cc, bcc, subject, body = html_options.delete("cc"), html_options.delete("bcc"), html_options.delete("subject"), html_options.delete("body")
478
-
479
- extras = []
480
- extras << "cc=#{Rack::Utils.escape(cc).gsub("+", "%20")}" unless cc.nil?
481
- extras << "bcc=#{Rack::Utils.escape(bcc).gsub("+", "%20")}" unless bcc.nil?
482
- extras << "body=#{Rack::Utils.escape(body).gsub("+", "%20")}" unless body.nil?
483
- extras << "subject=#{Rack::Utils.escape(subject).gsub("+", "%20")}" unless subject.nil?
484
- extras = extras.empty? ? '' : '?' + html_escape(extras.join('&'))
485
493
 
486
- email_address_obfuscated = email_address.to_str
487
- email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.has_key?("replace_at")
488
- email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
489
-
490
- string = ''
491
-
492
- if encode == "javascript"
494
+ extras = %w{ cc bcc body subject }.map { |item|
495
+ option = html_options.delete(item) || next
496
+ "#{item}=#{Rack::Utils.escape(option).gsub("+", "%20")}"
497
+ }.compact
498
+ extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&'))
499
+
500
+ email_address_obfuscated = email_address.dup
501
+ email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.key?("replace_at")
502
+ email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.key?("replace_dot")
503
+ case encode
504
+ when "javascript"
505
+ string = ''
493
506
  html = content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))
494
507
  html = escape_javascript(html)
495
508
  "document.write('#{html}');".each_byte do |c|
496
509
  string << sprintf("%%%x", c)
497
510
  end
498
511
  "<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>".html_safe
499
- elsif encode == "hex"
500
- email_address_encoded = ''
501
- email_address_obfuscated.each_byte do |c|
502
- email_address_encoded << sprintf("&#%d;", c)
503
- end
504
-
505
- protocol = 'mailto:'
506
- protocol.each_byte { |c| string << sprintf("&#%d;", c) }
507
-
508
- email_address.each_byte do |c|
512
+ when "hex"
513
+ email_address_encoded = email_address_obfuscated.unpack('C*').map {|c|
514
+ sprintf("&#%d;", c)
515
+ }.join
516
+
517
+ string = 'mailto:'.unpack('C*').map { |c|
518
+ sprintf("&#%d;", c)
519
+ }.join + email_address.unpack('C*').map { |c|
509
520
  char = c.chr
510
- string << (char =~ /\w/ ? sprintf("%%%x", c) : char)
511
- end
521
+ char =~ /\w/ ? sprintf("%%%x", c) : char
522
+ }.join
523
+
512
524
  content_tag "a", name || email_address_encoded.html_safe, html_options.merge("href" => "#{string}#{extras}".html_safe)
513
525
  else
514
526
  content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)
@@ -543,10 +555,10 @@ module ActionView
543
555
  # current_page?(:controller => 'shop', :action => 'checkout')
544
556
  # # => true
545
557
  #
546
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page=>'1')
558
+ # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '1')
547
559
  # # => true
548
560
  #
549
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page=>'2')
561
+ # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '2')
550
562
  # # => false
551
563
  #
552
564
  # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc')
@@ -584,40 +596,31 @@ module ActionView
584
596
 
585
597
  private
586
598
  def convert_options_to_data_attributes(options, html_options)
587
- html_options = {} if html_options.nil?
588
- html_options = html_options.stringify_keys
599
+ if html_options.nil?
600
+ link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
601
+ else
602
+ html_options = html_options.stringify_keys
603
+ html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
589
604
 
590
- if (options.is_a?(Hash) && options.key?('remote') && options.delete('remote')) || (html_options.is_a?(Hash) && html_options.key?('remote') && html_options.delete('remote'))
591
- html_options['data-remote'] = 'true'
592
- end
605
+ disable_with = html_options.delete("disable_with")
606
+ confirm = html_options.delete('confirm')
607
+ method = html_options.delete('method')
593
608
 
594
- disable_with = html_options.delete("disable_with")
595
- confirm = html_options.delete("confirm")
609
+ html_options["data-disable-with"] = disable_with if disable_with
610
+ html_options["data-confirm"] = confirm if confirm
611
+ add_method_to_attributes!(html_options, method) if method
596
612
 
597
- if html_options.key?("popup")
598
- ActiveSupport::Deprecation.warn(":popup has been deprecated", caller)
613
+ html_options
599
614
  end
600
-
601
- method, href = html_options.delete("method"), html_options['href']
602
-
603
- add_disable_with_to_attributes!(html_options, disable_with) if disable_with
604
- add_confirm_to_attributes!(html_options, confirm) if confirm
605
- add_method_to_attributes!(html_options, method) if method
606
-
607
- html_options
608
- end
609
-
610
- def add_confirm_to_attributes!(html_options, confirm)
611
- html_options["data-confirm"] = confirm if confirm
612
615
  end
613
616
 
614
- def add_disable_with_to_attributes!(html_options, disable_with)
615
- html_options["data-disable-with"] = disable_with if disable_with
617
+ def link_to_remote_options?(options)
618
+ options.is_a?(Hash) && options.key?('remote') && options.delete('remote')
616
619
  end
617
620
 
618
621
  def add_method_to_attributes!(html_options, method)
619
- html_options["rel"] = "nofollow" if method && method.to_s.downcase != "get"
620
- html_options["data-method"] = method if method
622
+ html_options["rel"] = "nofollow" if method.to_s.downcase != "get"
623
+ html_options["data-method"] = method
621
624
  end
622
625
 
623
626
  def options_for_javascript(options)