actionview 5.2.8.1 → 6.0.6.1

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +280 -94
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -3
  5. data/lib/action_view/base.rb +108 -11
  6. data/lib/action_view/buffers.rb +15 -0
  7. data/lib/action_view/cache_expiry.rb +53 -0
  8. data/lib/action_view/context.rb +5 -9
  9. data/lib/action_view/digestor.rb +12 -20
  10. data/lib/action_view/flows.rb +0 -1
  11. data/lib/action_view/gem_version.rb +3 -3
  12. data/lib/action_view/helpers/active_model_helper.rb +0 -1
  13. data/lib/action_view/helpers/asset_tag_helper.rb +8 -31
  14. data/lib/action_view/helpers/asset_url_helper.rb +4 -3
  15. data/lib/action_view/helpers/cache_helper.rb +19 -12
  16. data/lib/action_view/helpers/capture_helper.rb +4 -0
  17. data/lib/action_view/helpers/csp_helper.rb +4 -2
  18. data/lib/action_view/helpers/csrf_helper.rb +1 -1
  19. data/lib/action_view/helpers/date_helper.rb +70 -27
  20. data/lib/action_view/helpers/form_helper.rb +240 -8
  21. data/lib/action_view/helpers/form_options_helper.rb +27 -18
  22. data/lib/action_view/helpers/form_tag_helper.rb +17 -15
  23. data/lib/action_view/helpers/javascript_helper.rb +9 -8
  24. data/lib/action_view/helpers/number_helper.rb +8 -2
  25. data/lib/action_view/helpers/output_safety_helper.rb +1 -1
  26. data/lib/action_view/helpers/rendering_helper.rb +6 -4
  27. data/lib/action_view/helpers/sanitize_helper.rb +12 -18
  28. data/lib/action_view/helpers/tag_helper.rb +8 -7
  29. data/lib/action_view/helpers/tags/base.rb +9 -6
  30. data/lib/action_view/helpers/tags/check_box.rb +0 -1
  31. data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -1
  32. data/lib/action_view/helpers/tags/collection_helpers.rb +0 -1
  33. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -1
  34. data/lib/action_view/helpers/tags/color_field.rb +1 -2
  35. data/lib/action_view/helpers/tags/date_field.rb +0 -1
  36. data/lib/action_view/helpers/tags/date_select.rb +0 -1
  37. data/lib/action_view/helpers/tags/datetime_field.rb +0 -1
  38. data/lib/action_view/helpers/tags/datetime_local_field.rb +0 -1
  39. data/lib/action_view/helpers/tags/label.rb +0 -1
  40. data/lib/action_view/helpers/tags/month_field.rb +0 -1
  41. data/lib/action_view/helpers/tags/radio_button.rb +0 -1
  42. data/lib/action_view/helpers/tags/select.rb +0 -1
  43. data/lib/action_view/helpers/tags/text_field.rb +0 -1
  44. data/lib/action_view/helpers/tags/time_field.rb +0 -1
  45. data/lib/action_view/helpers/tags/translator.rb +1 -6
  46. data/lib/action_view/helpers/tags/week_field.rb +0 -1
  47. data/lib/action_view/helpers/text_helper.rb +3 -4
  48. data/lib/action_view/helpers/translation_helper.rb +19 -17
  49. data/lib/action_view/helpers/url_helper.rb +14 -14
  50. data/lib/action_view/helpers.rb +0 -2
  51. data/lib/action_view/layouts.rb +5 -8
  52. data/lib/action_view/log_subscriber.rb +6 -7
  53. data/lib/action_view/lookup_context.rb +75 -32
  54. data/lib/action_view/path_set.rb +5 -11
  55. data/lib/action_view/railtie.rb +24 -1
  56. data/lib/action_view/record_identifier.rb +2 -3
  57. data/lib/action_view/renderer/abstract_renderer.rb +56 -4
  58. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +63 -17
  59. data/lib/action_view/renderer/partial_renderer.rb +67 -57
  60. data/lib/action_view/renderer/renderer.rb +16 -4
  61. data/lib/action_view/renderer/streaming_template_renderer.rb +5 -7
  62. data/lib/action_view/renderer/template_renderer.rb +25 -20
  63. data/lib/action_view/rendering.rb +51 -32
  64. data/lib/action_view/routing_url_for.rb +12 -11
  65. data/lib/action_view/template/error.rb +30 -15
  66. data/lib/action_view/template/handlers/builder.rb +2 -2
  67. data/lib/action_view/template/handlers/erb/erubi.rb +7 -3
  68. data/lib/action_view/template/handlers/erb.rb +17 -8
  69. data/lib/action_view/template/handlers/html.rb +1 -1
  70. data/lib/action_view/template/handlers/raw.rb +2 -2
  71. data/lib/action_view/template/handlers.rb +27 -1
  72. data/lib/action_view/template/html.rb +14 -5
  73. data/lib/action_view/template/inline.rb +22 -0
  74. data/lib/action_view/template/raw_file.rb +28 -0
  75. data/lib/action_view/template/resolver.rb +134 -135
  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 +5 -3
  79. data/lib/action_view/template.rb +102 -71
  80. data/lib/action_view/test_case.rb +3 -4
  81. data/lib/action_view/testing/resolvers.rb +33 -21
  82. data/lib/action_view/unbound_template.rb +31 -0
  83. data/lib/action_view/view_paths.rb +25 -2
  84. data/lib/action_view.rb +4 -2
  85. data/lib/assets/compiled/rails-ujs.js +30 -4
  86. metadata +27 -18
  87. data/lib/action_view/helpers/record_tag_helper.rb +0 -23
