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
@@ -15,7 +15,7 @@ module ActionView
15
15
  # that new object is called in turn. This abstracts the setup and rendering
16
16
  # into a separate classes for partials and templates.
17
17
  class AbstractRenderer #:nodoc:
18
- delegate :find_template, :find_file, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
18
+ delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
19
19
 
20
20
  def initialize(lookup_context)
21
21
  @lookup_context = lookup_context
@@ -29,8 +29,9 @@ module ActionView
29
29
 
30
30
  def extract_details(options)
31
31
  @lookup_context.registered_details.each_with_object({}) do |key, details|
32
- next unless value = options[key]
33
- details[key] = Array(value)
32
+ value = options[key]
33
+
34
+ details[key] = Array(value) if value
34
35
  end
35
36
  end
36
37
 
@@ -41,6 +42,7 @@ module ActionView
41
42
  def prepend_formats(formats)
42
43
  formats = Array(formats)
43
44
  return if formats.empty? || @lookup_context.html_fallback_for_js
45
+
44
46
  @lookup_context.formats = formats | @lookup_context.formats
45
47
  end
46
48
  end
@@ -1,6 +1,33 @@
1
1
  require 'thread_safe'
2
2
 
3
3
  module ActionView
4
+ class PartialIteration
5
+ # The number of iterations that will be done by the partial.
6
+ attr_reader :size
7
+
8
+ # The current iteration of the partial.
9
+ attr_reader :index
10
+
11
+ def initialize(size)
12
+ @size = size
13
+ @index = 0
14
+ end
15
+
16
+ # Check if this is the first iteration of the partial.
17
+ def first?
18
+ index == 0
19
+ end
20
+
21
+ # Check if this is the last iteration of the partial.
22
+ def last?
23
+ index == size - 1
24
+ end
25
+
26
+ def iterate! # :nodoc:
27
+ @index += 1
28
+ end
29
+ end
30
+
4
31
  # = Action View Partials
5
32
  #
6
33
  # There's also a convenience method for rendering sub templates within the current controller that depends on a
@@ -56,8 +83,12 @@ module ActionView
56
83
  # <%= render partial: "ad", collection: @advertisements %>
57
84
  #
58
85
  # This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An
59
- # iteration counter will automatically be made available to the template with a name of the form
60
- # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+.
86
+ # iteration object will automatically be made available to the template with a name of the form
87
+ # +partial_name_iteration+. The iteration object has knowledge about which index the current object has in
88
+ # the collection and the total size of the collection. The iteration object also has two convenience methods,
89
+ # +first?+ and +last?+. In the case of the example above, the template would be fed +ad_iteration+.
90
+ # For backwards compatibility the +partial_name_counter+ is still present and is mapped to the iteration's
91
+ # +index+ method.
61
92
  #
62
93
  # The <tt>:as</tt> option may be used when rendering partials.
63
94
  #
@@ -281,6 +312,8 @@ module ActionView
281
312
  end
282
313
  end
283
314
 
315
+ private
316
+
284
317
  def render_collection
285
318
  return nil if @collection.blank?
286
319
 
@@ -322,37 +355,39 @@ module ActionView
322
355
  # respond to +to_partial_path+ in order to setup the path.
323
356
  def setup(context, options, block)
324
357
  @view = context
325
- partial = options[:partial]
326
-
327
358
  @options = options
328
- @locals = options[:locals] || {}
329
359
  @block = block
360
+
361
+ @locals = options[:locals] || {}
330
362
  @details = extract_details(options)
331
363
 
332
364
  prepend_formats(options[:formats])
333
365
 
366
+ partial = options[:partial]
367
+
334
368
  if String === partial
335
- @object = options[:object] if options.has_key?(:object)
369
+ @object = options[:object]
370
+ @collection = collection_from_options
336
371
  @path = partial
337
- @collection = collection
338
372
  else
339
373
  @object = partial
