actionpack 3.0.20 → 3.1.0.beta1

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 (161) hide show
  1. data/CHANGELOG +88 -142
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +5 -6
  4. data/lib/abstract_controller.rb +1 -0
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +24 -19
  7. data/lib/abstract_controller/callbacks.rb +19 -19
  8. data/lib/abstract_controller/helpers.rb +11 -13
  9. data/lib/abstract_controller/layouts.rb +4 -5
  10. data/lib/abstract_controller/railties/routes_helpers.rb +18 -0
  11. data/lib/abstract_controller/rendering.rb +34 -31
  12. data/lib/abstract_controller/url_for.rb +27 -0
  13. data/lib/abstract_controller/view_paths.rb +31 -6
  14. data/lib/action_controller.rb +5 -3
  15. data/lib/action_controller/base.rb +15 -16
  16. data/lib/action_controller/caching.rb +2 -2
  17. data/lib/action_controller/caching/actions.rb +11 -12
  18. data/lib/action_controller/caching/fragments.rb +41 -19
  19. data/lib/action_controller/caching/pages.rb +3 -9
  20. data/lib/action_controller/caching/sweeping.rb +0 -1
  21. data/lib/action_controller/deprecated.rb +1 -1
  22. data/lib/action_controller/log_subscriber.rb +1 -1
  23. data/lib/action_controller/metal.rb +78 -20
  24. data/lib/action_controller/metal/compatibility.rb +0 -9
  25. data/lib/action_controller/metal/conditional_get.rb +9 -9
  26. data/lib/action_controller/metal/data_streaming.rb +145 -0
  27. data/lib/action_controller/metal/force_ssl.rb +35 -0
  28. data/lib/action_controller/metal/head.rb +1 -1
  29. data/lib/action_controller/metal/helpers.rb +37 -44
  30. data/lib/action_controller/metal/hide_actions.rb +2 -3
  31. data/lib/action_controller/metal/http_authentication.rb +41 -38
  32. data/lib/action_controller/metal/implicit_render.rb +13 -13
  33. data/lib/action_controller/metal/instrumentation.rb +2 -2
  34. data/lib/action_controller/metal/mime_responds.rb +25 -19
  35. data/lib/action_controller/metal/params_wrapper.rb +224 -0
  36. data/lib/action_controller/metal/redirecting.rb +6 -2
  37. data/lib/action_controller/metal/renderers.rb +50 -36
  38. data/lib/action_controller/metal/rendering.rb +34 -25
  39. data/lib/action_controller/metal/request_forgery_protection.rb +18 -36
  40. data/lib/action_controller/metal/responder.rb +47 -12
  41. data/lib/action_controller/metal/streaming.rb +244 -138
  42. data/lib/action_controller/metal/testing.rb +0 -9
  43. data/lib/action_controller/metal/url_for.rb +12 -14
  44. data/lib/action_controller/railtie.rb +19 -37
  45. data/lib/action_controller/railties/paths.rb +24 -0
  46. data/lib/action_controller/record_identifier.rb +4 -10
  47. data/lib/action_controller/test_case.rb +36 -19
  48. data/lib/action_controller/vendor/html-scanner/html/node.rb +5 -5
  49. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +3 -3
  50. data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -0
  51. data/lib/action_dispatch.rb +4 -1
  52. data/lib/action_dispatch/http/cache.rb +5 -32
  53. data/lib/action_dispatch/http/filter_parameters.rb +3 -1
  54. data/lib/action_dispatch/http/mime_negotiation.rb +22 -3
  55. data/lib/action_dispatch/http/mime_type.rb +45 -5
  56. data/lib/action_dispatch/http/rack_cache.rb +58 -0
  57. data/lib/action_dispatch/http/request.rb +27 -41
  58. data/lib/action_dispatch/http/response.rb +56 -54
  59. data/lib/action_dispatch/http/upload.rb +1 -11
  60. data/lib/action_dispatch/http/url.rb +102 -42
  61. data/lib/action_dispatch/middleware/callbacks.rb +8 -25
  62. data/lib/action_dispatch/middleware/closed_error.rb +7 -0
  63. data/lib/action_dispatch/middleware/cookies.rb +37 -15
  64. data/lib/action_dispatch/middleware/flash.rb +80 -11
  65. data/lib/action_dispatch/middleware/params_parser.rb +2 -2
  66. data/lib/action_dispatch/middleware/reloader.rb +76 -0
  67. data/lib/action_dispatch/middleware/session/abstract_store.rb +56 -226
  68. data/lib/action_dispatch/middleware/session/cookie_store.rb +20 -44
  69. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -46
  70. data/lib/action_dispatch/middleware/show_exceptions.rb +15 -2
  71. data/lib/action_dispatch/middleware/stack.rb +50 -17
  72. data/lib/action_dispatch/middleware/static.rb +41 -29
  73. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +3 -3
  74. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
  75. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +3 -3
  76. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -2
  77. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +2 -6
  78. data/lib/action_dispatch/railtie.rb +8 -0
  79. data/lib/action_dispatch/routing.rb +13 -1
  80. data/lib/action_dispatch/routing/mapper.rb +345 -227
  81. data/lib/action_dispatch/routing/polymorphic_routes.rb +33 -13
  82. data/lib/action_dispatch/routing/redirection.rb +110 -0
  83. data/lib/action_dispatch/routing/route.rb +15 -13
  84. data/lib/action_dispatch/routing/route_set.rb +116 -90
  85. data/lib/action_dispatch/routing/routes_proxy.rb +35 -0
  86. data/lib/action_dispatch/routing/url_for.rb +25 -1
  87. data/lib/action_dispatch/testing/assertions/response.rb +8 -10
  88. data/lib/action_dispatch/testing/assertions/routing.rb +15 -15
  89. data/lib/action_dispatch/testing/assertions/selector.rb +13 -220
  90. data/lib/action_dispatch/testing/integration.rb +37 -28
  91. data/lib/action_dispatch/testing/performance_test.rb +1 -3
  92. data/lib/action_dispatch/testing/test_process.rb +1 -1
  93. data/lib/action_dispatch/testing/test_request.rb +9 -3
  94. data/lib/action_dispatch/testing/test_response.rb +4 -111
  95. data/lib/action_pack.rb +1 -1
  96. data/lib/action_pack/version.rb +3 -3
  97. data/lib/action_view.rb +39 -24
  98. data/lib/action_view/base.rb +61 -86
  99. data/lib/action_view/buffers.rb +43 -0
  100. data/lib/action_view/context.rb +21 -24
  101. data/lib/action_view/flows.rb +79 -0
  102. data/lib/action_view/helpers.rb +8 -6
  103. data/lib/action_view/helpers/active_model_helper.rb +0 -23
  104. data/lib/action_view/helpers/asset_paths.rb +79 -0
  105. data/lib/action_view/helpers/asset_tag_helper.rb +30 -500
  106. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +147 -0
  107. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +101 -0
  108. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +200 -0
  109. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +152 -0
  110. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  111. data/lib/action_view/helpers/cache_helper.rb +11 -19
  112. data/lib/action_view/helpers/capture_helper.rb +19 -8
  113. data/lib/action_view/helpers/controller_helper.rb +21 -0
  114. data/lib/action_view/helpers/csrf_helper.rb +22 -4
  115. data/lib/action_view/helpers/date_helper.rb +36 -22
  116. data/lib/action_view/helpers/form_helper.rb +199 -113
  117. data/lib/action_view/helpers/form_options_helper.rb +10 -11
  118. data/lib/action_view/helpers/form_tag_helper.rb +94 -22
  119. data/lib/action_view/helpers/javascript_helper.rb +24 -107
  120. data/lib/action_view/helpers/number_helper.rb +36 -33
  121. data/lib/action_view/helpers/output_safety_helper.rb +38 -0
  122. data/lib/action_view/helpers/record_tag_helper.rb +6 -6
  123. data/lib/action_view/helpers/rendering_helper.rb +90 -0
  124. data/lib/action_view/helpers/sanitize_helper.rb +2 -2
  125. data/lib/action_view/helpers/sprockets_helper.rb +69 -0
  126. data/lib/action_view/helpers/tag_helper.rb +34 -12
  127. data/lib/action_view/helpers/text_helper.rb +30 -145
  128. data/lib/action_view/helpers/translation_helper.rb +10 -17
  129. data/lib/action_view/helpers/url_helper.rb +70 -67
  130. data/lib/action_view/locale/en.yml +1 -1
  131. data/lib/action_view/lookup_context.rb +36 -14
  132. data/lib/action_view/{paths.rb → path_set.rb} +9 -8
  133. data/lib/action_view/railtie.rb +12 -4
  134. data/lib/action_view/renderer/abstract_renderer.rb +36 -0
  135. data/lib/action_view/{render/partials.rb → renderer/partial_renderer.rb} +147 -146
  136. data/lib/action_view/renderer/renderer.rb +54 -0
  137. data/lib/action_view/renderer/streaming_template_renderer.rb +106 -0
  138. data/lib/action_view/renderer/template_renderer.rb +74 -0
  139. data/lib/action_view/template.rb +91 -54
  140. data/lib/action_view/template/error.rb +11 -8
  141. data/lib/action_view/template/handler.rb +9 -1
  142. data/lib/action_view/template/handlers.rb +9 -9
  143. data/lib/action_view/template/handlers/builder.rb +4 -4
  144. data/lib/action_view/template/handlers/erb.rb +21 -41
  145. data/lib/action_view/template/resolver.rb +171 -57
  146. data/lib/action_view/template/text.rb +0 -4
  147. data/lib/action_view/test_case.rb +32 -16
  148. data/lib/action_view/testing/resolvers.rb +16 -10
  149. data/lib/sprockets/railtie.rb +100 -0
  150. metadata +162 -140
  151. checksums.yaml +0 -7
  152. data/lib/action_controller/deprecated/base.rb +0 -143
  153. data/lib/action_controller/deprecated/dispatcher.rb +0 -28
  154. data/lib/action_controller/deprecated/url_writer.rb +0 -14
  155. data/lib/action_dispatch/routing/deprecated_mapper.rb +0 -525
  156. data/lib/action_view/helpers/prototype_helper.rb +0 -851
  157. data/lib/action_view/helpers/raw_output_helper.rb +0 -18
  158. data/lib/action_view/helpers/scriptaculous_helper.rb +0 -263
  159. data/lib/action_view/render/layouts.rb +0 -83
  160. data/lib/action_view/render/rendering.rb +0 -67
  161. data/lib/action_view/template/handlers/rjs.rb +0 -17
