public
Description: PLEASE CHECK http://github.com/lifo/docrails/wikis
Homepage: http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch
Clone URL: git://github.com/lifo/docrails.git
Search Repo:
Refactor template preloading. New abstractions include Renderable mixins 
and a refactored Template class.
josh (author)
Sat Jul 12 12:33:46 -0700 2008
commit  73b34e9f75d33dc0709d4ad36c912bdbb8977994
tree    eea7cb8c3cb428ecfacb6263f4e8f1d903a71824
parent  30204c4e66cea989c4ee48b52c8827c79e98f14a
...
426
427
428
429
 
430
431
432
...
426
427
428
 
429
430
431
432
0
@@ -426,7 +426,7 @@ module ActionMailer #:nodoc:
0
       end
0
 
0
       def template_root=(root)
0
- write_inheritable_attribute(:template_root, ActionView::ViewLoadPaths.new(Array(root)))
0
+ write_inheritable_attribute(:template_root, ActionView::PathSet.new(Array(root)))
0
       end
0
     end
0
 
...
1
2
 
 
3
4
5
...
1
2
3
4
5
6
7
0
@@ -1,5 +1,7 @@
0
 *Edge*
0
 
0
+* Refactor template preloading. New abstractions include Renderable mixins and a refactored Template class [Josh Peek]
0
+
0
 * Changed ActionView::TemplateHandler#render API method signature to render(template, local_assigns = {}) [Josh Peek]
0
 
0
 * Changed PrototypeHelper#submit_to_remote to PrototypeHelper#button_to_remote to stay consistent with link_to_remote (submit_to_remote still works as an alias) #8994 [clemens]
...
93
94
95
96
 
97
98
99
...
93
94
95
 
96
97
98
99
0
@@ -93,7 +93,7 @@ module ActionController
0
             if expected.nil?
0
               !@response.rendered_with_file?
0
             else
0
- expected == rendered
0
+ rendered.match(expected)
0
             end
0
           end
0
         end
...
431
432
433
434
 
435
436
437
...
652
653
654
655
 
656
657
658
...
1248
1249
1250
1251
1252
1253
 
 
1254
1255
1256
...
431
432
433
 
434
435
436
437
...
652
653
654
 
655
656
657
658
...
1248
1249
1250
 
 
 
1251
1252
1253
1254
1255
0
@@ -431,7 +431,7 @@ module ActionController #:nodoc:
0
       end
0
 
0
       def view_paths=(value)
0
- @view_paths = ActionView::ViewLoadPaths.new(Array(value)) if value
0
+ @view_paths = ActionView::PathSet.new(Array(value)) if value
0
       end
0
 
0
       # Adds a view_path to the front of the view_paths array.
0
@@ -652,7 +652,7 @@ module ActionController #:nodoc:
0
       end
0
 
0
       def view_paths=(value)
0
- @template.view_paths = ViewLoadPaths.new(value)
0
+ @template.view_paths = PathSet.new(value)
0
       end
0
 
0
       # Adds a view_path to the front of the view_paths array.
0
@@ -1248,9 +1248,8 @@ module ActionController #:nodoc:
0
       end
0
 
0
       def template_exempt_from_layout?(template_name = default_template_name)
0
- extension = @template && @template.pick_template_extension(template_name)
0
- name_with_extension = !template_name.include?('.') && extension ? "#{template_name}.#{extension}" : template_name
0
- @@exempt_from_layout.any? { |ext| name_with_extension =~ ext }
0
+ template_name = @template.pick_template(template_name).to_s if @template
0
+ @@exempt_from_layout.any? { |ext| template_name =~ ext }
0
       end
0
 
0
       def default_template_name(action_name = self.action_name)
...
304
305
306
307
 
308
309
310
...
304
305
306
 
307
308
309
310
0
@@ -304,7 +304,7 @@ module ActionController #:nodoc:
0
       end
0
 
0
       def layout_directory?(layout_name)