374
+ @collection = collection_from_object || collection_from_options
340
375
 
341
- if @collection = collection_from_object || collection
376
+ if @collection
342
377
  paths = @collection_data = @collection.map { |o| partial_path(o) }
343
- @path = paths.uniq.size == 1 ? paths.first : nil
378
+ @path = paths.uniq.one? ? paths.first : nil
344
379
  else
345
380
  @path = partial_path
346
381
  end
347
382
  end
348
383
 
349
384
  if as = options[:as]
350
- raise_invalid_option_as(as) unless as.to_s =~ /\A[a-z_]\w*\z/
385
+ raise_invalid_identifier(as) unless as.to_s =~ /\A[a-z_]\w*\z/
351
386
  as = as.to_sym
352
387
  end
353
388
 
354
389
  if @path
355
- @variable, @variable_counter = retrieve_variable(@path, as)
390
+ @variable, @variable_counter, @variable_iteration = retrieve_variable(@path, as)
356
391
  @template_keys = retrieve_template_keys
357
392
  else
358
393
  paths.map! { |path| retrieve_variable(path, as).unshift(path) }
@@ -361,7 +396,7 @@ module ActionView
361
396
  self
362
397
  end
363
398
 
364
- def collection
399
+ def collection_from_options
365
400
  if @options.key?(:collection)
366
401
  collection = @options[:collection]
367
402
  collection.respond_to?(:to_ary) ? collection.to_ary : []
@@ -373,9 +408,7 @@ module ActionView
373
408
  end
374
409
 
375
410
  def find_partial
376
- if path = @path
377
- find_template(path, @template_keys)
378
- end
411
+ find_template(@path, @template_keys) if @path
379
412
  end
380
413
 
381
414
  def find_template(path, locals)
@@ -385,19 +418,22 @@ module ActionView
385
418
 
386
419
  def collection_with_template
387
420
  view, locals, template = @view, @locals, @template
388
- as, counter = @variable, @variable_counter
421
+ as, counter, iteration = @variable, @variable_counter, @variable_iteration
389
422
 
390
423
  if layout = @options[:layout]
391
424
  layout = find_template(layout, @template_keys)
392
425
  end
393
426
 
394
- index = -1
427
+ partial_iteration = PartialIteration.new(@collection.size)
428
+ locals[iteration] = partial_iteration
429
+
395
430
  @collection.map do |object|
396
- locals[as] = object
397
- locals[counter] = (index += 1)
431
+ locals[as] = object
432
+ locals[counter] = partial_iteration.index
398
433
 
399
434
  content = template.render(view, locals)
400
435
  content = layout.render(view, locals) { content } if layout
436
+ partial_iteration.iterate!
401
437
  content
402
438
  end
403
439
  end
@@ -407,16 +443,20 @@ module ActionView
407
443
  cache = {}
408
444
  keys = @locals.keys
409
445
 
410
- index = -1
446
+ partial_iteration = PartialIteration.new(@collection.size)
447
+
411
448
  @collection.map do |object|
412
- index += 1
413
- path, as, counter = collection_data[index]
449
+ index = partial_iteration.index
450
+ path, as, counter, iteration = collection_data[index]
414
451
 
415
- locals[as] = object
416
- locals[counter] = index
452
+ locals[as] = object
453
+ locals[counter] = index
454
+ locals[iteration] = partial_iteration
417
455
 
418
456
  template = (cache[path] ||= find_template(path, keys + [as, counter]))
419
- template.render(view, locals)
457
+ content = template.render(view, locals)
458
+ partial_iteration.iterate!
459
+ content
420
460
  end
421
461
  end
422
462
 
@@ -466,8 +506,11 @@ module ActionView
466
506
 
467
507
  def retrieve_template_keys
468
508
  keys = @locals.keys
469
- keys << @variable if defined?(@object) || @collection
470
- keys << @variable_counter if @collection
509
+ keys << @variable if @object || @collection
510
+ if @collection
511
+ keys << @variable_counter
512
+ keys << @variable_iteration
513
+ end
471
514
  keys
