actionview 4.1.16 → 4.2.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 actionview might be problematic. Click here for more details.

Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +85 -483
  3. data/README.rdoc +7 -2
  4. data/lib/action_view.rb +0 -1
  5. data/lib/action_view/base.rb +2 -10
  6. data/lib/action_view/digestor.rb +1 -1
  7. data/lib/action_view/gem_version.rb +3 -3
  8. data/lib/action_view/helpers/asset_tag_helper.rb +32 -25
  9. data/lib/action_view/helpers/asset_url_helper.rb +29 -18
  10. data/lib/action_view/helpers/cache_helper.rb +2 -2
  11. data/lib/action_view/helpers/capture_helper.rb +1 -12
  12. data/lib/action_view/helpers/date_helper.rb +16 -12
  13. data/lib/action_view/helpers/debug_helper.rb +7 -11
  14. data/lib/action_view/helpers/form_helper.rb +65 -13
  15. data/lib/action_view/helpers/form_options_helper.rb +3 -3
  16. data/lib/action_view/helpers/form_tag_helper.rb +137 -28
  17. data/lib/action_view/helpers/javascript_helper.rb +7 -1
  18. data/lib/action_view/helpers/number_helper.rb +8 -10
  19. data/lib/action_view/helpers/output_safety_helper.rb +6 -6
  20. data/lib/action_view/helpers/rendering_helper.rb +6 -6
  21. data/lib/action_view/helpers/sanitize_helper.rb +67 -109
  22. data/lib/action_view/helpers/tag_helper.rb +15 -5
  23. data/lib/action_view/helpers/tags/base.rb +1 -1
  24. data/lib/action_view/helpers/tags/collection_check_boxes.rb +5 -1
  25. data/lib/action_view/helpers/tags/collection_helpers.rb +1 -1
  26. data/lib/action_view/helpers/tags/datetime_field.rb +10 -2
  27. data/lib/action_view/helpers/tags/label.rb +3 -3
  28. data/lib/action_view/helpers/tags/placeholderable.rb +32 -0
  29. data/lib/action_view/helpers/tags/text_area.rb +4 -0
  30. data/lib/action_view/helpers/tags/text_field.rb +4 -1
  31. data/lib/action_view/helpers/tags/week_field.rb +1 -1
  32. data/lib/action_view/helpers/text_helper.rb +25 -8
  33. data/lib/action_view/helpers/translation_helper.rb +29 -25
  34. data/lib/action_view/helpers/url_helper.rb +13 -14
  35. data/lib/action_view/log_subscriber.rb +5 -5
  36. data/lib/action_view/lookup_context.rb +6 -12
  37. data/lib/action_view/model_naming.rb +1 -1
  38. data/lib/action_view/path_set.rb +7 -19
  39. data/lib/action_view/renderer/abstract_renderer.rb +5 -3
  40. data/lib/action_view/renderer/partial_renderer.rb +76 -38
  41. data/lib/action_view/renderer/renderer.rb +0 -4
  42. data/lib/action_view/renderer/template_renderer.rb +5 -6
  43. data/lib/action_view/rendering.rb +7 -6
  44. data/lib/action_view/routing_url_for.rb +13 -5
  45. data/lib/action_view/tasks/dependencies.rake +7 -9
  46. data/lib/action_view/template/handlers.rb +9 -0
  47. data/lib/action_view/template/resolver.rb +7 -24
  48. data/lib/action_view/test_case.rb +13 -7
  49. data/lib/action_view/testing/resolvers.rb +2 -2
  50. data/lib/action_view/view_paths.rb +26 -15
  51. metadata +52 -18
  52. data/lib/action_view/vendor/html-scanner.rb +0 -20
  53. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  54. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  55. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  56. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  57. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  58. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
@@ -5,8 +5,8 @@ module ActionView
5
5
  def render
6
6
  options = @options.stringify_keys
7
7
  options["value"] ||= format_date(value(object))
8
- options["min"] = format_date(options["min"])
9
- options["max"] = format_date(options["max"])
8
+ options["min"] = format_date(datetime_value(options["min"]))
9
+ options["max"] = format_date(datetime_value(options["max"]))
10
10
  @options = options
11
11
  super
12
12
  end
@@ -16,6 +16,14 @@ module ActionView
16
16
  def format_date(value)
17
17
  value.try(:strftime, "%Y-%m-%dT%T.%L%z")
18
18
  end
19
+
20
+ def datetime_value(value)
21
+ if value.is_a? String
22
+ DateTime.parse(value) rescue nil
23
+ else
24
+ value
25
+ end
26
+ end
19
27
  end
