actionview 5.2.3 → 6.0.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +203 -67
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -2
  5. data/lib/action_view.rb +3 -2
  6. data/lib/action_view/base.rb +107 -10
  7. data/lib/action_view/buffers.rb +15 -0
  8. data/lib/action_view/cache_expiry.rb +54 -0
  9. data/lib/action_view/context.rb +5 -9
  10. data/lib/action_view/digestor.rb +12 -20
  11. data/lib/action_view/gem_version.rb +3 -3
  12. data/lib/action_view/helpers.rb +0 -2
  13. data/lib/action_view/helpers/asset_tag_helper.rb +7 -30
  14. data/lib/action_view/helpers/asset_url_helper.rb +4 -3
  15. data/lib/action_view/helpers/cache_helper.rb +18 -10
  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 +69 -25
  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 +14 -9
  23. data/lib/action_view/helpers/javascript_helper.rb +9 -8
  24. data/lib/action_view/helpers/number_helper.rb +5 -0
  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 +7 -6
  29. data/lib/action_view/helpers/tags/base.rb +9 -5
  30. data/lib/action_view/helpers/tags/color_field.rb +1 -1
  31. data/lib/action_view/helpers/tags/translator.rb +1 -6
  32. data/lib/action_view/helpers/text_helper.rb +3 -3
  33. data/lib/action_view/helpers/translation_helper.rb +16 -12
  34. data/lib/action_view/helpers/url_helper.rb +15 -15
  35. data/lib/action_view/layouts.rb +5 -5
  36. data/lib/action_view/log_subscriber.rb +6 -6
  37. data/lib/action_view/lookup_context.rb +73 -31
  38. data/lib/action_view/path_set.rb +5 -10
  39. data/lib/action_view/railtie.rb +24 -1
  40. data/lib/action_view/record_identifier.rb +2 -2
  41. data/lib/action_view/renderer/abstract_renderer.rb +56 -3
  42. data/lib/action_view/renderer/partial_renderer.rb +66 -55
  43. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +62 -16
  44. data/lib/action_view/renderer/renderer.rb +16 -4
  45. data/lib/action_view/renderer/streaming_template_renderer.rb +5 -5
  46. data/lib/action_view/renderer/template_renderer.rb +24 -18
  47. data/lib/action_view/rendering.rb +51 -31
  48. data/lib/action_view/routing_url_for.rb +12 -11
  49. data/lib/action_view/template.rb +102 -70
  50. data/lib/action_view/template/error.rb +21 -1
  51. data/lib/action_view/template/handlers.rb +27 -1
  52. data/lib/action_view/template/handlers/builder.rb +2 -2
  53. data/lib/action_view/template/handlers/erb.rb +17 -7
  54. data/lib/action_view/template/handlers/erb/erubi.rb +7 -3
  55. data/lib/action_view/template/handlers/html.rb +1 -1
  56. data/lib/action_view/template/handlers/raw.rb +2 -2
  57. data/lib/action_view/template/html.rb +14 -5
  58. data/lib/action_view/template/inline.rb +22 -0
  59. data/lib/action_view/template/raw_file.rb +28 -0
  60. data/lib/action_view/template/resolver.rb +136 -133
  61. data/lib/action_view/template/sources.rb +13 -0
  62. data/lib/action_view/template/sources/file.rb +17 -0
  63. data/lib/action_view/template/text.rb +5 -3
  64. data/lib/action_view/test_case.rb +1 -1
  65. data/lib/action_view/testing/resolvers.rb +33 -20
  66. data/lib/action_view/unbound_template.rb +32 -0
  67. data/lib/action_view/view_paths.rb +25 -1
  68. data/lib/assets/compiled/rails-ujs.js +33 -7
  69. metadata +26 -18
  70. data/lib/action_view/helpers/record_tag_helper.rb +0 -23
@@ -19,10 +19,14 @@ module ActionView
19
19
 