@@ -5,19 +5,15 @@ require "active_support/core_ext/object/try"
5
5
  module ActionView
6
6
  class TemplateRenderer < AbstractRenderer #:nodoc:
7
7
  def render(context, options)
8
- @view = context
9
8
  @details = extract_details(options)
10
9
  template = determine_template(options)
11
10
 
12
- prepend_formats(template.formats)
11
+ prepend_formats(template.format)
13
12
 
14
- @lookup_context.rendered_format ||= (template.formats.first || formats.first)
15
-
16
- render_template(template, options[:layout], options[:locals])
13
+ render_template(context, template, options[:layout], options[:locals] || {})
17
14
  end
18
15
 
19
16
  private
20
-
21
17
  # Determine the template to be rendered using the given options.
22
18
  def determine_template(options)
23
19
  keys = options.has_key?(:locals) ? options[:locals].keys : []
@@ -29,15 +25,25 @@ module ActionView
29
25
  elsif options.key?(:html)
30
26
  Template::HTML.new(options[:html], formats.first)
31
27
  elsif options.key?(:file)
32
- with_fallbacks { find_file(options[:file], nil, false, keys, @details) }
28
+ if File.exist?(options[:file])
29
+ Template::RawFile.new(options[:file])
30
+ else
31
+ ActiveSupport::Deprecation.warn "render file: should be given the absolute path to a file"
32
+ @lookup_context.with_fallbacks.find_template(options[:file], nil, false, keys, @details)
33
+ end
33
34
  elsif options.key?(:inline)
34
35
  handler = Template.handler_for_extension(options[:type] || "erb")
35
- Template.new(options[:inline], "inline template", handler, locals: keys)
36
+ format = if handler.respond_to?(:default_format)
37
+ handler.default_format
38
+ else
39
+ @lookup_context.formats.first
40
+ end
41
+ Template::Inline.new(options[:inline], "inline template", handler, locals: keys, format: format)
36
42
  elsif options.key?(:template)
37
43
  if options[:template].respond_to?(:render)
38
44
  options[:template]
39
45
  else
40
- find_template(options[:template], options[:prefixes], false, keys, @details)
46
+ @lookup_context.find_template(options[:template], options[:prefixes], false, keys, @details)
41
47
  end
42
48
  else
43
49
  raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file, :plain, :html or :body option."
@@ -46,27 +52,25 @@ module ActionView
46
52
 
47
53
  # Renders the given template. A string representing the layout can be
48
54
  # supplied as well.
49
- def render_template(template, layout_name = nil, locals = nil)
50
- view, locals = @view, locals || {}
51
-
52
- render_with_layout(layout_name, locals) do |layout|
55
+ def render_template(view, template, layout_name, locals)
56
+ render_with_layout(view, template, layout_name, locals) do |layout|
53
57
  instrument(:template, identifier: template.identifier, layout: layout.try(:virtual_path)) do
