actionview 6.0.0.beta1 → 6.1.0

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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +206 -119
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -2
  5. data/lib/action_view/base.rb +81 -15
  6. data/lib/action_view/cache_expiry.rb +52 -0
  7. data/lib/action_view/context.rb +0 -6
  8. data/lib/action_view/dependency_tracker.rb +10 -4
  9. data/lib/action_view/digestor.rb +11 -19
  10. data/lib/action_view/flows.rb +0 -1
  11. data/lib/action_view/gem_version.rb +2 -2
  12. data/lib/action_view/helpers/active_model_helper.rb +0 -1
  13. data/lib/action_view/helpers/asset_tag_helper.rb +46 -21
  14. data/lib/action_view/helpers/asset_url_helper.rb +6 -4
  15. data/lib/action_view/helpers/atom_feed_helper.rb +2 -1
  16. data/lib/action_view/helpers/cache_helper.rb +16 -23
  17. data/lib/action_view/helpers/csp_helper.rb +4 -2
  18. data/lib/action_view/helpers/date_helper.rb +5 -6
  19. data/lib/action_view/helpers/form_helper.rb +63 -21
  20. data/lib/action_view/helpers/form_options_helper.rb +10 -18
  21. data/lib/action_view/helpers/form_tag_helper.rb +12 -9
  22. data/lib/action_view/helpers/javascript_helper.rb +7 -5
  23. data/lib/action_view/helpers/number_helper.rb +9 -8
  24. data/lib/action_view/helpers/output_safety_helper.rb +1 -1
  25. data/lib/action_view/helpers/rendering_helper.rb +17 -7
  26. data/lib/action_view/helpers/sanitize_helper.rb +10 -16
  27. data/lib/action_view/helpers/tag_helper.rb +94 -19
  28. data/lib/action_view/helpers/tags/base.rb +10 -7
  29. data/lib/action_view/helpers/tags/check_box.rb +0 -1
  30. data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -1
  31. data/lib/action_view/helpers/tags/collection_helpers.rb +0 -1
  32. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -1
  33. data/lib/action_view/helpers/tags/color_field.rb +0 -1
  34. data/lib/action_view/helpers/tags/date_field.rb +1 -2
  35. data/lib/action_view/helpers/tags/date_select.rb +2 -3
  36. data/lib/action_view/helpers/tags/datetime_field.rb +0 -1
  37. data/lib/action_view/helpers/tags/datetime_local_field.rb +1 -2
  38. data/lib/action_view/helpers/tags/label.rb +4 -1
  39. data/lib/action_view/helpers/tags/month_field.rb +1 -2
  40. data/lib/action_view/helpers/tags/radio_button.rb +0 -1
  41. data/lib/action_view/helpers/tags/select.rb +1 -2
  42. data/lib/action_view/helpers/tags/text_field.rb +0 -1
  43. data/lib/action_view/helpers/tags/time_field.rb +1 -2
  44. data/lib/action_view/helpers/tags/week_field.rb +1 -2
  45. data/lib/action_view/helpers/text_helper.rb +1 -2
  46. data/lib/action_view/helpers/translation_helper.rb +99 -54
  47. data/lib/action_view/helpers/url_helper.rb +109 -15
  48. data/lib/action_view/layouts.rb +8 -10
  49. data/lib/action_view/log_subscriber.rb +26 -11
  50. data/lib/action_view/lookup_context.rb +59 -31
  51. data/lib/action_view/path_set.rb +3 -12
  52. data/lib/action_view/railtie.rb +36 -42
  53. data/lib/action_view/record_identifier.rb +0 -1
  54. data/lib/action_view/renderer/abstract_renderer.rb +142 -11
  55. data/lib/action_view/renderer/collection_renderer.rb +192 -0
  56. data/lib/action_view/renderer/object_renderer.rb +34 -0
  57. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +35 -29
  58. data/lib/action_view/renderer/partial_renderer.rb +21 -273
  59. data/lib/action_view/renderer/renderer.rb +59 -4
  60. data/lib/action_view/renderer/streaming_template_renderer.rb +9 -7
  61. data/lib/action_view/renderer/template_renderer.rb +35 -27
  62. data/lib/action_view/rendering.rb +49 -29
  63. data/lib/action_view/routing_url_for.rb +1 -1
  64. data/lib/action_view/template/error.rb +30 -15
  65. data/lib/action_view/template/handlers/builder.rb +2 -2
  66. data/lib/action_view/template/handlers/erb/erubi.rb +15 -9
  67. data/lib/action_view/template/handlers/erb.rb +14 -19
  68. data/lib/action_view/template/handlers/html.rb +1 -1
  69. data/lib/action_view/template/handlers/raw.rb +2 -2
  70. data/lib/action_view/template/handlers.rb +1 -1
  71. data/lib/action_view/template/html.rb +5 -6
  72. data/lib/action_view/template/inline.rb +22 -0
  73. data/lib/action_view/template/raw_file.rb +25 -0
  74. data/lib/action_view/template/renderable.rb +24 -0
  75. data/lib/action_view/template/resolver.rb +141 -140
  76. data/lib/action_view/template/sources/file.rb +17 -0
  77. data/lib/action_view/template/sources.rb +13 -0
  78. data/lib/action_view/template/text.rb +2 -3
  79. data/lib/action_view/template.rb +49 -75
  80. data/lib/action_view/test_case.rb +20 -28
  81. data/lib/action_view/testing/resolvers.rb +18 -27
  82. data/lib/action_view/unbound_template.rb +31 -0
  83. data/lib/action_view/view_paths.rb +59 -38
  84. data/lib/action_view.rb +7 -2
  85. data/lib/assets/compiled/rails-ujs.js +22 -13
  86. metadata +30 -18