20
20
  # Main render entry point shared by Action View and Action Controller.
21
21
  def render(context, options)
22
+ render_to_object(context, options).body
23
+ end
24
+
25
+ def render_to_object(context, options) # :nodoc:
22
26
  if options.key?(:partial)
23
- render_partial(context, options)
27
+ render_partial_to_object(context, options)
24
28
  else
25
- render_template(context, options)
29
+ render_template_to_object(context, options)
26
30
  end
27
31
  end
28
32
 
@@ -41,16 +45,24 @@ module ActionView
41
45
 
42
46
  # Direct access to template rendering.
43
47
  def render_template(context, options) #:nodoc:
44
- TemplateRenderer.new(@lookup_context).render(context, options)
48
+ render_template_to_object(context, options).body
45
49
  end
46
50
 
47
51
  # Direct access to partial rendering.
48
52
  def render_partial(context, options, &block) #:nodoc:
49
- PartialRenderer.new(@lookup_context).render(context, options, block)
53
+ render_partial_to_object(context, options, &block).body
50
54
  end
51
55
 
52
56
  def cache_hits # :nodoc:
53
57
  @cache_hits ||= {}
54
58
  end
59
+
60
+ def render_template_to_object(context, options) #:nodoc:
61
+ TemplateRenderer.new(@lookup_context).render(context, options)
62
+ end
63
+
64
+ def render_partial_to_object(context, options, &block) #:nodoc:
65
+ PartialRenderer.new(@lookup_context).render(context, options, block)
66
+ end
55
67
  end
56
68
  end
@@ -33,8 +33,8 @@ module ActionView
33
33
  logger = ActionView::Base.logger
34
34
  return unless logger
35
35
 
36
- message = "\n#{exception.class} (#{exception.message}):\n".dup
37
- message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
36
+ message = +"\n#{exception.class} (#{exception.message}):\n"
37
+ message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
38
38
  message << " " << exception.backtrace.join("\n ")
39
39
  logger.fatal("#{message}\n\n")
40
40
  end
@@ -43,14 +43,14 @@ module ActionView
43
43
  # For streaming, instead of rendering a given a template, we return a Body
44
44
  # object that responds to each. This object is initialized with a block
45
45
  # that knows how to render the template.
46
- def render_template(template, layout_name = nil, locals = {}) #:nodoc:
47
- return [super] unless layout_name && template.supports_streaming?
46
+ def render_template(view, template, layout_name = nil, locals = {}) #:nodoc:
47
+ return [super.body] unless layout_name && template.supports_streaming?
48
48
 
49
49
  locals ||= {}
50
50
  layout = layout_name && find_layout(layout_name, locals.keys, [formats.first])
51
51
 
52
52
  Body.new do |buffer|
53
- delayed_render(buffer, template, layout, @view, locals)
53
+ delayed_render(buffer, template, layout, view, locals)
54
54
  end
55
55
  end
56
56
 
@@ -5,15 +5,12 @@ 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
@@ -29,15 +26,25 @@ module ActionView
29
26
  elsif options.key?(:html)
30
27
  Template::HTML.new(options[:html], formats.first)
31
28
  elsif options.key?(:file)
32
- with_fallbacks { find_file(options[:file], nil, false, keys, @details) }
29
+ if File.exist?(options[:file])
30
+ Template::RawFile.new(options[:file])
31
+ else
32
+ ActiveSupport::Deprecation.warn "render file: should be given the absolute path to a file"
33
+ @lookup_context.with_fallbacks.find_template(options[:file], nil, false, keys, @details)
34
+ end
33
35
  elsif options.key?(:inline)
34
36
  handler = Template.handler_for_extension(options[:type] || "erb")
35
- Template.new(options[:inline], "inline template", handler, locals: keys)
37
+ format = if handler.respond_to?(:default_format)
38
+ handler.default_format
39
+ else
40
+ @lookup_context.formats.first
41
+ end
42
+ Template::Inline.new(options[:inline], "inline template", handler, locals: keys, format: format)
36
43
  elsif options.key?(:template)