0
- @template.view_paths.find_template_file_for_path("#{File.join('layouts', layout_name)}.#{@template.template_format}.erb") ? true : false
0
+ @template.file_exists?("#{File.join('layouts', layout_name)}.#{@template.template_format}")
0
       end
0
   end
0
 end
...
207
208
209
210
211
212
213
214
215
216
 
 
 
217
218
219
...
207
208
209
 
 
 
 
 
 
 
210
211
212
213
214
215
0
@@ -207,13 +207,9 @@ module ActionController #:nodoc:
0
 
0
     # Returns the template path of the file which was used to
0
     # render this response (or nil)
0
- def rendered_file(with_controller=false)
0
- unless template.first_render.nil?
0
- unless with_controller
0
- template.first_render
0
- else
0
- template.first_render.split('/').last || template.first_render
0
- end
0
+ def rendered_file(with_controller = false)
0
+ if template.first_render
0
+ template.first_render.to_s
0
       end
0
     end
0
 
...
21
22
23
24
25
26
27
 
28
 
 
29
30
31
 
32
33
34
...
21
22
23
 
 
 
24
25
26
27
28
29
 
30
31
32
33
34
0
@@ -21,14 +21,14 @@
0
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0
 #++
0
 
0
-require 'action_view/template_handlers'
0
-require 'action_view/template_file'
0
-require 'action_view/view_load_paths'
0
 
0
+require 'action_view/template_handlers'
0
 require 'action_view/renderable'
0
+require 'action_view/renderable_partial'
0
+
0
 require 'action_view/template'
0
-require 'action_view/partial_template'
0
 require 'action_view/inline_template'
0
+require 'action_view/paths'
0
 
0
 require 'action_view/base'
0
 require 'action_view/partials'
...
3
4
5
 
 
 
 
 
 
6
7
8
...
216
217
218
219
 
220
221
222
223
224
 
 
225
226
227
...
270
271
272
273
 
 
 
274
275
276
277
278
279
280
 
 
281
282
283
 
 
284
285
286
287
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
289
290
...
292
293
294
 
 
 
 
295
296
297
...
305
306
307
308
 
 
309
310
311
312
 
313
314
315
...
333
334
335
336
 
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
...
3
4
5
6
7
8
9
10
11
12
13
14
...
222
223
224
 
225
226
227
228
229
230
231
232
233
234
235
...
278
279
280
 
281
282
283
284
285
286
287
288
 
 
289
290
291
 
 
292
293
294
 
 
 
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
...
319
320
321
322
323
324
325
326
327
328
...
336
337
338
 
339
340
341
342
343
 
344
345
346
347
...
365
366
367
 
368
369
370
371
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
372
373
0
@@ -3,6 +3,12 @@ module ActionView #:nodoc:
0
   end
0
 
0
   class MissingTemplate < ActionViewError #:nodoc:
0
+ def initialize(paths, path, template_format = nil)
0
+ full_template_path = path.include?('.') ? path : "#{path}.erb"
0
+ display_paths = paths.join(':')
0
+ template_type = (path =~ /layouts/i) ? 'layout' : 'template'
0
+ super("Missing #{template_type} #{full_template_path} in view path #{display_paths}")
0
+ end
0
   end
0
 
0
   # Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb
0
@@ -216,12 +222,14 @@ module ActionView #:nodoc:
0
     attr_reader :view_paths
0
 
0
     def view_paths=(paths)
0
- @view_paths = ViewLoadPaths.new(Array(paths))
0
+ @view_paths = PathSet.new(Array(paths))
0
     end
0
 
0
     # Renders the template present at <tt>template_path</tt> (relative to the view_paths array).
0
     # The hash in <tt>local_assigns</tt> is made available as local variables.
0
     def render(options = {}, local_assigns = {}, &block) #:nodoc:
0
+ local_assigns ||= {}
0
+
0
       if options.is_a?(String)
0
         render_file(options, nil, local_assigns)
0
       elsif options == :update
0
@@ -270,21 +278,40 @@ module ActionView #:nodoc:
0
     end
0
 
0
     def file_exists?(template_path)