472
515
  end
473
516
 
@@ -477,24 +520,19 @@ module ActionView
477
520
  raise_invalid_identifier(path) unless base =~ /\A_?([a-z]\w*)(\.\w+)*\z/
478
521
  $1.to_sym
479
522
  end
480
- variable_counter = :"#{variable}_counter" if @collection
481
- [variable, variable_counter]
523
+ if @collection
524
+ variable_counter = :"#{variable}_counter"
525
+ variable_iteration = :"#{variable}_iteration"
526
+ end
527
+ [variable, variable_counter, variable_iteration]
482
528
  end
483
529
 
484
530
  IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " +
485
- "make sure your partial name starts with underscore, " +
486
- "and is followed by any combination of letters, numbers and underscores."
487
-
488
- OPTION_AS_ERROR_MESSAGE = "The value (%s) of the option `as` is not a valid Ruby identifier; " +
489
- "make sure it starts with lowercase letter, " +
531
+ "make sure your partial name starts with a lowercase letter or underscore, " +
490
532
  "and is followed by any combination of letters, numbers and underscores."
491
533
 
492
534
  def raise_invalid_identifier(path)
493
535
  raise ArgumentError.new(IDENTIFIER_ERROR_MESSAGE % (path))
494
536
  end
495
-
496
- def raise_invalid_option_as(as)
497
- raise ArgumentError.new(OPTION_AS_ERROR_MESSAGE % (as))
498
- end
499
537
  end
500
538
  end
@@ -17,10 +17,6 @@ module ActionView
17
17
 
18
18
  # Main render entry point shared by AV and AC.
19
19
  def render(context, options)
20
- if options.respond_to?(:permitted?) && !options.permitted?
21
- raise ArgumentError, "render parameters are not permitted"
22
- end
23
-
24
20
  if options.key?(:partial)
25
21
  render_partial(context, options)
26
22
  else
@@ -6,19 +6,18 @@ module ActionView
6
6
  @view = context
7
7
  @details = extract_details(options)
8
8
  template = determine_template(options)
9
- context = @lookup_context
10
9
 
11
10
  prepend_formats(template.formats)
12
11
 
13
- unless context.rendered_format
14
- context.rendered_format = template.formats.first || formats.first
15
- end
12
+ @lookup_context.rendered_format ||= (template.formats.first || formats.first)
16
13
 
17
14
  render_template(template, options[:layout], options[:locals])
18
15
  end
19
16
 
17
+ private
18
+
20
19
  # Determine the template to be rendered using the given options.
21
- def determine_template(options) #:nodoc:
20
+ def determine_template(options)
22
21
  keys = options.fetch(:locals, {}).keys
23
22
 
24
23
  if options.key?(:body)
@@ -30,7 +29,7 @@ module ActionView
30
29
  elsif options.key?(:html)
31
30
  Template::HTML.new(options[:html], formats.first)
32
31
  elsif options.key?(:file)
33
- with_fallbacks { find_file(options[:file], nil, false, keys, @details) }
32
+ with_fallbacks { find_template(options[:file], nil, false, keys, @details) }
34
33
  elsif options.key?(:inline)
35
34
  handler = Template.handler_for_extension(options[:type] || "erb")
36
35
  Template.new(options[:inline], "inline template", handler, :locals => keys)
@@ -35,12 +35,13 @@ module ActionView
35
35
  module ClassMethods
36
36
  def view_context_class
37
37
  @view_context_class ||= begin
38
- routes = respond_to?(:_routes) && _routes
38
+ include_path_helpers = supports_path?
39
+ routes = respond_to?(:_routes) && _routes
39
40
  helpers = respond_to?(:_helpers) && _helpers
40
41
 
41
42
  Class.new(ActionView::Base) do
42
43
  if routes