@@ -26,7 +26,14 @@ module ActionView
26
26
  extend ActiveSupport::Concern
27
27
  include ActionView::ViewPaths
28
28
 
29
- # Overwrite process to setup I18n proxy.
29
+ attr_reader :rendered_format
30
+
31
+ def initialize
32
+ @rendered_format = nil
33
+ super
34
+ end
35
+
36
+ # Overwrite process to set up I18n proxy.
30
37
  def process(*) #:nodoc:
31
38
  old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
32
39
  super
@@ -35,30 +42,40 @@ module ActionView
35
42
  end
36
43
 
37
44
  module ClassMethods
38
- def view_context_class
39
- @view_context_class ||= begin
40
- supports_path = supports_path?
41
- routes = respond_to?(:_routes) && _routes
42
- helpers = respond_to?(:_helpers) && _helpers
43
-
44
- Class.new(ActionView::Base) do
45
- if routes
46
- include routes.url_helpers(supports_path)
47
- include routes.mounted_helpers
48
- end
49
-
50
- if helpers
51
- include helpers
52
- end
45
+ def _routes
46
+ end
47
+
48
+ def _helpers
49
+ end
50
+
51
+ def build_view_context_class(klass, supports_path, routes, helpers)
52
+ Class.new(klass) do
53
+ if routes
54
+ include routes.url_helpers(supports_path)
55
+ include routes.mounted_helpers
56
+ end
57
+
58
+ if helpers
59
+ include helpers
53
60
  end
54
61
  end
55
62
  end
56
- end
57
63
 
58
- attr_internal_writer :view_context_class
64
+ def view_context_class
65
+ klass = ActionView::LookupContext::DetailsKey.view_context_class(ActionView::Base)
66
+
67
+ @view_context_class ||= build_view_context_class(klass, supports_path?, _routes, _helpers)
68
+
69
+ if klass.changed?(@view_context_class)
70
+ @view_context_class = build_view_context_class(klass, supports_path?, _routes, _helpers)
71
+ end
72
+
73
+ @view_context_class
74
+ end
75
+ end
59
76
 
60
77
  def view_context_class
61
- @_view_context_class ||= self.class.view_context_class
78
+ self.class.view_context_class
62
79
  end
63
80
 
64
81
  # An instance of a view class. The default view class is ActionView::Base.
@@ -72,11 +89,12 @@ module ActionView
72
89
  #
73
90
  # Override this method in a module to change the default behavior.
74
91
  def view_context
75
- view_context_class.new(view_renderer, view_assigns, self)
92
+ view_context_class.new(lookup_context, view_assigns, self)
76
93
  end
77
94
 
78
95
  # Returns an object that is able to render templates.
79
96
  def view_renderer # :nodoc:
97
+ # Lifespan: Per controller
80
98
  @_view_renderer ||= ActionView::Renderer.new(lookup_context)
81
99
  end
82
100
 
@@ -85,12 +103,7 @@ module ActionView
85
103
  _render_template(options)
86
104
  end
87
105
 
88
- def rendered_format
89
- Template::Types[lookup_context.rendered_format]
90
- end
91
-
92
106
  private
93
-
94
107
  # Find and render a template based on the options given.
95
108
  def _render_template(options)
96
109
  variant = options.delete(:variant)
@@ -98,17 +111,22 @@ module ActionView
98
111
  context = view_context