0
- view_paths.template_exists?(template_file_from_name(template_path))
0
+ pick_template(template_path) ? true : false
0
+ rescue MissingTemplate
0
+ false
0
     end
0
 
0
     # Gets the extension for an existing template with the given template_path.
0
     # Returns the format with the extension if that template exists.
0
     #
0
- # pick_template_extension('users/show')
0
- # # => 'html.erb'
0
+ # pick_template('users/show')
0
+ # # => 'users/show.html.erb'
0
     #
0
- # pick_template_extension('users/legacy')
0
- # # => "rhtml"
0
+ # pick_template('users/legacy')
0
+ # # => 'users/legacy.rhtml'
0
     #
0
- def pick_template_extension(template_path)
0
- if template = template_file_from_name(template_path)
0
- template.extension
0
+ def pick_template(template_path)
0
+ path = template_path.sub(/^\//, '')
0
+ if m = path.match(/(.*)\.(\w+)$/)
0
+ template_file_name, template_file_extension = m[1], m[2]
0
+ else
0
+ template_file_name = path
0
+ end
0
+
0
+ # OPTIMIZE: Checks to lookup template in view path
0
+ if template = self.view_paths["#{template_file_name}.#{template_format}"]
0
+ template
0
+ elsif template = self.view_paths[template_file_name]
0
+ template
0
+ elsif first_render && template = self.view_paths["#{template_file_name}.#{first_render.extension}"]
0
+ template
0
+ elsif template_format == :js && template = self.view_paths["#{template_file_name}.html"]
0
+ @template_format = :html
0
+ template
0
+ else
0
+ Template.new(template_path, view_paths)
0
       end
0
     end
0
 
0
@@ -292,6 +319,10 @@ module ActionView #:nodoc:
0
       # Renders the template present at <tt>template_path</tt>. The hash in <tt>local_assigns</tt>
0
       # is made available as local variables.
0
       def render_file(template_path, use_full_path = nil, local_assigns = {}) #:nodoc:
0
+ unless use_full_path == nil
0
+ ActiveSupport::Deprecation.warn("use_full_path option has been deprecated and has no affect.", caller)
0
+ end
0
+
0
         if defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) && !template_path.include?("/")
0
           raise ActionViewError, <<-END_ERROR
0
   Due to changes in ActionMailer, you need to provide the mailer_name along with the template name.
0
@@ -305,11 +336,12 @@ module ActionView #:nodoc:
0
           END_ERROR
0
         end
0
 
0
- Template.new(self, template_path, use_full_path, local_assigns).render_template
0
+ template = pick_template(template_path)
0
+ template.render_template(self, local_assigns)
0
       end
0
 
0
       def render_inline(text, local_assigns = {}, type = nil)
0
- InlineTemplate.new(self, text, local_assigns, type).render
0
+ InlineTemplate.new(text, type).render(self, local_assigns)
0
       end
0
 
0
       def wrap_content_for_layout(content)
0
@@ -333,32 +365,9 @@ module ActionView #:nodoc:
0
       end
0
 
0
       def execute(template, local_assigns = {})
0
- send(template.method, local_assigns) do |*names|
0
+ send(template.method(local_assigns), local_assigns) do |*names|
0
           instance_variable_get "@content_for_#{names.first || 'layout'}"
0
         end
0
       end
0
-
0
- def template_file_from_name(template_name)
0
- template_name = TemplateFile.from_path(template_name)
0
- pick_template(template_name) unless template_name.extension
0
- end
0
-
0
- def pick_template(file)
0
- if f = self.view_paths.find_template_file_for_path(file.dup_with_extension(template_format)) || file_from_first_render(file)
0
- f
0
- elsif template_format == :js && f = self.view_paths.find_template_file_for_path(file.dup_with_extension(:html))
0
- @template_format = :html
0
- f
0
- else
0
- nil
0
- end
0
- end
0
-
0
- # Determine the template extension from the <tt>@first_render</tt> filename
0
- def file_from_first_render(file)
0
- if extension = File.basename(@first_render.to_s)[/^[^.]+\.(.+)$/, 1]
0
- file.dup_with_extension(extension)
0
- end
0
- end
0
   end
