actionpack 1.9.1 → 1.10.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (123) hide show
  1. data/CHANGELOG +237 -0
  2. data/README +12 -12
  3. data/lib/action_controller.rb +17 -12
  4. data/lib/action_controller/assertions.rb +119 -67
  5. data/lib/action_controller/base.rb +184 -102
  6. data/lib/action_controller/benchmarking.rb +35 -6
  7. data/lib/action_controller/caching.rb +115 -58
  8. data/lib/action_controller/cgi_ext/cgi_methods.rb +54 -21
  9. data/lib/action_controller/cgi_ext/cookie_performance_fix.rb +39 -35
  10. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +34 -21
  11. data/lib/action_controller/cgi_process.rb +23 -20
  12. data/lib/action_controller/components.rb +11 -2
  13. data/lib/action_controller/dependencies.rb +0 -5
  14. data/lib/action_controller/deprecated_redirects.rb +17 -0
  15. data/lib/action_controller/filters.rb +13 -9
  16. data/lib/action_controller/flash.rb +7 -7
  17. data/lib/action_controller/helpers.rb +1 -14
  18. data/lib/action_controller/layout.rb +40 -29
  19. data/lib/action_controller/macros/auto_complete.rb +52 -0
  20. data/lib/action_controller/macros/in_place_editing.rb +32 -0
  21. data/lib/action_controller/pagination.rb +44 -28
  22. data/lib/action_controller/request.rb +54 -40
  23. data/lib/action_controller/rescue.rb +8 -6
  24. data/lib/action_controller/routing.rb +77 -28
  25. data/lib/action_controller/scaffolding.rb +10 -14
  26. data/lib/action_controller/session/active_record_store.rb +36 -7
  27. data/lib/action_controller/session_management.rb +126 -0
  28. data/lib/action_controller/streaming.rb +14 -5
  29. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +1 -1
  30. data/lib/action_controller/templates/rescues/_trace.rhtml +24 -0
  31. data/lib/action_controller/templates/rescues/diagnostics.rhtml +2 -13
  32. data/lib/action_controller/templates/rescues/template_error.rhtml +4 -2
  33. data/lib/action_controller/templates/scaffolds/list.rhtml +1 -1
  34. data/lib/action_controller/test_process.rb +35 -17
  35. data/lib/action_controller/upload_progress.rb +52 -0
  36. data/lib/action_controller/url_rewriter.rb +21 -16
  37. data/lib/action_controller/vendor/html-scanner/html/document.rb +2 -2
  38. data/lib/action_controller/vendor/html-scanner/html/node.rb +30 -3
  39. data/lib/action_pack/version.rb +9 -0
  40. data/lib/action_view.rb +1 -1
  41. data/lib/action_view/base.rb +204 -60
  42. data/lib/action_view/compiled_templates.rb +70 -0
  43. data/lib/action_view/helpers/active_record_helper.rb +7 -3
  44. data/lib/action_view/helpers/asset_tag_helper.rb +22 -12
  45. data/lib/action_view/helpers/capture_helper.rb +2 -10
  46. data/lib/action_view/helpers/date_helper.rb +21 -13
  47. data/lib/action_view/helpers/form_helper.rb +14 -10
  48. data/lib/action_view/helpers/form_options_helper.rb +4 -4
  49. data/lib/action_view/helpers/form_tag_helper.rb +59 -25
  50. data/lib/action_view/helpers/java_script_macros_helper.rb +188 -0
  51. data/lib/action_view/helpers/javascript_helper.rb +68 -133
  52. data/lib/action_view/helpers/javascripts/controls.js +427 -165
  53. data/lib/action_view/helpers/javascripts/dragdrop.js +256 -277
  54. data/lib/action_view/helpers/javascripts/effects.js +766 -277
  55. data/lib/action_view/helpers/javascripts/prototype.js +906 -218
  56. data/lib/action_view/helpers/javascripts/slider.js +258 -0
  57. data/lib/action_view/helpers/number_helper.rb +4 -3
  58. data/lib/action_view/helpers/pagination_helper.rb +42 -27
  59. data/lib/action_view/helpers/tag_helper.rb +25 -11
  60. data/lib/action_view/helpers/text_helper.rb +119 -13
  61. data/lib/action_view/helpers/upload_progress_helper.rb +2 -2
  62. data/lib/action_view/helpers/url_helper.rb +68 -21
  63. data/lib/action_view/partials.rb +17 -6
  64. data/lib/action_view/template_error.rb +19 -24
  65. data/rakefile +4 -3
  66. data/test/abstract_unit.rb +2 -1
  67. data/test/controller/action_pack_assertions_test.rb +62 -2
  68. data/test/controller/active_record_assertions_test.rb +5 -6
  69. data/test/controller/active_record_store_test.rb +23 -1
  70. data/test/controller/addresses_render_test.rb +4 -0
  71. data/test/controller/{base_tests.rb → base_test.rb} +4 -3
  72. data/test/controller/benchmark_test.rb +36 -0
  73. data/test/controller/caching_filestore.rb +22 -40
  74. data/test/controller/capture_test.rb +10 -1
  75. data/test/controller/cgi_test.rb +145 -23
  76. data/test/controller/components_test.rb +50 -0
  77. data/test/controller/custom_handler_test.rb +3 -3
  78. data/test/controller/fake_controllers.rb +24 -0
  79. data/test/controller/filters_test.rb +6 -6
  80. data/test/controller/flash_test.rb +6 -6
  81. data/test/controller/fragment_store_setting_test.rb +45 -0
  82. data/test/controller/helper_test.rb +1 -3
  83. data/test/controller/new_render_test.rb +119 -7
  84. data/test/controller/redirect_test.rb +11 -1
  85. data/test/controller/render_test.rb +34 -1
  86. data/test/controller/request_test.rb +14 -5
  87. data/test/controller/routing_test.rb +238 -42
  88. data/test/controller/send_file_test.rb +11 -10
  89. data/test/controller/session_management_test.rb +94 -0
  90. data/test/controller/test_test.rb +194 -5
  91. data/test/controller/url_rewriter_test.rb +46 -0
  92. data/test/fixtures/layouts/talk_from_action.rhtml +2 -0
  93. data/test/fixtures/layouts/yield.rhtml +2 -0
  94. data/test/fixtures/multipart/binary_file +0 -0
  95. data/test/fixtures/multipart/large_text_file +10 -0
  96. data/test/fixtures/multipart/mixed_files +0 -0
  97. data/test/fixtures/multipart/single_parameter +5 -0
  98. data/test/fixtures/multipart/text_file +10 -0
  99. data/test/fixtures/test/_customer_greeting.rhtml +1 -0
  100. data/test/fixtures/test/_hash_object.rhtml +1 -0
  101. data/test/fixtures/test/_person.rhtml +2 -0
  102. data/test/fixtures/test/action_talk_to_layout.rhtml +2 -0
  103. data/test/fixtures/test/content_for.rhtml +2 -0
  104. data/test/fixtures/test/potential_conflicts.rhtml +4 -0
  105. data/test/template/active_record_helper_test.rb +15 -8
  106. data/test/template/asset_tag_helper_test.rb +40 -16
  107. data/test/template/compiled_templates_tests.rb +63 -0
  108. data/test/template/date_helper_test.rb +80 -4
  109. data/test/template/form_helper_test.rb +48 -42
  110. data/test/template/form_options_helper_test.rb +40 -40
  111. data/test/template/form_tag_helper_test.rb +21 -15
  112. data/test/template/java_script_macros_helper_test.rb +56 -0
  113. data/test/template/javascript_helper_test.rb +70 -47
  114. data/test/template/number_helper_test.rb +2 -0
  115. data/test/template/tag_helper_test.rb +9 -0
  116. data/test/template/text_helper_test.rb +146 -1
  117. data/test/template/upload_progress_helper_testx.rb +11 -147
  118. data/test/template/url_helper_test.rb +90 -22
  119. data/test/testing_sandbox.rb +26 -0
  120. metadata +37 -7
  121. data/lib/action_controller/auto_complete.rb +0 -47
  122. data/lib/action_controller/deprecated_renders_and_redirects.rb +0 -76
  123. data/lib/action_controller/session.rb +0 -14