37
44
  if options[:template].respond_to?(:render)
38
45
  options[:template]
39
46
  else
40
- find_template(options[:template], options[:prefixes], false, keys, @details)
47
+ @lookup_context.find_template(options[:template], options[:prefixes], false, keys, @details)
41
48
  end
42
49
  else
43
50
  raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file, :plain, :html or :body option."
@@ -46,27 +53,25 @@ module ActionView
46
53
 
47
54
  # Renders the given template. A string representing the layout can be
48
55
  # 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|
56
+ def render_template(view, template, layout_name, locals)
57
+ render_with_layout(view, template, layout_name, locals) do |layout|
53
58
  instrument(:template, identifier: template.identifier, layout: layout.try(:virtual_path)) do
54
59
  template.render(view, locals) { |*name| view._layout_for(*name) }
55
60
  end
56
61
  end
57
62
  end
58
63
 
59
- def render_with_layout(path, locals)
64
+ def render_with_layout(view, template, path, locals)
60
65
  layout = path && find_layout(path, locals.keys, [formats.first])
61
66
  content = yield(layout)
62
67
 
63
- if layout
64
- view = @view
68
+ body = if layout
65
69
  view.view_flow.set(:layout, content)
66
70
  layout.render(view, locals) { |*name| view._layout_for(*name) }
67
71
  else
68
72
  content
69
73
  end
74
+ build_rendered_template(body, template, layout)
70
75
  end
71
76
 
72
77
  # This is the method which actually finds the layout using details in the lookup
@@ -84,16 +89,17 @@ module ActionView
84
89
  when String
85
90
  begin
86
91
  if layout.start_with?("/")
87
- with_fallbacks { find_template(layout, nil, false, [], details) }
92
+ ActiveSupport::Deprecation.warn "Rendering layouts from an absolute path is deprecated."
93
+ @lookup_context.with_fallbacks.find_template(layout, nil, false, [], details)
88
94
  else
89
- find_template(layout, nil, false, [], details)
95
+ @lookup_context.find_template(layout, nil, false, [], details)
90
96
  end
91
97
  rescue ActionView::MissingTemplate
92
98
  all_details = @details.merge(formats: @lookup_context.default_formats)
93
99
  raise unless template_exists?(layout, nil, false, [], all_details)
94
100
  end
95
101
  when Proc
96
- resolve_layout(layout.call(formats), keys, formats)
102
+ resolve_layout(layout.call(@lookup_context, formats), keys, formats)
97
103
  else
98
104
  layout
99
105
  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,10 +103,6 @@ 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
107
 
93
108
  # Find and render a template based on the options given.
@@ -97,17 +112,22 @@ module ActionView
97
112
  context = view_context
98
113
 
99
114
  context.assign assigns if assigns
100
- lookup_context.rendered_format = nil if options[:formats]
101
115
  lookup_context.variants = variant if variant
102
116
 
103
- view_renderer.render(context, options)
117
+ rendered_template = context.in_rendering_context(options) do |renderer|
118
+ renderer.render_to_object(context, options)
119
+ end
120
+
121
+ rendered_format = rendered_template.format || lookup_context.formats.first
122
+ @rendered_format = Template::Types[rendered_format]
123
+
124
+ rendered_template.body
104
125
  end
105
126
 
106
127
  # Assign the rendered format to look up context.
107
128
  def _process_format(format)
108
129
  super
109
- lookup_context.formats = [format.to_sym]
110
- lookup_context.rendered_format = lookup_context.formats.first
130
+ lookup_context.formats = [format.to_sym] if format.to_sym
111
131
  end
112
132
 
113
133
  # 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
@@ -2,13 +2,23 @@
2
2
 