20
28
  end
21
29
  end
@@ -35,12 +35,12 @@ module ActionView
35
35
  if block_given?
36
36
  content = @template_object.capture(&block)
37
37
  else
38
+ method_and_value = tag_value.present? ? "#{@method_name}.#{tag_value}" : @method_name
38
39
  content = if @content.blank?
39
40
  @object_name.gsub!(/\[(.*)_attributes\]\[\d+\]/, '.\1')
40
- method_and_value = tag_value.present? ? "#{@method_name}.#{tag_value}" : @method_name
41
41
 
42
42
  if object.respond_to?(:to_model)
43
- key = object.class.model_name.i18n_key
43
+ key = object.model_name.i18n_key
44
44
  i18n_default = ["#{key}.#{method_and_value}".to_sym, ""]
45
45
  end
46
46
 
@@ -51,7 +51,7 @@ module ActionView
51
51
  end
52
52
 
53
53
  content ||= if object && object.class.respond_to?(:human_attribute_name)
54
- object.class.human_attribute_name(@method_name)
54
+ object.class.human_attribute_name(method_and_value)
55
55
  end
56
56
 
57
57
  content ||= @method_name.humanize
@@ -0,0 +1,32 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags # :nodoc:
4
+ module Placeholderable # :nodoc:
5
+ def initialize(*)
6
+ super
7
+
8
+ if tag_value = @options[:placeholder]
9
+ object_name = @object_name.gsub(/\[(.*)_attributes\]\[\d+\]/, '.\1')
10
+ method_and_value = tag_value.is_a?(TrueClass) ? @method_name : "#{@method_name}.#{tag_value}"
11
+
12
+ if object.respond_to?(:to_model)
13
+ key = object.class.model_name.i18n_key
14
+ i18n_default = ["#{key}.#{method_and_value}".to_sym, ""]
15
+ end
16
+
17
+ i18n_default ||= ""
18
+ placeholder = I18n.t("#{object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.placeholder").presence
19
+
20
+ placeholder ||= if object && object.class.respond_to?(:human_attribute_name)
21
+ object.class.human_attribute_name(method_and_value)
22
+ end
23
+
24
+ placeholder ||= @method_name.humanize
25
+
26
+ @options[:placeholder] = placeholder
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,7 +1,11 @@
1
+ require 'action_view/helpers/tags/placeholderable'
2
+
1
3
  module ActionView
2
4
  module Helpers
3
5
  module Tags # :nodoc:
4
6
  class TextArea < Base # :nodoc:
7
+ include Placeholderable
8
+
5
9
  def render
6
10
  options = @options.stringify_keys
7
11
  add_default_name_and_id(options)
@@ -1,13 +1,16 @@
1
+ require 'action_view/helpers/tags/placeholderable'
2
+
1
3
  module ActionView
2
4
  module Helpers
3
5
  module Tags # :nodoc:
4
6
  class TextField < Base # :nodoc:
7
+ include Placeholderable
8
+
5
9
  def render
6
10
  options = @options.stringify_keys
7
11
  options["size"] = options["maxlength"] unless options.key?("size")
8
12
  options["type"] ||= field_type
9
13
  options["value"] = options.fetch("value") { value_before_type_cast(object) } unless field_type == "file"
10
- options["value"] &&= ERB::Util.html_escape(options["value"])
11
14
  add_default_name_and_id(options)
12
15
  tag("input", options)
13
16
  end
@@ -5,7 +5,7 @@ module ActionView
5
5
  private
6
6
 
7
7
  def format_date(value)
8
- value.try(:strftime, "%Y-W%V")
8
+ value.try(:strftime, "%Y-W%W")
9
9
  end
10
10
  end
11
11
  end
@@ -103,11 +103,14 @@ 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>')
106
+ # '<mark>\1</mark>') or passing a block that receives each matched term.
107
107
  #
108
108
  # highlight('You searched for: rails', 'rails')
109
109
  # # => You searched for: <mark>rails</mark>
110
110
  #
111
+ # highlight('You searched for: rails', /for|rails/)
112
+ # # => You searched <mark>for</mark>: <mark>rails</mark>
113
+ #
111
114
  # highlight('You searched for: ruby, rails, dhh', 'actionpack')
112
115
  # # => You searched for: ruby, rails, dhh
113
116
  #
@@ -116,15 +119,25 @@ module ActionView
116
119
  #
117
120
  # highlight('You searched for: rails', 'rails', highlighter: '<a href="search?q=\1">\1</a>')
118
121
  # # => You searched for: <a href="search?q=rails">rails</a>