@@ -0,0 +1,9 @@
1
+ module ActionPack
2
+ module Version #:nodoc:
3
+ MAJOR = 1
4
+ MINOR = 10
5
+ TINY = 1
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -31,4 +31,4 @@ ActionView::Base.class_eval do
31
31
  include ActionView::Partials
32
32
  end
33
33
 
34
- ActionView::Base.load_helpers(File.dirname(__FILE__) + "/action_view/helpers/")
34
+ ActionView::Base.load_helpers(File.dirname(__FILE__) + "/action_view/helpers/")
@@ -1,6 +1,7 @@
1
1
  require 'erb'
2
2
 
3
3
  module ActionView #:nodoc:
4
+
4
5
  class ActionViewError < StandardError #:nodoc:
5
6
  end
6
7
 
@@ -37,7 +38,7 @@ module ActionView #:nodoc:
37
38
  # result of the rendering. The output embedding writes it to the current template.
38
39
  #
39
40
  # But you don't have to restrict yourself to static includes. Templates can share variables amongst themselves by using instance
40
- # variables defined in using the regular embedding tags. Like this:
41
+ # variables defined using the regular embedding tags. Like this:
41
42
  #
42
43
  # <% @page_title = "A Wonderful Hello" %>
43
44
  # <%= render "shared/header" %>