0
 end
...
2
3
4
5
6
 
7
 
8
9
10
11
12
13
14
 
 
 
 
 
 
15
16
...
2
3
4
 
 
5
6
7
8
9
 
 
10
 
11
12
13
14
15
16
17
18
19
0
@@ -2,15 +2,18 @@ module ActionView #:nodoc:
0
   class InlineTemplate #:nodoc:
0
     include Renderable
0
 
0
- def initialize(view, source, locals = {}, type = nil)
0
- @view = view
0
+ attr_reader :source, :extension, :method_segment
0
 
0
+ def initialize(source, type = nil)
0
       @source = source
0
       @extension = type
0
- @locals = locals || {}
0
-
0
       @method_segment = "inline_#{@source.hash.abs}"
0
- @handler = Template.handler_class_for_extension(@extension).new(@view)
0
     end
0
+
0
+ private
0
+ # Always recompile inline templates
0
+ def recompile?(local_assigns)
0
+ true
0
+ end
0
   end
0
 end
...
104
105
106
 
 
107
108
109
110
 
 
111
112
113
...
128
129
130
 
 
131
132
133
134
135
136
137
 
 
 
 
 
138
139
140
141
142
143
 
 
 
 
 
 
 
 
 
 
 
 
 
144
145
146
147
148
149
150
151
152
153
154
 
 
 
 
 
 
 
 
 
 
155
 
 
156
157
158
...
104
105
106
107
108
109
110
 
 
111
112
113
114
115
...
130
131
132
133
134
135
 
 
 
 
 
 
136
137
138
139
140
141
 
 
 
 
 
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
 
 
 
 
 
 
 
 
 
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
0
@@ -104,10 +104,12 @@ module ActionView
0
   module Partials
0
     private
0
       def render_partial(partial_path, object_assigns = nil, local_assigns = {}) #:nodoc:
0
+ local_assigns ||= {}
0
+
0
         case partial_path
0
         when String, Symbol, NilClass
0
- # Render the template
0
- ActionView::PartialTemplate.new(self, partial_path, object_assigns, local_assigns).render_template
0
+ variable_name, path = partial_pieces(partial_path)
0
+ pick_template(path).render_partial(self, variable_name, object_assigns, local_assigns)
0
         when ActionView::Helpers::FormBuilder
0
           builder_partial_path = partial_path.class.to_s.demodulize.underscore.sub(/_builder$/, '')
0
           render_partial(builder_partial_path, object_assigns, (local_assigns || {}).merge(builder_partial_path.to_sym => partial_path))
0
@@ -128,31 +130,43 @@ module ActionView
0
 
0
         local_assigns = local_assigns ? local_assigns.clone : {}
0
         spacer = partial_spacer_template ? render(:partial => partial_spacer_template) : ''
0
+ _partial_pieces = {}
0
+ _templates = {}
0
 
0
- if partial_path.nil?
0
- render_partial_collection_with_unknown_partial_path(collection, local_assigns, as)
0
- else
0
- render_partial_collection_with_known_partial_path(collection, partial_path, local_assigns, as)
0
- end.join(spacer)
0
- end
0
+ index = 0
0
+ collection.map do |object|
0
+ _partial_path ||= partial_path || ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path)
0
+ variable_name, path = _partial_pieces[_partial_path] ||= partial_pieces(_partial_path)
0
+ template = _templates[path] ||= pick_template(path)
0
 
0
- def render_partial_collection_with_known_partial_path(collection, partial_path, local_assigns, as)
0
- template = ActionView::PartialTemplate.new(self, partial_path, nil, local_assigns, as)
0
- collection.map do |element|
0
- template.render_member(element)
0
- end
0
+ local_assigns["#{variable_name}_counter".to_sym] = index
0
+ local_assigns[:object] = local_assigns[variable_name] = object
0
+ local_assigns[as] = object if as
0
+
0
+ result = template.render_partial(self, variable_name, object, local_assigns)
0
+
0
+ local_assigns.delete(as)
0
+ local_assigns.delete(variable_name)
0
+ local_assigns.delete(:object)
0
+ index += 1
0
+
0
+ result
0
+ end.join(spacer)
0
       end