3
3
  require "active_support/core_ext/object/try"
4
4
  require "active_support/core_ext/kernel/singleton_class"
5
+ require "active_support/deprecation"
5
6
  require "thread"
7
+ require "delegate"
6
8
 
7
9
  module ActionView
8
10
  # = Action View Template
9
11
  class Template
10
12
  extend ActiveSupport::Autoload
11
13
 
14
+ def self.finalize_compiled_template_methods
15
+ ActiveSupport::Deprecation.warn "ActionView::Template.finalize_compiled_template_methods is deprecated and has no effect"
16
+ end
17
+
18
+ def self.finalize_compiled_template_methods=(_)
19
+ ActiveSupport::Deprecation.warn "ActionView::Template.finalize_compiled_template_methods= is deprecated and has no effect"
20
+ end
21
+
12
22
  # === Encodings in ActionView::Template
13
23
  #
14
24
  # ActionView::Template is one of a few sources of potential
@@ -103,44 +113,60 @@ module ActionView
103
113
 
104
114
  eager_autoload do
105
115
  autoload :Error
116
+ autoload :RawFile
106
117
  autoload :Handlers
107
118
  autoload :HTML
119
+ autoload :Inline
120
+ autoload :Sources
108
121
  autoload :Text
109
122
  autoload :Types
110
123
  end
111
124
 
112
125
  extend Template::Handlers
113
126
 
114
- attr_accessor :locals, :formats, :variants, :virtual_path
127
+ attr_reader :identifier, :handler, :original_encoding, :updated_at
128
+ attr_reader :variable, :format, :variant, :locals, :virtual_path
115
129
 
116
- attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
117
-
118
- # This finalizer is needed (and exactly with a proc inside another proc)
119
- # otherwise templates leak in development.
120
- Finalizer = proc do |method_name, mod| # :nodoc:
121
- proc do
122
- mod.module_eval do
123
- remove_possible_method method_name
124
- end
130
+ def initialize(source, identifier, handler, format: nil, variant: nil, locals: nil, virtual_path: nil, updated_at: nil)
131
+ unless locals
132
+ ActiveSupport::Deprecation.warn "ActionView::Template#initialize requires a locals parameter"
133
+ locals = []
125
134
  end
126
- end
127
-
128
- def initialize(source, identifier, handler, details)
129
- format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
130
135
 
131
136
  @source = source
132
137
  @identifier = identifier
133
138
  @handler = handler
134
139
  @compiled = false
135
- @original_encoding = nil
136
- @locals = details[:locals] || []
137
- @virtual_path = details[:virtual_path]
138
- @updated_at = details[:updated_at] || Time.now
139
- @formats = Array(format).map { |f| f.respond_to?(:ref) ? f.ref : f }
140
- @variants = [details[:variant]]
140
+ @locals = locals
141
+ @virtual_path = virtual_path
142
+
143
+ @variable = if @virtual_path
144
+ base = @virtual_path[-1] == "/" ? "" : ::File.basename(@virtual_path)
145
+ base =~ /\A_?(.*?)(?:\.\w+)*\z/
146
+ $1.to_sym
147
+ end
148
+
149
+ if updated_at
150
+ ActiveSupport::Deprecation.warn "ActionView::Template#updated_at is deprecated"
151
+ @updated_at = updated_at
152
+ else
153
+ @updated_at = Time.now
154
+ end
155
+ @format = format
156
+ @variant = variant
141
157
  @compile_mutex = Mutex.new
142
158
  end
143
159
 
160
+ deprecate :original_encoding
161
+ deprecate :updated_at
162
+ deprecate def virtual_path=(_); end
163
+ deprecate def locals=(_); end
164
+ deprecate def formats=(_); end
165
+ deprecate def formats; Array(format); end
166
+ deprecate def variants=(_); end
167
+ deprecate def variants; [variant]; end
168
+ deprecate def refresh(_); self; end
169
+
144
170
  # Returns whether the underlying handler supports streaming. If so,