@@ -59,10 +60,8 @@ module ActionView #:nodoc:
59
60
  #
60
61
  # == Template caching
61
62
  #
62
- # The parsing of ERb templates are cached by default, but the reading of them are not. This means that the application by default
63
- # will reflect changes to the templates immediatly. If you'd like to sacrifice that immediacy for the speed gain given by also
64
- # caching the loading of templates (reading from the file system), you can turn that on with
65
- # <tt>ActionView::Base.cache_template_loading = true</tt>.
63
+ # By default, Rails will compile each template to a method in order to render it. When you alter a template, Rails will
64
+ # check the file's modification time and recompile it.
66
65
  #
67
66
  # == Builder
68
67
  #
@@ -118,22 +117,47 @@ module ActionView #:nodoc:
118
117
  # More builder documentation can be found at http://builder.rubyforge.org.
119
118
  class Base
120
119
  include ERB::Util
121
-
120
+
122
121
  attr_reader :first_render
123
122
  attr_accessor :base_path, :assigns, :template_extension
124
123
  attr_accessor :controller
125
-
124
+
126
125
  attr_reader :logger, :params, :response, :session, :headers, :flash
127
126
 
128
- # Turn on to cache the reading of templates from the file system. Doing so means that you have to restart the server
129
- # when changing templates, but that rendering will be faster.
127
+ # Specify trim mode for the ERB compiler. Defaults to '-'.
128
+ # See ERB documentation for suitable values.
129
+ @@erb_trim_mode = '-'
130
+ cattr_accessor :erb_trim_mode
131
+
132
+ # Specify whether file modification times should be checked to see if a template needs recompilation
130
133
  @@cache_template_loading = false
131
134
  cattr_accessor :cache_template_loading
132
135
 
133
- @@compiled_erb_templates = {}
134
- @@loaded_templates = {}
136
+ # Specify whether local_assigns should be able to use string keys.
137
+ # Defaults to +true+. String keys are deprecated and will be removed
138
+ # shortly.
139
+ @@local_assigns_support_string_keys = true
140
+ cattr_accessor :local_assigns_support_string_keys
141
+
135
142
  @@template_handlers = {}
143
+
144
+ module CompiledTemplates #:nodoc:
145
+ # holds compiled template code
146
+ end
147
+ include CompiledTemplates
148
+
149
+ # maps inline templates to their method names
150
+ @@method_names = {}
151
+ # map method names to their compile time
152
+ @@compile_time = {}
153
+ # map method names to the names passed in local assigns so far
154
+ @@template_args = {}
155
+ # count the number of inline templates
156
+ @@inline_template_count = 0
136
157
 
158
+ class ObjectWrapper < Struct.new(:value) #:nodoc:
159
+ end
160
+
137
161
  def self.load_helpers(helper_dir)#:nodoc:
138
162
  Dir.foreach(helper_dir) do |helper_file|
139
163
  next unless helper_file =~ /_helper.rb$/
