actionpack 1.11.2 → 1.12.0

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 (149) hide show
  1. data/CHANGELOG +392 -5
  2. data/lib/action_controller.rb +8 -4
  3. data/lib/action_controller/assertions.rb +9 -10
  4. data/lib/action_controller/base.rb +177 -88
  5. data/lib/action_controller/benchmarking.rb +5 -5
  6. data/lib/action_controller/caching.rb +44 -36
  7. data/lib/action_controller/cgi_ext/cgi_methods.rb +71 -6
  8. data/lib/action_controller/cgi_ext/cookie_performance_fix.rb +1 -1
  9. data/lib/action_controller/cgi_process.rb +36 -24
  10. data/lib/action_controller/components.rb +152 -52
  11. data/lib/action_controller/dependencies.rb +1 -1
  12. data/lib/action_controller/deprecated_redirects.rb +2 -2
  13. data/lib/action_controller/deprecated_request_methods.rb +34 -0
  14. data/lib/action_controller/filters.rb +59 -19
  15. data/lib/action_controller/flash.rb +53 -47
  16. data/lib/action_controller/helpers.rb +2 -2
  17. data/lib/action_controller/integration.rb +524 -0
  18. data/lib/action_controller/layout.rb +58 -23
  19. data/lib/action_controller/mime_responds.rb +163 -0
  20. data/lib/action_controller/mime_type.rb +142 -0
  21. data/lib/action_controller/pagination.rb +13 -7
  22. data/lib/action_controller/request.rb +59 -56
  23. data/lib/action_controller/rescue.rb +1 -1
  24. data/lib/action_controller/routing.rb +29 -10
  25. data/lib/action_controller/scaffolding.rb +8 -0
  26. data/lib/action_controller/session/active_record_store.rb +21 -10
  27. data/lib/action_controller/session/mem_cache_store.rb +18 -12
  28. data/lib/action_controller/session_management.rb +30 -11
  29. data/lib/action_controller/templates/rescues/_trace.rhtml +1 -1
  30. data/lib/action_controller/templates/scaffolds/layout.rhtml +4 -4
  31. data/lib/action_controller/templates/scaffolds/list.rhtml +1 -1
  32. data/lib/action_controller/test_process.rb +189 -118
  33. data/lib/action_controller/vendor/html-scanner/html/node.rb +20 -1
  34. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +3 -0
  35. data/lib/action_controller/vendor/html-scanner/html/version.rb +1 -1
  36. data/lib/action_controller/vendor/xml_node.rb +97 -0
  37. data/lib/action_controller/verification.rb +2 -0
  38. data/lib/action_pack/version.rb +3 -3
  39. data/lib/action_view.rb +0 -2
  40. data/lib/action_view/base.rb +109 -36
  41. data/lib/action_view/compiled_templates.rb +1 -1
  42. data/lib/action_view/helpers/active_record_helper.rb +4 -2
  43. data/lib/action_view/helpers/asset_tag_helper.rb +6 -7
  44. data/lib/action_view/helpers/capture_helper.rb +49 -12
  45. data/lib/action_view/helpers/date_helper.rb +14 -4
  46. data/lib/action_view/helpers/form_helper.rb +136 -20
  47. data/lib/action_view/helpers/form_options_helper.rb +29 -7
  48. data/lib/action_view/helpers/form_tag_helper.rb +22 -20
  49. data/lib/action_view/helpers/java_script_macros_helper.rb +29 -9
  50. data/lib/action_view/helpers/javascript_helper.rb +50 -446
  51. data/lib/action_view/helpers/javascripts/controls.js +95 -30
  52. data/lib/action_view/helpers/javascripts/dragdrop.js +161 -21
  53. data/lib/action_view/helpers/javascripts/effects.js +310 -211
  54. data/lib/action_view/helpers/javascripts/prototype.js +228 -28
  55. data/lib/action_view/helpers/number_helper.rb +9 -9
  56. data/lib/action_view/helpers/pagination_helper.rb +1 -1
  57. data/lib/action_view/helpers/prototype_helper.rb +900 -0
  58. data/lib/action_view/helpers/scriptaculous_helper.rb +135 -0
  59. data/lib/action_view/helpers/text_helper.rb +7 -6
  60. data/lib/action_view/helpers/url_helper.rb +23 -14
  61. data/lib/action_view/partials.rb +12 -4
  62. data/rakefile +13 -5
  63. data/test/abstract_unit.rb +4 -3
  64. data/test/active_record_unit.rb +88 -0
  65. data/test/{controller → activerecord}/active_record_assertions_test.rb +7 -50
  66. data/test/{controller → activerecord}/active_record_store_test.rb +27 -4
  67. data/test/activerecord/pagination_test.rb +161 -0
  68. data/test/controller/action_pack_assertions_test.rb +18 -15
  69. data/test/controller/base_test.rb +31 -42
  70. data/test/controller/benchmark_test.rb +8 -11
  71. data/test/controller/capture_test.rb +33 -1
  72. data/test/controller/cgi_test.rb +33 -0
  73. data/test/controller/custom_handler_test.rb +8 -0
  74. data/test/controller/fake_controllers.rb +9 -17
  75. data/test/controller/filters_test.rb +32 -3
  76. data/test/controller/flash_test.rb +26 -41
  77. data/test/controller/fragment_store_setting_test.rb +1 -1
  78. data/test/controller/layout_test.rb +73 -0
  79. data/test/controller/mime_responds_test.rb +257 -0
  80. data/test/controller/mime_type_test.rb +24 -0
  81. data/test/controller/new_render_test.rb +157 -1
  82. data/test/controller/redirect_test.rb +23 -0
  83. data/test/controller/render_test.rb +54 -56
  84. data/test/controller/request_test.rb +25 -0
  85. data/test/controller/routing_test.rb +74 -66
  86. data/test/controller/test_test.rb +66 -1
  87. data/test/controller/verification_test.rb +3 -1
  88. data/test/controller/webservice_test.rb +255 -0
  89. data/test/fixtures/companies.yml +24 -0
  90. data/test/fixtures/company.rb +9 -0
  91. data/test/fixtures/db_definitions/sqlite.sql +42 -0
  92. data/test/fixtures/developer.rb +7 -0
  93. data/test/fixtures/developers.yml +21 -0
  94. data/test/fixtures/developers_projects.yml +13 -0
  95. data/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml +1 -0
  96. data/test/fixtures/layout_tests/layouts/item.rhtml +1 -0
  97. data/test/fixtures/layout_tests/layouts/layout_test.rhtml +1 -0
  98. data/test/fixtures/layout_tests/layouts/third_party_template_library.mab +1 -0
  99. data/test/fixtures/layout_tests/views/hello.rhtml +1 -0
  100. data/test/fixtures/multipart/mona_lisa.jpg +0 -0
  101. data/test/fixtures/project.rb +3 -0
  102. data/test/fixtures/projects.yml +7 -0
  103. data/test/fixtures/replies.yml +13 -0
  104. data/test/fixtures/reply.rb +5 -0
  105. data/test/fixtures/respond_to/all_types_with_layout.rhtml +1 -0
  106. data/test/fixtures/respond_to/all_types_with_layout.rjs +1 -0
  107. data/test/fixtures/respond_to/layouts/standard.rhtml +1 -0
  108. data/test/fixtures/respond_to/using_defaults.rhtml +1 -0
  109. data/test/fixtures/respond_to/using_defaults.rjs +1 -0
  110. data/test/fixtures/respond_to/using_defaults.rxml +1 -0
  111. data/test/fixtures/respond_to/using_defaults_with_type_list.rhtml +1 -0
  112. data/test/fixtures/respond_to/using_defaults_with_type_list.rjs +1 -0
  113. data/test/fixtures/respond_to/using_defaults_with_type_list.rxml +1 -0
  114. data/test/fixtures/test/block_content_for.rhtml +2 -0
  115. data/test/fixtures/test/delete_with_js.rjs +2 -0
  116. data/test/fixtures/test/dot.directory/render_file_with_ivar.rhtml +1 -0
  117. data/test/fixtures/test/enum_rjs_test.rjs +6 -0
  118. data/test/fixtures/test/erb_content_for.rhtml +2 -0
  119. data/test/fixtures/test/hello_world.rxml +3 -0
  120. data/test/fixtures/test/hello_world_with_layout_false.rhtml +1 -0
  121. data/test/fixtures/test/non_erb_block_content_for.rxml +4 -0
  122. data/test/fixtures/topic.rb +3 -0
  123. data/test/fixtures/topics.yml +22 -0
  124. data/test/template/active_record_helper_test.rb +4 -0
  125. data/test/template/asset_tag_helper_test.rb +7 -2
  126. data/test/template/date_helper_test.rb +39 -2
  127. data/test/template/form_helper_test.rb +238 -5
  128. data/test/template/form_options_helper_test.rb +78 -0
  129. data/test/template/form_tag_helper_test.rb +11 -0
  130. data/test/template/java_script_macros_helper_test.rb +51 -6
  131. data/test/template/javascript_helper_test.rb +7 -153
  132. data/test/template/number_helper_test.rb +14 -13
  133. data/test/template/prototype_helper_test.rb +423 -0
  134. data/test/template/scriptaculous_helper_test.rb +90 -0
  135. data/test/template/text_helper_test.rb +12 -9
  136. data/test/template/url_helper_test.rb +31 -15
  137. metadata +291 -246
  138. data/lib/action_controller/cgi_ext/multipart_progress.rb +0 -169
  139. data/lib/action_controller/upload_progress.rb +0 -473
  140. data/lib/action_controller/vendor/html-scanner/html/node.rb.rej +0 -17
  141. data/lib/action_view/helpers/upload_progress_helper.rb +0 -433
  142. data/lib/action_view/vendor/builder.rb +0 -13
  143. data/lib/action_view/vendor/builder/blankslate.rb +0 -53
  144. data/lib/action_view/vendor/builder/xmlbase.rb +0 -143
  145. data/lib/action_view/vendor/builder/xmlevents.rb +0 -63
  146. data/lib/action_view/vendor/builder/xmlmarkup.rb +0 -308
  147. data/test/controller/multipart_progress_testx.rb +0 -365
  148. data/test/controller/upload_progress_testx.rb +0 -89
  149. data/test/template/upload_progress_helper_testx.rb +0 -136