145
171
  # a streaming buffer *may* be passed when it starts rendering.
146
172
  def supports_streaming?
@@ -153,40 +179,29 @@ module ActionView
153
179
  # This method is instrumented as "!render_template.action_view". Notice that
154
180
  # we use a bang in this instrumentation because you don't want to
155
181
  # consume this in production. This is only slow if it's being listened to.
156
- def render(view, locals, buffer = nil, &block)
182
+ def render(view, locals, buffer = ActionView::OutputBuffer.new, &block)
157
183
  instrument_render_template do
158
184
  compile!(view)
159
- view.send(method_name, locals, buffer, &block)
185
+ view._run(method_name, self, locals, buffer, &block)
160
186
  end
161
187
  rescue => e
162
188
  handle_render_error(view, e)
163
189
  end
164
190
 
165
191
  def type
166
- @type ||= Types[@formats.first] if @formats.first
192
+ @type ||= Types[format]
167
193
  end
168
194
 
169
- # Receives a view object and return a template similar to self by using @virtual_path.
170
- #
171
- # This method is useful if you have a template object but it does not contain its source
172
- # anymore since it was already compiled. In such cases, all you need to do is to call
173
- # refresh passing in the view object.
174
- #
175
- # Notice this method raises an error if the template to be refreshed does not have a
176
- # virtual path set (true just for inline templates).
177
- def refresh(view)
178
- raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
179
- lookup = view.lookup_context
180
- pieces = @virtual_path.split("/")
181
- name = pieces.pop
182
- partial = !!name.sub!(/^_/, "")
183
- lookup.disable_cache do
184
- lookup.find_template(name, [ pieces.join("/") ], partial, @locals)
185
- end
195
+ def short_identifier
196
+ @short_identifier ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "") : identifier
186
197
  end
187
198
 
188
199
  def inspect
189
- @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "".freeze) : identifier
200
+ "#<#{self.class.name} #{short_identifier} locals=#{@locals.inspect}>"
201
+ end
202
+
203
+ def source
204
+ @source.to_s
190
205
  end
191
206
 
192
207
  # This method is responsible for properly setting the encoding of the
@@ -200,7 +215,9 @@ module ActionView
200
215
  # before passing the source on to the template engine, leaving a
201
216
  # blank line in its stead.
202
217
  def encode!
203
- return unless source.encoding == Encoding::BINARY
218
+ source = self.source
219
+
220
+ return source unless source.encoding == Encoding::BINARY
204
221
 
205
222
  # Look for # encoding: *. If we find one, we'll encode the
206
223
  # String in that encoding, otherwise, we'll use the
@@ -233,6 +250,19 @@ module ActionView
233
250
  end
234
251
  end
235
252
 
253
+
254
+ # Exceptions are marshalled when using the parallel test runner with DRb, so we need
255
+ # to ensure that references to the template object can be marshalled as well. This means forgoing
256
+ # the marshalling of the compiler mutex and instantiating that again on unmarshalling.
257
+ def marshal_dump # :nodoc:
258
+ [ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant ]
259
+ end
260
+
261
+ def marshal_load(array) # :nodoc:
262
+ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant = *array
263
+ @compile_mutex = Mutex.new
264
+ end
265
+
236
266
  private
237
267
 
238
268
  # Compile a template. This method ensures a template is compiled
@@ -249,23 +279,25 @@ module ActionView
249
279
  # re-compilation
250
280
  return if @compiled
251
281
 
252
- if view.is_a?(ActionView::CompiledTemplates)
253
- mod = ActionView::CompiledTemplates
254
- else
255
- mod = view.singleton_class
256
- end
282
+ mod = view.compiled_method_container
257
283
 
258
284
  instrument("!compile_template") do
259
285
  compile(mod)
260
286
  end
261
287
 