@@ -144,13 +168,22 @@ module ActionView #:nodoc:
144
168
  end
145
169
  end
146
170
 
171
+ # Register a class that knows how to handle template files with the given
172
+ # extension. This can be used to implement new template types.
173
+ # The constructor for the class must take the ActiveView::Base instance
174
+ # as a parameter, and the class must implement a #render method that
175
+ # takes the contents of the template to render as well as the Hash of
176
+ # local assigns available to the template. The #render method ought to
177
+ # return the rendered template as a string.
147
178
  def self.register_template_handler(extension, klass)
148
179
  @@template_handlers[extension] = klass
149
180
  end
150
181
 
151
182
  def initialize(base_path = nil, assigns_for_first_render = {}, controller = nil)#:nodoc:
152
183
  @base_path, @assigns = base_path, assigns_for_first_render
184
+ @assigns_added = nil
153
185
  @controller = controller
186
+ @logger = controller && controller.logger
154
187
  end
155
188
 
156
189
  # Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true,
@@ -158,19 +191,19 @@ module ActionView #:nodoc:
158
191
  # is made available as local variables.
159
192
  def render_file(template_path, use_full_path = true, local_assigns = {})
160
193
  @first_render = template_path if @first_render.nil?
161
-
194
+
162
195
  if use_full_path
163
196
  template_extension = pick_template_extension(template_path)
164
197
  template_file_name = full_template_path(template_path, template_extension)
165
198
  else
166
199
  template_file_name = template_path
167
- template_extension = template_path.split(".").last
200
+ template_extension = template_path.split('.').last
168
201
  end
169
-
170
- template_source = read_template_file(template_file_name)
202
+
203
+ template_source = nil # Don't read the source until we know that it is required
171
204
 
172
205
  begin
173
- render_template(template_extension, template_source, local_assigns)
206
+ render_template(template_extension, template_source, template_file_name, local_assigns)
174
207
  rescue Exception => e
175
208
  if TemplateError === e
176
209
  e.sub_template_of(template_file_name)
@@ -180,7 +213,7 @@ module ActionView #:nodoc:
180
213
  end
181
214
  end
182
215
  end
183
-
216
+
184
217
  # Renders the template present at <tt>template_path</tt> (relative to the template_root).
185
218
  # The hash in <tt>local_assigns</tt> is made available as local variables.
186
219
  def render(options = {}, old_local_assigns = {})
@@ -189,56 +222,77 @@ module ActionView #:nodoc:
189
222
  elsif options.is_a?(Hash)
190
223
  options[:locals] ||= {}
191
224
  options[:use_full_path] = options[:use_full_path].nil? ? true : options[:use_full_path]
192
-
225
+
193
226
  if options[:file]
194
227
  render_file(options[:file], options[:use_full_path], options[:locals])
195
228
  elsif options[:partial] && options[:collection]
196
229
  render_partial_collection(options[:partial], options[:collection], options[:spacer_template], options[:locals])
197
230
  elsif options[:partial]
198
- render_partial(options[:partial], options[:object], options[:locals])
231
+ render_partial(options[:partial], ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals])
199
232
  elsif options[:inline]
200
- render_template(options[:type] || :rhtml, options[:inline], options[:locals] || {})
233
+ render_template(options[:type] || :rhtml, options[:inline], nil, options[:locals] || {})
201
234
  end
202
235
  end
203
236
  end
204
-
237
+
205
238
  # Renders the +template+ which is given as a string as either rhtml or rxml depending on <tt>template_extension</tt>.
206
239
  # The hash in <tt>local_assigns</tt> is made available as local variables.