99
112
 
100
113
  context.assign assigns if assigns
101
- lookup_context.rendered_format = nil if options[:formats]
102
114
  lookup_context.variants = variant if variant
103
115
 
104
- view_renderer.render(context, options)
116
+ rendered_template = context.in_rendering_context(options) do |renderer|
117
+ renderer.render_to_object(context, options)
118
+ end
119
+
120
+ rendered_format = rendered_template.format || lookup_context.formats.first
121
+ @rendered_format = Template::Types[rendered_format]
122
+
123
+ rendered_template.body
105
124
  end
106
125
 
107
126
  # Assign the rendered format to look up context.
108
127
  def _process_format(format)
109
128
  super
110
- lookup_context.formats = [format.to_sym]
111
- lookup_context.rendered_format = lookup_context.formats.first
129
+ lookup_context.formats = [format.to_sym] if format.to_sym
112
130
  end
113
131
 
114
132
  # Normalize args by converting render "foo" to render :action => "foo" and
@@ -126,6 +144,8 @@ module ActionView
126
144
  else
127
145
  if action.respond_to?(:permitted?) && action.permitted?
128
146
  options = action
147
+ elsif action.respond_to?(:render_in)
148
+ options[:renderable] = action
129
149
  else
130
150
  options[:partial] = action
131
151
  end
@@ -105,7 +105,7 @@ module ActionView
105
105
  end
106
106
  else
107
107
  method = _generate_paths_by_default ? :path : :url
108
- builder = ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.send(method)
108
+ builder = ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.public_send(method)
109
109
 
110
110
  case options
111
111
  when Symbol
@@ -81,19 +81,19 @@ module ActionView
81
81
  end
82
82
  end
83
83
 
84
- def source_extract(indentation = 0, output = :console)
85
- return unless num = line_number
84
+ def source_extract(indentation = 0)
85
+ return [] unless num = line_number
86
86
  num = num.to_i
87
87
 
88
- source_code = @template.source.split("\n")
88
+ source_code = @template.encode!.split("\n")
89
89
 
90
90
  start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
91
91
  end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
92
92
 
93
93
  indent = end_on_line.to_s.size + indentation
94
- return unless source_code = source_code[start_on_line..end_on_line]
94
+ return [] unless source_code = source_code[start_on_line..end_on_line]
95
95
 
96
- formatted_code_for(source_code, start_on_line, indent, output)
96
+ formatted_code_for(source_code, start_on_line, indent)
97
97
  end
98
98
 
99
99
  def sub_template_of(template_path)
@@ -109,12 +109,11 @@ module ActionView
109
109
  end
110
110
  end
111
111
 
112
- def annoted_source_code
112
+ def annotated_source_code
113
113
  source_extract(4)
114
114
  end
115
115
 
116
116
  private
117
-
118
117
  def source_location
119
118
  if line_number
120
119
  "on line ##{line_number} of "
@@ -123,19 +122,35 @@ module ActionView
123
122
  end + file_name
124
123
  end
125
124
 
126
- def formatted_code_for(source_code, line_counter, indent, output)
127
- start_value = (output == :html) ? {} : []
128
- source_code.inject(start_value) do |result, line|
125
+ def formatted_code_for(source_code, line_counter, indent)
126
+ indent_template = "%#{indent}s: %s"
127
+ source_code.map do |line|
129
128
  line_counter += 1
130
- if output == :html
131
- result.update(line_counter.to_s => "%#{indent}s %s\n" % ["", line])
132
- else
133
- result << "%#{indent}s: %s" % [line_counter, line]
134
- end
129
+ indent_template % [line_counter, line]
135
130
  end
136
131
  end
137
132
  end
138
133
  end
139
134
 
140
135
  TemplateError = Template::Error
136
+
137
+ class SyntaxErrorInTemplate < TemplateError #:nodoc
138
+ def initialize(template, offending_code_string)
139
+ @offending_code_string = offending_code_string
140
+ super(template)
141
+ end
142
+
143
+ def message
144
+ <<~MESSAGE
145
+ Encountered a syntax error while rendering template: check #{@offending_code_string}
146
+ MESSAGE
147
+ end
148
+
149
+ def annotated_source_code
150
+ @offending_code_string.split("\n").map.with_index(1) { |line, index|
151
+ indentation = " " * 4
152
+ "#{index}:#{indentation}#{line}"
153
+ }
154
+ end
155
+ end
141
156
  end
@@ -5,11 +5,11 @@ module ActionView
5
5
  class Builder