@@ -150,6 +150,11 @@ module HTML #:nodoc:
150
150
  end
151
151
  end
152
152
 
153
+ if scanner.skip(/!\[CDATA\[/)
154
+ scanner.scan_until(/\]\]>/)
155
+ return CDATA.new(parent, line, pos, scanner.pre_match)
156
+ end
157
+
153
158
  closing = ( scanner.scan(/\//) ? :close : nil )
154
159
  return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:]+/)
155
160
  name.downcase!
@@ -256,6 +261,14 @@ module HTML #:nodoc:
256
261
  content == node.content
257
262
  end
258
263
  end
264
+
265
+ # A CDATA node is simply a text node with a specialized way of displaying
266
+ # itself.
267
+ class CDATA < Text #:nodoc:
268
+ def to_s
269
+ "<![CDATA[#{super}]>"
270
+ end
271
+ end
259
272
 
260
273
  # A Tag is any node that represents markup. It may be an opening tag, a
261
274
  # closing tag, or a self-closing tag. It has a name, and may have a hash of
@@ -399,7 +412,13 @@ module HTML #:nodoc:
399
412
  conditions = validate_conditions(conditions)
400
413
 
401
414
  # check content of child nodes
402
- return false unless children.find { |child| child.match(conditions[:content]) } if conditions[:content]
415
+ if conditions[:content]
416
+ if children.empty?
417
+ return false unless match_condition("", conditions[:content])
418
+ else
419
+ return false unless children.find { |child| child.match(conditions[:content]) }
420
+ end
421
+ end
403
422
 
