actionview 4.2.10 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +141 -272
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/action_view/base.rb +33 -21
  6. data/lib/action_view/buffers.rb +1 -1
  7. data/lib/action_view/context.rb +1 -1
  8. data/lib/action_view/dependency_tracker.rb +52 -20
  9. data/lib/action_view/digestor.rb +86 -83
  10. data/lib/action_view/flows.rb +9 -11
  11. data/lib/action_view/gem_version.rb +3 -3
  12. data/lib/action_view/helpers/active_model_helper.rb +8 -8
  13. data/lib/action_view/helpers/asset_tag_helper.rb +74 -38
  14. data/lib/action_view/helpers/asset_url_helper.rb +160 -59
  15. data/lib/action_view/helpers/atom_feed_helper.rb +16 -16
  16. data/lib/action_view/helpers/cache_helper.rb +90 -35
  17. data/lib/action_view/helpers/capture_helper.rb +7 -6
  18. data/lib/action_view/helpers/controller_helper.rb +3 -2
  19. data/lib/action_view/helpers/csrf_helper.rb +3 -3
  20. data/lib/action_view/helpers/date_helper.rb +156 -108
  21. data/lib/action_view/helpers/debug_helper.rb +3 -4
  22. data/lib/action_view/helpers/form_helper.rb +475 -94
  23. data/lib/action_view/helpers/form_options_helper.rb +87 -47
  24. data/lib/action_view/helpers/form_tag_helper.rb +88 -57
  25. data/lib/action_view/helpers/javascript_helper.rb +10 -10
  26. data/lib/action_view/helpers/number_helper.rb +76 -59
  27. data/lib/action_view/helpers/output_safety_helper.rb +34 -4
  28. data/lib/action_view/helpers/record_tag_helper.rb +12 -99
  29. data/lib/action_view/helpers/rendering_helper.rb +3 -3
  30. data/lib/action_view/helpers/sanitize_helper.rb +17 -14
  31. data/lib/action_view/helpers/tag_helper.rb +198 -73
  32. data/lib/action_view/helpers/tags/base.rb +132 -97
  33. data/lib/action_view/helpers/tags/check_box.rb +17 -17
  34. data/lib/action_view/helpers/tags/collection_check_boxes.rb +9 -33
  35. data/lib/action_view/helpers/tags/collection_helpers.rb +68 -36
  36. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +3 -11
  37. data/lib/action_view/helpers/tags/collection_select.rb +2 -2
  38. data/lib/action_view/helpers/tags/date_select.rb +36 -36
  39. data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
  40. data/lib/action_view/helpers/tags/grouped_collection_select.rb +2 -2
  41. data/lib/action_view/helpers/tags/label.rb +5 -1
  42. data/lib/action_view/helpers/tags/password_field.rb +1 -1
  43. data/lib/action_view/helpers/tags/placeholderable.rb +1 -1
  44. data/lib/action_view/helpers/tags/radio_button.rb +4 -4
  45. data/lib/action_view/helpers/tags/search_field.rb +12 -9
  46. data/lib/action_view/helpers/tags/select.rb +9 -9
  47. data/lib/action_view/helpers/tags/text_area.rb +1 -1
  48. data/lib/action_view/helpers/tags/text_field.rb +5 -6
  49. data/lib/action_view/helpers/tags/translator.rb +15 -13
  50. data/lib/action_view/helpers/text_helper.rb +47 -30
  51. data/lib/action_view/helpers/translation_helper.rb +60 -30
  52. data/lib/action_view/helpers/url_helper.rb +132 -104
  53. data/lib/action_view/helpers.rb +1 -1
  54. data/lib/action_view/layouts.rb +59 -54
  55. data/lib/action_view/log_subscriber.rb +56 -7
  56. data/lib/action_view/lookup_context.rb +76 -61
  57. data/lib/action_view/model_naming.rb +1 -1
  58. data/lib/action_view/path_set.rb +28 -19
  59. data/lib/action_view/railtie.rb +30 -6
  60. data/lib/action_view/record_identifier.rb +51 -25
  61. data/lib/action_view/renderer/abstract_renderer.rb +19 -15
  62. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +55 -0
  63. data/lib/action_view/renderer/partial_renderer.rb +208 -206
  64. data/lib/action_view/renderer/renderer.rb +2 -6
  65. data/lib/action_view/renderer/streaming_template_renderer.rb +46 -48
  66. data/lib/action_view/renderer/template_renderer.rb +65 -66
  67. data/lib/action_view/rendering.rb +16 -9
  68. data/lib/action_view/routing_url_for.rb +25 -17
  69. data/lib/action_view/tasks/cache_digests.rake +23 -0
  70. data/lib/action_view/template/error.rb +14 -13
  71. data/lib/action_view/template/handlers/builder.rb +7 -7
  72. data/lib/action_view/template/handlers/erb/deprecated_erubis.rb +9 -0
  73. data/lib/action_view/template/handlers/erb/erubi.rb +81 -0
  74. data/lib/action_view/template/handlers/erb/erubis.rb +81 -0
  75. data/lib/action_view/template/handlers/erb.rb +9 -76
  76. data/lib/action_view/template/handlers/html.rb +9 -0
  77. data/lib/action_view/template/handlers/raw.rb +1 -3
  78. data/lib/action_view/template/handlers.rb +8 -6
  79. data/lib/action_view/template/html.rb +2 -4
  80. data/lib/action_view/template/resolver.rb +133 -109
  81. data/lib/action_view/template/text.rb +5 -8
  82. data/lib/action_view/template/types.rb +15 -17
  83. data/lib/action_view/template.rb +51 -28
  84. data/lib/action_view/test_case.rb +32 -27
  85. data/lib/action_view/testing/resolvers.rb +29 -31
  86. data/lib/action_view/version.rb +1 -1
  87. data/lib/action_view/view_paths.rb +26 -32
  88. data/lib/action_view.rb +5 -5
  89. data/lib/assets/compiled/rails-ujs.js +685 -0
  90. metadata +23 -23
  91. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,5 +1,5 @@