6
6
  class_attribute :default_format, default: :xml
7
7
 
8
- def call(template)
8
+ def call(template, source)
9
9
  require_engine
10
10
  "xml = ::Builder::XmlMarkup.new(:indent => 2);" \
11
11
  "self.output_buffer = xml.target!;" +
12
- template.source +
12
+ source +
13
13
  ";xml.target!;"
14
14
  end
15
15
 
@@ -13,17 +13,23 @@ module ActionView
13
13
 
14
14
  # Dup properties so that we don't modify argument
15
15
  properties = Hash[properties]
16
- properties[:preamble] = "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
17
- properties[:postamble] = "@output_buffer.to_s"
18
- properties[:bufvar] = "@output_buffer"
16
+
17
+ properties[:bufvar] ||= "@output_buffer"
18
+ properties[:preamble] ||= ""
19
+ properties[:postamble] ||= "#{properties[:bufvar]}.to_s"
20
+
19
21
  properties[:escapefunc] = ""
20
22
 
21
23
  super
22
24
  end
23
25
 
24
26
  def evaluate(action_view_erb_handler_context)
25
- pr = eval("proc { #{@src} }", binding, @filename || "(erubi)")
26
- action_view_erb_handler_context.instance_eval(&pr)
27
+ src = @src
28
+ view = Class.new(ActionView::Base) {
29
+ include action_view_erb_handler_context._routes.url_helpers
30
+ class_eval("define_method(:_template) { |local_assigns, output_buffer| #{src} }", defined?(@filename) ? @filename : "(erubi)", 0)
31
+ }.empty
32
+ view._run(:_template, nil, {}, ActionView::OutputBuffer.new)
27
33
  end
28
34
 
29
35
  private
@@ -33,7 +39,7 @@ module ActionView
33
39
  if text == "\n"
34
40
  @newline_pending += 1
35
41
  else
36
- src << "@output_buffer.safe_append='"
42
+ src << bufvar << ".safe_append='"
37
43
  src << "\n" * @newline_pending if @newline_pending > 0
