actionpack 1.4.0 → 1.5.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 (84) hide show
  1. data/CHANGELOG +66 -0
  2. data/README +94 -64
  3. data/install.rb +1 -20
  4. data/lib/action_controller.rb +15 -7
  5. data/lib/action_controller/assertions/action_pack_assertions.rb +56 -3
  6. data/lib/action_controller/base.rb +137 -64
  7. data/lib/action_controller/caching.rb +11 -11
  8. data/lib/action_controller/cgi_ext/cgi_ext.rb +2 -2
  9. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +4 -4
  10. data/lib/action_controller/cgi_process.rb +9 -1
  11. data/lib/action_controller/components.rb +73 -0
  12. data/lib/action_controller/cookies.rb +1 -1
  13. data/lib/action_controller/dependencies.rb +6 -1
  14. data/lib/action_controller/filters.rb +1 -1
  15. data/lib/action_controller/flash.rb +3 -3
  16. data/lib/action_controller/helpers.rb +17 -21
  17. data/lib/action_controller/layout.rb +2 -2
  18. data/lib/action_controller/request.rb +16 -6
  19. data/lib/action_controller/rescue.rb +15 -3
  20. data/lib/action_controller/routing.rb +304 -0
  21. data/lib/action_controller/scaffolding.rb +10 -12
  22. data/lib/action_controller/session/active_record_store.rb +4 -7
  23. data/lib/action_controller/session/mem_cache_store.rb +2 -2
  24. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +9 -1
  25. data/lib/action_controller/templates/rescues/diagnostics.rhtml +1 -1
  26. data/lib/action_controller/templates/rescues/routing_error.rhtml +8 -0
  27. data/lib/action_controller/test_process.rb +29 -7
  28. data/lib/action_controller/url_rewriter.rb +28 -112
  29. data/lib/action_view.rb +1 -1
  30. data/lib/action_view/base.rb +44 -17
  31. data/lib/action_view/helpers/active_record_helper.rb +1 -1
  32. data/lib/action_view/helpers/asset_tag_helper.rb +59 -0
  33. data/lib/action_view/helpers/date_helper.rb +24 -13
  34. data/lib/action_view/helpers/form_helper.rb +6 -1
  35. data/lib/action_view/helpers/form_options_helper.rb +87 -9
  36. data/lib/action_view/helpers/form_tag_helper.rb +80 -0
  37. data/lib/action_view/helpers/tag_helper.rb +0 -23
  38. data/lib/action_view/helpers/text_helper.rb +26 -1
  39. data/lib/action_view/helpers/url_helper.rb +29 -35
  40. data/lib/action_view/partials.rb +2 -2
  41. data/lib/action_view/vendor/builder/xmlbase.rb +3 -3
  42. data/rakefile +5 -2
  43. data/test/abstract_unit.rb +3 -1
  44. data/test/controller/action_pack_assertions_test.rb +29 -1
  45. data/test/controller/active_record_assertions_test.rb +109 -101
  46. data/test/controller/base_tests.rb +72 -0
  47. data/test/controller/components_test.rb +74 -0
  48. data/test/controller/cookie_test.rb +0 -9
  49. data/test/controller/custom_handler_test.rb +33 -0
  50. data/test/controller/filters_test.rb +36 -0
  51. data/test/controller/helper_test.rb +27 -10
  52. data/test/controller/redirect_test.rb +23 -31
  53. data/test/controller/render_test.rb +81 -66
  54. data/test/controller/request_test.rb +22 -0
  55. data/test/controller/routing_tests.rb +490 -0
  56. data/test/controller/{url_test.rb → url_obsolete.rb} +24 -14
  57. data/test/controller/url_obsolete.rb.rej +747 -0
  58. data/test/fixtures/fun/games/hello_world.rhtml +1 -0
  59. data/test/fixtures/helpers/fun/games_helper.rb +3 -0
  60. data/test/template/asset_tag_helper_test.rb +45 -0
  61. data/test/template/form_options_helper_test.rb +161 -1
  62. data/test/template/form_tag_helper_test.rb +22 -0
  63. data/test/template/text_helper_test.rb +7 -0
  64. data/test/template/url_helper_test.rb +5 -2
  65. data/test/template/url_helper_test.rb.rej +105 -0
  66. metadata +32 -27
  67. data/lib/action_controller/support/binding_of_caller.rb +0 -83
  68. data/lib/action_controller/support/breakpoint.rb +0 -518
  69. data/lib/action_controller/support/class_attribute_accessors.rb +0 -57
  70. data/lib/action_controller/support/class_inheritable_attributes.rb +0 -117
  71. data/lib/action_controller/support/clean_logger.rb +0 -10
  72. data/lib/action_controller/support/core_ext.rb +0 -1
  73. data/lib/action_controller/support/core_ext/hash.rb +0 -5
  74. data/lib/action_controller/support/core_ext/hash/keys.rb +0 -35
  75. data/lib/action_controller/support/core_ext/numeric.rb +0 -7
  76. data/lib/action_controller/support/core_ext/numeric/bytes.rb +0 -33
  77. data/lib/action_controller/support/core_ext/numeric/time.rb +0 -59
  78. data/lib/action_controller/support/core_ext/object_and_class.rb +0 -24
  79. data/lib/action_controller/support/core_ext/string.rb +0 -5
  80. data/lib/action_controller/support/core_ext/string/inflections.rb +0 -45
  81. data/lib/action_controller/support/dependencies.rb +0 -63
  82. data/lib/action_controller/support/inflector.rb +0 -84
  83. data/lib/action_controller/support/misc.rb +0 -8
  84. data/lib/action_controller/support/module_attribute_accessors.rb +0 -57