207
- def render_template(template_extension, template, local_assigns = {})
208
- send(pick_rendering_method(template_extension), template_extension,
209
- template, local_assigns)
240
+ def render_template(template_extension, template, file_path = nil, local_assigns = {})
241
+ if handler = @@template_handlers[template_extension]
242
+ template ||= read_template_file(file_path, template_extension) # Make sure that a lazyily-read template is loaded.
243
+ delegate_render(handler, template, local_assigns)
244
+ else
245
+ compile_and_render_template(template_extension, template, file_path, local_assigns)
246
+ end
247
+ end
248
+
249
+ # Render the provided template with the given local assigns. If the template has not been rendered with the provided
250
+ # local assigns yet, or if the template has been updated on disk, then the template will be compiled to a method.
251
+ #
252
+
253
+ # Either, but not both, of template and file_path may be nil. If file_path is given, the template
254
+ # will only be read if it has to be compiled.
255
+ #
256
+ def compile_and_render_template(extension, template = nil, file_path = nil, local_assigns = {})
257
+ # compile the given template, if necessary
258
+ if compile_template?(template, file_path, local_assigns)
259
+ template ||= read_template_file(file_path, extension)
260
+ compile_template(extension, template, file_path, local_assigns)
261
+ end
262
+
263
+ # Get the method name for this template and run it
264
+ method_name = @@method_names[file_path || template]
265
+ evaluate_assigns
266
+
267
+ local_assigns = local_assigns.symbolize_keys if @@local_assigns_support_string_keys
268
+
269
+ send(method_name, local_assigns) do |*name|
270
+ instance_variable_get "@content_for_#{name.first || 'layout'}"
271
+ end
210
272
  end
211
273
 
212
274
  def pick_template_extension(template_path)#:nodoc:
213
275
  if match = delegate_template_exists?(template_path)
214
276
  match.first
215
277
  elsif erb_template_exists?(template_path)
216
- "rhtml"
278
+ 'rhtml'
217
279
  elsif builder_template_exists?(template_path)
218
- "rxml"
280
+ 'rxml'
219
281
  else
220
282
  raise ActionViewError, "No rhtml, rxml, or delegate template found for #{template_path}"
221
283
  end
222
284
  end
223
-
224
- def pick_rendering_method(template_extension)#:nodoc:
225
- if @@template_handlers[template_extension]
226
- "delegate_render"
227
- else
228
- (template_extension.to_s == "rxml" ? "rxml" : "rhtml") + "_render"
229
- end
230
- end
231
285
 
232
286
  def delegate_template_exists?(template_path)#:nodoc:
233
287
  @@template_handlers.find { |k,| template_exists?(template_path, k) }
234
288
  end
235
289
 
236
290
  def erb_template_exists?(template_path)#:nodoc:
237
- template_exists?(template_path, "rhtml")
291
+ template_exists?(template_path, :rhtml)
238
292
  end
239
293
 
240
294
  def builder_template_exists?(template_path)#:nodoc:
241
- template_exists?(template_path, "rxml")
295
+ template_exists?(template_path, :rxml)
242
296
  end
243
297
 
244
298
  def file_exists?(template_path)#:nodoc:
@@ -247,7 +301,7 @@ module ActionView #:nodoc:
247
301
 
248
302
  # Returns true is the file may be rendered implicitly.
249
303
  def file_public?(template_path)#:nodoc:
250
- template_path.split("/").last[0,1] != "_"
304
+ template_path.split('/').last[0,1] != '_'
251
305
  end
252
306
 
253
307
  private
@@ -256,43 +310,133 @@ module ActionView #:nodoc:
256
310
  end
257
311
 
258
312
  def template_exists?(template_path, extension)
259
- (cache_template_loading && @@loaded_templates.has_key?(template_path)) ||
260
- FileTest.exists?(full_template_path(template_path, extension))
313
+ file_path = full_template_path(template_path, extension)
314
+ @@method_names.has_key?(file_path) || FileTest.exists?(file_path)
315
+ end
316
+
317
+ # This method reads a template file.
318
+ def read_template_file(template_path, extension)
319
+ File.read(template_path)
261
320
  end
262
321
 
263
- def read_template_file(template_path)
264
- unless cache_template_loading && @@loaded_templates[template_path]
265
- @@loaded_templates[template_path] = File.read(template_path)
322
+ def evaluate_assigns
323
+ unless @assigns_added
324
+ assign_variables_from_controller
325
+ @assigns_added = true
266
326
  end
327
+ end
267
328
 
268
- @@loaded_templates[template_path]
329
+ def delegate_render(handler, template, local_assigns)
330
+ handler.new(self).render(template, local_assigns)
331
+ end
332
+
333
+ def assign_variables_from_controller
334
+ @assigns.each { |key, value| instance_variable_set("@#{key}", value) }
269
335
  end