404
423
  # test the name
405
424
  return false unless match_condition(@name, conditions[:tag]) if conditions[:tag]
@@ -52,6 +52,9 @@ module HTML #:nodoc:
52
52
  if @scanner.scan(/!--/) # comment
53
53
  tag << @scanner.matched
54
54
  tag << (@scanner.scan_until(/--\s*>/) || @scanner.scan_until(/\Z/))
55
+ elsif @scanner.scan(/!\[CDATA\[/)
56
+ tag << @scanner.matched
57
+ tag << @scanner.scan_until(/\]\]>/)
55
58
  elsif @scanner.scan(/!/) # doctype
56
59
  tag << @scanner.matched
57
60
  tag << consume_quoted_regions
@@ -3,7 +3,7 @@ module HTML #:nodoc:
3
3
 
4
4
  MAJOR = 0
5
5
  MINOR = 5
6
- TINY = 2
6
+ TINY = 3
7
7
 
8
8
  STRING = [ MAJOR, MINOR, TINY ].join(".")
9
9
 
@@ -0,0 +1,97 @@
1
+ require 'rexml/document'
2
+
3
+ # SimpleXML like xml parser. Written by leon breet from the ruby on rails Mailing list
4
+ class XmlNode #:nodoc:
5
+ attr :node
6
+
7
+ def initialize(node, options = {})
8
+ @node = node
9
+ @children = {}
10
+ @raise_errors = options[:raise_errors]
11
+ end
12
+
13
+ def self.from_xml(xml_or_io)
14
+ document = REXML::Document.new(xml_or_io)
15
+ if document.root
16
+ XmlNode.new(document.root)
17
+ else
18
+ XmlNode.new(document)
19
+ end
20
+ end
21
+
22
+ def node_encoding
23
+ @node.encoding
24
+ end
25
+
26
+ def node_name
27
+ @node.name
28
+ end
29
+
30
+ def node_value
31
+ @node.text
32
+ end
33
+
34
+ def node_value=(value)
35
+ @node.text = value
36
+ end
37
+
38
+ def xpath(expr)
39
+ matches = nil
40
+ REXML::XPath.each(@node, expr) do |element|
41
+ matches ||= XmlNodeList.new
42
+ matches << (@children[element] ||= XmlNode.new(element))
43
+ end
44
+ matches
45
+ end
46
+
47
+ def method_missing(name, *args)
48
+ name = name.to_s
49
+ nodes = nil
50
+ @node.each_element(name) do |element|
51
+ nodes ||= XmlNodeList.new
52
+ nodes << (@children[element] ||= XmlNode.new(element))
53
+ end
54
+ nodes
55
+ end
56
+
57
+ def <<(node)
58
+ if node.is_a? REXML::Node
59
+ child = node
60
+ elsif node.respond_to? :node
61
+ child = node.node
62
+ end
63
+ @node.add_element child
64
+ @children[child] ||= XmlNode.new(child)
65
+ end
66
+
67
+ def [](name)
68
+ @node.attributes[name.to_s]
69
+ end
70
+
71
+ def []=(name, value)
72
+ @node.attributes[name.to_s] = value
73
+ end
74
+
75
+ def to_s
76
+ @node.to_s
77
+ end
78
+
79
+ def to_i
80
+ to_s.to_i
81
+ end
82
+ end
83
+
84
+ class XmlNodeList < Array #:nodoc:
85
+ def [](i)
86
+ i.is_a?(String) ? super(0)[i] : super(i)
87
+ end
88
+
89
+ def []=(i, value)
90
+ i.is_a?(String) ? self[0][i] = value : super(i, value)
91
+ end
92
+
93
+ def method_missing(name, *args)
94
+ name = name.to_s
95
+ self[0].__send__(name, *args)
96
+ end
97
+ end
@@ -47,6 +47,8 @@ module ActionController #:nodoc:
47
47
  # must match the current request method in order for the action(s) to