@@ -44,10 +44,10 @@ module ActionController
44
44
  # def create
45
45
  # @entry = Entry.new(@params["entry"])
46
46
  # if @entry.save
47
- # flash["notice"] = "Entry was succesfully created"
47
+ # flash["notice"] = "Entry was successfully created"
48
48
  # redirect_to :action => "list"
49
49
  # else
50
- # render "entry/new"
50
+ # render_scaffold('new')
51
51
  # end
52
52
  # end
53
53
  #
@@ -61,10 +61,10 @@ module ActionController
61
61
  # @entry.attributes = @params["entry"]
62
62
  #
63
63
  # if @entry.save
64
- # flash["notice"] = "Entry was succesfully updated"
64
+ # flash["notice"] = "Entry was successfully updated"
65
65
  # redirect_to :action => "show/" + @entry.id.to_s
66
66
  # else
67
- # render "entry/edit"
67
+ # render_scaffold('edit')
68
68
  # end
69
69
  # end
70
70
  # end
@@ -84,8 +84,6 @@ module ActionController
84
84
  def scaffold(model_id, options = {})
85
85
  validate_options([ :class_name, :suffix ], options.keys)
86
86
 
87
- require "#{model_id.id2name}" rescue logger.warn "Couldn't auto-require #{model_id.id2name}.rb" unless logger.nil?
88
-
89
87
  singular_name = model_id.id2name
90
88
  class_name = options[:class_name] || Inflector.camelize(singular_name)
91
89
  plural_name = Inflector.pluralize(singular_name)
@@ -123,10 +121,10 @@ module ActionController
123
121
  def create#{suffix}
124
122
  @#{singular_name} = #{class_name}.new(@params["#{singular_name}"])
125
123
  if @#{singular_name}.save
126
- flash["notice"] = "#{class_name} was succesfully created"
124
+ flash["notice"] = "#{class_name} was successfully created"
127
125
  redirect_to :action => "list#{suffix}"
128
126
  else
129
- render "#{singular_name}/new#{suffix}"
127
+ render#{suffix}_scaffold('new')
130
128
  end
131
129
  end
132
130
 
@@ -140,16 +138,16 @@ module ActionController
140
138
  @#{singular_name}.attributes = @params["#{singular_name}"]
141
139
 
142
140
  if @#{singular_name}.save
143
- flash["notice"] = "#{class_name} was succesfully updated"
144
- redirect_to :action => "show#{suffix}/" + @#{singular_name}.id.to_s
141
+ flash["notice"] = "#{class_name} was successfully updated"
142
+ redirect_to :action => "show#{suffix}", :id => @#{singular_name}.id.to_s
145
143
  else