@@ -5,7 +5,7 @@
5
5
  format:
6
6
  # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
7
7
  separator: "."
8
- # Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
8
+ # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
9
9
  delimiter: ","
10
10
  # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
11
11
  precision: 3
@@ -9,8 +9,10 @@ module ActionView
9
9
  # generate a key, given to view paths, used in the resolver cache lookup. Since
10
10
  # this key is generated just once during the request, it speeds up all cache accesses.
11
11
  class LookupContext #:nodoc:
12
+ attr_accessor :prefixes
13
+
12
14
  mattr_accessor :fallbacks
13
- @@fallbacks = [FileSystemResolver.new(""), FileSystemResolver.new("/")]
15
+ @@fallbacks = FallbackFileSystemResolver.instances
14
16
 
15
17
  mattr_accessor :registered_details
16
18
  self.registered_details = []
@@ -58,9 +60,11 @@ module ActionView
58
60
  end
59
61
  end
60
62
 
61
- def initialize(view_paths, details = {})
63
+ def initialize(view_paths, details = {}, prefixes = [])
62
64
  @details, @details_key = { :handlers => default_handlers }, nil
63
65
  @frozen_formats, @skip_default_locale = false, false
66
+ @cache = true
67
+ @prefixes = prefixes
64
68
 
65
69
  self.view_paths = view_paths
