actionview 5.2.3

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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +142 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +38 -0
  5. data/lib/action_view.rb +97 -0
  6. data/lib/action_view/base.rb +215 -0
  7. data/lib/action_view/buffers.rb +52 -0
  8. data/lib/action_view/context.rb +36 -0
  9. data/lib/action_view/dependency_tracker.rb +175 -0
  10. data/lib/action_view/digestor.rb +134 -0
  11. data/lib/action_view/flows.rb +76 -0
  12. data/lib/action_view/gem_version.rb +17 -0
  13. data/lib/action_view/helpers.rb +68 -0
  14. data/lib/action_view/helpers/active_model_helper.rb +55 -0
  15. data/lib/action_view/helpers/asset_tag_helper.rb +511 -0
  16. data/lib/action_view/helpers/asset_url_helper.rb +469 -0
  17. data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
  18. data/lib/action_view/helpers/cache_helper.rb +263 -0
  19. data/lib/action_view/helpers/capture_helper.rb +212 -0
  20. data/lib/action_view/helpers/controller_helper.rb +36 -0
  21. data/lib/action_view/helpers/csp_helper.rb +24 -0
  22. data/lib/action_view/helpers/csrf_helper.rb +35 -0
  23. data/lib/action_view/helpers/date_helper.rb +1156 -0
  24. data/lib/action_view/helpers/debug_helper.rb +36 -0
  25. data/lib/action_view/helpers/form_helper.rb +2337 -0
  26. data/lib/action_view/helpers/form_options_helper.rb +887 -0
  27. data/lib/action_view/helpers/form_tag_helper.rb +917 -0
  28. data/lib/action_view/helpers/javascript_helper.rb +94 -0
  29. data/lib/action_view/helpers/number_helper.rb +451 -0
  30. data/lib/action_view/helpers/output_safety_helper.rb +70 -0
  31. data/lib/action_view/helpers/record_tag_helper.rb +23 -0
  32. data/lib/action_view/helpers/rendering_helper.rb +99 -0
  33. data/lib/action_view/helpers/sanitize_helper.rb +177 -0
  34. data/lib/action_view/helpers/tag_helper.rb +313 -0
  35. data/lib/action_view/helpers/tags.rb +44 -0
  36. data/lib/action_view/helpers/tags/base.rb +192 -0
  37. data/lib/action_view/helpers/tags/check_box.rb +66 -0
  38. data/lib/action_view/helpers/tags/checkable.rb +18 -0
  39. data/lib/action_view/helpers/tags/collection_check_boxes.rb +36 -0
  40. data/lib/action_view/helpers/tags/collection_helpers.rb +119 -0
  41. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
  42. data/lib/action_view/helpers/tags/collection_select.rb +30 -0
  43. data/lib/action_view/helpers/tags/color_field.rb +27 -0
  44. data/lib/action_view/helpers/tags/date_field.rb +15 -0
  45. data/lib/action_view/helpers/tags/date_select.rb +74 -0
  46. data/lib/action_view/helpers/tags/datetime_field.rb +32 -0
  47. data/lib/action_view/helpers/tags/datetime_local_field.rb +21 -0
  48. data/lib/action_view/helpers/tags/datetime_select.rb +10 -0
  49. data/lib/action_view/helpers/tags/email_field.rb +10 -0
  50. data/lib/action_view/helpers/tags/file_field.rb +10 -0
  51. data/lib/action_view/helpers/tags/grouped_collection_select.rb +31 -0
  52. data/lib/action_view/helpers/tags/hidden_field.rb +10 -0
  53. data/lib/action_view/helpers/tags/label.rb +81 -0
  54. data/lib/action_view/helpers/tags/month_field.rb +15 -0
  55. data/lib/action_view/helpers/tags/number_field.rb +20 -0
  56. data/lib/action_view/helpers/tags/password_field.rb +14 -0
  57. data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
  58. data/lib/action_view/helpers/tags/radio_button.rb +33 -0
  59. data/lib/action_view/helpers/tags/range_field.rb +10 -0
  60. data/lib/action_view/helpers/tags/search_field.rb +27 -0
  61. data/lib/action_view/helpers/tags/select.rb +43 -0
  62. data/lib/action_view/helpers/tags/tel_field.rb +10 -0
  63. data/lib/action_view/helpers/tags/text_area.rb +24 -0
  64. data/lib/action_view/helpers/tags/text_field.rb +34 -0
  65. data/lib/action_view/helpers/tags/time_field.rb +15 -0
  66. data/lib/action_view/helpers/tags/time_select.rb +10 -0
  67. data/lib/action_view/helpers/tags/time_zone_select.rb +22 -0
  68. data/lib/action_view/helpers/tags/translator.rb +44 -0
  69. data/lib/action_view/helpers/tags/url_field.rb +10 -0
  70. data/lib/action_view/helpers/tags/week_field.rb +15 -0
  71. data/lib/action_view/helpers/text_helper.rb +486 -0
  72. data/lib/action_view/helpers/translation_helper.rb +141 -0
  73. data/lib/action_view/helpers/url_helper.rb +676 -0
  74. data/lib/action_view/layouts.rb +433 -0
  75. data/lib/action_view/locale/en.yml +56 -0
  76. data/lib/action_view/log_subscriber.rb +96 -0
  77. data/lib/action_view/lookup_context.rb +274 -0
  78. data/lib/action_view/model_naming.rb +14 -0
  79. data/lib/action_view/path_set.rb +100 -0
  80. data/lib/action_view/railtie.rb +82 -0
  81. data/lib/action_view/record_identifier.rb +112 -0
  82. data/lib/action_view/renderer/abstract_renderer.rb +55 -0
  83. data/lib/action_view/renderer/partial_renderer.rb +552 -0
  84. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +57 -0
  85. data/lib/action_view/renderer/renderer.rb +56 -0
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +105 -0
  87. data/lib/action_view/renderer/template_renderer.rb +102 -0
  88. data/lib/action_view/rendering.rb +151 -0
  89. data/lib/action_view/routing_url_for.rb +145 -0
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template.rb +361 -0
  92. data/lib/action_view/template/error.rb +141 -0
  93. data/lib/action_view/template/handlers.rb +66 -0
  94. data/lib/action_view/template/handlers/builder.rb +25 -0
  95. data/lib/action_view/template/handlers/erb.rb +74 -0
  96. data/lib/action_view/template/handlers/erb/erubi.rb +83 -0
  97. data/lib/action_view/template/handlers/html.rb +11 -0
  98. data/lib/action_view/template/handlers/raw.rb +11 -0
  99. data/lib/action_view/template/html.rb +34 -0
  100. data/lib/action_view/template/resolver.rb +391 -0
  101. data/lib/action_view/template/text.rb +33 -0
  102. data/lib/action_view/template/types.rb +57 -0
  103. data/lib/action_view/test_case.rb +300 -0
  104. data/lib/action_view/testing/resolvers.rb +54 -0
  105. data/lib/action_view/version.rb +10 -0
  106. data/lib/action_view/view_paths.rb +105 -0
  107. data/lib/assets/compiled/rails-ujs.js +720 -0
  108. metadata +255 -0
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_dispatch/routing/polymorphic_routes"
4
+
5
+ module ActionView
6
+ module RoutingUrlFor
7
+ # Returns the URL for the set of +options+ provided. This takes the
8
+ # same options as +url_for+ in Action Controller (see the
9
+ # documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
10
+ # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action"
11
+ # instead of the fully qualified URL like "http://example.com/controller/action".
12
+ #
13
+ # ==== Options
14
+ # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
15
+ # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
16
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
17
+ # is currently not recommended since it breaks caching.
18
+ # * <tt>:host</tt> - Overrides the default (current) host if provided.
19
+ # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
20
+ # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
21
+ # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
22
+ #
23
+ # ==== Relying on named routes
24
+ #
25
+ # Passing a record (like an Active Record) instead of a hash as the options parameter will
26
+ # trigger the named route for that record. The lookup will happen on the name of the class. So passing a
27
+ # Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
28
+ # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
29
+ #
30
+ # ==== Implicit Controller Namespacing
31
+ #
32
+ # Controllers passed in using the +:controller+ option will retain their namespace unless it is an absolute one.
33
+ #
34
+ # ==== Examples
35
+ # <%= url_for(action: 'index') %>
36
+ # # => /blogs/
37
+ #
38
+ # <%= url_for(action: 'find', controller: 'books') %>
39
+ # # => /books/find
40
+ #
41
+ # <%= url_for(action: 'login', controller: 'members', only_path: false, protocol: 'https') %>
42
+ # # => https://www.example.com/members/login/
43
+ #
44
+ # <%= url_for(action: 'play', anchor: 'player') %>
45
+ # # => /messages/play/#player
46
+ #
47
+ # <%= url_for(action: 'jump', anchor: 'tax&ship') %>
48
+ # # => /testing/jump/#tax&ship
49
+ #
50
+ # <%= url_for(Workshop.new) %>
51
+ # # relies on Workshop answering a persisted? call (and in this case returning false)
52
+ # # => /workshops
53
+ #
54
+ # <%= url_for(@workshop) %>
55
+ # # calls @workshop.to_param which by default returns the id
56
+ # # => /workshops/5
57
+ #
58
+ # # to_param can be re-defined in a model to provide different URL names:
59
+ # # => /workshops/1-workshop-name
60
+ #
61
+ # <%= url_for("http://www.example.com") %>
62
+ # # => http://www.example.com
63
+ #
64
+ # <%= url_for(:back) %>
65
+ # # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
66
+ # # => http://www.example.com
67
+ #
68
+ # <%= url_for(:back) %>
69
+ # # if request.env["HTTP_REFERER"] is not set or is blank
70
+ # # => javascript:history.back()
71
+ #
72
+ # <%= url_for(action: 'index', controller: 'users') %>
73
+ # # Assuming an "admin" namespace
74
+ # # => /admin/users
75
+ #
76
+ # <%= url_for(action: 'index', controller: '/users') %>
77
+ # # Specify absolute path with beginning slash
78
+ # # => /users
79
+ def url_for(options = nil)
80
+ case options
81
+ when String
82
+ options
83
+ when nil
84
+ super(only_path: _generate_paths_by_default)
85
+ when Hash
86
+ options = options.symbolize_keys
87
+ unless options.key?(:only_path)
88
+ options[:only_path] = only_path?(options[:host])
89
+ end
90
+
91
+ super(options)
92
+ when ActionController::Parameters
93
+ unless options.key?(:only_path)
94
+ options[:only_path] = only_path?(options[:host])
95
+ end
96
+
97
+ super(options)
98
+ when :back
99
+ _back_url
100
+ when Array
101
+ components = options.dup
102
+ if _generate_paths_by_default
103
+ polymorphic_path(components, components.extract_options!)
104
+ else
105
+ polymorphic_url(components, components.extract_options!)
106
+ end
107
+ else
108
+ method = _generate_paths_by_default ? :path : :url
109
+ builder = ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.send(method)
110
+
111
+ case options
112
+ when Symbol
113
+ builder.handle_string_call(self, options)
114
+ when Class
115
+ builder.handle_class_call(self, options)
116
+ else
117
+ builder.handle_model_call(self, options)
118
+ end
119
+ end
120
+ end
121
+
122
+ def url_options #:nodoc:
123
+ return super unless controller.respond_to?(:url_options)
124
+ controller.url_options
125
+ end
126
+
127
+ private
128
+ def _routes_context
129
+ controller
130
+ end
131
+
132
+ def optimize_routes_generation?
133
+ controller.respond_to?(:optimize_routes_generation?, true) ?
134
+ controller.optimize_routes_generation? : super
135
+ end
136
+
137
+ def _generate_paths_by_default
138
+ true
139
+ end
140
+
141
+ def only_path?(host)
142
+ _generate_paths_by_default unless host
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :cache_digests do
4
+ desc "Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)"
5
+ task nested_dependencies: :environment do
6
+ abort "You must provide TEMPLATE for the task to run" unless ENV["TEMPLATE"].present?
7
+ puts JSON.pretty_generate ActionView::Digestor.tree(CacheDigests.template_name, CacheDigests.finder).children.map(&:to_dep_map)
8
+ end
9
+
10
+ desc "Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_comment.html)"
11
+ task dependencies: :environment do
12
+ abort "You must provide TEMPLATE for the task to run" unless ENV["TEMPLATE"].present?
13
+ puts JSON.pretty_generate ActionView::Digestor.tree(CacheDigests.template_name, CacheDigests.finder).children.map(&:name)
14
+ end
15
+
16
+ class CacheDigests
17
+ def self.template_name
18
+ ENV["TEMPLATE"].split(".", 2).first
19
+ end
20
+
21
+ def self.finder
22
+ ApplicationController.new.lookup_context
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,361 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/try"
4
+ require "active_support/core_ext/kernel/singleton_class"
5
+ require "thread"
6
+
7
+ module ActionView
8
+ # = Action View Template
9
+ class Template
10
+ extend ActiveSupport::Autoload
11
+
12
+ # === Encodings in ActionView::Template
13
+ #
14
+ # ActionView::Template is one of a few sources of potential
15
+ # encoding issues in Rails. This is because the source for
16
+ # templates are usually read from disk, and Ruby (like most
17
+ # encoding-aware programming languages) assumes that the
18
+ # String retrieved through File IO is encoded in the
19
+ # <tt>default_external</tt> encoding. In Rails, the default
20
+ # <tt>default_external</tt> encoding is UTF-8.
21
+ #
22
+ # As a result, if a user saves their template as ISO-8859-1
23
+ # (for instance, using a non-Unicode-aware text editor),
24
+ # and uses characters outside of the ASCII range, their
25
+ # users will see diamonds with question marks in them in
26
+ # the browser.
27
+ #
28
+ # For the rest of this documentation, when we say "UTF-8",
29
+ # we mean "UTF-8 or whatever the default_internal encoding
30
+ # is set to". By default, it will be UTF-8.
31
+ #
32
+ # To mitigate this problem, we use a few strategies:
33
+ # 1. If the source is not valid UTF-8, we raise an exception
34
+ # when the template is compiled to alert the user
35
+ # to the problem.
36
+ # 2. The user can specify the encoding using Ruby-style
37
+ # encoding comments in any template engine. If such
38
+ # a comment is supplied, Rails will apply that encoding
39
+ # to the resulting compiled source returned by the
40
+ # template handler.
41
+ # 3. In all cases, we transcode the resulting String to
42
+ # the UTF-8.
43
+ #
44
+ # This means that other parts of Rails can always assume
45
+ # that templates are encoded in UTF-8, even if the original
46
+ # source of the template was not UTF-8.
47
+ #
48
+ # From a user's perspective, the easiest thing to do is
49
+ # to save your templates as UTF-8. If you do this, you
50
+ # do not need to do anything else for things to "just work".
51
+ #
52
+ # === Instructions for template handlers
53
+ #
54
+ # The easiest thing for you to do is to simply ignore
55
+ # encodings. Rails will hand you the template source
56
+ # as the default_internal (generally UTF-8), raising
57
+ # an exception for the user before sending the template
58
+ # to you if it could not determine the original encoding.
59
+ #
60
+ # For the greatest simplicity, you can support only
61
+ # UTF-8 as the <tt>default_internal</tt>. This means
62
+ # that from the perspective of your handler, the
63
+ # entire pipeline is just UTF-8.
64
+ #
65
+ # === Advanced: Handlers with alternate metadata sources
66
+ #
67
+ # If you want to provide an alternate mechanism for
68
+ # specifying encodings (like ERB does via <%# encoding: ... %>),
69
+ # you may indicate that you will handle encodings yourself
70
+ # by implementing <tt>handles_encoding?</tt> on your handler.
71
+ #
72
+ # If you do, Rails will not try to encode the String
73
+ # into the default_internal, passing you the unaltered
74
+ # bytes tagged with the assumed encoding (from
75
+ # default_external).
76
+ #
77
+ # In this case, make sure you return a String from
78
+ # your handler encoded in the default_internal. Since
79
+ # you are handling out-of-band metadata, you are
80
+ # also responsible for alerting the user to any
81
+ # problems with converting the user's data to
82
+ # the <tt>default_internal</tt>.
83
+ #
84
+ # To do so, simply raise +WrongEncodingError+ as follows:
85
+ #
86
+ # raise WrongEncodingError.new(
87
+ # problematic_string,
88
+ # expected_encoding
89
+ # )
90
+
91
+ ##
92
+ # :method: local_assigns
93
+ #
94
+ # Returns a hash with the defined local variables.
95
+ #
96
+ # Given this sub template rendering:
97
+ #
98
+ # <%= render "shared/header", { headline: "Welcome", person: person } %>
99
+ #
100
+ # You can use +local_assigns+ in the sub templates to access the local variables:
101
+ #
102
+ # local_assigns[:headline] # => "Welcome"
103
+
104
+ eager_autoload do
105
+ autoload :Error
106
+ autoload :Handlers
107
+ autoload :HTML
108
+ autoload :Text
109
+ autoload :Types
110
+ end
111
+
112
+ extend Template::Handlers
113
+
114
+ attr_accessor :locals, :formats, :variants, :virtual_path
115
+
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
125
+ 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
+
131
+ @source = source
132
+ @identifier = identifier
133
+ @handler = handler
134
+ @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]]
141
+ @compile_mutex = Mutex.new
142
+ end
143
+
144
+ # Returns whether the underlying handler supports streaming. If so,
145
+ # a streaming buffer *may* be passed when it starts rendering.
146
+ def supports_streaming?
147
+ handler.respond_to?(:supports_streaming?) && handler.supports_streaming?
148
+ end
149
+
150
+ # Render a template. If the template was not compiled yet, it is done
151
+ # exactly before rendering.
152
+ #
153
+ # This method is instrumented as "!render_template.action_view". Notice that
154
+ # we use a bang in this instrumentation because you don't want to
155
+ # consume this in production. This is only slow if it's being listened to.
156
+ def render(view, locals, buffer = nil, &block)
157
+ instrument_render_template do
158
+ compile!(view)
159
+ view.send(method_name, locals, buffer, &block)
160
+ end
161
+ rescue => e
162
+ handle_render_error(view, e)
163
+ end
164
+
165
+ def type
166
+ @type ||= Types[@formats.first] if @formats.first
167
+ end
168
+
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
186
+ end
187
+
188
+ def inspect
189
+ @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "".freeze) : identifier
190
+ end
191
+
192
+ # This method is responsible for properly setting the encoding of the
193
+ # source. Until this point, we assume that the source is BINARY data.
194
+ # If no additional information is supplied, we assume the encoding is
195
+ # the same as <tt>Encoding.default_external</tt>.
196
+ #
197
+ # The user can also specify the encoding via a comment on the first
198
+ # line of the template (# encoding: NAME-OF-ENCODING). This will work
199
+ # with any template engine, as we process out the encoding comment
200
+ # before passing the source on to the template engine, leaving a
201
+ # blank line in its stead.
202
+ def encode!
203
+ return unless source.encoding == Encoding::BINARY
204
+
205
+ # Look for # encoding: *. If we find one, we'll encode the
206
+ # String in that encoding, otherwise, we'll use the
207
+ # default external encoding.
208
+ if source.sub!(/\A#{ENCODING_FLAG}/, "")
209
+ encoding = magic_encoding = $1
210
+ else
211
+ encoding = Encoding.default_external
212
+ end
213
+
214
+ # Tag the source with the default external encoding
215
+ # or the encoding specified in the file
216
+ source.force_encoding(encoding)
217
+
218
+ # If the user didn't specify an encoding, and the handler
219
+ # handles encodings, we simply pass the String as is to
220
+ # the handler (with the default_external tag)
221
+ if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding?
222
+ source
223
+ # Otherwise, if the String is valid in the encoding,
224
+ # encode immediately to default_internal. This means
225
+ # that if a handler doesn't handle encodings, it will
226
+ # always get Strings in the default_internal
227
+ elsif source.valid_encoding?
228
+ source.encode!
229
+ # Otherwise, since the String is invalid in the encoding
230
+ # specified, raise an exception
231
+ else
232
+ raise WrongEncodingError.new(source, encoding)
233
+ end
234
+ end
235
+
236
+ private
237
+
238
+ # Compile a template. This method ensures a template is compiled
239
+ # just once and removes the source after it is compiled.
240
+ def compile!(view)
241
+ return if @compiled
242
+
243
+ # Templates can be used concurrently in threaded environments
244
+ # so compilation and any instance variable modification must
245
+ # be synchronized
246
+ @compile_mutex.synchronize do
247
+ # Any thread holding this lock will be compiling the template needed
248
+ # by the threads waiting. So re-check the @compiled flag to avoid
249
+ # re-compilation
250
+ return if @compiled
251
+
252
+ if view.is_a?(ActionView::CompiledTemplates)
253
+ mod = ActionView::CompiledTemplates
254
+ else
255
+ mod = view.singleton_class
256
+ end
257
+
258
+ instrument("!compile_template") do
259
+ compile(mod)
260
+ end
261
+
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
+ @compiled = true
266
+ end
267
+ end
268
+
269
+ # Among other things, this method is responsible for properly setting
270
+ # the encoding of the compiled template.
271
+ #
272
+ # If the template engine handles encodings, we send the encoded
273
+ # String to the engine without further processing. This allows
274
+ # the template engine to support additional mechanisms for
275
+ # specifying the encoding. For instance, ERB supports <%# encoding: %>
276
+ #
277
+ # Otherwise, after we figure out the correct encoding, we then
278
+ # encode the source into <tt>Encoding.default_internal</tt>.
279
+ # In general, this means that templates will be UTF-8 inside of Rails,
280
+ # regardless of the original source encoding.
281
+ def compile(mod)
282
+ encode!
283
+ code = @handler.call(self)
284
+
285
+ # Make sure that the resulting String to be eval'd is in the
286
+ # encoding of the code
287
+ source = <<-end_src.dup
288
+ 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
292
+ end
293
+ end_src
294
+
295
+ # Make sure the source is in the encoding of the returned code
296
+ source.force_encoding(code.encoding)
297
+
298
+ # In case we get back a String from a handler that is not in
299
+ # BINARY or the default_internal, encode it to the default_internal
300
+ source.encode!
301
+
302
+ # Now, validate that the source we got back from the template
303
+ # handler is valid in the default_internal. This is for handlers
304
+ # that handle encoding but screw up
305
+ unless source.valid_encoding?
306
+ raise WrongEncodingError.new(@source, Encoding.default_internal)
307
+ end
308
+
309
+ mod.module_eval(source, identifier, 0)
310
+ ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
311
+ end
312
+
313
+ def handle_render_error(view, e)
314
+ if e.is_a?(Template::Error)
315
+ e.sub_template_of(self)
316
+ raise e
317
+ else
318
+ template = self
319
+ unless template.source
320
+ template = refresh(view)
321
+ template.encode!
322
+ end
323
+ raise Template::Error.new(template)
324
+ end
325
+ end
326
+
327
+ def locals_code
328
+ # Only locals with valid variable names get set directly. Others will
329
+ # still be available in local_assigns.
330
+ locals = @locals - Module::RUBY_RESERVED_KEYWORDS
331
+ locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
332
+
333
+ # 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};" }
335
+ end
336
+
337
+ def method_name
338
+ @method_name ||= begin
339
+ m = "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".dup
340
+ m.tr!("-".freeze, "_".freeze)
341
+ m
342
+ end
343
+ end
344
+
345
+ def identifier_method_name
346
+ inspect.tr("^a-z_".freeze, "_".freeze)
347
+ end
348
+
349
+ def instrument(action, &block) # :doc:
350
+ ActiveSupport::Notifications.instrument("#{action}.action_view", instrument_payload, &block)
351
+ end
352
+
353
+ def instrument_render_template(&block)
354
+ ActiveSupport::Notifications.instrument("!render_template.action_view".freeze, instrument_payload, &block)
355
+ end
356
+
357
+ def instrument_payload
358
+ { virtual_path: @virtual_path, identifier: @identifier }
359
+ end
360
+ end
361
+ end