48
48
  # be safely called. (The key should be a symbol: <tt>:get</tt> or
49
49
  # <tt>:post</tt>, for example.)
50
+ # * <tt>:xhr</tt>: true/false option to ensure that the request is coming
51
+ # from an Ajax call or not.
50
52
  # * <tt>:add_flash</tt>: a hash of name/value pairs that should be merged
51
53
  # into the session's flash if the prerequisites cannot be satisfied.
52
54
  # * <tt>:redirect_to</tt>: the redirection parameters to be used when
@@ -1,8 +1,8 @@
1
- module ActionPack
1
+ module ActionPack #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
- MINOR = 11
5
- TINY = 2
4
+ MINOR = 12
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -22,8 +22,6 @@
22
22
  #++
23
23
 
24
24
  $:.unshift(File.dirname(__FILE__) + "/action_view/vendor")
25
- require 'action_view/vendor/builder'
26
-
27
25
  require 'action_view/base'
28
26
  require 'action_view/partials'
29
27
 
@@ -5,8 +5,9 @@ module ActionView #:nodoc:
5
5
  class ActionViewError < StandardError #:nodoc:
6
6
  end
7
7
 
8
- # Action View templates can be written in two ways. If the template file has a +.rhtml+ extension then it uses a mixture of ERb
9
- # (included in Ruby) and HTML. If the template file has a +.rxml+ extension then Jim Weirich's Builder::XmlMarkup library is used.
8
+ # Action View templates can be written in three ways. If the template file has a +.rhtml+ extension then it uses a mixture of ERb
9
+ # (included in Ruby) and HTML. If the template file has a +.rxml+ extension then Jim Weirich's Builder::XmlMarkup library is used.
10
+ # If the template file has a +.rjs+ extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.
10
11
  #