146
- render "#{singular_name}/edit#{suffix}"
144
+ render#{suffix}_scaffold('edit')
147
145
  end
148
146
  end
149
147
 
150
148
  private
151
149
  def render#{suffix}_scaffold(action = caller_method_name(caller))
152
- if template_exists?("\#{controller_name}/\#{action}")
150
+ if template_exists?("\#{self.class.controller_path}/\#{action}")
153
151
  render_action(action)
154
152
  else
155
153
  @scaffold_class = #{class_name}
@@ -29,11 +29,9 @@ class CGI
29
29
  #
30
30
  # This session's ActiveRecord database row will be created if it does not exist, or opened if it does.
31
31
  def initialize(session, option=nil)
32
- @session = Session.find_first(["sessid = '%s'", session.session_id])
33
- if @session
34
- @data = @session.data
35
- else
36
- @session = Session.new("sessid" => session.session_id, "data" => {})
32
+ ActiveRecord::Base.silence do
33
+ @session = Session.find_by_sessid(session.session_id) || Session.new("sessid" => session.session_id, "data" => {})
34
+ @data = @session.data
37
35
  end
38
36
  end
39
37
 
@@ -60,8 +58,7 @@ class CGI
60
58
  # Save session state in the session's ActiveRecord object.
61
59
  def update
62
60
  return unless @session
63
- @session.data = @data
64
- @session.save
61
+ ActiveRecord::Base.silence { @session.update_attribute "data", @data }
65
62
  end
66
63
  end #ActiveRecordStore
67
64
  end #Session
@@ -33,7 +33,7 @@ begin
33
33
  # characters; automatically generated session ids observe
34
34
  # this requirement.
35
35
  #
36
- # +option+ is a hash of options for the initialiser. The
36
+ # +option+ is a hash of options for the initializer. The
37
37
  # following options are recognized:
38
38
  #
39
39
  # cache:: an instance of a MemCache client to use as the
@@ -92,4 +92,4 @@ begin
92
92
  end
93
93
  rescue LoadError
94
94
  # MemCache wasn't available so neither can the store be
95
- end
95
+ end
@@ -1,6 +1,11 @@
1
+ <% if @exception.blamed_files && !@exception.blamed_files.empty? %>
2
+ <a href="#" onclick="document.getElementById('blame_trace').style.display='block'; return false;">Show blamed files</a>
3
+ <pre id="blame_trace" style="display:none"><code><%=h @exception.describe_blame %></code></pre>
4
+ <% end %>
5
+
1
6
  <% if defined?(Breakpoint) %>
2
7
  <br /><br />
3
- <%= form_tag({:params => {}, :only_path => true}, "method" => @request.method) %>
8
+ <% begin %><%= form_tag({:params => {}, :only_path => true}, "method" => @request.method) %>
4
9
  <input type="hidden" name="BP-RETRY" value="1" />
5
10
 
6
11
  <% for key, values in @params %>
@@ -12,6 +17,9 @@
12
17
 
13
18
  <input type="submit" value="Retry with Breakpoint" />
14
19
  </form>
20
+ <% rescue Exception => e %>
21
+ <%=h "Couldn't render breakpoint link due to #{e.class} #{e.message}" %>
22
+ <% end %>
15
23
  <% end %>
16
24
 
17
25
  <%
@@ -6,7 +6,7 @@
6
6
 
7
7
  <h1>
8
8
  <%=h @exception.class.to_s %> in
9
- <%=h @request.parameters["controller"].capitalize %>#<%=h @request.parameters["action"] %>
9
+ <%=h (@request.parameters["controller"] || "<controller not set>").capitalize %>#<%=h @request.parameters["action"] || "<action not set>" %>
10
10
  </h1>
11
11
  <p><%=h Object.const_defined?(:RAILS_ROOT) ? @exception.message.gsub(RAILS_ROOT, "") : @exception.message %></p>
12
12
 
@@ -0,0 +1,8 @@
1
+ <h1>Routing Error</h1>
2
+ <p><%=h @exception.message %></p>
3
+ <% unless @exception.failures.empty? %><p>
4
+ <h2>Failure reasons:</h2>
5
+ <% @exception.failures.each do |route, reason| %>
6
+ <%=h route.inspect.gsub('\\', '') %> failed because <%=h reason.downcase %><br />
7
+ <% end %>
8
+ </p><% end %>
@@ -30,9 +30,9 @@ module ActionController #:nodoc:
30
30
  end