270
336
 
271
- def evaluate_locals(local_assigns = {})
272
- b = binding
273
337
 
274
- local_assigns.stringify_keys!
275
- local_assigns.each { |key, value| eval "#{key} = local_assigns[\"#{key}\"]", b }
276
- @assigns.each { |key, value| instance_variable_set "@#{key}", value }
277
- xml = Builder::XmlMarkup.new(:indent => 2)
338
+ # Return true if the given template was compiled for a superset of the keys in local_assigns
339
+ def supports_local_assigns?(render_symbol, local_assigns)
340
+ local_assigns.empty? ||
341
+ ((args = @@template_args[render_symbol]) && local_assigns.all? { |k,_| args.has_key?(k) })
342
+ end
343
+
344
+ # Check whether compilation is necessary.
345
+ # Compile if the inline template or file has not been compiled yet.
346
+ # Or if local_assigns has a new key, which isn't supported by the compiled code yet.
347
+ # Or if the file has changed on disk and checking file mods hasn't been disabled.
348
+ def compile_template?(template, file_name, local_assigns)
349
+ method_key = file_name || template
350
+ render_symbol = @@method_names[method_key]
278
351
 
279
- b
352
+ if @@compile_time[render_symbol] && supports_local_assigns?(render_symbol, local_assigns)
353
+ if file_name && !@@cache_template_loading
354
+ @@compile_time[render_symbol] < File.mtime(file_name)
355
+ end
356
+ else
357
+ true
358
+ end
280
359
  end
281
360
 
282
- def rhtml_render(extension, template, local_assigns)
283
- b = evaluate_locals(local_assigns)
284
- @@compiled_erb_templates[template] ||= ERB.new(template, nil, '-')
285
- @@compiled_erb_templates[template].result(b)
361
+ # Create source code for given template
362
+ def create_template_source(extension, template, render_symbol, locals)
363
+ if extension && (extension.to_sym == :rxml)
364
+ body = "xml = Builder::XmlMarkup.new(:indent => 2)\n" +
365
+ "@controller.headers['Content-Type'] ||= 'text/xml'\n" +
366
+ template
367
+ else
368
+ body = ERB.new(template, nil, @@erb_trim_mode).src
369
+ end
370
+
371
+ @@template_args[render_symbol] ||= {}
372
+ locals_keys = @@template_args[render_symbol].keys | locals
373
+ @@template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h }
374
+
375
+ locals_code = ""
376
+ locals_keys.each do |key|
377
+ locals_code << "#{key} = local_assigns[:#{key}] if local_assigns.has_key?(:#{key})\n"
378
+ end
379
+
380
+ "def #{render_symbol}(local_assigns)\n#{locals_code}#{body}\nend"
286
381
  end
287
382
 
288
- def rxml_render(extension, template, local_assigns)
289
- @controller.headers["Content-Type"] ||= 'text/xml'
290
- eval(template, evaluate_locals(local_assigns))
383
+ def assign_method_name(extension, template, file_name)
384
+ method_name = '_run_'
385
+
386
+ if extension && (extension.to_sym == :rxml)
387
+ method_name << 'xml_'
388
+ else
389
+ method_name << 'html_'
390
+ end
391
+
392
+ if file_name
393
+ file_path = File.expand_path(file_name)
394
+ base_path = File.expand_path(@base_path)
395
+
396
+ i = file_path.index(base_path)
397
+ l = base_path.length
398
+
399
+ method_name_file_part = i ? file_path[i+l+1,file_path.length-l-1] : file_path.clone
400
+ method_name_file_part.sub!(/\.r(ht|x)ml$/,'')
401
+ method_name_file_part.tr!('/:-', '_')
402
+ method_name_file_part.gsub!(/[^a-zA-Z0-9_]/){|s| s[0].to_s}
403
+
404
+ method_name += method_name_file_part
405
+ else
406
+ @@inline_template_count += 1
407
+ method_name << @@inline_template_count.to_s
408
+ end
409
+
410
+ @@method_names[file_name || template] = method_name.intern
291
411
  end
292
412
 