66
70
  self.registered_detail_setters.each do |key, setter|
@@ -77,17 +81,17 @@ module ActionView
77
81
  @view_paths = ActionView::Base.process_view_paths(paths)
78
82
  end
79
83
 
80
- def find(name, prefix = nil, partial = false)
81
- @view_paths.find(*args_for_lookup(name, prefix, partial))
84
+ def find(name, prefixes = [], partial = false, keys = [])
85
+ @view_paths.find(*args_for_lookup(name, prefixes, partial, keys))
82
86
  end
83
87
  alias :find_template :find
84
88
 
85
- def find_all(name, prefix = nil, partial = false)
86
- @view_paths.find_all(*args_for_lookup(name, prefix, partial))
89
+ def find_all(name, prefixes = [], partial = false, keys = [])
90
+ @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys))
87
91
  end
88
92
 
89
- def exists?(name, prefix = nil, partial = false)
90
- @view_paths.exists?(*args_for_lookup(name, prefix, partial))
93
+ def exists?(name, prefixes = [], partial = false, keys = [])
94
+ @view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys))
91
95
  end
92
96
  alias :template_exists? :exists?
93
97
 
@@ -106,18 +110,26 @@ module ActionView
106
110
 
107
111
  protected
108
112
 
109
- def args_for_lookup(name, prefix, partial) #:nodoc:
110
- name, prefix = normalize_name(name, prefix)
111
- [name, prefix, partial || false, @details, details_key]
113
+ def args_for_lookup(name, prefixes, partial, keys) #:nodoc:
114
+ name, prefixes = normalize_name(name, prefixes)
115
+ [name, prefixes, partial || false, @details, details_key, keys]
112
116
  end