54
58
  template.render(view, locals) { |*name| view._layout_for(*name) }
55
59
  end
56
60
  end
57
61
  end
58
62
 
59
- def render_with_layout(path, locals)
63
+ def render_with_layout(view, template, path, locals)
60
64
  layout = path && find_layout(path, locals.keys, [formats.first])
61
65
  content = yield(layout)
62
66
 
63
- if layout
64
- view = @view
67
+ body = if layout
65
68
  view.view_flow.set(:layout, content)
66
69
  layout.render(view, locals) { |*name| view._layout_for(*name) }
67
70
  else
68
71
  content
69
72
  end
73
+ build_rendered_template(body, template, layout)
70
74
  end
71
75
 
72
76
  # This is the method which actually finds the layout using details in the lookup
@@ -84,16 +88,17 @@ module ActionView
84
88
  when String
85
89
  begin
86
90
  if layout.start_with?("/")
87
- with_fallbacks { find_template(layout, nil, false, [], details) }
91
+ ActiveSupport::Deprecation.warn "Rendering layouts from an absolute path is deprecated."
92
+ @lookup_context.with_fallbacks.find_template(layout, nil, false, [], details)
88
93
  else
89
- find_template(layout, nil, false, [], details)
94
+ @lookup_context.find_template(layout, nil, false, [], details)
90
95
  end
91
96
  rescue ActionView::MissingTemplate
92
97
  all_details = @details.merge(formats: @lookup_context.default_formats)
93
- raise unless template_exists?(layout, nil, false, [], all_details)
98
+ raise unless template_exists?(layout, nil, false, [], **all_details)
94
99
  end
95
100
  when Proc
96
- resolve_layout(layout.call(formats), keys, formats)
101
+ resolve_layout(layout.call(@lookup_context, formats), keys, formats)
97
102
  else
98
103
  layout
99
104
  end
@@ -26,6 +26,13 @@ module ActionView
26
26
  extend ActiveSupport::Concern
27
27
  include ActionView::ViewPaths
28
28
 
29
+ attr_reader :rendered_format
30
+
31
+ def initialize
32
+ @rendered_format = nil
33
+ super
34
+ end
35
+
29
36
  # Overwrite process to setup I18n proxy.
30
37
  def process(*) #:nodoc:
31
38
  old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
@@ -35,47 +42,59 @@ 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.
65
82
  #
66
83
  # The view class must have the following methods:
67
- # View.new[lookup_context, assigns, controller]
68
- # Create a new ActionView instance for a controller and we can also pass the arguments.
69
- # View#render(option)
70
- # Returns String with the rendered template
84
+ #
85
+ # * <tt>View.new(lookup_context, assigns, controller)</tt> Create a new
86
+ # ActionView instance for a controller and we can also pass the arguments.
87
+ #
88
+ # * <tt>View#render(option)</tt> — Returns String with the rendered template.
71
89
  #
72
90
  # Override this method in a module to change the default behavior.
73
91
  def view_context
74
- view_context_class.new(view_renderer, view_assigns, self)
92
+ view_context_class.new(lookup_context, view_assigns, self)
75
93
  end
76
94
 
77
95
  # Returns an object that is able to render templates.
78
96
  def view_renderer # :nodoc:
97
+ # Lifespan: Per controller
79
98
  @_view_renderer ||= ActionView::Renderer.new(lookup_context)
80
99
  end
81
100
 
@@ -84,12 +103,7 @@ module ActionView
84
103
  _render_template(options)
85
104
  end
86
105
 
87
- def rendered_format
88
- Template::Types[lookup_context.rendered_format]
89
- end
90
-
91
106
  private
92
-
93
107
  # Find and render a template based on the options given.
94
108
  def _render_template(options)
95
109
  variant = options.delete(:variant)
@@ -97,17 +111,22 @@ module ActionView
97
111
  context = view_context
98
112
 
99
113
  context.assign assigns if assigns
100
- lookup_context.rendered_format = nil if options[:formats]
101
114
  lookup_context.variants = variant if variant
102
115
 
103
- 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
104
124
  end
105
125
 
106
126
  # Assign the rendered format to look up context.