11
12
  # = ERb
12
13
  #
@@ -73,7 +74,7 @@ module ActionView #:nodoc:
73
74
  # xml.em("emphasized") # => <em>emphasized</em>
74
75
  # xml.em { xml.b("emp & bold") } # => <em><b>emph &amp; bold</b></em>
75
76
  # xml.a("A Link", "href"=>"http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
76
- # xm.target("name"=>"compile", "option"=>"fast") # => <target option="fast" name="compile"\>
77
+ # xml.target("name"=>"compile", "option"=>"fast") # => <target option="fast" name="compile"\>
77
78
  # # NOTE: order of attributes is not specified.
78
79
  #
79
80
  # Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
@@ -115,6 +116,29 @@ module ActionView #:nodoc:
115
116
  # end
116
117
  #
117
118
  # More builder documentation can be found at http://builder.rubyforge.org.
119
+ #
120
+ # == JavaScriptGenerator
121
+ #
122
+ # JavaScriptGenerator templates end in +.rjs+. Unlike conventional templates which are used to
123
+ # render the results of an action, these templates generate instructions on how to modify an already rendered page. This makes it easy to
124
+ # modify multiple elements on your page in one declarative Ajax response. Actions with these templates are called in the background with Ajax
125
+ # and make updates to the page where the request originated from.
126
+ #
127
+ # An instance of the JavaScriptGenerator object named +page+ is automatically made available to your template, which is implicitly wrapped in an ActionView::Helpers::PrototypeHelper#update_page block.
128
+ #
129
+ # When an .rjs action is called with +link_to_remote+, the generated JavaScript is automatically evaluated. Example:
130
+ #
131
+ # link_to_remote :url => {:action => 'delete'}
132
+ #
133
+ # The subsequently rendered +delete.rjs+ might look like:
134
+ #
135
+ # page.replace_html 'sidebar', :partial => 'sidebar'
136
+ # page.remove "person-#{@person.id}"
137
+ # page.visual_effect :highlight, 'user-list'
138
+ #
139
+ # This refreshes the sidebar, removes a person element and highlights the user list.
140
+ #
141
+ # See the ActionView::Helpers::PrototypeHelper::JavaScriptGenerator documentation for more details.
118
142
  class Base
119
143
  include ERB::Util
120
144
 
@@ -138,8 +162,13 @@ module ActionView #:nodoc:
138
162
  # shortly.
139
163
  @@local_assigns_support_string_keys = true
140
164
  cattr_accessor :local_assigns_support_string_keys
165
+
166
+ # Specify whether RJS responses should be wrapped in a try/catch block
167
+ # that alert()s the caught exception (and then re-raises it).
168
+ @@debug_rjs = false
169
+ cattr_accessor :debug_rjs
141
170
 
142
- @@template_handlers = {}
171
+ @@template_handlers = HashWithIndifferentAccess.new
143
172
 
144
173
  module CompiledTemplates #:nodoc:
145
174
  # holds compiled template code
@@ -153,7 +182,11 @@ module ActionView #:nodoc:
153
182
  # map method names to the names passed in local assigns so far
154
183
  @@template_args = {}
155
184
  # count the number of inline templates
156
- @@inline_template_count = 0
185
+ @@inline_template_count = 0
186
+ # maps template paths without extension to their file extension returned by pick_template_extension.
187
+ # if for a given path, path.ext1 and path.ext2 exist on the file system, the order of extensions
188
+ # used by pick_template_extension determines whether ext1 or ext2 will be stored
189
+ @@cached_template_extension = {}
157
190
 
158
191
  class ObjectWrapper < Struct.new(:value) #:nodoc:
159
192
  end
@@ -188,12 +221,18 @@ module ActionView #:nodoc:
188
221
  # Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true,
189
222
  # it's relative to the template_root, otherwise it's absolute. The hash in <tt>local_assigns</tt>
190
223
  # is made available as local variables.
191
- def render_file(template_path, use_full_path = true, local_assigns = {})
192
- @first_render = template_path if @first_render.nil?
224
+ def render_file(template_path, use_full_path = true, local_assigns = {}) #:nodoc:
225
+ @first_render ||= template_path
193
226
 
194
227
  if use_full_path