43
- include routes.url_helpers
44
+ include routes.url_helpers(include_path_helpers)
44
45
  include routes.mounted_helpers
45
46
  end
46
47
 
@@ -62,8 +63,8 @@ module ActionView
62
63
  #
63
64
  # The view class must have the following methods:
64
65
  # View.new[lookup_context, assigns, controller]
65
- # Create a new ActionView instance for a controller
66
- # View#render[options]
66
+ # Create a new ActionView instance for a controller and we can also pass the arguments.
67
+ # View#render(option)
67
68
  # Returns String with the rendered template
68
69
  #
69
70
  # Override this method in a module to change the default behavior.
@@ -107,7 +108,7 @@ module ActionView
107
108
  end
108
109
 
109
110
  # Normalize args by converting render "foo" to render :action => "foo" and
110
- # render "foo/bar" to render :template => "foo/bar".
111
+ # render "foo/bar" to render :file => "foo/bar".
111
112
  # :api: private
112
113
  def _normalize_args(action=nil, options={})
113
114
  options = super(action, options)
@@ -117,7 +118,7 @@ module ActionView
117
118
  options = action
118
119
  when String, Symbol
119
120
  action = action.to_s
120
- key = action.include?(?/) ? :template : :action
121
+ key = action.include?(?/) ? :file : :action
121
122
  options[key] = action
122
123
  else
123
124
  options[:partial] = action
@@ -1,3 +1,5 @@
1
+ require 'action_dispatch/routing/polymorphic_routes'
2
+
1
3
  module ActionView
2
4
  module RoutingUrlFor
3
5
 
@@ -77,16 +79,22 @@ module ActionView
77
79
  case options
78
80
  when String
79
81
  options
80
- when nil, Hash
81
- options ||= {}
82
- options = { :only_path => options[:host].nil? }.merge!(options.symbolize_keys)
83
- super
82
+ when nil
83
+ super({:only_path => true})
84
+ when Hash
85
+ options = options.symbolize_keys
86
+ options[:only_path] = options[:host].nil? unless options.key?(:only_path)
87
+ super(options)
84
88
  when :back
85
89
  _back_url
90
+ when Symbol
91
+ ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.path.handle_string_call self, options
86
92
  when Array
87
93
  polymorphic_path(options, options.extract_options!)
94
+ when Class
95
+ ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.path.handle_class_call self, options
88
96
  else
89
- polymorphic_path(options)
97
+ ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.path.handle_model_call self, options
90
98
  end
91
99
  end
92
100
 
@@ -2,22 +2,20 @@ namespace :cache_digests do
2
2
  desc 'Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)'
3
3
  task :nested_dependencies => :environment do
4
4
  abort 'You must provide TEMPLATE for the task to run' unless ENV['TEMPLATE'].present?
5
- puts JSON.pretty_generate ActionView::Digestor.new(name: CacheDigests.template_name, finder: CacheDigests.finder).nested_dependencies
5
+ puts JSON.pretty_generate ActionView::Digestor.new(name: template_name, finder: finder).nested_dependencies
6
6
  end
7
7
 
8
8
  desc 'Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_comment.html)'
9
9
  task :dependencies => :environment do
10
10
  abort 'You must provide TEMPLATE for the task to run' unless ENV['TEMPLATE'].present?
11
- puts JSON.pretty_generate ActionView::Digestor.new(name: CacheDigests.template_name, finder: CacheDigests.finder).dependencies
11
+ puts JSON.pretty_generate ActionView::Digestor.new(name: template_name, finder: finder).dependencies
12
12
  end
13
13
 
14
- class CacheDigests
15
- def self.template_name
16
- ENV['TEMPLATE'].split('.', 2).first
17
- end
14
+ def template_name
15
+ ENV['TEMPLATE'].split('.', 2).first
16
+ end
18
17
 
19
- def self.finder
20
- ApplicationController.new.lookup_context
21
- end
18
+ def finder
19
+ ApplicationController.new.lookup_context
22
20
  end