38
44
  src << text.gsub(/['\\]/, '\\\\\&')
39
45
  src << "'.freeze;"
@@ -48,9 +54,9 @@ module ActionView
48
54
  flush_newline_if_pending(src)
49
55
 
50
56
  if (indicator == "==") || @escape
51
- src << "@output_buffer.safe_expr_append="
57
+ src << bufvar << ".safe_expr_append="
52
58
  else
53
- src << "@output_buffer.append="
59
+ src << bufvar << ".append="
54
60
  end
55
61
 
56
62
  if BLOCK_EXPR.match?(code)
@@ -72,7 +78,7 @@ module ActionView
72
78
 
73
79
  def flush_newline_if_pending(src)
74
80
  if @newline_pending > 0
75
- src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
81
+ src << bufvar << ".safe_append='#{"\n" * @newline_pending}'.freeze;"
76
82
  @newline_pending = 0
77
83
  end
78
84
  end
@@ -16,20 +16,10 @@ module ActionView
16
16
  # Do not escape templates of these mime types.
17
17
  class_attribute :escape_ignore_list, default: ["text/plain"]
18
18
 
19
- [self, singleton_class].each do |base|
20
- base.alias_method :escape_whitelist, :escape_ignore_list
21
- base.alias_method :escape_whitelist=, :escape_ignore_list=
22
-
23
- base.deprecate(
24
- escape_whitelist: "use #escape_ignore_list instead",
25
- :escape_whitelist= => "use #escape_ignore_list= instead"
26
- )
27
- end
28
-
29
19
  ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
30
20
 
31
- def self.call(template)
32
- new.call(template)
21
+ def self.call(template, source)
22
+ new.call(template, source)
33
23
  end
34
24
 
35
25
  def supports_streaming?
@@ -40,30 +30,35 @@ module ActionView
40
30
  true
41
31
  end
42
32
 
43
- def call(template)
33
+ def call(template, source)
44
34
  # First, convert to BINARY, so in case the encoding is
45
35
  # wrong, we can still find an encoding tag
46
36
  # (<%# encoding %>) inside the String using a regular
47
37
  # expression
48
- template_source = template.source.dup.force_encoding(Encoding::ASCII_8BIT)
38
+ template_source = source.b
49
39
 
50
40
  erb = template_source.gsub(ENCODING_TAG, "")
51
41
  encoding = $2
52
42
 
53
- erb.force_encoding valid_encoding(template.source.dup, encoding)
43
+ erb.force_encoding valid_encoding(source.dup, encoding)
54
44
 
55
45
  # Always make sure we return a String in the default_internal
56
46
  erb.encode!
57
47
 
58
- self.class.erb_implementation.new(
59
- erb,
48
+ options = {
60
49
  escape: (self.class.escape_ignore_list.include? template.type),
61
50
  trim: (self.class.erb_trim_mode == "-")
62
- ).src
51
+ }
52
+
53
+ if ActionView::Base.annotate_rendered_view_with_filenames && template.format == :html
54
+ options[:preamble] = "@output_buffer.safe_append='<!-- BEGIN #{template.short_identifier} -->';"
55
+ options[:postamble] = "@output_buffer.safe_append='<!-- END #{template.short_identifier} -->';@output_buffer.to_s"
56
+ end
57
+
58
+ self.class.erb_implementation.new(erb, options).src
63
59
  end
64
60
 
65
61
  private
66
-
67
62
  def valid_encoding(string, encoding)
68
63
  # If a magic encoding comment was found, tag the
69
64
  # String with this encoding. This is for a case
@@ -3,7 +3,7 @@
3
3
  module ActionView
4
4
  module Template::Handlers
5
5
  class Html < Raw
6
- def call(template)
6
+ def call(template, source)
7
7
  "ActionView::OutputBuffer.new #{super}"
8
8
  end
9
9
  end
@@ -3,8 +3,8 @@
3
3
  module ActionView
4
4
  module Template::Handlers
5
5
  class Raw
6
- def call(template)
7
- "#{template.source.inspect}.html_safe;"
6
+ def call(template, source)
7
+ "#{source.inspect}.html_safe;"
8
8
  end
9
9
  end
10
10
  end
@@ -14,7 +14,7 @@ module ActionView #:nodoc:
14
14
  base.register_template_handler :erb, ERB.new
15
15
  base.register_template_handler :html, Html.new
16
16
  base.register_template_handler :builder, Builder.new
17
- base.register_template_handler :ruby, :source.to_proc
17
+ base.register_template_handler :ruby, lambda { |_, source| source }
18
18
  end
19
19
 
20
20
  @@template_handlers = {}
@@ -4,12 +4,11 @@ module ActionView #:nodoc:
4
4
  # = Action View HTML Template
5
5
  class Template #:nodoc:
6
6
  class HTML #:nodoc:
7
- attr_accessor :type
7
+ attr_reader :type
8
8
 
9
- def initialize(string, type = nil)
9
+ def initialize(string, type)
10
10
  @string = string.to_s
11
- @type = Types[type] || type if type
12
- @type ||= Types[:html]
11
+ @type = type
13
12
  end
14
13
 
15
14
  def identifier
@@ -26,8 +25,8 @@ module ActionView #:nodoc:
26
25
  to_str
27
26
  end
28
27
 
29
- def formats
30
- [@type.respond_to?(:ref) ? @type.ref : @type.to_s]
28
+ def format
29
+ @type
31
30
  end
32
31
  end
33
32
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView #:nodoc:
4
+ class Template #:nodoc:
5
+ class Inline < Template #:nodoc:
6
+ # This finalizer is needed (and exactly with a proc inside another proc)
7
+ # otherwise templates leak in development.
8
+ Finalizer = proc do |method_name, mod| # :nodoc:
9
+ proc do
10
+ mod.module_eval do
11
+ remove_possible_method method_name
12
+ end
13
+ end
14
+ end
15
+
16
+ def compile(mod)
17
+ super
18
+ ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView #:nodoc:
4
+ # = Action View RawFile Template
5
+ class Template #:nodoc:
6
+ class RawFile #:nodoc:
7
+ attr_accessor :type, :format
8
+
9
+ def initialize(filename)
10
+ @filename = filename.to_s
11
+ extname = ::File.extname(filename).delete(".")
12
+ @type = Template::Types[extname] || Template::Types[:text]
13
+ @format = @type.symbol
14
+ end
15
+
16
+ def identifier
17
+ @filename
18
+ end
19
+
20
+ def render(*args)
21
+ ::File.read(@filename)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ # = Action View Renderable Template for objects that respond to #render_in
5
+ class Template
6
+ class Renderable # :nodoc:
7
+ def initialize(renderable)
8
+ @renderable = renderable
9
+ end
10
+
11
+ def identifier
12
+ @renderable.class.name
13
+ end
14
+
15
+ def render(context, *args)
16
+ @renderable.render_in(context)
17
+ end
18
+
19
+ def format
20
+ @renderable.format
21
+ end
22
+ end
23
+ end
24
+ end