113
117
 
114
118
  # Support legacy foo.erb names even though we now ignore .erb
115
119
  # as well as incorrectly putting part of the path in the template
116
120
  # name instead of the prefix.
117
- def normalize_name(name, prefix) #:nodoc:
121
+ def normalize_name(name, prefixes) #:nodoc:
118
122
  name = name.to_s.gsub(handlers_regexp, '')
119
123
  parts = name.split('/')
120
- return parts.pop, [prefix, *parts].compact.join("/")
124
+ name = parts.pop
125
+
126
+ prefixes = if prefixes.blank?
127
+ [parts.join('/')]
128
+ else
129
+ prefixes.map { |prefix| [prefix, *parts].compact.join('/') }
130
+ end
131
+
132
+ return name, prefixes
121
133
  end
122
134
 
123
135
  def default_handlers #:nodoc:
@@ -130,10 +142,20 @@ module ActionView
130
142
  end
131
143
 
132
144
  module Details
145
+ attr_accessor :cache
146
+
133
147
  # Calculate the details key. Remove the handlers from calculation to improve performance
134
148
  # since the user cannot modify it explicitly.
135
149
  def details_key #:nodoc:
136
- @details_key ||= DetailsKey.get(@details)
150
+ @details_key ||= DetailsKey.get(@details) if @cache
151
+ end
152
+
153
+ # Temporary skip passing the details_key forward.
154
+ def disable_cache
155
+ old_value, @cache = @cache, false
156
+ yield
157
+ ensure
158
+ @cache = old_value
137
159
  end
138
160
 
139
161
  # Freeze the current formats in the lookup context. By freezing them, you are guaranteeing
@@ -10,16 +10,17 @@ module ActionView #:nodoc:
10
10
  METHOD
11
11
  end
12
12
 
13
- def find(path, prefix = nil, partial = false, details = {}, key = nil)
14
- template = find_all(path, prefix, partial, details, key).first
15
- raise MissingTemplate.new(self, "#{prefix}/#{path}", details, partial) unless template
16
- template
13
+ def find(*args)
14
+ find_all(*args).first || raise(MissingTemplate.new(self, *args))
17
15
  end
18
16
 
19
- def find_all(*args)
20
- each do |resolver|
21
- templates = resolver.find_all(*args)
22
- return templates unless templates.empty?
17
+ def find_all(path, prefixes = [], *args)
18
+ prefixes = [prefixes] if String === prefixes
19
+ prefixes.each do |prefix|
20
+ each do |resolver|
21
+ templates = resolver.find_all(path, prefix, *args)
22
+ return templates unless templates.empty?
23
+ end
23
24
  end
24
25
  []
25
26
  end
@@ -6,12 +6,12 @@ module ActionView
6
6
  class Railtie < Rails::Railtie
7
7
  config.action_view = ActiveSupport::OrderedOptions.new
8
8
  config.action_view.stylesheet_expansions = {}
9
- config.action_view.javascript_expansions = { :defaults => ['prototype', 'effects', 'dragdrop', 'controls', 'rails'] }
9
+ config.action_view.javascript_expansions = { :defaults => %w(jquery jquery_ujs) }
10
10
 