195
- template_extension = pick_template_extension(template_path)
196
- template_file_name = full_template_path(template_path, template_extension)
228
+ template_path_without_extension, template_extension = path_and_extension(template_path)
229
+
230
+ if template_extension
231
+ template_file_name = full_template_path(template_path_without_extension, template_extension)
232
+ else
233
+ template_extension = pick_template_extension(template_path).to_s
234
+ template_file_name = full_template_path(template_path, template_extension)
235
+ end
197
236
  else
198
237
  template_file_name = template_path
199
238
  template_extension = template_path.split('.').last
@@ -215,9 +254,11 @@ module ActionView #:nodoc:
215
254
 
216
255
  # Renders the template present at <tt>template_path</tt> (relative to the template_root).
217
256
  # The hash in <tt>local_assigns</tt> is made available as local variables.
218
- def render(options = {}, old_local_assigns = {})
257
+ def render(options = {}, old_local_assigns = {}, &block) #:nodoc:
219
258
  if options.is_a?(String)
220
259
  render_file(options, true, old_local_assigns)
260
+ elsif options == :update
261
+ update_page(&block)
221
262
  elsif options.is_a?(Hash)
222
263
  options[:locals] ||= {}
223
264
  options[:use_full_path] = options[:use_full_path].nil? ? true : options[:use_full_path]
@@ -236,7 +277,7 @@ module ActionView #:nodoc:
236
277
 
237
278
  # Renders the +template+ which is given as a string as either rhtml or rxml depending on <tt>template_extension</tt>.
238
279
  # The hash in <tt>local_assigns</tt> is made available as local variables.
239
- def render_template(template_extension, template, file_path = nil, local_assigns = {})
280
+ def render_template(template_extension, template, file_path = nil, local_assigns = {}) #:nodoc:
240
281
  if handler = @@template_handlers[template_extension]
241
282
  template ||= read_template_file(file_path, template_extension) # Make sure that a lazyily-read template is loaded.
242
283
  delegate_render(handler, template, local_assigns)
@@ -252,7 +293,7 @@ module ActionView #:nodoc:
252
293
  # Either, but not both, of template and file_path may be nil. If file_path is given, the template
253
294
  # will only be read if it has to be compiled.
254
295
  #
255
- def compile_and_render_template(extension, template = nil, file_path = nil, local_assigns = {})
296
+ def compile_and_render_template(extension, template = nil, file_path = nil, local_assigns = {}) #:nodoc:
256
297
  # compile the given template, if necessary
257
298
  if compile_template?(template, file_path, local_assigns)
258
299
  template ||= read_template_file(file_path, extension)
@@ -271,15 +312,15 @@ module ActionView #:nodoc:
271
312
  end
272
313
 
273
314
  def pick_template_extension(template_path)#:nodoc:
274
- if match = delegate_template_exists?(template_path)
275
- match.first
276
- elsif erb_template_exists?(template_path)
277
- 'rhtml'
278
- elsif builder_template_exists?(template_path)
279
- 'rxml'
280
- else
281
- raise ActionViewError, "No rhtml, rxml, or delegate template found for #{template_path}"
282
- end
315
+ @@cached_template_extension[template_path] ||=
316
+ if match = delegate_template_exists?(template_path)
317
+ match.first.to_sym
318
+ elsif erb_template_exists?(template_path): :rhtml
319
+ elsif builder_template_exists?(template_path): :rxml
320
+ elsif javascript_template_exists?(template_path): :rjs
321
+ else
322
+ raise ActionViewError, "No rhtml, rxml, rjs or delegate template found for #{template_path}"
323
+ end
283
324
  end
284
325
 
285
326
  def delegate_template_exists?(template_path)#:nodoc:
@@ -293,9 +334,22 @@ module ActionView #:nodoc:
293
334
  def builder_template_exists?(template_path)#:nodoc:
294
335
  template_exists?(template_path, :rxml)
295
336
  end
337
+
338
+ def javascript_template_exists?(template_path)#:nodoc:
339
+ template_exists?(template_path, :rjs)
340
+ end
296
341
 
297
342
  def file_exists?(template_path)#:nodoc:
298
- erb_template_exists?(template_path) || builder_template_exists?(template_path) || delegate_template_exists?(template_path)
343
+ template_file_name, template_file_extension = path_and_extension(template_path)
344
+
345
+ if template_file_extension
346
+ template_exists?(template_file_name, template_file_extension)
347
+ else
348
+ @@cached_template_extension[template_path] ||
349
+ %w(erb builder javascript delegate).any? do |template_type|
350
+ send("#{template_type}_template_exists?", template_path)
351
+ end
352
+ end
299
353
  end