0
 
0
- def render_partial_collection_with_unknown_partial_path(collection, local_assigns, as)
0
- templates = Hash.new
0
- i = 0
0
- collection.map do |element|
0
- partial_path = ActionController::RecordIdentifier.partial_path(element, controller.class.controller_path)
0
- template = templates[partial_path] ||= ActionView::PartialTemplate.new(self, partial_path, nil, local_assigns, as)
0
- template.counter = i
0
- i += 1
0
- template.render_member(element)
0
+ def partial_pieces(partial_path)
0
+ if partial_path.include?('/')
0
+ variable_name = File.basename(partial_path)
0
+ path = "#{File.dirname(partial_path)}/_#{variable_name}"
0
+ elsif respond_to?(:controller)
0
+ variable_name = partial_path
0
+ path = "#{controller.class.controller_path}/_#{variable_name}"
0
+ else
0
+ variable_name = partial_path
0
+ path = "_#{variable_name}"
0
         end
0
+ variable_name = variable_name.sub(/\..*$/, '').to_sym
0
+ return variable_name, path
0
       end
0
   end
0
 end
...
1
2
3
4
 
 
5
6
7
 
 
 
 
 
 
 
 
8
9
10
11
 
 
 
12
13
14
15
 
 
 
 
 
 
 
 
 
 
 
 
 
16
17
18
19
20
21
22
 
 
 
23
24
25
26
 
 
 
 
 
 
 
 
 
 
 
 
27
28
 
 
 
 
 
 
 
 
 
 
 
 
29
30
31
32
33
34
 
 
 
 
 
 
 
 
35
36
37
...
1
2
 
 
3
4
5
 
 
6
7
8
9
10
11
12
13
14
 
 
 
15
16
17
18
19
 
 
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 
 
 
 
36
37
38
39
 
 
 
40
41
42
43
44
45
46
47
48
49
50
51
52
 
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
 
 
 
68
69
70
71
72
73
74
75
76
77
78
0
@@ -1,37 +1,78 @@
0
 module ActionView
0
   module Renderable
0
- # TODO: Local assigns should not be tied to template instance
0
- attr_accessor :locals
0
+ # NOTE: The template that this mixin is beening include into is frozen
0
+ # So you can not set or modify any instance variables
0
 
0
- # TODO: These readers should be private
0
- attr_reader :filename, :source, :handler
0
+ def self.included(base)
0
+ @@mutex = Mutex.new
0
+ end
0
+
0
+ # NOTE: Exception to earlier notice. Ensure this is called before freeze
0
+ def handler
0
+ @handler ||= Template.handler_class_for_extension(extension)
0
+ end
0
 
0
- def render
0
- prepare!
0
- @handler.render(self, @locals)
0
+ # NOTE: Exception to earlier notice. Ensure this is called before freeze
0
+ def compiled_source
0
+ @compiled_source ||= handler.new(nil).compile(self) if handler.compilable?
0
     end
0
 
0
- def method
0
- ['_run', @extension, @method_segment, local_assigns_keys].compact.join('_').to_sym
0
+ def render(view, local_assigns = {})
0
+ view.first_render ||= self
0
+ view.send(:evaluate_assigns)
0
+ view.current_render_extension = extension
0
+ compile(local_assigns) if handler.compilable?
0
+ handler.new(view).render(self, local_assigns)
0
+ end
0
+
0
+ def method(local_assigns)
0
+ if local_assigns && local_assigns.any?
0
+ local_assigns_keys = "locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
0
+ end
0
+ ['_run', extension, method_segment, local_assigns_keys].compact.join('_').to_sym
0
     end
0
 
0
     private
0
- def prepare!
0
- unless @prepared
0
- @view.send(:evaluate_assigns)
0
- @view.current_render_extension = @extension
0
+ # Compile and evaluate the template's code
0
+ def compile(local_assigns)
0
+ render_symbol = method(local_assigns)
0
 