262
- # Just discard the source if we have a virtual path. This
263
- # means we can get the template back.
264
- @source = nil if @virtual_path
265
288
  @compiled = true
266
289
  end
267
290
  end
268
291
 
292
+ class LegacyTemplate < DelegateClass(Template) # :nodoc:
293
+ attr_reader :source
294
+
295
+ def initialize(template, source)
296
+ super(template)
297
+ @source = source
298
+ end
299
+ end
300
+
269
301
  # Among other things, this method is responsible for properly setting
270
302
  # the encoding of the compiled template.
271
303
  #
@@ -279,16 +311,15 @@ module ActionView
279
311
  # In general, this means that templates will be UTF-8 inside of Rails,
280
312
  # regardless of the original source encoding.
281
313
  def compile(mod)
282
- encode!
283
- code = @handler.call(self)
314
+ source = encode!
315
+ code = @handler.call(self, source)
284
316
 
285
317
  # Make sure that the resulting String to be eval'd is in the
286
318
  # encoding of the code
287
- source = <<-end_src.dup
319
+ original_source = source
320
+ source = +<<-end_src
288
321
  def #{method_name}(local_assigns, output_buffer)
289
- _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
290
- ensure
291
- @virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
322
+ @virtual_path = #{@virtual_path.inspect};#{locals_code};#{code}
292
323
  end
293
324
  end_src
294
325
 
@@ -303,11 +334,17 @@ module ActionView
303
334
  # handler is valid in the default_internal. This is for handlers
304
335
  # that handle encoding but screw up
305
336
  unless source.valid_encoding?
306
- raise WrongEncodingError.new(@source, Encoding.default_internal)
337
+ raise WrongEncodingError.new(source, Encoding.default_internal)
307
338
  end
308
339
 
309
- mod.module_eval(source, identifier, 0)
310
- ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
340
+ begin
341
+ mod.module_eval(source, identifier, 0)
342
+ rescue SyntaxError
343
+ # Account for when code in the template is not syntactically valid; e.g. if we're using
344
+ # ERB and the user writes <%= foo( %>, attempting to call a helper `foo` and interpolate
345
+ # the result into the template, but missing an end parenthesis.
346
+ raise SyntaxErrorInTemplate.new(self, original_source)
347
+ end
311
348
  end
312
349
 
313
350
  def handle_render_error(view, e)
@@ -315,12 +352,7 @@ module ActionView
315
352
  e.sub_template_of(self)
316
353
  raise e
317
354
  else
318
- template = self
319
- unless template.source
320
- template = refresh(view)
321
- template.encode!
322
- end
323
- raise Template::Error.new(template)
355
+ raise Template::Error.new(self)
324
356
  end
325
357
  end
326
358
 
@@ -331,19 +363,19 @@ module ActionView
331
363
  locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
332
364
 
333
365
  # Assign for the same variable is to suppress unused variable warning
334
- locals.each_with_object("".dup) { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
366
+ locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
335
367
  end
336
368
 
337
369
  def method_name
338
370
  @method_name ||= begin
339
- m = "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".dup
340
- m.tr!("-".freeze, "_".freeze)
371
+ m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
372
+ m.tr!("-", "_")
341
373
  m
342
374
  end
343
375
  end
344
376
 
345
377
  def identifier_method_name
346
- inspect.tr("^a-z_".freeze, "_".freeze)
378
+ short_identifier.tr("^a-z_", "_")
347
379
  end
348
380
 
349
381
  def instrument(action, &block) # :doc:
@@ -351,7 +383,7 @@ module ActionView
351
383
  end
352
384
 
353
385
  def instrument_render_template(&block)
354
- ActiveSupport::Notifications.instrument("!render_template.action_view".freeze, instrument_payload, &block)
386
+ ActiveSupport::Notifications.instrument("!render_template.action_view", instrument_payload, &block)
355
387
  end
356
388
 
357
389
  def instrument_payload