107
127
  def _process_format(format)
108
128
  super
109
- lookup_context.formats = [format.to_sym]
110
- lookup_context.rendered_format = lookup_context.formats.first
129
+ lookup_context.formats = [format.to_sym] if format.to_sym
111
130
  end
112
131
 
113
132
  # Normalize args by converting render "foo" to render :action => "foo" and
@@ -84,25 +84,24 @@ module ActionView
84
84
  super(only_path: _generate_paths_by_default)
85
85
  when Hash
86
86
  options = options.symbolize_keys
87
- unless options.key?(:only_path)
88
- options[:only_path] = only_path?(options[:host])
89
- end
87
+ ensure_only_path_option(options)
90
88
 
91
89
  super(options)
92
90
  when ActionController::Parameters
93
- unless options.key?(:only_path)
94
- options[:only_path] = only_path?(options[:host])
95
- end
91
+ ensure_only_path_option(options)
96
92
 
97
93
  super(options)
98
94
  when :back
99
95
  _back_url
100
96
  when Array
101
97
  components = options.dup
102
- if _generate_paths_by_default
103
- polymorphic_path(components, components.extract_options!)
98
+ options = components.extract_options!
99
+ ensure_only_path_option(options)
100
+
101
+ if options[:only_path]
102
+ polymorphic_path(components, options)
104
103
  else
105
- polymorphic_url(components, components.extract_options!)
104
+ polymorphic_url(components, options)
106
105
  end
107
106
  else
108
107
  method = _generate_paths_by_default ? :path : :url
@@ -138,8 +137,10 @@ module ActionView
138
137
  true
139
138
  end
140
139
 
141
- def only_path?(host)
142
- _generate_paths_by_default unless host
140
+ def ensure_only_path_option(options)
141
+ unless options.key?(:only_path)
142
+ options[:only_path] = _generate_paths_by_default unless options[:host]
143
+ end
143
144
  end
144
145
  end
145
146
  end
@@ -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,7 +13,7 @@ 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;"
16
+ properties[:preamble] = ""
17
17
  properties[:postamble] = "@output_buffer.to_s"
18
18
  properties[:bufvar] = "@output_buffer"
19
19
  properties[:escapefunc] = ""
@@ -22,8 +22,12 @@ module ActionView
22
22
  end
23
23
 
24
24
  def evaluate(action_view_erb_handler_context)
25
- pr = eval("proc { #{@src} }", binding, @filename || "(erubi)")
26
- action_view_erb_handler_context.instance_eval(&pr)
25
+ src = @src
26
+ view = Class.new(ActionView::Base) {
27
+ include action_view_erb_handler_context._routes.url_helpers
28
+ class_eval("define_method(:_template) { |local_assigns, output_buffer| #{src} }", defined?(@filename) ? @filename : "(erubi)", 0)
29
+ }.empty
30
+ view._run(:_template, nil, {}, ActionView::OutputBuffer.new)
27
31
  end
28
32
 
29
33
  private
@@ -14,12 +14,22 @@ module ActionView
14
14
  class_attribute :erb_implementation, default: Erubi
15
15
 
16
16
  # Do not escape templates of these mime types.
17
- class_attribute :escape_whitelist, default: ["text/plain"]
17
+ class_attribute :escape_ignore_list, default: ["text/plain"]
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
18
28
 
19
29
  ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
20
30
 
21
- def self.call(template)
22
- new.call(template)
31
+ def self.call(template, source)
32
+ new.call(template, source)
23
33
  end
24
34
 
25
35
  def supports_streaming?
@@ -30,30 +40,29 @@ module ActionView
30
40
  true
31
41
  end
32
42
 
33
- def call(template)
43
+ def call(template, source)
34
44
  # First, convert to BINARY, so in case the encoding is
35
45
  # wrong, we can still find an encoding tag
36
46
  # (<%# encoding %>) inside the String using a regular
37
47
  # expression
38
- template_source = template.source.dup.force_encoding(Encoding::ASCII_8BIT)
48
+ template_source = source.dup.force_encoding(Encoding::ASCII_8BIT)
39
49
 
40
50
  erb = template_source.gsub(ENCODING_TAG, "")
41
51
  encoding = $2
42
52
 