0
- if @handler.compilable?
0
- @handler.compile_template(self) # compile the given template, if necessary
0
- end
0
+ @@mutex.synchronize do
0
+ return false unless recompile?(render_symbol)
0
+
0
+ locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join
0
+
0
+ source = <<-end_src
0
+ def #{render_symbol}(local_assigns)
0
+ old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
0
+ ensure
0
+ self.output_buffer = old_output_buffer
0
+ end
0
+ end_src
0
 
0
- @prepared = true
0
+ begin
0
+ file_name = respond_to?(:filename) ? filename : 'compiled-template'
0
+ ActionView::Base::CompiledTemplates.module_eval(source, file_name, 0)
0
+ rescue Exception => e # errors from template code
0
+ if logger = ActionController::Base.logger
0
+ logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
0
+ logger.debug "Function body: #{source}"
0
+ logger.debug "Backtrace: #{e.backtrace.join("\n")}"
0
+ end
0
+
0
+ raise ActionView::TemplateError.new(self, {}, e)
0
+ end
0
         end
0
       end
0
 
0
- def local_assigns_keys
0
- if @locals && @locals.any?
0
- "locals_#{@locals.keys.map { |k| k.to_s }.sort.join('_')}"
0
+ # Method to check whether template compilation is necessary.
0
+ # The template will be compiled if the file has not been compiled yet, or
0
+ # if local_assigns has a new key, which isn't supported by the compiled code yet.
0
+ def recompile?(symbol)
0
+ unless Base::CompiledTemplates.instance_methods.include?(symbol) && Base.cache_template_loading
0
+ true
0
+ else
0
+ false
0
         end
0
       end
0
   end
...
1
2
 
3
4
5
6
 
 
7
8
9
10
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
13
14
 
 
 
 
 
 
 
15
16
17
18
 
 
 
19
20
 
 
 
21
22
23
24
 
 
25
26
27
28
 
 
 
 
 
 
 
 
 
 
 
 
 
29
30
31
32
33
34
35
 
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 
 
66
67
68
69
70
71
72
 
 
 
 
 
 
 
73
74
75
76
77
78
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
81
82
...
1
 
2
3
4
5
 
6
7
8
 
 
 
 
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 
 
37
38
39
40
41
42
43
44
 
 
 
45
46
47
48
 
49
50
51
52
 
 
 
53
54
55
56
 
 
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
 
76
77
78
79
 
 
 
 
 
 
 
 
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
82
83
84
 
 
 
 
 
85
86
87
88
89
90
91
92
93
 
 
 
 
 
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
0
@@ -1,82 +1,112 @@
0
 module ActionView #:nodoc:
0
- class Template #:nodoc:
0
+ class Template
0
     extend TemplateHandlers
0
     include Renderable
0
 
0
- attr_reader :path, :extension
0
+ attr_accessor :filename, :load_path, :base_path, :name, :format, :extension
0
+ delegate :to_s, :to => :path
0
 
0
- def initialize(view, path, use_full_path = nil, locals = {})
0
- unless use_full_path == nil
0
- ActiveSupport::Deprecation.warn("use_full_path option has been deprecated and has no affect.", caller)
0
- end
0
+ def initialize(template_path, load_paths = [])
0
+ template_path = template_path.dup
0
+ @base_path, @name, @format, @extension = split(template_path)
0
+ @base_path.to_s.gsub!(/\/$/, '') # Push to split method
0
+ @load_path, @filename = find_full_path(template_path, load_paths)
0
+
0
+ # Extend with partial super powers
0
+ extend RenderablePartial if @name =~ /^_/
0
+ end
0
+
0
+ def freeze
0
+ # Eager load memoized methods
0
+ format_and_extension
0
+ path
0
+ path_without_extension
0
+ path_without_format_and_extension
0
+ source
0
+ method_segment
0
+
0
+ # Eager load memoized methods from Renderable
0
+ handler
0
+ compiled_source
0
+
0
+ instance_variables.each { |ivar| ivar.freeze }
0
+
0
+ super
0
+ end
0
 