23
21
  end
@@ -32,6 +32,15 @@ module ActionView #:nodoc:
32
32
  @@template_extensions = nil
33
33
  end
34
34
 
35
+ # Opposite to register_template_handler.
36
+ def unregister_template_handler(*extensions)
37
+ extensions.each do |extension|
38
+ handler = @@template_handlers.delete extension.to_sym
39
+ @@default_template_handlers = nil if @@default_template_handlers == handler
40
+ end
41
+ @@template_extensions = nil
42
+ end
43
+
35
44
  def template_handler_extensions
36
45
  @@template_handlers.keys.map {|key| key.to_s }.sort
37
46
  end
@@ -112,13 +112,7 @@ module ActionView
112
112
  # Normalizes the arguments and passes it on to find_templates.
113
113
  def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
114
114
  cached(key, [name, prefix, partial], details, locals) do
115
- find_templates(name, prefix, partial, details, false)
116
- end
117
- end
118
-
119
- def find_all_anywhere(name, prefix, partial=false, details={}, key=nil, locals=[])
120
- cached(key, [name, prefix, partial], details, locals) do
121
- find_templates(name, prefix, partial, details, true)
115
+ find_templates(name, prefix, partial, details)
122
116
  end
123
117
  end
124
118
 
@@ -129,8 +123,8 @@ module ActionView
129
123
  # This is what child classes implement. No defaults are needed
130
124
  # because Resolver guarantees that the arguments are present and
131
125
  # normalized.
132
- def find_templates(name, prefix, partial, details, outside_app_allowed = false)
133
- raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, outside_app_allowed) method"
126
+ def find_templates(name, prefix, partial, details)
127
+ raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details) method"
134
128
  end
135
129
 
136
130
  # Helpers that builds a path. Useful for building virtual paths.
@@ -179,16 +173,15 @@ module ActionView
179
173
 
180
174
  private
181
175
 
182
- def find_templates(name, prefix, partial, details, outside_app_allowed = false)
176
+ def find_templates(name, prefix, partial, details)
183
177
  path = Path.build(name, prefix, partial)
184
- query(path, details, details[:formats], outside_app_allowed)
178
+ query(path, details, details[:formats])
185
179
  end
186
180
 
187
- def query(path, details, formats, outside_app_allowed)
181
+ def query(path, details, formats)
188
182
  query = build_query(path, details)
189
183
 
190
184
  template_paths = find_template_paths query
191
- template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed
192
185
 
193
186
  template_paths.map { |template|
194
187
  handler, format, variant = extract_handler_and_format_and_variant(template, formats)
@@ -203,10 +196,6 @@ module ActionView
203
196
  }
204
197
  end
205
198
 
206
- def reject_files_external_to_app(files)
207
- files.reject { |filename| !inside_path?(@path, filename) }
208
- end
209
-
210
199
  if RUBY_VERSION >= '2.2.0'
211
200
  def find_template_paths(query)
212
201
  Dir[query].reject { |filename|
@@ -227,12 +216,6 @@ module ActionView
227
216
  end
228
217
  end
229
218
 
230
- def inside_path?(path, filename)
231
- filename = File.expand_path(filename)
232
- path = File.join(path, '')
233
- filename.start_with?(path)
234
- end
235
-
236
219
  # Helper for building query glob string based on resolver's pattern.
237
220
  def build_query(path, details)
238
221
  query = @pattern.dup
@@ -259,7 +242,7 @@ module ActionView
259
242
  File.mtime(p)
260
243
  end
261
244
 
262
- # Extract handler and formats from path. If a format cannot be a found neither
245
+ # Extract handler, formats and variant from path. If a format cannot be found neither
263
246
  # from the path, or the handler, we should return the array of formats given
264
247
  # to the resolver.
265
248
  def extract_handler_and_format_and_variant(path, default_formats)