43
- erb.force_encoding valid_encoding(template.source.dup, encoding)
53
+ erb.force_encoding valid_encoding(source.dup, encoding)
44
54
 
45
55
  # Always make sure we return a String in the default_internal
46
56
  erb.encode!
47
57
 
48
58
  self.class.erb_implementation.new(
49
59
  erb,
50
- escape: (self.class.escape_whitelist.include? template.type),
60
+ escape: (self.class.escape_ignore_list.include? template.type),
51
61
  trim: (self.class.erb_trim_mode == "-")
52
62
  ).src
53
63
  end
54
64
 
55
65
  private
56
-
57
66
  def valid_encoding(string, encoding)
58
67
  # If a magic encoding comment was found, tag the
59
68
  # 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
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/deprecation"
4
+
3
5
  module ActionView #:nodoc:
4
6
  # = Action View Template Handlers
5
7
  class Template #:nodoc:
@@ -14,7 +16,7 @@ module ActionView #:nodoc:
14
16
  base.register_template_handler :erb, ERB.new
15
17
  base.register_template_handler :html, Html.new
16
18
  base.register_template_handler :builder, Builder.new
17
- base.register_template_handler :ruby, :source.to_proc
19
+ base.register_template_handler :ruby, lambda { |_, source| source }
18
20
  end
19
21
 
20
22
  @@template_handlers = {}
@@ -24,11 +26,35 @@ module ActionView #:nodoc:
24
26
  @@template_extensions ||= @@template_handlers.keys
25
27
  end
26
28
 
29
+ class LegacyHandlerWrapper < SimpleDelegator # :nodoc:
30
+ def call(view, source)
31
+ __getobj__.call(ActionView::Template::LegacyTemplate.new(view, source))
32
+ end
33
+ end
34
+
27
35
  # Register an object that knows how to handle template files with the given
28
36
  # extensions. This can be used to implement new template types.
29
37
  # The handler must respond to +:call+, which will be passed the template
30
38
  # and should return the rendered template as a String.
31
39
  def register_template_handler(*extensions, handler)
40
+ params = if handler.is_a?(Proc)
41
+ handler.parameters
42
+ else
43
+ handler.method(:call).parameters
44
+ end
45
+
46
+ unless params.find_all { |type, _| type == :req || type == :opt }.length >= 2
47
+ ActiveSupport::Deprecation.warn <<~eowarn
48
+ Single arity template handlers are deprecated. Template handlers must
49
+ now accept two parameters, the view object and the source for the view object.
50
+ Change:
51
+ >> #{handler}.call(#{params.map(&:last).join(", ")})
52
+ To:
53
+ >> #{handler}.call(#{params.map(&:last).join(", ")}, source)
54
+ eowarn
55
+ handler = LegacyHandlerWrapper.new(handler)
56
+ end
57
+
32
58
  raise(ArgumentError, "Extension is required") if extensions.empty?
33
59
  extensions.each do |extension|
34
60
  @@template_handlers[extension.to_sym] = handler
@@ -1,15 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/deprecation"
4
+
3
5
  module ActionView #:nodoc:
4
6
  # = Action View HTML Template
5
7
  class Template #:nodoc:
6
8
  class HTML #:nodoc:
7
- attr_accessor :type
9
+ attr_reader :type
8
10
 
9
11
  def initialize(string, type = nil)
12
+ unless type
13
+ ActiveSupport::Deprecation.warn "ActionView::Template::HTML#initialize requires a type parameter"
14
+ type = :html
15
+ end
16
+
10
17
  @string = string.to_s
11
- @type = Types[type] || type if type
12
- @type ||= Types[:html]
18
+ @type = type
13
19
  end
14
20
 
15
21
  def identifier
@@ -26,9 +32,12 @@ module ActionView #:nodoc:
26
32
  to_str
27
33
  end
28
34
 
29
- def formats
30
- [@type.respond_to?(:ref) ? @type.ref : @type.to_s]
35
+ def format
36
+ @type
31
37
  end
38
+
39
+ def formats; Array(format); end
40
+ deprecate :formats
32
41
  end
33
42
  end
34
43
  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,28 @@
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
+
24
+ def formats; Array(format); end
25
+ deprecate :formats
26
+ end
27
+ end
28
+ end