300
354
 
301
355
  # Returns true is the file may be rendered implicitly.
@@ -313,6 +367,11 @@ module ActionView #:nodoc:
313
367
  @@method_names.has_key?(file_path) || FileTest.exists?(file_path)
314
368
  end
315
369
 
370
+ def path_and_extension(template_path)
371
+ template_path_without_extension = template_path.sub(/\.(\w+)$/, '')
372
+ [ template_path_without_extension, $1 ]
373
+ end
374
+
316
375
  # This method reads a template file.
317
376
  def read_template_file(template_path, extension)
318
377
  File.read(template_path)
@@ -345,7 +404,7 @@ module ActionView #:nodoc:
345
404
  # Or if local_assigns has a new key, which isn't supported by the compiled code yet.
346
405
  # Or if the file has changed on disk and checking file mods hasn't been disabled.
347
406
  def compile_template?(template, file_name, local_assigns)
348
- method_key = file_name || template
407
+ method_key = file_name || template
349
408
  render_symbol = @@method_names[method_key]
350
409
 
351
410
  if @@compile_time[render_symbol] && supports_local_assigns?(render_symbol, local_assigns)
@@ -359,10 +418,16 @@ module ActionView #:nodoc:
359
418
 
360
419
  # Create source code for given template
361
420
  def create_template_source(extension, template, render_symbol, locals)
362
- if extension && (extension.to_sym == :rxml)
363
- body = "xml = Builder::XmlMarkup.new(:indent => 2)\n" +
364
- "@controller.headers['Content-Type'] ||= 'text/xml'\n" +
365
- template
421
+ if template_requires_setup?(extension)
422
+ body = case extension.to_sym
423
+ when :rxml
424
+ "xml = Builder::XmlMarkup.new(:indent => 2)\n" +
425
+ "@controller.headers['Content-Type'] ||= 'application/xml'\n" +
426
+ template
427
+ when :rjs
428
+ "@controller.headers['Content-Type'] ||= 'text/javascript'\n" +
429
+ "update_page do |page|\n#{template}\nend"
430
+ end
366
431
  else
367
432
  body = ERB.new(template, nil, @@erb_trim_mode).src
368
433
  end
@@ -379,14 +444,17 @@ module ActionView #:nodoc:
379
444
  "def #{render_symbol}(local_assigns)\n#{locals_code}#{body}\nend"
380
445
  end
381
446
 
447
+ def template_requires_setup?(extension)
448
+ templates_requiring_setup.include? extension.to_s
449
+ end
450
+
451
+ def templates_requiring_setup
452
+ %w(rxml rjs)
453
+ end
454
+
382
455
  def assign_method_name(extension, template, file_name)
383
456
  method_name = '_run_'
384
-
385
- if extension && (extension.to_sym == :rxml)
386
- method_name << 'xml_'
387
- else
388
- method_name << 'html_'
389
- end
457
+ method_name << "#{extension}_" if extension
390
458
 
391
459
  if file_name
392
460
  file_path = File.expand_path(file_name)
@@ -396,7 +464,7 @@ module ActionView #:nodoc:
396
464
  l = base_path.length
397
465
 
398
466
  method_name_file_part = i ? file_path[i+l+1,file_path.length-l-1] : file_path.clone
399
- method_name_file_part.sub!(/\.r(ht|x)ml$/,'')
467
+ method_name_file_part.sub!(/\.r(html|xml|js)$/,'')
400
468
  method_name_file_part.tr!('/:-', '_')
401
469
  method_name_file_part.gsub!(/[^a-zA-Z0-9_]/){|s| s[0].to_s}
402
470
 
@@ -416,8 +484,13 @@ module ActionView #:nodoc:
416
484
  render_source = create_template_source(extension, template, render_symbol, local_assigns.keys)
417
485
 
418
486
  line_offset = @@template_args[render_symbol].size
419
- line_offset += 2 if extension && (extension.to_sym == :rxml)
420
-
487
+ if extension
488
+ case extension.to_sym
489
+ when :rxml, :rjs
490
+ line_offset += 2
491
+ end
492
+ end
493
+
421
494
  begin
422
495
  unless file_name.blank?
423
496
  CompiledTemplates.module_eval(render_source, file_name, -line_offset)