293
- def delegate_render(extension, template, local_assigns)
294
- delegator = @@template_handlers[extension].new(self)
295
- delegator.render(template, local_assigns)
413
+ def compile_template(extension, template, file_name, local_assigns)
414
+ method_key = file_name || template
415
+
416
+ render_symbol = @@method_names[method_key] || assign_method_name(extension, template, file_name)
417
+ render_source = create_template_source(extension, template, render_symbol, local_assigns.keys)
418
+
419
+ line_offset = @@template_args[render_symbol].size
420
+ line_offset += 2 if extension && (extension.to_sym == :rxml)
421
+
422
+ begin
423
+ unless file_name.blank?
424
+ CompiledTemplates.module_eval(render_source, file_name, -line_offset)
425
+ else
426
+ CompiledTemplates.module_eval(render_source, 'compiled-template', -line_offset)
427
+ end
428
+ rescue Object => e
429
+ if logger
430
+ logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
431
+ logger.debug "Function body: #{render_source}"
432
+ logger.debug "Backtrace: #{e.backtrace.join("\n")}"
433
+ end
434
+
435
+ raise TemplateError.new(@base_path, method_key, @assigns, template, e)
436
+ end
437
+
438
+ @@compile_time[render_symbol] = Time.now
439
+ # logger.debug "Compiled template #{method_key}\n ==> #{render_symbol}" if logger
296
440
  end
297
441
  end
298
442
  end
@@ -0,0 +1,70 @@
1
+
2
+ module ActionView
3
+
4
+ # CompiledTemplates modules hold methods that have been compiled.
5
+ # Templates are compiled into these methods so that they do not need to be
6
+ # re-read and re-parsed each request.
7
+ #
8
+ # Each template may be compiled into one or more methods. Each method accepts a given
9
+ # set of parameters which is used to implement local assigns passing.
10
+ #
11
+ # To use a compiled template module, create a new instance and include it into the class
12
+ # in which you want the template to be rendered.
13
+ class CompiledTemplates < Module
14
+ attr_reader :method_names
15
+
16
+ def initialize
17
+ @method_names = Hash.new do |hash, key|
18
+ hash[key] = "__compiled_method_#{(hash.length + 1)}"
19
+ end
20
+ @mtimes = {}
21
+ end
22
+
23
+ # Return the full key for the given identifier and argument names
24
+ def full_key(identifier, arg_names)
25
+ [identifier, arg_names]
26
+ end
27
+
28
+ # Return the selector for this method or nil if it has not been compiled
29
+ def selector(identifier, arg_names)
30
+ key = full_key(identifier, arg_names)
31
+ method_names.key?(key) ? method_names[key] : nil
32
+ end
33
+ alias :compiled? :selector
34
+
35
+ # Return the time at which the method for the given identifier and argument names was compiled.
36
+ def mtime(identifier, arg_names)
37
+ @mtimes[full_key(identifier, arg_names)]
38
+ end
39
+
40
+ # Compile the provided source code for the given argument names and with the given initial line number.
41
+ # The identifier should be unique to this source.
42
+ #
43
+ # The file_name, if provided will appear in backtraces. If not provded, the file_name defaults
44
+ # to the identifier.
45
+ #
46
+ # This method will return the selector for the compiled version of this method.
47
+ def compile_source(identifier, arg_names, source, initial_line_number = 0, file_name = nil)
48
+ file_name ||= identifier
49
+ name = method_names[full_key(identifier, arg_names)]
50
+ arg_desc = arg_names.empty? ? '' : "(#{arg_names * ', '})"
51
+ fake_file_name = "#{file_name}#{arg_desc}" # Include the arguments for this version (for now)
52
+
53
+ method_def = wrap_source(name, arg_names, source)
54
+
55
+ begin
56
+ module_eval(method_def, fake_file_name, initial_line_number)
57
+ @mtimes[full_key(identifier, arg_names)] = Time.now
58
+ rescue Object => e
59
+ e.blame_file! identifier
60
+ raise
61
+ end
62
+ name
63
+ end
64
+
65
+ # Wrap the provided source in a def ... end block.
66
+ def wrap_source(name, arg_names, source)
67
+ "def #{name}(#{arg_names * ', '})\n#{source}\nend"
68
+ end
69
+ end
70
+ end