1
- require 'active_support/core_ext/string/filters'
2
- require 'active_support/core_ext/array/extract_options'
1
+ require "active_support/core_ext/string/filters"
2
+ require "active_support/core_ext/array/extract_options"
3
3
 
4
4
  module ActionView
5
5
  # = Action View Text Helpers
@@ -103,7 +103,9 @@ module ActionView
103
103
  # Highlights one or more +phrases+ everywhere in +text+ by inserting it into
104
104
  # a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
105
105
  # as a single-quoted string with <tt>\1</tt> where the phrase is to be inserted (defaults to
106
- # '<mark>\1</mark>') or passing a block that receives each matched term.
106
+ # '<mark>\1</mark>') or passing a block that receives each matched term. By default +text+
107
+ # is sanitized to prevent possible XSS attacks. If the input is trustworthy, passing false
108
+ # for <tt>:sanitize</tt> will turn sanitizing off.
107
109
  #
108
110
  # highlight('You searched for: rails', 'rails')
109
111
  # # => You searched for: <mark>rails</mark>
@@ -122,6 +124,9 @@ module ActionView
122
124
  #
123
125
  # highlight('You searched for: rails', 'rails') { |match| link_to(search_path(q: match, match)) }
124
126
  # # => You searched for: <a href="search?q=rails">rails</a>
127
+ #
128
+ # highlight('<a href="javascript:alert(\'no!\')">ruby</a> on rails', 'rails', sanitize: false)
129
+ # # => "<a>ruby</a> on <mark>rails</mark>"
125
130
  def highlight(text, phrases, options = {})
126
131
  text = sanitize(text) if options.fetch(:sanitize, true)
127
132
 
@@ -130,7 +135,7 @@ module ActionView
130
135
  else
131
136
  match = Array(phrases).map do |p|
132
137
  Regexp === p ? p.to_s : Regexp.escape(p)