31
31
 
32
32
  class TestRequest < AbstractRequest #:nodoc:
33
- attr_writer :cookies
34
- attr_accessor :query_parameters, :request_parameters, :session, :env
35
- attr_accessor :host, :path, :request_uri, :remote_addr
33
+ attr_accessor :cookies
34
+ attr_accessor :query_parameters, :request_parameters, :path, :session, :env
35
+ attr_accessor :host, :remote_addr
36
36
 
37
37
  def initialize(query_parameters = nil, request_parameters = nil, session = nil)
38
38
  @query_parameters = query_parameters || {}
@@ -49,10 +49,6 @@ module ActionController #:nodoc:
49
49
  @session = {}
50
50
  end
51
51
 
52
- def cookies
53
- @cookies.freeze
54
- end
55
-
56
52
  def port=(number)
57
53
  @env["SERVER_PORT"] = number.to_i
58
54
  end
@@ -62,11 +58,28 @@ module ActionController #:nodoc:
62
58
  @parameters = nil
63
59
  end
64
60
 
61
+ # Used to check AbstractRequest's request_uri functionality.
62
+ # Disables the use of @path and @request_uri so superclass can handle those.
63
+ def set_REQUEST_URI(value)
64
+ @env["REQUEST_URI"] = value
65
+ @request_uri = nil
66
+ @path = nil
67
+ end
68
+
65
69
  def request_uri=(uri)
66
70
  @request_uri = uri
67
71
  @path = uri.split("?").first
68
72
  end
69
73
 
74
+ def request_uri
75
+ @request_uri || super()
76
+ end
77
+
78
+ def path
79
+ @path || super()
80
+ end
81
+
82
+
70
83
  private
71
84
  def initialize_containers
72
85
  @env, @cookies = {}, {}
@@ -241,6 +254,7 @@ module Test
241
254
  def process(action, parameters = nil, session = nil)
242
255
  @request.env['REQUEST_METHOD'] ||= "GET"
243
256
  @request.action = action.to_s
257
+ @request.path_parameters = { :controller => @controller.class.controller_path }
244
258
  @request.parameters.update(parameters) unless parameters.nil?
245
259
  @request.session = ActionController::TestSession.new(session) unless session.nil?
246
260
  @controller.process(@request, @response)
@@ -255,6 +269,14 @@ module Test
255
269
  end
256
270
  EOV
257
271
  end
272
+
273
+ def follow_redirect
274
+ if @response.redirected_to[:controller]
275
+ raise "Can't follow redirects outside of current controller (#{@response.redirected_to[:controller]})"
276
+ end
277
+
278
+ get(@response.redirected_to.delete(:action), @response.redirected_to.stringify_keys)
279
+ end
258
280
  end
259
281
  end
260
282
  end
@@ -1,121 +1,46 @@
1
1
  module ActionController
2
- # Rewrites urls for Base.redirect_to and Base.url_for in the controller.
2
+ # Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
3
+
3
4
  class UrlRewriter #:nodoc:
4
- VALID_OPTIONS = [:action, :action_prefix, :action_suffix, :application_prefix, :module, :controller, :controller_prefix, :anchor, :params, :path_params, :id, :only_path, :overwrite_params, :host, :protocol ]
5
-
6
- def initialize(request, controller, action)
7
- @request, @controller, @action = request, controller, action
5
+ RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol]
6
+ def initialize(request, parameters)
7
+ @request, @parameters = request, parameters
8
8
  @rewritten_path = @request.path ? @request.path.dup : ""
9
9
  end
10
10
 
11
11
  def rewrite(options = {})
12
- validate_options(VALID_OPTIONS, options.keys)
13
-
14
- rewrite_url(
15
- rewrite_path(@rewritten_path, resolve_aliases(options)),
16
- options
17
- )
18
- end
19
-
20
- def to_s
21
- to_str
12
+ rewrite_url(rewrite_path(options), options)
22
13
  end