11
- initializer "action_view.cache_asset_timestamps" do |app|
11
+ initializer "action_view.cache_asset_ids" do |app|
12
12
  unless app.config.cache_classes
13
13
  ActiveSupport.on_load(:action_view) do
14
- ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
14
+ ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
15
15
  end
16
16
  end
17
17
  end
@@ -35,5 +35,13 @@ module ActionView
35
35
  end
36
36
  end
37
37
  end
38
+
39
+ initializer "action_view.caching" do |app|
40
+ ActiveSupport.on_load(:action_view) do
41
+ if app.config.action_view.cache_template_loading.nil?
42
+ ActionView::Resolver.caching = app.config.cache_classes
43
+ end
44
+ end
45
+ end
38
46
  end
39
- end
47
+ end
@@ -0,0 +1,36 @@
1
+ module ActionView
2
+ class AbstractRenderer #:nodoc:
3
+ delegate :find_template, :template_exists?, :with_fallbacks, :update_details,
4
+ :with_layout_format, :formats, :freeze_formats, :to => :@lookup_context
5
+
6
+ def initialize(lookup_context)
7
+ @lookup_context = lookup_context
8
+ end
9
+
10
+ def render
11
+ raise NotImplementedError
12
+ end
13
+
14
+ # Checks if the given path contains a format and if so, change
15
+ # the lookup context to take this new format into account.
16
+ def wrap_formats(value)
17
+ return yield unless value.is_a?(String)
18
+
19
+ if value.sub!(formats_regexp, "")
20
+ update_details(:formats => [$1.to_sym]){ yield }
21
+ else
22
+ yield
23
+ end
24
+ end
25
+
26
+ def formats_regexp
27
+ @@formats_regexp ||= /\.(#{Mime::SET.symbols.join('|')})$/
28
+ end
29
+
30
+ protected
31
+
32
+ def instrument(name, options={})
33
+ ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
34
+ end
35
+ end
36
+ end
@@ -12,50 +12,49 @@ module ActionView
12
12
  #
13
13
  # <%= render :partial => "account" %>
14
14
  #
15
- # This would render "advertiser/_account.erb" and pass the instance variable @account in as a local variable
15
+ # This would render "advertiser/_account.html.erb" and pass the instance variable @account in as a local variable
16
16
  # +account+ to the template for display.
17
17
  #
18
18
  # In another template for Advertiser#buy, we could have:
19
19
  #
20
20
  # <%= render :partial => "account", :locals => { :account => @buyer } %>
21
21
  #
22
- # <% for ad in @advertisements %>
22
+ # <% @advertisements.each do |ad| %>
23
23
  # <%= render :partial => "ad", :locals => { :ad => ad } %>
24
24
  # <% end %>
25
25
  #
26
- # This would first render "advertiser/_account.erb" with @buyer passed in as the local variable +account+, then
27
- # render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display.
26
+ # This would first render "advertiser/_account.html.erb" with @buyer passed in as the local variable +account+, then
27
+ # render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display.
28
28
  #
29
29
  # == The :as and :object options
30
30
  #
31
- # By default PartialRenderer uses the template name for the local name of the object passed into the template.
32
- # These examples are effectively the same:
33
- #
34
- # <%= render :partial => "contract", :locals => { :contract => @contract } %>
31
+ # By default <tt>ActionView::Partials::PartialRenderer</tt> has its object in a local variable with the same
32
+ # name as the template. So, given
35
33
  #
36
34
  # <%= render :partial => "contract" %>
37
35
  #
38
- # By specifying the :as option we can change the way the local variable is namedin the template.
39
- # These examples are effectively the same:
36
+ # within contract we'll get <tt>@contract</tt> in the local variable +contract+, as if we had written
37
+ #
38
+ # <%= render :partial => "contract", :locals => { :contract => @contract } %>
40
39
  #
41
- # <%= render :partial => "contract", :as => :agreement
40
+ # With the <tt>:as</tt> option we can specify a different name for said local variable. For example, if we
41
+ # wanted it to be +agreement+ instead of +contract+ we'd do:
42
42
  #
43
- # <%= render :partial => "contract", :locals => { :agreement => @contract }
43
+ # <%= render :partial => "contract", :as => 'agreement' %>
44
44
  #
45
- # The :object option can be used to directly specify which object is rendered into the partial.
45
+ # The <tt>:object</tt> option can be used to directly specify which object is rendered into the partial;
46
+ # useful when the template's object is elsewhere, in a different ivar or in a local variable for instance.
46
47
  #
47
- # Revisiting a previous example we could have written this code.
48
+ # Revisiting a previous example we could have written this code:
48
49
  #
49
50
  # <%= render :partial => "account", :object => @buyer %>
50
51
  #
51
- # <% for ad in @advertisements %>
52
+ # <% @advertisements.each do |ad| %>
52
53
  # <%= render :partial => "ad", :object => ad %>
53
54
  # <% end %>
54
55
  #
55
- # The :object and :as options can be used together. We might have a partial which we have named genericly,
56
- # such as 'form'. Using :object and :as together helps us.
56
+ # The <tt>:object</tt> and <tt>:as</tt> options can be used together.
57
57
  #
58
- # <%= render :partial => "form", :object => @contract, :as => :contract %>
59
58
  # == Rendering a collection of partials
60
59
  #
61
60
  # The example of partial use describes a familiar pattern where a template needs to iterate over an array and
@@ -65,17 +64,22 @@ module ActionView
65
64
  #
66
65
  # <%= render :partial => "ad", :collection => @advertisements %>
67
66
  #
68
- # This will render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display. An
67
+ # This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An
69
68
  # iteration counter will automatically be made available to the template with a name of the form
70
69
  # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+.
71
70
  #
72
- # The :as option may be used when rendering partials.
71
+ # The <tt>:as</tt> option may be used when rendering partials.
73
72
  #
74
- # Also, you can specify a partial which will be render as a spacer between each element by passing partial name to
75
- # +:spacer_template+. The following example will render "advertiser/_ad_divider.erb" between each ad partial.
73
+ # You can specify a partial to be rendered between elements via the <tt>:spacer_template</tt> option.
74
+ # The following example will render <tt>advertiser/_ad_divider.html.erb</tt> between each ad partial:
76
75
  #
77
76
  # <%= render :partial => "ad", :collection => @advertisements, :spacer_template => "ad_divider" %>
78
77
  #
78
+ # If the given <tt>:collection</tt> is nil or empty, <tt>render</tt> will return nil. This will allow you
79
+ # to specify a text which will displayed instead by using this form:
80
+ #
81
+ # <%= render(:partial => "ad", :collection => @advertisements) || "There's no ad to be displayed" %>
82
+ #
79
83
  # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
80
84
  # just keep domain objects, like Active Records, in there.
81
85
  #
@@ -85,7 +89,7 @@ module ActionView
85
89
  #
86
90
  # <%= render :partial => "advertisement/ad", :locals => { :ad => @advertisement } %>
87
91
  #
88
- # This will render the partial "advertisement/_ad.erb" regardless of which controller this is being called from.
92
+ # This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from.
89
93
  #
90
94
  # == Rendering objects with the RecordIdentifier
91
95
  #
@@ -210,173 +214,170 @@ module ActionView
210
214
  # Deadline: <%= user.deadline %>
211
215
  # <%- end -%>
212
216
  # <% end %>
213
- module Partials
214
- extend ActiveSupport::Concern
215
-
216
- class PartialRenderer
217
- PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
218
-
219
- def initialize(view_context, options, block)
220
- @view = view_context
221
- @partial_names = PARTIAL_NAMES[@view.controller.class.name]
217
+ class PartialRenderer < AbstractRenderer #:nodoc:
218
+ PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
222
219
 
223
- setup(options, block)
224
- end
225
-
226
- def setup(options, block)
227
- partial = options[:partial]
228
-
229
- @options = options
230
- @locals = options[:locals] || {}
231
- @block = block
220
+ def initialize(*)
221
+ super
222
+ @partial_names = PARTIAL_NAMES[@lookup_context.prefixes.first]
223
+ end
232
224
 
233
- if String === partial
234
- @object = options[:object]
235
- @path = partial
236
- @collection = collection
237
- else
238
- @object = partial
225
+ def render(context, options, block)
226
+ setup(context, options, block)
239
227
 
240
- if @collection = collection_from_object || collection
241
- paths = @collection_paths = @collection.map { |o| partial_path(o) }
242
- @path = paths.uniq.size == 1 ? paths.first : nil
243
- else
244
- @path = partial_path
245
- end
246
- end
247
- end
248
-
249
- def render
250
- identifier = ((@template = find_template) ? @template.identifier : @path)
228
+ wrap_formats(@path) do
229
+ identifier = ((@template = find_partial) ? @template.identifier : @path)
251
230
 
252
231
  if @collection
253
- ActiveSupport::Notifications.instrument("render_collection.action_view",
254
- :identifier => identifier || "collection", :count => @collection.size) do
232
+ instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do
255
233
  render_collection
256
234
  end
257
235
  else
258
- block, options, locals = @block, @options, @locals
259
-
260
- content = ActiveSupport::Notifications.instrument("render_partial.action_view",
261
- :identifier => identifier) do
236
+ instrument(:partial, :identifier => identifier) do
262
237
  render_partial
263
238
  end
264
-
265
- if !block && (layout = options[:layout])
266
- content = @view._render_layout(find_template(layout), locals){ content }
267
- end
268
-
269
- content
270
239
  end
271
240
  end
241
+ end
272
242
 
273
- def render_collection
274
- return nil if @collection.blank?
275
-
276
- if @options.key?(:spacer_template)
277
- spacer = find_template(@options[:spacer_template]).render(@view, @locals)
278
- end
243
+ def render_collection
244
+ return nil if @collection.blank?
279
245
 
280
- result = @template ? collection_with_template : collection_without_template
281
- result.join(spacer).html_safe
246
+ if @options.key?(:spacer_template)
247
+ spacer = find_template(@options[:spacer_template]).render(@view, @locals)
282
248
  end
283
249
 
284
- def collection_with_template(template = @template)
285
- segments, locals, template = [], @locals, @template
286
-
287
- if @options[:as]
288
- as = @options[:as]
289
- counter = "#{as}_counter".to_sym
290
- else
291
- as = template.variable_name
292
- counter = template.counter_name
293
- end
294
-
295
- locals[counter] = -1
250
+ result = @template ? collection_with_template : collection_without_template
251
+ result.join(spacer).html_safe
252
+ end
296
253
 
297
- @collection.each do |object|
298
- locals[counter] += 1
299
- locals[as] = object
300
- segments << template.render(@view, locals)
301
- end
254
+ def render_partial
255
+ locals, view, block = @locals, @view, @block
256
+ object, as = @object, @variable
302
257
 
303
- segments
258
+ if !block && (layout = @options[:layout])
259
+ layout = find_template(layout)
304
260
  end
305
261
 
306
- def collection_without_template(collection_paths = @collection_paths)
307
- segments, locals = [], @locals
308
- index, template = -1, nil
262
+ object ||= locals[as]
263
+ locals[as] = object
309
264
 
310
- if @options[:as]
311
- as = @options[:as]
312
- counter = "#{as}_counter"
313
- end
265
+ content = @template.render(view, locals) do |*name|
266
+ view._layout_for(*name, &block)
267
+ end
314
268
 
315
- @collection.each_with_index do |object, i|
316
- template = find_template(collection_paths[i])
317
- locals[as || template.variable_name] = object
318
- locals[counter || template.counter_name] = (index += 1)
269
+ content = layout.render(view, locals){ content } if layout
270
+ content
271
+ end
319
272
 
320
- segments << template.render(@view, locals)
321
- end
273
+ private
322
274
 
323
- @template = template
324
- segments
325
- end
275
+ def setup(context, options, block)
276
+ @view = context
277
+ partial = options[:partial]
326
278
 
327
- def render_partial(object = @object)
328
- locals, view, template, block = @locals, @view, @template, @block
279
+ @options = options
280
+ @locals = options[:locals] || {}
281
+ @block = block
329
282
 
330
- object ||= locals[template.variable_name]
331
- locals[@options[:as] || template.variable_name] = object
283
+ if String === partial
284
+ @object = options[:object]
285
+ @path = partial
286
+ @collection = collection
287
+ else
288
+ @object = partial
332
289
 
333
- template.render(view, locals) do |*name|
334
- view._layout_for(*name, &block)
290
+ if @collection = collection_from_object || collection
291
+ paths = @collection_data = @collection.map { |o| partial_path(o) }
292
+ @path = paths.uniq.size == 1 ? paths.first : nil
293
+ else
294
+ @path = partial_path
335
295
  end
336
296
  end
337
297
 
338
- private
298
+ if @path
299
+ @variable, @variable_counter = retrieve_variable(@path)
300
+ else
301
+ paths.map! { |path| retrieve_variable(path).unshift(path) }
302
+ end
339
303
 
340
- def collection
341
- if @options.key?(:collection)
342
- collection = @options[:collection]
343
- collection.respond_to?(:to_ary) ? collection.to_ary : []
344
- end
304
+ self
305
+ end
306
+
307
+ def collection
308
+ if @options.key?(:collection)
309
+ collection = @options[:collection]
310
+ collection.respond_to?(:to_ary) ? collection.to_ary : []
345
311
  end
312
+ end
346
313
 
347
- def collection_from_object
348
- if @object.respond_to?(:to_ary)
349
- @object.to_ary
350
- end
314
+ def collection_from_object
315
+ if @object.respond_to?(:to_ary)
316
+ @object.to_ary
351
317
  end
318
+ end
352
319
 
353
- def find_template(path=@path)
354
- return path unless path.is_a?(String)
355
- prefix = @view.controller_path unless path.include?(?/)
356
- @view.find_template(path, prefix, true)
320
+ def find_partial
321
+ if path = @path
322
+ locals = @locals.keys
323
+ locals << @variable
324
+ locals << @variable_counter if @collection
325
+ find_template(path, locals)
357
326
  end
327
+ end
358
328
 
359
- def partial_path(object = @object)
360
- @partial_names[object.class.name] ||= begin
361
- object = object.to_model if object.respond_to?(:to_model)
329
+ def find_template(path=@path, locals=@locals.keys)
330
+ prefixes = path.include?(?/) ? [] : @lookup_context.prefixes
331
+ @lookup_context.find_template(path, prefixes, true, locals)
332
+ end
362
333
 
363
- object.class.model_name.partial_path.dup.tap do |partial|
364
- path = @view.controller_path
365
- partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
366
- end
367
- end
334
+ def collection_with_template
335
+ segments, locals, template = [], @locals, @template
336
+ as, counter = @variable, @variable_counter
337
+
338
+ locals[counter] = -1
339
+
340
+ @collection.each do |object|
341
+ locals[counter] += 1
342
+ locals[as] = object
343
+ segments << template.render(@view, locals)
368
344
  end
345
+
346
+ segments
369
347
  end
370
348
 
371
- def _render_partial(options, &block) #:nodoc:
372
- if defined?(@renderer)
373
- @renderer.setup(options, block)
374
- else
375
- @renderer = PartialRenderer.new(self, options, block)
349
+ def collection_without_template
350
+ segments, locals, collection_data = [], @locals, @collection_data
351
+ index, template, cache = -1, nil, {}
352
+ keys = @locals.keys
353
+
354
+ @collection.each_with_index do |object, i|
355
+ path, *data = collection_data[i]
356
+ template = (cache[path] ||= find_template(path, keys + data))
357
+ locals[data[0]] = object
358
+ locals[data[1]] = (index += 1)
359
+ segments << template.render(@view, locals)
376
360
  end
377
361
 
378
- @renderer.render
362
+ @template = template
363
+ segments
379
364
  end
380
365
 
366
+ def partial_path(object = @object)
367
+ @partial_names[object.class.name] ||= begin
368
+ object = object.to_model if object.respond_to?(:to_model)
369
+
370
+ object.class.model_name.partial_path.dup.tap do |partial|
371
+ path = @lookup_context.prefixes.first
372
+ partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
373
+ end
374
+ end
375
+ end
376
+
377
+ def retrieve_variable(path)
378
+ variable = @options[:as].try(:to_sym) || path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
379
+ variable_counter = :"#{variable}_counter" if @collection
380
+ [variable, variable_counter]
381
+ end
381
382
  end
382
383
  end