133
- end.join('|')
138
+ end.join("|")
134
139
 
135
140
  if block_given?
136
141
  text.gsub(/(#{match})(?![^<]*?>)/i) { |found| yield found }
@@ -146,7 +151,7 @@ module ActionView
146
151
  # defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
147
152
  # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. Use the
148
153
  # <tt>:separator</tt> option to choose the delimitation. The resulting string will be stripped in any case. If the +phrase+
149
- # isn't found, nil is returned.
154
+ # isn't found, +nil+ is returned.
150
155
  #
151
156
  # excerpt('This is an example', 'an', radius: 5)
152
157
  # # => ...s is an exam...
@@ -182,7 +187,7 @@ module ActionView
182
187
  unless separator.empty?
183
188
  text.split(separator).each do |value|
184
189
  if value.match(regex)
185
- regex = phrase = value
190
+ phrase = value
186
191
  break
187
192
  end
188
193
  end
@@ -199,7 +204,12 @@ module ActionView
199
204
 
200
205
  # Attempts to pluralize the +singular+ word unless +count+ is 1. If
201
206
  # +plural+ is supplied, it will use that when count is > 1, otherwise
202
- # it will use the Inflector to determine the plural form.
207
+ # it will use the Inflector to determine the plural form for the given locale,
208
+ # which defaults to I18n.locale
209
+ #
210
+ # The word will be pluralized using rules defined for the locale
211
+ # (you must define your own inflection rules for languages other than English).
212
+ # See ActiveSupport::Inflector.pluralize
203
213
  #
204
214
  # pluralize(1, 'person')
205
215
  # # => 1 person
@@ -207,16 +217,19 @@ module ActionView
207
217
  # pluralize(2, 'person')
208
218
  # # => 2 people
209
219
  #
210
- # pluralize(3, 'person', 'users')
220
+ # pluralize(3, 'person', plural: 'users')
211
221
  # # => 3 users
212
222
  #
213
223
  # pluralize(0, 'person')
214
224
  # # => 0 people
215
- def pluralize(count, singular, plural = nil)
225
+ #
226
+ # pluralize(2, 'Person', locale: :de)
227
+ # # => 2 Personen
228
+ def pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)
216
229
  word = if (count == 1 || count =~ /^1(\.0+)?$/)
217
230
  singular
218
231
  else
219
- plural || singular.pluralize
232
+ plural || singular.pluralize(locale)
220
233
  end
221
234
 
222
235
  "#{count || 0} #{word}"
@@ -237,19 +250,23 @@ module ActionView
237
250
  #
238
251
  # word_wrap('Once upon a time', line_width: 1)
239
252
  # # => Once\nupon\na\ntime
240
- def word_wrap(text, options = {})
241
- line_width = options.fetch(:line_width, 80)
242
-
253
+ #
254
+ # You can also specify a custom +break_sequence+ ("\n" by default)
255
+ #
256
+ # word_wrap('Once upon a time', line_width: 1, break_sequence: "\r\n")
257
+ # # => Once\r\nupon\r\na\r\ntime
258
+ def word_wrap(text, line_width: 80, break_sequence: "\n")
243
259
  text.split("\n").collect! do |line|
244
- line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
245
- end * "\n"
260
+ line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1#{break_sequence}").strip : line
261
+ end * break_sequence
246
262
  end
247
263
 
248
264
  # Returns +text+ transformed into HTML using simple formatting rules.
249
- # Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
250
- # paragraph and wrapped in <tt><p></tt> tags. One newline (<tt>\n</tt>) is
251
- # considered as a linebreak and a <tt><br /></tt> tag is appended. This
252
- # method does not remove the newlines from the +text+.
265
+ # Two or more consecutive newlines(<tt>\n\n</tt> or <tt>\r\n\r\n</tt>) are
266
+ # considered a paragraph and wrapped in <tt><p></tt> tags. One newline
267
+ # (<tt>\n</tt> or <tt>\r\n</tt>) is considered a linebreak and a
268
+ # <tt><br /></tt> tag is appended. This method does not remove the
269
+ # newlines from the +text+.
253
270
  #
254
271
  # You can pass any HTML attributes into <tt>html_options</tt>. These
255
272
  # will be added to all created paragraphs.
@@ -309,7 +326,7 @@ module ActionView
309
326
  # <table>
310
327
  # <% @items.each do |item| %>
311
328
  # <tr class="<%= cycle("odd", "even") -%>">
312
- # <td>item</td>
329
+ # <td><%= item %></td>
313
330
  # </tr>
314
331
  # <% end %>
315
332
  # </table>
@@ -334,7 +351,7 @@ module ActionView
334
351
  # <% end %>
335
352
  def cycle(first_value, *values)
336
353
  options = values.extract_options!
337
- name = options.fetch(:name, 'default')
354
+ name = options.fetch(:name, "default")
338
355
 
339
356
  values.unshift(*first_value)
340
357
 
@@ -408,17 +425,17 @@ module ActionView
408
425
 
409
426
  private
410
427
 
411
- def next_index
412
- step_index(1)
413
- end
428
+ def next_index
429
+ step_index(1)
430
+ end
414
431
 
415
- def previous_index
416
- step_index(-1)
417
- end
432
+ def previous_index
433
+ step_index(-1)
434
+ end
418
435
 
419
- def step_index(n)
420
- (@index + n) % @values.size
421
- end
436
+ def step_index(n)
437
+ (@index + n) % @values.size
438
+ end
422
439
  end
423
440
 
424
441
  private
@@ -1,40 +1,61 @@
1
- require 'action_view/helpers/tag_helper'
2
- require 'active_support/core_ext/string/access'
3
- require 'i18n/exceptions'
1
+ require "action_view/helpers/tag_helper"
2
+ require "active_support/core_ext/string/access"
3
+ require "i18n/exceptions"
4
4
 
5
5
  module ActionView
6
6
  # = Action View Translation Helpers
7
7
  module Helpers
8
8
  module TranslationHelper
9
+ extend ActiveSupport::Concern
10
+
9
11
  include TagHelper
10
- # Delegates to <tt>I18n#translate</tt> but also performs three additional functions.
12
+
13
+ included do
14
+ mattr_accessor :debug_missing_translation
15
+ self.debug_missing_translation = true
16
+ end
17
+
18
+ # Delegates to <tt>I18n#translate</tt> but also performs three additional
19
+ # functions.
20
+ #
21
+ # First, it will ensure that any thrown +MissingTranslation+ messages will
22
+ # be rendered as inline spans that:
23
+ #
24
+ # * Have a <tt>translation-missing</tt> class applied
25
+ # * Contain the missing key as the value of the +title+ attribute
26
+ # * Have a titleized version of the last key segment as text
11
27
  #
12
- # First, it will ensure that any thrown +MissingTranslation+ messages will be turned
13
- # into inline spans that:
28
+ # For example, the value returned for the missing translation key
29
+ # <tt>"blog.post.title"</tt> will be:
14
30
  #
15
- # * have a "translation-missing" class set,
16
- # * contain the missing key as a title attribute and
17
- # * a titleized version of the last key segment as a text.
31
+ # <span
32
+ # class="translation_missing"
33
+ # title="translation missing: en.blog.post.title">Title</span>
18
34
  #
19
- # E.g. the value returned for a missing translation key :"blog.post.title" will be
20
- # <span class="translation_missing" title="translation missing: en.blog.post.title">Title</span>.
21
- # This way your views will display rather reasonable strings but it will still
22
- # be easy to spot missing translations.
35
+ # This allows for views to display rather reasonable strings while still
36
+ # giving developers a way to find missing translations.
23
37
  #
24
- # Second, it'll scope the key by the current partial if the key starts
25
- # with a period. So if you call <tt>translate(".foo")</tt> from the
26
- # <tt>people/index.html.erb</tt> template, you'll actually be calling
27
- # <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
28
- # to translate many keys within the same partials and gives you a simple framework
29
- # for scoping them consistently. If you don't prepend the key with a period,
30
- # nothing is converted.
38
+ # If you would prefer missing translations to raise an error, you can
39
+ # opt out of span-wrapping behavior globally by setting
40
+ # <tt>ActionView::Base.raise_on_missing_translations = true</tt> or
41
+ # individually by passing <tt>raise: true</tt> as an option to
42
+ # <tt>translate</tt>.
31
43
  #
32
- # Third, it'll mark the translation as safe HTML if the key has the suffix
33
- # "_html" or the last element of the key is the word "html". For example,
34
- # calling translate("footer_html") or translate("footer.html") will return
35
- # a safe HTML string that won't be escaped by other HTML helper methods. This
36
- # naming convention helps to identify translations that include HTML tags so that
37
- # you know what kind of output to expect when you call translate in a template.
44
+ # Second, if the key starts with a period <tt>translate</tt> will scope
45
+ # the key by the current partial. Calling <tt>translate(".foo")</tt> from
46
+ # the <tt>people/index.html.erb</tt> template is equivalent to calling
47
+ # <tt>translate("people.index.foo")</tt>. This makes it less
48
+ # repetitive to translate many keys within the same partial and provides
49
+ # a convention to scope keys consistently.
50
+ #
51
+ # Third, the translation will be marked as <tt>html_safe</tt> if the key
52
+ # has the suffix "_html" or the last element of the key is "html". Calling
53
+ # <tt>translate("footer_html")</tt> or <tt>translate("footer.html")</tt>
54
+ # will return an HTML safe string that won't be escaped by other HTML
55
+ # helper methods. This naming convention helps to identify translations
56
+ # that include HTML tags so that you know what kind of output to expect
57
+ # when you call translate in a template and translators know which keys
58
+ # they can provide HTML values for.
38
59
  def translate(key, options = {})
39
60
  options = options.dup
40
61
  has_default = options.has_key?(:default)
@@ -47,11 +68,11 @@ module ActionView
47
68
  # If the user has explicitly decided to NOT raise errors, pass that option to I18n.
48
69
  # Otherwise, tell I18n to raise an exception, which we rescue further in this method.
49
70
  # Note: `raise_error` refers to us re-raising the error in this method. I18n is forced to raise by default.
50
- if options[:raise] == false || (options.key?(:rescue_format) && options[:rescue_format].nil?)
71
+ if options[:raise] == false
51
72
  raise_error = false
52
73
  i18n_raise = false
53
74
  else
54
- raise_error = options[:raise] || options[:rescue_format] || ActionView::Base.raise_on_missing_translations
75
+ raise_error = options[:raise] || ActionView::Base.raise_on_missing_translations
55
76
  i18n_raise = true
56
77
  end
57
78
 
@@ -75,7 +96,16 @@ module ActionView
75
96
  raise e if raise_error
76
97
 
77
98
  keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
78
- content_tag('span', keys.last.to_s.titleize, :class => 'translation_missing', :title => "translation missing: #{keys.join('.')}")
99
+ title = "translation missing: #{keys.join('.')}"
100
+
101
+ interpolations = options.except(:default, :scope)
102
+ if interpolations.any?
103
+ title << ", " << interpolations.map { |k, v| "#{k}: #{ERB::Util.html_escape(v)}" }.join(", ")
104
+ end
105
+
106
+ return title unless ActionView::Base.debug_missing_translation
107
+
108
+ content_tag("span", keys.last.to_s.titleize, class: "translation_missing", title: title)
79
109
  end
80
110
  end
81
111
  alias :t :translate
@@ -103,7 +133,7 @@ module ActionView
103
133
  end
104
134
 
105
135
  def html_safe_translation_key?(key)
106
- key.to_s =~ /(\b|_|\.)html$/
136
+ /(\b|_|\.)html$/.match?(key.to_s)
107
137
  end
108
138
  end
109
139
  end