Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 339 lines (302 sloc) 13.139 kb
737abf2 @josevalim Doh, be sure to store the string modification.
josevalim authored
1 require 'active_support/core_ext/object/try'
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
2 require 'active_support/core_ext/kernel/singleton_class'
74c4e7b @josevalim Merge pull request #6407 from pinetops/565c1b0a0772ac6cf91c77e9285806f7b...
josevalim authored
3 require 'thread'
3c11876 @wycats Makes rails-dev-boost work again
wycats authored
4
cecafc5 Refactor ActionView::Template
Yehuda Katz + Carl Lerche authored
5 module ActionView
f09736b @rizwanreza Added title to some other files in actionpack/lib/action_view
rizwanreza authored
6 # = Action View Template
73b34e9 @josh Refactor template preloading. New abstractions include Renderable mixins...
josh authored
7 class Template
c130409 Reorganize autoloads:
Carlhuda authored
8 extend ActiveSupport::Autoload
ace20bd @josh Flip deferrable autoload convention
josh authored
9
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
10 # === Encodings in ActionView::Template
11 #
12 # ActionView::Template is one of a few sources of potential
13 # encoding issues in Rails. This is because the source for
14 # templates are usually read from disk, and Ruby (like most
15 # encoding-aware programming languages) assumes that the
16 # String retrieved through File IO is encoded in the
17 # <tt>default_external</tt> encoding. In Rails, the default
18 # <tt>default_external</tt> encoding is UTF-8.
19 #
20 # As a result, if a user saves their template as ISO-8859-1
21 # (for instance, using a non-Unicode-aware text editor),
22 # and uses characters outside of the ASCII range, their
23 # users will see diamonds with question marks in them in
24 # the browser.
25 #
0078df6 @wycats Update template to allow handlers to more cleanly handle encodings (ht: ...
wycats authored
26 # For the rest of this documentation, when we say "UTF-8",
27 # we mean "UTF-8 or whatever the default_internal encoding
28 # is set to". By default, it will be UTF-8.
29 #
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
30 # To mitigate this problem, we use a few strategies:
31 # 1. If the source is not valid UTF-8, we raise an exception
32 # when the template is compiled to alert the user
33 # to the problem.
34 # 2. The user can specify the encoding using Ruby-style
35 # encoding comments in any template engine. If such
36 # a comment is supplied, Rails will apply that encoding
37 # to the resulting compiled source returned by the
38 # template handler.
39 # 3. In all cases, we transcode the resulting String to
0078df6 @wycats Update template to allow handlers to more cleanly handle encodings (ht: ...
wycats authored
40 # the UTF-8.
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
41 #
42 # This means that other parts of Rails can always assume
43 # that templates are encoded in UTF-8, even if the original
44 # source of the template was not UTF-8.
45 #
46 # From a user's perspective, the easiest thing to do is
47 # to save your templates as UTF-8. If you do this, you
48 # do not need to do anything else for things to "just work".
49 #
50 # === Instructions for template handlers
51 #
52 # The easiest thing for you to do is to simply ignore
53 # encodings. Rails will hand you the template source
54 # as the default_internal (generally UTF-8), raising
55 # an exception for the user before sending the template
56 # to you if it could not determine the original encoding.
57 #
58 # For the greatest simplicity, you can support only
59 # UTF-8 as the <tt>default_internal</tt>. This means
60 # that from the perspective of your handler, the
61 # entire pipeline is just UTF-8.
62 #
63 # === Advanced: Handlers with alternate metadata sources
64 #
65 # If you want to provide an alternate mechanism for
66 # specifying encodings (like ERB does via <%# encoding: ... %>),
0078df6 @wycats Update template to allow handlers to more cleanly handle encodings (ht: ...
wycats authored
67 # you may indicate that you will handle encodings yourself
68 # by implementing <tt>self.handles_encoding?</tt>
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
69 # on your handler.
70 #
0078df6 @wycats Update template to allow handlers to more cleanly handle encodings (ht: ...
wycats authored
71 # If you do, Rails will not try to encode the String
72 # into the default_internal, passing you the unaltered
73 # bytes tagged with the assumed encoding (from
74 # default_external).
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
75 #
76 # In this case, make sure you return a String from
77 # your handler encoded in the default_internal. Since
78 # you are handling out-of-band metadata, you are
79 # also responsible for alerting the user to any
80 # problems with converting the user's data to
1f7d55e @smartinez87 Fix styiling issue on ActionView::Template docs
smartinez87 authored
81 # the <tt>default_internal</tt>.
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
82 #
4313461 @fxn generic pass before merging docrails
fxn authored
83 # To do so, simply raise +WrongEncodingError+ as follows:
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
84 #
85 # raise WrongEncodingError.new(
86 # problematic_string,
87 # expected_encoding
88 # )
89
ace20bd @josh Flip deferrable autoload convention
josh authored
90 eager_autoload do
91 autoload :Error
92 autoload :Handlers
93 autoload :Text
67f55e2 @drogus Implement ActionView::Template::Types
drogus authored
94 autoload :Types
ace20bd @josh Flip deferrable autoload convention
josh authored
95 end
96
c130409 Reorganize autoloads:
Carlhuda authored
97 extend Template::Handlers
00d6271 @josevalim Clean up the API required from ActionView::Template.
josevalim authored
98
38d78f9 @josevalim Resolvers now consider timestamps.
josevalim authored
99 attr_accessor :locals, :formats, :virtual_path
b2600bf @josevalim Remove locals dependency from template.
josevalim authored
100
38d78f9 @josevalim Resolvers now consider timestamps.
josevalim authored
101 attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
610b81b @josh Clean up log output for rendered templates
josh authored
102
6a55ca3 @josevalim Revert "removing crazy finalizer code until there is proof that we need ...
josevalim authored
103 # This finalizer is needed (and exactly with a proc inside another proc)
104 # otherwise templates leak in development.
105 Finalizer = proc do |method_name, mod|
106 proc do
107 mod.module_eval do
108 remove_possible_method method_name
109 end
110 end
111 end
112
cecafc5 Refactor ActionView::Template
Yehuda Katz + Carl Lerche authored
113 def initialize(source, identifier, handler, details)
38d78f9 @josevalim Resolvers now consider timestamps.
josevalim authored
114 format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
115
116 @source = source
117 @identifier = identifier
118 @handler = handler
119 @compiled = false
120 @original_encoding = nil
121 @locals = details[:locals] || []
122 @virtual_path = details[:virtual_path]
123 @updated_at = details[:updated_at] || Time.now
67f55e2 @drogus Implement ActionView::Template::Types
drogus authored
124 @formats = Array(format).map { |f| f.respond_to?(:ref) ? f.ref : f }
74c4e7b @josevalim Merge pull request #6407 from pinetops/565c1b0a0772ac6cf91c77e9285806f7b...
josevalim authored
125 @compile_mutex = Mutex.new
3c11876 @wycats Makes rails-dev-boost work again
wycats authored
126 end
610b81b @josh Clean up log output for rendered templates
josh authored
127
e30ca00 @josevalim Yo dawg, I heard you like streaming. So I put a fiber, inside a block, i...
josevalim authored
128 # Returns if the underlying handler supports streaming. If so,
129 # a streaming buffer *may* be passed when it start rendering.
130 def supports_streaming?
131 handler.respond_to?(:supports_streaming?) && handler.supports_streaming?
132 end
133
64c7f7e @josevalim Add more docs and tests to templates.
josevalim authored
134 # Render a template. If the template was not compiled yet, it is done
135 # exactly before rendering.
136 #
137 # This method is instrumented as "!render_template.action_view". Notice that
138 # we use a bang in this instrumentation because you don't want to
139 # consume this in production. This is only slow if it's being listened to.
2dd43c3 @josevalim Buffer should be an option passed down to template rendering.
josevalim authored
140 def render(view, locals, buffer=nil, &block)
731d439 @jaggederest Change event namespace ordering to most-significant first [#4504 state:r...
jaggederest authored
141 ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do
b2600bf @josevalim Remove locals dependency from template.
josevalim authored
142 compile!(view)
2dd43c3 @josevalim Buffer should be an option passed down to template rendering.
josevalim authored
143 view.send(method_name, locals, buffer, &block)
947f86c Modify assert_template to use instrumentation
Carlhuda authored
144 end
27adcd1 @wycats Clean up ActionView some:
wycats authored
145 rescue Exception => e
64c7f7e @josevalim Add more docs and tests to templates.
josevalim authored
146 handle_render_error(view, e)
cecafc5 Refactor ActionView::Template
Yehuda Katz + Carl Lerche authored
147 end
610b81b @josh Clean up log output for rendered templates
josh authored
148
582a7f4 @drogus Deprecate Template#mime_type
drogus authored
149 def mime_type
0b7067d @nikitug Provide a call stack for deprecation warnings where needed.
nikitug authored
150 message = 'Template#mime_type is deprecated and will be removed in Rails 4.1. Please use type method instead.'
b955939 @gazay Make caller attribute in deprecation methods optional
gazay authored
151 ActiveSupport::Deprecation.warn message
582a7f4 @drogus Deprecate Template#mime_type
drogus authored
152 @mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
153 end
154
f21a528 @drogus Remove Mime::Type translations from Action View
drogus authored
155 def type
67f55e2 @drogus Implement ActionView::Template::Types
drogus authored
156 @type ||= Types[@formats.first] if @formats.first
00d6271 @josevalim Clean up the API required from ActionView::Template.
josevalim authored
157 end
158
64c7f7e @josevalim Add more docs and tests to templates.
josevalim authored
159 # Receives a view object and return a template similar to self by using @virtual_path.
160 #
161 # This method is useful if you have a template object but it does not contain its source
162 # anymore since it was already compiled. In such cases, all you need to do is to call
163 # refresh passing in the view object.
164 #
165 # Notice this method raises an error if the template to be refreshed does not have a
166 # virtual path set (true just for inline templates).
167 def refresh(view)
11aa515 @josevalim Add expire! and rerender to the template API. This will be used by SASS ...
josevalim authored
168 raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
157ea76 @spastorino format lookup for partials is derived from the format in which the templ...
spastorino authored
169 lookup = view.lookup_context
170 pieces = @virtual_path.split("/")
171 name = pieces.pop
172 partial = !!name.sub!(/^_/, "")
173 lookup.disable_cache do
174 lookup.find_template(name, [ pieces.join('/') ], partial, @locals)
11aa515 @josevalim Add expire! and rerender to the template API. This will be used by SASS ...
josevalim authored
175 end
176 end
177
610b81b @josh Clean up log output for rendered templates
josh authored
178 def inspect
bebaccd @josevalim Remove dependency from _template.
josevalim authored
179 @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier
610b81b @josh Clean up log output for rendered templates
josh authored
180 end
692dbbf @NZKoz Introduce a Template class to ActionView. Closes #11024 [lifofifo]
NZKoz authored
181
45e85c3 @josevalim Check if source is encoding aware.
josevalim authored
182 # This method is responsible for properly setting the encoding of the
19292a7 @josevalim Do not hard code encoding to UTF8
josevalim authored
183 # source. Until this point, we assume that the source is BINARY data.
184 # If no additional information is supplied, we assume the encoding is
185 # the same as <tt>Encoding.default_external</tt>.
186 #
187 # The user can also specify the encoding via a comment on the first
188 # line of the template (# encoding: NAME-OF-ENCODING). This will work
189 # with any template engine, as we process out the encoding comment
190 # before passing the source on to the template engine, leaving a
191 # blank line in its stead.
192 def encode!
5ca86ac @lest deprecate String#encoding_aware? and remove its usage
lest authored
193 return unless source.encoding == Encoding::BINARY
45e85c3 @josevalim Check if source is encoding aware.
josevalim authored
194
195 # Look for # encoding: *. If we find one, we'll encode the
196 # String in that encoding, otherwise, we'll use the
197 # default external encoding.
198 if source.sub!(/\A#{ENCODING_FLAG}/, '')
199 encoding = magic_encoding = $1
200 else
201 encoding = Encoding.default_external
202 end
19292a7 @josevalim Do not hard code encoding to UTF8
josevalim authored
203
45e85c3 @josevalim Check if source is encoding aware.
josevalim authored
204 # Tag the source with the default external encoding
205 # or the encoding specified in the file
206 source.force_encoding(encoding)
207
208 # If the user didn't specify an encoding, and the handler
209 # handles encodings, we simply pass the String as is to
210 # the handler (with the default_external tag)
211 if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding?
212 source
213 # Otherwise, if the String is valid in the encoding,
214 # encode immediately to default_internal. This means
215 # that if a handler doesn't handle encodings, it will
216 # always get Strings in the default_internal
217 elsif source.valid_encoding?
218 source.encode!
219 # Otherwise, since the String is invalid in the encoding
220 # specified, raise an exception
221 else
222 raise WrongEncodingError.new(source, encoding)
19292a7 @josevalim Do not hard code encoding to UTF8
josevalim authored
223 end
224 end
225
64c7f7e @josevalim Add more docs and tests to templates.
josevalim authored
226 protected
b2600bf @josevalim Remove locals dependency from template.
josevalim authored
227
64c7f7e @josevalim Add more docs and tests to templates.
josevalim authored
228 # Compile a template. This method ensures a template is compiled
229 # just once and removes the source after it is compiled.
230 def compile!(view) #:nodoc:
231 return if @compiled
b2600bf @josevalim Remove locals dependency from template.
josevalim authored
232
74c4e7b @josevalim Merge pull request #6407 from pinetops/565c1b0a0772ac6cf91c77e9285806f7b...
josevalim authored
233 # Templates can be used concurrently in threaded environments
234 # so compilation and any instance variable modification must
235 # be synchronized
236 @compile_mutex.synchronize do
237 # Any thread holding this lock will be compiling the template needed
238 # by the threads waiting. So re-check the @compiled flag to avoid
239 # re-compilation
240 return if @compiled
241
242 if view.is_a?(ActionView::CompiledTemplates)
243 mod = ActionView::CompiledTemplates
244 else
245 mod = view.singleton_class
246 end
247
248 compile(view, mod)
249
250 # Just discard the source if we have a virtual path. This
251 # means we can get the template back.
252 @source = nil if @virtual_path
253 @compiled = true
64c7f7e @josevalim Add more docs and tests to templates.
josevalim authored
254 end
255 end
b2600bf @josevalim Remove locals dependency from template.
josevalim authored
256
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
257 # Among other things, this method is responsible for properly setting
19292a7 @josevalim Do not hard code encoding to UTF8
josevalim authored
258 # the encoding of the compiled template.
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
259 #
0078df6 @wycats Update template to allow handlers to more cleanly handle encodings (ht: ...
wycats authored
260 # If the template engine handles encodings, we send the encoded
261 # String to the engine without further processing. This allows
262 # the template engine to support additional mechanisms for
263 # specifying the encoding. For instance, ERB supports <%# encoding: %>
264 #
265 # Otherwise, after we figure out the correct encoding, we then
1f7d55e @smartinez87 Fix styiling issue on ActionView::Template docs
smartinez87 authored
266 # encode the source into <tt>Encoding.default_internal</tt>.
267 # In general, this means that templates will be UTF-8 inside of Rails,
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
268 # regardless of the original source encoding.
64c7f7e @josevalim Add more docs and tests to templates.
josevalim authored
269 def compile(view, mod) #:nodoc:
19292a7 @josevalim Do not hard code encoding to UTF8
josevalim authored
270 encode!
b2600bf @josevalim Remove locals dependency from template.
josevalim authored
271 method_name = self.method_name
2dd43c3 @josevalim Buffer should be an option passed down to template rendering.
josevalim authored
272 code = @handler.call(self)
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
273
0078df6 @wycats Update template to allow handlers to more cleanly handle encodings (ht: ...
wycats authored
274 # Make sure that the resulting String to be evalled is in the
275 # encoding of the code
610b81b @josh Clean up log output for rendered templates
josh authored
276 source = <<-end_src
2dd43c3 @josevalim Buffer should be an option passed down to template rendering.
josevalim authored
277 def #{method_name}(local_assigns, output_buffer)
bebaccd @josevalim Remove dependency from _template.
josevalim authored
278 _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
610b81b @josh Clean up log output for rendered templates
josh authored
279 ensure
bebaccd @josevalim Remove dependency from _template.
josevalim authored
280 @virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
610b81b @josh Clean up log output for rendered templates
josh authored
281 end
282 end_src
283
5ca86ac @lest deprecate String#encoding_aware? and remove its usage
lest authored
284 # Make sure the source is in the encoding of the returned code
285 source.force_encoding(code.encoding)
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
286
5ca86ac @lest deprecate String#encoding_aware? and remove its usage
lest authored
287 # In case we get back a String from a handler that is not in
288 # BINARY or the default_internal, encode it to the default_internal
289 source.encode!
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
290
5ca86ac @lest deprecate String#encoding_aware? and remove its usage
lest authored
291 # Now, validate that the source we got back from the template
292 # handler is valid in the default_internal. This is for handlers
293 # that handle encoding but screw up
294 unless source.valid_encoding?
295 raise WrongEncodingError.new(@source, Encoding.default_internal)
cecafc5 Refactor ActionView::Template
Yehuda Katz + Carl Lerche authored
296 end
73b34e9 @josh Refactor template preloading. New abstractions include Renderable mixins...
josh authored
297
610b81b @josh Clean up log output for rendered templates
josh authored
298 begin
80b6067 @wycats Revert "Moved encoding work in progress to a feature branch."
wycats authored
299 mod.module_eval(source, identifier, 0)
6a55ca3 @josevalim Revert "removing crazy finalizer code until there is proof that we need ...
josevalim authored
300 ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
610b81b @josh Clean up log output for rendered templates
josh authored
301 rescue Exception => e # errors from template code
302 if logger = (view && view.logger)
303 logger.debug "ERROR: compiling #{method_name} RAISED #{e}"
304 logger.debug "Function body: #{source}"
305 logger.debug "Backtrace: #{e.backtrace.join("\n")}"
306 end
307
73709f7 @brainopia Removed unused assigns from ActionView::Template::Error
brainopia authored
308 raise ActionView::Template::Error.new(self, e)
610b81b @josh Clean up log output for rendered templates
josh authored
309 end
cecafc5 Refactor ActionView::Template
Yehuda Katz + Carl Lerche authored
310 end
9b552fb @wycats Caches and cache clearing seems to actually work, but the actual archite...
wycats authored
311
64c7f7e @josevalim Add more docs and tests to templates.
josevalim authored
312 def handle_render_error(view, e) #:nodoc:
313 if e.is_a?(Template::Error)
314 e.sub_template_of(self)
315 raise e
316 else
19292a7 @josevalim Do not hard code encoding to UTF8
josevalim authored
317 template = self
318 unless template.source
319 template = refresh(view)
320 template.encode!
321 end
73709f7 @brainopia Removed unused assigns from ActionView::Template::Error
brainopia authored
322 raise Template::Error.new(template, e)
c776080 @josevalim Allow cache to be temporarily disabled through lookup_context.
josevalim authored
323 end
b2600bf @josevalim Remove locals dependency from template.
josevalim authored
324 end
325
64c7f7e @josevalim Add more docs and tests to templates.
josevalim authored
326 def locals_code #:nodoc:
327 @locals.map { |key| "#{key} = local_assigns[:#{key}];" }.join
328 end
329
330 def method_name #:nodoc:
b2600bf @josevalim Remove locals dependency from template.
josevalim authored
331 @method_name ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".gsub('-', "_")
7ea85ff @jeremy Revert "Revert "Name compiled render methods". This caused several failu...
jeremy authored
332 end
333
64c7f7e @josevalim Add more docs and tests to templates.
josevalim authored
334 def identifier_method_name #:nodoc:
b2600bf @josevalim Remove locals dependency from template.
josevalim authored
335 inspect.gsub(/[^a-z_]/, '_')
610b81b @josh Clean up log output for rendered templates
josh authored
336 end
cecafc5 Refactor ActionView::Template
Yehuda Katz + Carl Lerche authored
337 end
5ec2c7d @jeremy Ruby 1.9: ERB template encoding using a magic comment at the top of the ...
jeremy authored
338 end
Something went wrong with that request. Please try again.