0
- @view = view
0
- @paths = view.view_paths
0
+ def format_and_extension
0
+ @format_and_extension ||= (extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
0
+ end
0
+
0
+ def path
0
+ @path ||= [base_path, [name, format, extension].compact.join('.')].compact.join('/')
0
+ end
0
 
0
- @original_path = path
0
- @path = TemplateFile.from_path(path)
0
- @view.first_render ||= @path.to_s
0
+ def path_without_extension
0
+ @path_without_extension ||= [base_path, [name, format].compact.join('.')].compact.join('/')
0
+ end
0
 
0
- set_extension_and_file_name
0
+ def path_without_format_and_extension
0
+ @path_without_format_and_extension ||= [base_path, name].compact.join('/')
0
+ end
0
 
0
- @method_segment = compiled_method_name_file_path_segment
0
- @locals = (locals && locals.dup) || {}
0
- @handler = self.class.handler_class_for_extension(@extension).new(@view)
0
+ def source
0
+ @source ||= File.read(@filename)
0
     end
0
 
0
- def render_template
0
- render
0
+ def method_segment
0
+ unless @method_segment
0
+ segment = File.expand_path(@filename)
0
+ segment.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}/, '') if defined?(RAILS_ROOT)
0
+ segment.gsub!(/([^a-zA-Z0-9_])/) { $1.ord }
0
+ @method_segment = segment
0
+ end
0
+
0
+ @method_segment
0
+ end
0
+
0
+ def render_template(view, local_assigns = {})
0
+ render(view, local_assigns)
0
     rescue Exception => e
0
       raise e unless filename
0
       if TemplateError === e
0
         e.sub_template_of(filename)
0
         raise e
0
       else
0
- raise TemplateError.new(self, @view.assigns, e)
0
+ raise TemplateError.new(self, view.assigns, e)
0
       end
0
     end
0
 
0
- def source
0
- @source ||= File.read(@filename)
0
- end
0
-
0
- def base_path_for_exception
0
- (@paths.find_load_path_for_path(@path) || @paths.first).to_s
0
- end
0
-
0
     private
0
- def set_extension_and_file_name
0
- @extension = @path.extension
0
-
0
- unless @extension
0
- @path = @view.send(:template_file_from_name, @path)
0
- raise_missing_template_exception unless @path
0
- @extension = @path.extension
0
- end
0
-
0
- if p = @paths.find_template_file_for_path(path)
0
- @path = p
0
- @filename = @path.full_path
0
- @extension = @path.extension
0
- raise_missing_template_exception if @filename.blank?
0
- else
0
- @filename = @original_path
0
- raise_missing_template_exception unless File.exist?(@filename)
0
- end
0
+ def valid_extension?(extension)
0
+ Template.template_handler_extensions.include?(extension)
0
       end
0
 
0
- def raise_missing_template_exception
0
- full_template_path = @original_path.include?('.') ? @original_path : "#{@original_path}.#{@view.template_format}.erb"
0
- display_paths = @paths.join(':')
0
- template_type = (@original_path =~ /layouts/i) ? 'layout' : 'template'
0
- raise MissingTemplate, "Missing #{template_type} #{full_template_path} in view path #{display_paths}"
0
+ def find_full_path(path, load_paths)
0
+ load_paths = Array(load_paths) + [nil]
0
+ load_paths.each do |load_path|
0
+ file = [load_path, path].compact.join('/')
0
+ return load_path, file if File.exist?(file)
0
+ end
0
+ raise MissingTemplate.new(load_paths, path)
0
       end
0
 
0
- def compiled_method_name_file_path_segment
0
- s = File.expand_path(@filename)
0
- s.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}/, '') if defined?(RAILS_ROOT)
0
- s.gsub!(/([^a-zA-Z0-9_])/) { $1.ord }
0
- s
0
+ # Returns file split into an array
0
+ # [base_path, name, format, extension]
0
+ def split(file)