23
14
 
24
15
  def to_str
25
- "#{@request.protocol}, #{@request.host_with_port}, #{@request.path}, #{@controller}, #{@action}, #{@request.parameters.inspect}"
16
+ "#{@request.protocol}, #{@request.host_with_port}, #{@request.path}, #{@parameters[:controller]}, #{@parameters[:action]}, #{@request.parameters.inspect}"
26
17
  end
27
18
 
28
- private
29
- def validate_options(valid_option_keys, supplied_option_keys)
30
- unknown_option_keys = supplied_option_keys - valid_option_keys
31
- raise(ActionController::ActionControllerError, "Unknown options: #{unknown_option_keys}") unless unknown_option_keys.empty?
32
- end
33
-
34
- def resolve_aliases(options)
35
- options[:controller_prefix] = options[:module] unless options[:module].nil?
36
- options
37
- end
19
+ alias_method :to_s, :to_str
38
20
 
21
+ private
39
22
  def rewrite_url(path, options)
40
23
  rewritten_url = ""
41
24
  rewritten_url << (options[:protocol] || @request.protocol) unless options[:only_path]
42
25
  rewritten_url << (options[:host] || @request.host_with_port) unless options[:only_path]
43
26
 
44
- rewritten_url << options[:application_prefix] if options[:application_prefix]
27
+ rewritten_url << options[:application_prefix] if options[:application_prefix]
45
28
  rewritten_url << path
46
- rewritten_url << build_query_string(new_parameters(options)) if options[:params] || options[:overwrite_params]
47
29
  rewritten_url << "##{options[:anchor]}" if options[:anchor]
48
- return rewritten_url
49
- end
50
30
 