122
+ #
123
+ # highlight('You searched for: rails', 'rails') { |match| link_to(search_path(q: match, match)) }
124
+ # # => You searched for: <a href="search?q=rails">rails</a>
119
125
  def highlight(text, phrases, options = {})
120
126
  text = sanitize(text) if options.fetch(:sanitize, true)
121
127
 
122
128
  if text.blank? || phrases.blank?
123
129
  text
124
130
  else
125
- highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
126
- match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
127
- text.gsub(/(#{match})(?![^<]*?>)/i, highlighter)
131
+ match = Array(phrases).map do |p|
132
+ Regexp === p ? p.to_s : Regexp.escape(p)
133
+ end.join('|')
134
+
135
+ if block_given?
136
+ text.gsub(/(#{match})(?![^<]*?>)/i) { |found| yield found }
137
+ else
138
+ highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
139
+ text.gsub(/(#{match})(?![^<]*?>)/i, highlighter)
140
+ end
128
141
  end.html_safe
129
142
  end
130
143
 
@@ -155,9 +168,13 @@ module ActionView
155
168
  def excerpt(text, phrase, options = {})
156
169
  return unless text && phrase
157
170
 
158
- separator = options[:separator] || ''
159
- phrase = Regexp.escape(phrase)
160
- regex = /#{phrase}/i
171
+ separator = options.fetch(:separator, nil) || ""
172
+ case phrase
173
+ when Regexp
174
+ regex = phrase
175
+ else
176
+ regex = /#{Regexp.escape(phrase)}/i
177
+ end
161
178
 
162
179
  return unless matches = text.match(regex)
163
180
  phrase = matches[0]
@@ -171,7 +188,7 @@ module ActionView
171
188
  end
172
189
  end
173
190
 
174
- first_part, second_part = text.split(regex, 2)
191
+ first_part, second_part = text.split(phrase, 2)
175
192
 
176
193
  prefix, first_part = cut_excerpt_part(:first, first_part, separator, options)
177
194
  postfix, second_part = cut_excerpt_part(:second, second_part, separator, options)
@@ -1,14 +1,14 @@
1
1
  require 'action_view/helpers/tag_helper'
2
+ require 'active_support/core_ext/string/access'
2
3
  require 'i18n/exceptions'
3
4
 
4
5
  module ActionView
5
6
  # = Action View Translation Helpers
6
7
  module Helpers
7
8
  module TranslationHelper
8
- include TagHelper
9
9
  # Delegates to <tt>I18n#translate</tt> but also performs three additional functions.
10
10
  #
11
- # First, it will ensure that any thrown +MissingTranslation+ messages will be turned
11
+ # First, it will ensure that any thrown +MissingTranslation+ messages will be turned
12
12
  # into inline spans that:
13
13
  #
14
14
  # * have a "translation-missing" class set,
@@ -36,22 +36,15 @@ module ActionView
36
36
  # you know what kind of output to expect when you call translate in a template.
37
37
  def translate(key, options = {})
38
38
  options = options.dup
39
- has_default = options.has_key?(:default)
40
- remaining_defaults = Array(options.delete(:default)).compact
39
+ options[:default] = wrap_translate_defaults(options[:default]) if options[:default]
41
40
 
42
- if has_default && !remaining_defaults.first.kind_of?(Symbol)
43
- options[:default] = remaining_defaults.shift
44
- end
41
+ # If the user has specified rescue_format then pass it all through, otherwise use
42
+ # raise and do the work ourselves
43
+ options[:raise] ||= ActionView::Base.raise_on_missing_translations
45
44
 
46
- # If the user has explicitly decided to NOT raise errors, pass that option to I18n.
47
- # Otherwise, tell I18n to raise an exception, which we rescue further in this method.
48
- # Note: `raise_error` refers to us re-raising the error in this method. I18n is forced to raise by default.
49
- if options[:raise] == false || (options.key?(:rescue_format) && options[:rescue_format].nil?)
50
- raise_error = false
51
- i18n_raise = false
52
- else
53
- raise_error = options[:raise] || options[:rescue_format] || ActionView::Base.raise_on_missing_translations
54
- i18n_raise = true
45
+ raise_error = options[:raise] || options.key?(:rescue_format)
46
+ unless raise_error
47
+ options[:raise] = true
55
48
  end
56
49
 
57
50
  if html_safe_translation_key?(key)
@@ -61,21 +54,17 @@ module ActionView
61
54
  html_safe_options[name] = ERB::Util.html_escape(value.to_s)
62
55
  end
63
56
  end
64
- translation = I18n.translate(scope_key_by_partial(key), html_safe_options.merge(raise: i18n_raise))
57
+ translation = I18n.translate(scope_key_by_partial(key), html_safe_options)
65
58
 
66
59
  translation.respond_to?(:html_safe) ? translation.html_safe : translation
67
60
  else
68
- I18n.translate(scope_key_by_partial(key), options.merge(raise: i18n_raise))
61
+ I18n.translate(scope_key_by_partial(key), options)
69
62
  end
70
63
  rescue I18n::MissingTranslationData => e
71
- if remaining_defaults.present?
72
- translate remaining_defaults.shift, options.merge(default: remaining_defaults)
73
- else
74
- raise e if raise_error
64
+ raise e if raise_error
75
65
 
76
- keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
77
- content_tag('span', keys.last.to_s.titleize, :class => 'translation_missing', :title => "translation missing: #{keys.join('.')}")
78
- end
66
+ keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
67
+ content_tag('span', keys.last.to_s.titleize, :class => 'translation_missing', :title => "translation missing: #{keys.join('.')}")
79
68
  end
80
69
  alias :t :translate
81
70
 
@@ -104,6 +93,21 @@ module ActionView
104
93
  def html_safe_translation_key?(key)
105
94
  key.to_s =~ /(\b|_|\.)html$/
106
95
  end
96
+
97
+ def wrap_translate_defaults(defaults)
98
+ new_defaults = []
99
+ defaults = Array(defaults)
100
+ while key = defaults.shift
101
+ if key.is_a?(Symbol)
102
+ new_defaults << lambda { |_, options| translate key, options.merge(:default => defaults) }
103
+ break
104
+ else
105
+ new_defaults << key
106
+ end
107
+ end
108
+
109
+ new_defaults
110
+ end
107
111
  end
108
112
  end
109
113
  end
@@ -323,7 +323,7 @@ module ActionView
323
323
  inner_tags.safe_concat tag(:input, type: "hidden", name: param_name, value: value.to_param)
324
324
  end
325
325
  end
326
- content_tag('form', content_tag('div', inner_tags), form_options)
326
+ content_tag('form', inner_tags, form_options)
327
327
  end
328
328
 
329
329
  # Creates a link tag of the given +name+ using a URL created by the set of
@@ -389,15 +389,7 @@ module ActionView
389
389
  # # If not...
390
390
  # # => <a href="/accounts/signup">Reply</a>
391
391
  def link_to_unless(condition, name, options = {}, html_options = {}, &block)
392
- if condition
393
- if block_given?
394
- block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
395
- else
396
- ERB::Util.html_escape(name)
397
- end
398
- else
399
- link_to(name, options, html_options)
400
- end
392
+ link_to_if !condition, name, options, html_options, &block
401
393
  end
402
394
 
403
395
  # Creates a link tag of the given +name+ using a URL created by the set of
@@ -421,7 +413,15 @@ module ActionView
421
413
  # # If they are logged in...
422
414
  # # => <a href="/accounts/show/3">my_username</a>
423
415
  def link_to_if(condition, name, options = {}, html_options = {}, &block)
424
- link_to_unless !condition, name, options, html_options, &block
416
+ if condition
417
+ link_to(name, options, html_options)
418
+ else
419
+ if block_given?
420
+ block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
421
+ else
422
+ ERB::Util.html_escape(name)
423
+ end
424
+ end
425
425
  end
426
426
 
427
427
  # Creates a mailto link tag to the specified +email_address+, which is
@@ -469,10 +469,9 @@ module ActionView
469
469
  option = html_options.delete(item) || next
470
470
  "#{item}=#{Rack::Utils.escape_path(option)}"
471
471
  }.compact
472
- extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&'))
472
+ extras = extras.empty? ? '' : '?' + extras.join('&')
473
473
 
474
- encoded_email_address = ERB::Util.url_encode(email_address ? email_address.to_str : '').gsub("%40", "@")
475
- html_options["href"] = "mailto:#{encoded_email_address}#{extras}".html_safe
474
+ html_options["href"] = "mailto:#{email_address}#{extras}"
476
475
 
477
476
  content_tag(:a, name || email_address, html_options, &block)
478
477
  end
@@ -13,11 +13,11 @@ module ActionView
13
13
  end
14
14
 
15
15
  def render_template(event)
16
- return unless logger.info?
17
- message = " Rendered #{from_rails_root(event.payload[:identifier])}"
18
- message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
19
- message << " (#{event.duration.round(1)}ms)"
20
- info(message)
16
+ info do
17
+ message = " Rendered #{from_rails_root(event.payload[:identifier])}"
18
+ message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
19
+ message << " (#{event.duration.round(1)}ms)"
20
+ end
21
21
  end
22
22
  alias :render_partial :render_template
23
23
  alias :render_collection :render_template
@@ -66,10 +66,7 @@ module ActionView
66
66
  def self.get(details)
67
67
  if details[:formats]
68
68
  details = details.dup
69
- syms = Set.new Mime::SET.symbols
70
- details[:formats] = details[:formats].select { |v|
71
- syms.include? v
72
- }
69
+ details[:formats] &= Mime::SET.symbols
73
70
  end
74
71
  @details_keys[details] ||= new
75
72
  end
@@ -114,7 +111,7 @@ module ActionView
114
111
  module ViewPaths
115
112
  attr_reader :view_paths, :html_fallback_for_js
116
113
 
117
- # Whenever setting view paths, makes a copy so we can manipulate then in
114
+ # Whenever setting view paths, makes a copy so that we can manipulate them in
118
115
  # instance objects as we wish.
119
116
  def view_paths=(paths)
120
117
  @view_paths = ActionView::PathSet.new(Array(paths))
@@ -125,10 +122,6 @@ module ActionView
125
122
  end
126
123
  alias :find_template :find
127
124
 
128
- def find_file(name, prefixes = [], partial = false, keys = [], options = {})
129
- @view_paths.find_file(*args_for_lookup(name, prefixes, partial, keys, options))
130
- end
131
-
132
125
  def find_all(name, prefixes = [], partial = false, keys = [], options = {})
133
126
  @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
134
127
  end
@@ -138,7 +131,8 @@ module ActionView
138
131
  end
139
132
  alias :template_exists? :exists?
140
133
 
141
- # Add fallbacks to the view paths. Useful in cases you are rendering a :file.
134
+ # Adds fallbacks to the view paths. Useful in cases when you are rendering
135
+ # a :file.
142
136
  def with_fallbacks
143
137
  added_resolvers = 0
144
138
  self.class.fallbacks.each do |resolver|
@@ -231,7 +225,7 @@ module ActionView
231
225
  end
232
226
 
233
227
  # Overload locale= to also set the I18n.locale. If the current I18n.config object responds
234
- # to original_config, it means that it's has a copy of the original I18n configuration and it's
228
+ # to original_config, it means that it has a copy of the original I18n configuration and it's
235
229
  # acting as proxy, which we need to skip.
236
230
  def locale=(value)
237
231
  if value
@@ -242,7 +236,7 @@ module ActionView
242
236
  super(@skip_default_locale ? I18n.locale : default_locale)
243
237
  end
244
238
 
245
- # A method which only uses the first format in the formats array for layout lookup.
239
+ # Uses the first format in the formats array for layout lookup.
246
240
  def with_layout_format
247
241
  if formats.size == 1
248
242
  yield
@@ -6,7 +6,7 @@ module ActionView
6
6
  end
7
7
 
8
8
  def model_name_from_record_or_class(record_or_class)
9
- (record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
9
+ convert_to_model(record_or_class).model_name
10
10
  end
11
11
  end
12
12
  end
@@ -46,35 +46,23 @@ module ActionView #:nodoc:
46
46
  find_all(*args).first || raise(MissingTemplate.new(self, *args))
47
47
  end
48
48
 
49
- def find_file(path, prefixes = [], *args)
50
- _find_all(path, prefixes, args, true).first || raise(MissingTemplate.new(self, path, prefixes, *args))
51
- end
52
-
53
49
  def find_all(path, prefixes = [], *args)
54
- _find_all path, prefixes, args, false
55
- end
56
-
57
- def exists?(path, prefixes, *args)
58
- find_all(path, prefixes, *args).any?
59
- end
60
-
61
- private
62
-
63
- def _find_all(path, prefixes, args, outside_app)
64
50
  prefixes = [prefixes] if String === prefixes
65
51
  prefixes.each do |prefix|
66
52
  paths.each do |resolver|
67
- if outside_app
68
- templates = resolver.find_all_anywhere(path, prefix, *args)
69
- else
70
- templates = resolver.find_all(path, prefix, *args)
71
- end
53
+ templates = resolver.find_all(path, prefix, *args)
72
54
  return templates unless templates.empty?
73
55
  end
74
56
  end
75
57
  []
76
58
  end
77
59
 
60
+ def exists?(path, prefixes, *args)
61
+ find_all(path, prefixes, *args).any?
62
+ end
63
+
64
+ private
65
+
78
66
  def typecast(paths)
79
67
  paths.map do |path|
80
68
  case path