51
- def rewrite_path(path, options)
52
- include_id_in_path_params(options)
53
-
54
- path = rewrite_action(path, options) if options[:action] || options[:action_prefix]
55
- path = rewrite_path_params(path, options) if options[:path_params]
56
- path = rewrite_controller(path, options) if options[:controller] || options[:controller_prefix]
57
- return path
58
- end
59
-
60
- def rewrite_path_params(path, options)
61
- index_action = options[:action] == 'index' || options[:action].nil? && @action == 'index'
62
- id_only = options[:path_params].size == 1 && options[:path_params]['id']
63
-
64
- if index_action && id_only
65
- path += '/' unless path[-1..-1] == '/'
66
- path += "index/#{options[:path_params]['id']}"
67
- path
68
- else
69
- options[:path_params].inject(path) do |path, pair|
70
- if options[:action].nil? && @request.parameters[pair.first]
71
- path.sub(/\b#{@request.parameters[pair.first]}\b/, pair.last.to_s)
72
- else
73
- path += "/#{pair.last}"
74
- end
75
- end
76
- end
31
+ return rewritten_url
77
32
  end
78
33
 
79
- def rewrite_action(path, options)
80
- # This regex assumes that "index" actions won't be included in the URL
81
- all, controller_prefix, action_prefix, action_suffix =
82
- /^\/(.*)#{@controller}\/(.*)#{@action == "index" ? "" : @action}(.*)/.match(path).to_a
83
-
84
- if @action == "index"
85
- if action_prefix == "index"
86
- # we broke the parsing assumption that this would be excluded, so
87
- # don't tell action_name about our little boo-boo
88
- path = path.sub(action_prefix, action_name(options, nil))
89
- elsif action_prefix && !action_prefix.empty?
90
- path = path.sub(%r(/#{action_prefix}/?), "/" + action_name(options, action_prefix))
91
- else
92
- path = path.sub(%r(#{@controller}/?$), @controller + "/" + action_name(options)) # " ruby-mode
93
- end
94
- else
95
- path = path.sub(
96
- @controller + "/" + (action_prefix || "") + @action + (action_suffix || ""),
97
- @controller + "/" + action_name(options, action_prefix)
98
- )
99
- end
100
-
101
- if options[:controller_prefix] && !options[:controller]
102
- ensure_slash_suffix(options, :controller_prefix)
103
- if controller_prefix
104
- path = path.sub(controller_prefix, options[:controller_prefix])
105
- else
106
- path = options[:controller_prefix] + path
107
- end
108
- end
34
+ def rewrite_path(options)
35
+ options = options.symbolize_keys
36
+ options.update((options[:params]).symbolize_keys) if options[:params]
37
+ RESERVED_OPTIONS.each {|k| options.delete k}
38
+
39
+ path, extras = Routing::Routes.generate(options, @request)
40
+ path = "/#{path.join('/')}".chomp '/'
41
+ path = '/' if path.empty?
42
+ path += build_query_string(extras)
109
43
 
110
- return path
111
- end
112
-
113
- def rewrite_controller(path, options)
114
- all, controller_prefix = /^\/(.*?)#{@controller}/.match(path).to_a
115
- path = "/"
116
- path << controller_name(options, controller_prefix)
117
- path << action_name(options) if options[:action]
118
- path << path_params_in_list(options) if options[:path_params]
119
44
  return path
120
45
  end
121
46
 
@@ -159,30 +84,21 @@ module ActionController
159
84
  options[:path_params] = (options[:path_params] || {}).merge({"id" => options[:id]}) if options[:id]
160
85
  end
161
86
 
162
- def new_parameters(options)
163
- parameters = options[:params] || existing_parameters
164
- parameters.update(options[:overwrite_params]) if options[:overwrite_params]
165
- parameters.reject { |key,value| value.nil? }
166
- end
167
-
168
- def existing_parameters
169
- @request.parameters.reject { |key, value| %w( id action controller).include?(key) }
170
- end
171
-
172
87
  # Returns a query string with escaped keys and values from the passed hash. If the passed hash contains an "id" it'll
173
88
  # be added as a path element instead of a regular parameter pair.
174
89
  def build_query_string(hash)
175
90
  elements = []
176
91
  query_string = ""
177
-
92
+
178
93
  hash.each do |key, value|
179
- key = CGI.escape key
180
- key += '[]' if value.class == Array
181
- value = [ value ] unless value.class == Array
182
- value.each { |val| elements << "#{key}=#{CGI.escape(val.to_s)}" }
183
- end
184
- unless elements.empty? then query_string << ("?" + elements.join("&")) end
94
+ key = key.to_s
95
+ key = CGI.escape key
96
+ key += '[]' if value.class == Array
97
+ value = [ value ] unless value.class == Array
98
+ value.each { |val| elements << "#{key}=#{CGI.escape(val.to_s)}" }
99
+ end
185
100
 
101
+ query_string << ("?" + elements.join("&")) unless elements.empty?
186
102
  return query_string
187
103
  end
188
104
  end
@@ -22,7 +22,7 @@
22
22
  #++
23
23
 
24
24
  begin
25
- require 'rubygems'
25
+ require 'rubygems'
26
26
  require 'builder'
27
27
  rescue LoadError
28
28
  # RubyGems is not available, use included Builder
@@ -61,12 +61,12 @@ module ActionView #:nodoc:
61
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
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 systen), you can turn that on with
64
+ # caching the loading of templates (reading from the file system), you can turn that on with
65
65
  # <tt>ActionView::Base.cache_template_loading = true</tt>.
66
66
  #
67
67
  # == Builder
68
68
  #
69
- # Builder templates are a more programatic alternative to ERb. They are especially useful for generating XML content. An +XmlMarkup+ object
69
+ # Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An +XmlMarkup+ object
70
70
  # named +xml+ is automatically made available to templates with a +.rxml+ extension.
71
71
  #
72
72
  # Here are some basic examples:
@@ -130,6 +130,7 @@ module ActionView #:nodoc:
130
130
 
131
131
  @@compiled_erb_templates = {}
132
132
  @@loaded_templates = {}
133
+ @@template_handlers = {}
133
134
 
134
135
  def self.load_helpers(helper_dir)#:nodoc:
135
136
  Dir.foreach(helper_dir) do |helper_file|
@@ -151,6 +152,10 @@ module ActionView #:nodoc:
151
152
  end
152
153
  end
153
154
 
155
+ def self.register_template_handler(extension, klass)
156
+ @@template_handlers[extension] = klass
157
+ end
158
+
154
159
  def initialize(base_path = nil, assigns_for_first_render = {}, controller = nil)#:nodoc:
155
160
  @base_path, @assigns = base_path, assigns_for_first_render
156
161
  @controller = controller
@@ -193,26 +198,32 @@ module ActionView #:nodoc:
193
198
  # Renders the +template+ which is given as a string as either rhtml or rxml depending on <tt>template_extension</tt>.
194
199
  # The hash in <tt>local_assigns</tt> is made available as local variables.
195
200
  def render_template(template_extension, template, local_assigns = {})
196
- b = binding
197
- local_assigns.each { |key, value| eval "#{key} = local_assigns[\"#{key}\"]", b }
198
- @assigns.each { |key, value| instance_variable_set "@#{key}", value }
199
- xml = Builder::XmlMarkup.new(:indent => 2)
200
-
201
- send(pick_rendering_method(template_extension), template, binding)
201
+ send(pick_rendering_method(template_extension), template_extension,
202
+ template, local_assigns)
202
203
  end
203
204
 
204
205
  def pick_template_extension(template_path)#:nodoc:
205
- if erb_template_exists?(template_path)
206
+ if match = delegate_template_exists?(template_path)
207
+ match.first
208
+ elsif erb_template_exists?(template_path)
206
209
  "rhtml"
207
210
  elsif builder_template_exists?(template_path)
208
211
  "rxml"
209
212
  else
210
- raise ActionViewError, "No rhtml or rxml template found for #{template_path}"
213
+ raise ActionViewError, "No rhtml, rxml, or delegate template found for #{template_path}"
211
214
  end
212
215
  end
213
216
 
214
217
  def pick_rendering_method(template_extension)#:nodoc:
215
- (template_extension == "rxml" ? "rxml" : "rhtml") + "_render"
218
+ if @@template_handlers[template_extension]
219
+ "delegate_render"
220
+ else
221
+ (template_extension == "rxml" ? "rxml" : "rhtml") + "_render"
222
+ end
223
+ end
224
+
225
+ def delegate_template_exists?(template_path)#:nodoc:
226
+ @@template_handlers.find { |k,| template_exists?(template_path, k) }
216
227
  end
217
228
 
218
229
  def erb_template_exists?(template_path)#:nodoc:
@@ -224,7 +235,7 @@ module ActionView #:nodoc:
224
235
  end
225
236
 
226
237
  def file_exists?(template_path)#:nodoc:
227
- erb_template_exists?(template_path) || builder_template_exists?(template_path)
238
+ erb_template_exists?(template_path) || builder_template_exists?(template_path) || delegate_template_exists?(template_path)
228
239
  end
229
240
 
230
241
  # Returns true is the file may be rendered implicitly.
@@ -250,16 +261,32 @@ module ActionView #:nodoc:
250
261
  @@loaded_templates[template_path]
251
262
  end
252
263
 
253
- def rhtml_render(template, binding)
264
+ def evaluate_locals(local_assigns = {})
265
+ b = binding
266
+
267
+ local_assigns.each { |key, value| eval "#{key} = local_assigns[\"#{key}\"]", b }
268
+ @assigns.each { |key, value| instance_variable_set "@#{key}", value }
269
+ xml = Builder::XmlMarkup.new(:indent => 2)
270
+
271
+ b
272
+ end
273
+
274
+ def rhtml_render(extension, template, local_assigns)
275
+ b = evaluate_locals(local_assigns)
254
276
  @@compiled_erb_templates[template] ||= ERB.new(template, nil, '-')
255
- @@compiled_erb_templates[template].result(binding)
277
+ @@compiled_erb_templates[template].result(b)
256
278
  end
257
279
 
258
- def rxml_render(template, binding)
280
+ def rxml_render(extension, template, local_assigns)
259
281
  @controller.headers["Content-Type"] ||= 'text/xml'
260
- eval(template, binding)
282
+ eval(template, evaluate_locals(local_assigns))
283
+ end
284
+
285
+ def delegate_render(extension, template, local_assigns)
286
+ delegator = @@template_handlers[extension].new(self)
287
+ delegator.render(template, local_assigns)
261
288
  end
262
289
  end
263
290
  end
264
291
 
265
- require 'action_view/template_error'
292
+ require 'action_view/template_error'