actionpack 2.0.5 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. data/CHANGELOG +149 -7
  2. data/MIT-LICENSE +1 -1
  3. data/README +1 -1
  4. data/Rakefile +5 -6
  5. data/lib/action_controller.rb +2 -2
  6. data/lib/action_controller/assertions/model_assertions.rb +2 -1
  7. data/lib/action_controller/assertions/response_assertions.rb +4 -2
  8. data/lib/action_controller/assertions/routing_assertions.rb +3 -3
  9. data/lib/action_controller/assertions/selector_assertions.rb +30 -27
  10. data/lib/action_controller/assertions/tag_assertions.rb +3 -3
  11. data/lib/action_controller/base.rb +103 -129
  12. data/lib/action_controller/benchmarking.rb +3 -3
  13. data/lib/action_controller/caching.rb +41 -652
  14. data/lib/action_controller/caching/actions.rb +144 -0
  15. data/lib/action_controller/caching/fragments.rb +138 -0
  16. data/lib/action_controller/caching/pages.rb +154 -0
  17. data/lib/action_controller/caching/sql_cache.rb +18 -0
  18. data/lib/action_controller/caching/sweeping.rb +97 -0
  19. data/lib/action_controller/cgi_ext/cookie.rb +27 -23
  20. data/lib/action_controller/cgi_ext/stdinput.rb +1 -0
  21. data/lib/action_controller/cgi_process.rb +6 -4
  22. data/lib/action_controller/components.rb +7 -6
  23. data/lib/action_controller/cookies.rb +31 -19
  24. data/lib/action_controller/dispatcher.rb +51 -84
  25. data/lib/action_controller/filters.rb +295 -421
  26. data/lib/action_controller/flash.rb +1 -6
  27. data/lib/action_controller/headers.rb +31 -0
  28. data/lib/action_controller/helpers.rb +26 -9
  29. data/lib/action_controller/http_authentication.rb +1 -1
  30. data/lib/action_controller/integration.rb +65 -13
  31. data/lib/action_controller/layout.rb +24 -39
  32. data/lib/action_controller/mime_responds.rb +7 -3
  33. data/lib/action_controller/mime_type.rb +25 -9
  34. data/lib/action_controller/mime_types.rb +1 -1
  35. data/lib/action_controller/polymorphic_routes.rb +32 -17
  36. data/lib/action_controller/record_identifier.rb +10 -4
  37. data/lib/action_controller/request.rb +46 -30
  38. data/lib/action_controller/request_forgery_protection.rb +10 -9
  39. data/lib/action_controller/request_profiler.rb +29 -8
  40. data/lib/action_controller/rescue.rb +24 -24
  41. data/lib/action_controller/resources.rb +66 -23
  42. data/lib/action_controller/response.rb +2 -2
  43. data/lib/action_controller/routing.rb +113 -1229
  44. data/lib/action_controller/routing/builder.rb +204 -0
  45. data/lib/action_controller/{routing_optimisation.rb → routing/optimisations.rb} +13 -12
  46. data/lib/action_controller/routing/recognition_optimisation.rb +158 -0
  47. data/lib/action_controller/routing/route.rb +240 -0
  48. data/lib/action_controller/routing/route_set.rb +435 -0
  49. data/lib/action_controller/routing/routing_ext.rb +46 -0
  50. data/lib/action_controller/routing/segments.rb +283 -0
  51. data/lib/action_controller/session/active_record_store.rb +13 -8
  52. data/lib/action_controller/session/cookie_store.rb +20 -17
  53. data/lib/action_controller/session_management.rb +10 -3
  54. data/lib/action_controller/streaming.rb +45 -31
  55. data/lib/action_controller/test_case.rb +33 -23
  56. data/lib/action_controller/test_process.rb +39 -35
  57. data/lib/action_controller/url_rewriter.rb +18 -12
  58. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +1 -1
  59. data/lib/action_pack.rb +1 -1
  60. data/lib/action_pack/version.rb +2 -2
  61. data/lib/action_view.rb +11 -3
  62. data/lib/action_view/base.rb +73 -390
  63. data/lib/action_view/helpers/active_record_helper.rb +83 -62
  64. data/lib/action_view/helpers/asset_tag_helper.rb +101 -44
  65. data/lib/action_view/helpers/atom_feed_helper.rb +35 -7
  66. data/lib/action_view/helpers/benchmark_helper.rb +5 -3
  67. data/lib/action_view/helpers/cache_helper.rb +3 -2
  68. data/lib/action_view/helpers/capture_helper.rb +1 -2
  69. data/lib/action_view/helpers/date_helper.rb +104 -82
  70. data/lib/action_view/helpers/form_helper.rb +148 -75
  71. data/lib/action_view/helpers/form_options_helper.rb +44 -23
  72. data/lib/action_view/helpers/form_tag_helper.rb +22 -13
  73. data/lib/action_view/helpers/javascripts/controls.js +1 -1
  74. data/lib/action_view/helpers/javascripts/dragdrop.js +1 -1
  75. data/lib/action_view/helpers/javascripts/effects.js +1 -1
  76. data/lib/action_view/helpers/number_helper.rb +10 -3
  77. data/lib/action_view/helpers/prototype_helper.rb +61 -29
  78. data/lib/action_view/helpers/record_tag_helper.rb +3 -3
  79. data/lib/action_view/helpers/sanitize_helper.rb +23 -17
  80. data/lib/action_view/helpers/scriptaculous_helper.rb +86 -60
  81. data/lib/action_view/helpers/text_helper.rb +153 -125
  82. data/lib/action_view/helpers/url_helper.rb +83 -28
  83. data/lib/action_view/inline_template.rb +20 -0
  84. data/lib/action_view/partial_template.rb +70 -0
  85. data/lib/action_view/partials.rb +31 -73
  86. data/lib/action_view/template.rb +127 -0
  87. data/lib/action_view/template_error.rb +8 -7
  88. data/lib/action_view/template_finder.rb +177 -0
  89. data/lib/action_view/template_handler.rb +18 -1
  90. data/lib/action_view/template_handlers/builder.rb +10 -2
  91. data/lib/action_view/template_handlers/compilable.rb +128 -0
  92. data/lib/action_view/template_handlers/erb.rb +37 -2
  93. data/lib/action_view/template_handlers/rjs.rb +14 -1
  94. data/lib/action_view/test_case.rb +58 -0
  95. data/test/abstract_unit.rb +1 -1
  96. data/test/active_record_unit.rb +3 -6
  97. data/test/activerecord/active_record_store_test.rb +1 -2
  98. data/test/activerecord/render_partial_with_record_identification_test.rb +158 -41
  99. data/test/adv_attr_test.rb +20 -0
  100. data/test/controller/action_pack_assertions_test.rb +16 -19
  101. data/test/controller/addresses_render_test.rb +1 -1
  102. data/test/controller/assert_select_test.rb +13 -6
  103. data/test/controller/base_test.rb +48 -2
  104. data/test/controller/benchmark_test.rb +1 -2
  105. data/test/controller/caching_test.rb +282 -21
  106. data/test/controller/capture_test.rb +1 -1
  107. data/test/controller/cgi_test.rb +1 -1
  108. data/test/controller/components_test.rb +1 -1
  109. data/test/controller/content_type_test.rb +2 -2
  110. data/test/controller/cookie_test.rb +13 -2
  111. data/test/controller/custom_handler_test.rb +14 -10
  112. data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -1
  113. data/test/controller/dispatcher_test.rb +31 -49
  114. data/test/controller/fake_controllers.rb +17 -0
  115. data/test/controller/fake_models.rb +6 -0
  116. data/test/controller/filter_params_test.rb +14 -8
  117. data/test/controller/filters_test.rb +44 -16
  118. data/test/controller/flash_test.rb +2 -2
  119. data/test/controller/header_test.rb +14 -0
  120. data/test/controller/helper_test.rb +19 -15
  121. data/test/controller/html-scanner/document_test.rb +1 -2
  122. data/test/controller/html-scanner/node_test.rb +1 -2
  123. data/test/controller/html-scanner/sanitizer_test.rb +8 -5
  124. data/test/controller/html-scanner/tag_node_test.rb +1 -2
  125. data/test/controller/html-scanner/text_node_test.rb +2 -3
  126. data/test/controller/html-scanner/tokenizer_test.rb +8 -2
  127. data/test/controller/http_authentication_test.rb +1 -1
  128. data/test/controller/integration_test.rb +14 -16
  129. data/test/controller/integration_upload_test.rb +43 -0
  130. data/test/controller/layout_test.rb +26 -6
  131. data/test/controller/mime_responds_test.rb +39 -7
  132. data/test/controller/mime_type_test.rb +29 -5
  133. data/test/controller/new_render_test.rb +105 -34
  134. data/test/controller/polymorphic_routes_test.rb +32 -20
  135. data/test/controller/record_identifier_test.rb +38 -2
  136. data/test/controller/redirect_test.rb +21 -1
  137. data/test/controller/render_test.rb +59 -15
  138. data/test/controller/request_forgery_protection_test.rb +92 -5
  139. data/test/controller/request_test.rb +64 -6
  140. data/test/controller/rescue_test.rb +22 -6
  141. data/test/controller/resources_test.rb +102 -14
  142. data/test/controller/routing_test.rb +231 -19
  143. data/test/controller/selector_test.rb +2 -2
  144. data/test/controller/send_file_test.rb +14 -3
  145. data/test/controller/session/cookie_store_test.rb +16 -4
  146. data/test/controller/session/mem_cache_store_test.rb +3 -4
  147. data/test/controller/session_fixation_test.rb +1 -1
  148. data/test/controller/session_management_test.rb +23 -1
  149. data/test/controller/test_test.rb +39 -18
  150. data/test/controller/url_rewriter_test.rb +35 -1
  151. data/test/controller/verification_test.rb +1 -1
  152. data/test/controller/view_paths_test.rb +15 -12
  153. data/test/controller/webservice_test.rb +48 -3
  154. data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
  155. data/test/fixtures/company.rb +1 -0
  156. data/test/fixtures/customers/_customer.html.erb +1 -0
  157. data/test/fixtures/db_definitions/sqlite.sql +6 -0
  158. data/test/fixtures/functional_caching/_partial.erb +3 -0
  159. data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
  160. data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
  161. data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
  162. data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
  163. data/test/fixtures/mascot.rb +3 -0
  164. data/test/fixtures/mascots.yml +4 -0
  165. data/test/fixtures/mascots/_mascot.html.erb +1 -0
  166. data/test/fixtures/multipart/boundary_problem_file +10 -0
  167. data/test/fixtures/public/javascripts/application.js +1 -0
  168. data/test/fixtures/public/javascripts/controls.js +1 -0
  169. data/test/fixtures/public/javascripts/dragdrop.js +1 -0
  170. data/test/fixtures/public/javascripts/effects.js +1 -0
  171. data/test/fixtures/public/javascripts/prototype.js +1 -0
  172. data/test/fixtures/public/javascripts/version.1.0.js +1 -0
  173. data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
  174. data/test/fixtures/reply.rb +1 -0
  175. data/test/fixtures/shared.html.erb +1 -0
  176. data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
  177. data/test/fixtures/test/_customer_counter.erb +1 -0
  178. data/test/fixtures/test/_form.erb +1 -0
  179. data/test/fixtures/test/_labelling_form.erb +1 -0
  180. data/test/fixtures/test/_raise.html.erb +1 -0
  181. data/test/fixtures/test/greeting.js.rjs +1 -0
  182. data/test/fixtures/topics/_topic.html.erb +1 -0
  183. data/test/template/active_record_helper_test.rb +25 -8
  184. data/test/template/asset_tag_helper_test.rb +100 -17
  185. data/test/template/atom_feed_helper_test.rb +29 -1
  186. data/test/template/benchmark_helper_test.rb +10 -22
  187. data/test/template/date_helper_test.rb +455 -153
  188. data/test/template/erb_util_test.rb +10 -42
  189. data/test/template/form_helper_test.rb +192 -66
  190. data/test/template/form_options_helper_test.rb +19 -8
  191. data/test/template/form_tag_helper_test.rb +11 -8
  192. data/test/template/javascript_helper_test.rb +3 -9
  193. data/test/template/number_helper_test.rb +6 -3
  194. data/test/template/prototype_helper_test.rb +27 -40
  195. data/test/template/record_tag_helper_test.rb +54 -0
  196. data/test/template/sanitize_helper_test.rb +5 -6
  197. data/test/template/scriptaculous_helper_test.rb +7 -13
  198. data/test/template/tag_helper_test.rb +3 -6
  199. data/test/template/template_finder_test.rb +73 -0
  200. data/test/template/template_object_test.rb +95 -0
  201. data/test/template/test_test.rb +56 -0
  202. data/test/template/text_helper_test.rb +46 -33
  203. data/test/template/url_helper_test.rb +8 -10
  204. metadata +65 -12
  205. data/lib/action_view/compiled_templates.rb +0 -69
  206. data/test/action_view_test.rb +0 -44
  207. data/test/activerecord/fixtures_test.rb +0 -24
  208. data/test/controller/fragment_store_setting_test.rb +0 -47
  209. data/test/template/compiled_templates_test.rb +0 -197
  210. data/test/template/deprecate_ivars_test.rb +0 -51
@@ -10,24 +10,24 @@ module ActionView
10
10
  include JavaScriptHelper
11
11
 
12
12
  # Returns the URL for the set of +options+ provided. This takes the
13
- # same options as url_for in ActionController (see the
13
+ # same options as +url_for+ in Action Controller (see the
14
14
  # documentation for ActionController::Base#url_for). Note that by default
15
15
  # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative /controller/action
16
16
  # instead of the fully qualified URL like http://example.com/controller/action.
17
17
  #
18
18
  # When called from a view, url_for returns an HTML escaped url. If you
19
- # need an unescaped url, pass :escape => false in the +options+.
19
+ # need an unescaped url, pass <tt>:escape => false</tt> in the +options+.
20
20
  #
21
21
  # ==== Options
22
- # * <tt>:anchor</tt> -- specifies the anchor name to be appended to the path.
23
- # * <tt>:only_path</tt> -- if true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified)
24
- # * <tt>:trailing_slash</tt> -- if true, adds a trailing slash, as in "/archive/2005/". Note that this
22
+ # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
23
+ # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
24
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
25
25
  # is currently not recommended since it breaks caching.
26
- # * <tt>:host</tt> -- overrides the default (current) host if provided
27
- # * <tt>:protocol</tt> -- overrides the default (current) protocol if provided
28
- # * <tt>:user</tt> -- Inline HTTP authentication (only plucked out if :password is also present)
29
- # * <tt>:password</tt> -- Inline HTTP authentication (only plucked out if :user is also present)
30
- # * <tt>:escape</tt> -- Determines whether the returned URL will be HTML escaped or not (<tt>true</tt> by default)
26
+ # * <tt>:host</tt> - Overrides the default (current) host if provided.
27
+ # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
28
+ # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
29
+ # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
30
+ # * <tt>:escape</tt> - Determines whether the returned URL will be HTML escaped or not (<tt>true</tt> by default).
31
31
  #
32
32
  # ==== Relying on named routes
33
33
  #
@@ -86,51 +86,106 @@ module ActionView
86
86
  # of +options+. See the valid options in the documentation for
87
87
  # url_for. It's also possible to pass a string instead
88
88
  # of an options hash to get a link tag that uses the value of the string as the
89
- # href for the link, or use +:back+ to link to the referrer - a JavaScript back
89
+ # href for the link, or use <tt>:back</tt> to link to the referrer - a JavaScript back
90
90
  # link will be used in place of a referrer if none exists. If nil is passed as
91
91
  # a name, the link itself will become the name.
92
92
  #
93
93
  # ==== Options
94
- # * <tt>:confirm => 'question?'</tt> -- This will add a JavaScript confirm
94
+ # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
95
95
  # prompt with the question specified. If the user accepts, the link is
96
96
  # processed normally, otherwise no action is taken.
97
- # * <tt>:popup => true || array of window options</tt> -- This will force the
97
+ # * <tt>:popup => true || array of window options</tt> - This will force the
98
98
  # link to open in a popup window. By passing true, a default browser window
99
99
  # will be opened with the URL. You can also specify an array of options
100
100
  # that are passed-thru to JavaScripts window.open method.
101
- # * <tt>:method => symbol of HTTP verb</tt> -- This modifier will dynamically
101
+ # * <tt>:method => symbol of HTTP verb</tt> - This modifier will dynamically
102
102
  # create an HTML form and immediately submit the form for processing using
103
103
  # the HTTP verb specified. Useful for having links perform a POST operation
104
104
  # in dangerous actions like deleting a record (which search bots can follow
105
- # while spidering your site). Supported verbs are :post, :delete and :put.
105
+ # while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt> and <tt>:put</tt>.
106
106
  # Note that if the user has JavaScript disabled, the request will fall back
107
107
  # to using GET. If you are relying on the POST behavior, you should check
108
108
  # for it in your controller's action by using the request object's methods
109
- # for post?, delete? or put?.
109
+ # for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
110
110
  # * The +html_options+ will accept a hash of html attributes for the link tag.
111
111
  #
112
112
  # Note that if the user has JavaScript disabled, the request will fall back
113
- # to using GET. If :href=>'#' is used and the user has JavaScript disabled
113
+ # to using GET. If <tt>:href => '#'</tt> is used and the user has JavaScript disabled
114
114
  # clicking the link will have no effect. If you are relying on the POST
115
115
  # behavior, your should check for it in your controller's action by using the
116
- # request object's methods for post?, delete? or put?.
116
+ # request object's methods for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
117
117
  #
118
118
  # You can mix and match the +html_options+ with the exception of
119
- # :popup and :method which will raise an ActionView::ActionViewError
119
+ # <tt>:popup</tt> and <tt>:method</tt> which will raise an ActionView::ActionViewError
120
120
  # exception.
121
121
  #
122
122
  # ==== Examples
123
+ # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
124
+ # and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
125
+ # your application on resources and use
126
+ #
127
+ # link_to "Profile", profile_path(@profile)
128
+ # # => <a href="/profiles/1">Profile</a>
129
+ #
130
+ # or the even pithier
131
+ #
132
+ # link_to "Profile", @profile
133
+ # # => <a href="/profiles/1">Profile</a>
134
+ #
135
+ # in place of the older more verbose, non-resource-oriented
136
+ #
137
+ # link_to "Profile", :controller => "profiles", :action => "show", :id => @profile
138
+ # # => <a href="/profiles/show/1">Profile</a>
139
+ #
140
+ # Similarly,
141
+ #
142
+ # link_to "Profiles", profiles_path
143
+ # # => <a href="/profiles">Profiles</a>
144
+ #
145
+ # is better than
146
+ #
147
+ # link_to "Profiles", :controller => "profiles"
148
+ # # => <a href="/profiles">Profiles</a>
149
+ #
150
+ # Classes and ids for CSS are easy to produce:
151
+ #
152
+ # link_to "Articles", articles_path, :id => "news", :class => "article"
153
+ # # => <a href="/articles" class="article" id="news">Articles</a>
154
+ #
155
+ # Be careful when using the older argument style, as an extra literal hash is needed:
156
+ #
157
+ # link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
158
+ # # => <a href="/articles" class="article" id="news">Articles</a>
159
+ #
160
+ # Leaving the hash off gives the wrong link:
161
+ #
162
+ # link_to "WRONG!", :controller => "articles", :id => "news", :class => "article"
163
+ # # => <a href="/articles/index/news?class=article">WRONG!</a>
164
+ #
165
+ # +link_to+ can also produce links with anchors or query strings:
166
+ #
167
+ # link_to "Comment wall", profile_path(@profile, :anchor => "wall")
168
+ # # => <a href="/profiles/1#wall">Comment wall</a>
169
+ #
170
+ # link_to "Ruby on Rails search", :controller => "searches", :query => "ruby on rails"
171
+ # # => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
172
+ #
173
+ # link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
174
+ # # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
175
+ #
176
+ # The three options specfic to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows:
177
+ #
123
178
  # link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
124
179
  # # => <a href="http://www.rubyonrails.org/" onclick="return confirm('Are you sure?');">Visit Other Site</a>
125
180
  #
126
181
  # link_to "Help", { :action => "help" }, :popup => true
127
182
  # # => <a href="/testing/help/" onclick="window.open(this.href);return false;">Help</a>
128
183
  #
129
- # link_to "View Image", { :action => "view" }, :popup => ['new_window_name', 'height=300,width=600']
130
- # # => <a href="/testing/view/" onclick="window.open(this.href,'new_window_name','height=300,width=600');return false;">View Image</a>
184
+ # link_to "View Image", @image, :popup => ['new_window_name', 'height=300,width=600']
185
+ # # => <a href="/images/9" onclick="window.open(this.href,'new_window_name','height=300,width=600');return false;">View Image</a>
131
186
  #
132
- # link_to "Delete Image", { :action => "delete", :id => @image.id }, :confirm => "Are you sure?", :method => :delete
133
- # # => <a href="/testing/delete/9/" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form');
187
+ # link_to "Delete Image", @image, :confirm => "Are you sure?", :method => :delete
188
+ # # => <a href="/images/9" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form');
134
189
  # f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;
135
190
  # var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method');
136
191
  # m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return false;">Delete Image</a>
@@ -178,9 +233,9 @@ module ActionView
178
233
  # The +options+ hash accepts the same options at url_for.
179
234
  #
180
235
  # There are a few special +html_options+:
181
- # * <tt>:method</tt> -- specifies the anchor name to be appended to the path.
182
- # * <tt>:disabled</tt> -- specifies the anchor name to be appended to the path.
183
- # * <tt>:confirm</tt> -- This will add a JavaScript confirm
236
+ # * <tt>:method</tt> - Specifies the anchor name to be appended to the path.
237
+ # * <tt>:disabled</tt> - Specifies the anchor name to be appended to the path.
238
+ # * <tt>:confirm</tt> - This will add a JavaScript confirm
184
239
  # prompt with the question specified. If the user accepts, the link is
185
240
  # processed normally, otherwise no action is taken.
186
241
  #
@@ -251,10 +306,10 @@ module ActionView
251
306
  # <li>About Us</li>
252
307
  # </ul>
253
308
  #
254
- # ...but if in the "home" action, it will render:
309
+ # ...but if in the "index" action, it will render:
255
310
  #
256
311
  # <ul id="navbar">
257
- # <li><a href="/controller/index">Home</a></li>
312
+ # <li>Home</li>
258
313
  # <li><a href="/controller/about">About Us</a></li>
259
314
  # </ul>
260
315
  #
@@ -0,0 +1,20 @@
1
+ module ActionView #:nodoc:
2
+ class InlineTemplate < Template #:nodoc:
3
+
4
+ def initialize(view, source, locals = {}, type = nil)
5
+ @view = view
6
+ @finder = @view.finder
7
+
8
+ @source = source
9
+ @extension = type
10
+ @locals = locals || {}
11
+
12
+ @handler = self.class.handler_class_for_extension(@extension).new(@view)
13
+ end
14
+
15
+ def method_key
16
+ @source
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,70 @@
1
+ module ActionView #:nodoc:
2
+ class PartialTemplate < Template #:nodoc:
3
+
4
+ attr_reader :variable_name, :object
5
+
6
+ def initialize(view, partial_path, object = nil, locals = {})
7
+ @path, @variable_name = extract_partial_name_and_path(view, partial_path)
8
+ super(view, @path, true, locals)
9
+ add_object_to_local_assigns!(object)
10
+
11
+ # This is needed here in order to compile template with knowledge of 'counter'
12
+ initialize_counter
13
+
14
+ # Prepare early. This is a performance optimization for partial collections
15
+ prepare!
16
+ end
17
+
18
+ def render
19
+ ActionController::Base.benchmark("Rendered #{@path}", Logger::DEBUG, false) do
20
+ @handler.render(self)
21
+ end
22
+ end
23
+
24
+ def render_member(object)
25
+ @locals[@counter_name] += 1
26
+ @locals[:object] = @locals[@variable_name] = object
27
+
28
+ template = render_template
29
+ @locals.delete(@variable_name)
30
+ @locals.delete(:object)
31
+
32
+ template
33
+ end
34
+
35
+ def counter=(num)
36
+ @locals[@counter_name] = num
37
+ end
38
+
39
+ private
40
+
41
+ def add_object_to_local_assigns!(object)
42
+ @locals[:object] ||=
43
+ @locals[@variable_name] ||=
44
+ if object.is_a?(ActionView::Base::ObjectWrapper)
45
+ object.value
46
+ else
47
+ object
48
+ end || @view.controller.instance_variable_get("@#{variable_name}")
49
+ end
50
+
51
+ def extract_partial_name_and_path(view, partial_path)
52
+ path, partial_name = partial_pieces(view, partial_path)
53
+ [File.join(path, "_#{partial_name}"), partial_name.split('/').last.split('.').first.to_sym]
54
+ end
55
+
56
+ def partial_pieces(view, partial_path)
57
+ if partial_path.include?('/')
58
+ return File.dirname(partial_path), File.basename(partial_path)
59
+ else
60
+ return view.controller.class.controller_path, partial_path
61
+ end
62
+ end
63
+
64
+ def initialize_counter
65
+ @counter_name ||= "#{@variable_name}_counter".to_sym
66
+ @locals[@counter_name] = 0
67
+ end
68
+
69
+ end
70
+ end
@@ -100,101 +100,59 @@ module ActionView
100
100
  # Title: <%= chief.name %>
101
101
  # </div>
102
102
  #
103
- # As you can see, the :locals hash is shared between both the partial and its layout.
103
+ # As you can see, the <tt>:locals</tt> hash is shared between both the partial and its layout.
104
104
  module Partials
105
105
  private
106
- def render_partial(partial_path, object_assigns = nil, local_assigns = nil) #:nodoc:
106
+ def render_partial(partial_path, object_assigns = nil, local_assigns = {}) #:nodoc:
107
107
  case partial_path
108
108
  when String, Symbol, NilClass
109
- path, partial_name = partial_pieces(partial_path)
110
- object = extracting_object(partial_name, object_assigns)
111
- local_assigns = local_assigns ? local_assigns.clone : {}
112
- add_counter_to_local_assigns!(partial_name, local_assigns)
113
- add_object_to_local_assigns!(partial_name, local_assigns, object)
114
-
115
- if logger && logger.debug?
116
- ActionController::Base.benchmark("Rendered #{path}/_#{partial_name}", Logger::DEBUG, false) do
117
- render("#{path}/_#{partial_name}", local_assigns)
118
- end
119
- else
120
- render("#{path}/_#{partial_name}", local_assigns)
121
- end
122
- when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Associations::HasManyThroughAssociation
109
+ # Render the template
110
+ ActionView::PartialTemplate.new(self, partial_path, object_assigns, local_assigns).render_template
111
+ when ActionView::Helpers::FormBuilder
112
+ builder_partial_path = partial_path.class.to_s.demodulize.underscore.sub(/_builder$/, '')
113
+ render_partial(builder_partial_path, object_assigns, (local_assigns || {}).merge(builder_partial_path.to_sym => partial_path))
114
+ when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope
123
115
  if partial_path.any?
124
- path = ActionController::RecordIdentifier.partial_path(partial_path.first)
125
116
  collection = partial_path
126
- render_partial_collection(path, collection, nil, object_assigns.value)
117
+ render_partial_collection(nil, collection, nil, local_assigns)
127
118
  else
128
119
  ""
129
120
  end
130
121
  else
131
- render_partial(
132
- ActionController::RecordIdentifier.partial_path(partial_path),
133
- object_assigns, local_assigns)
122
+ render_partial(ActionController::RecordIdentifier.partial_path(partial_path, controller.class.controller_path), partial_path, local_assigns)
134
123
  end
135
124
  end
136
125
 
137
- def render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = nil) #:nodoc:
138
- collection_of_partials = Array.new
139
- counter_name = partial_counter_name(partial_name)
140
- local_assigns = local_assigns ? local_assigns.clone : {}
141
- collection.each_with_index do |element, counter|
142
- local_assigns[counter_name] = counter
143
- collection_of_partials.push(render_partial(partial_name, element, local_assigns))
144
- end
126
+ def render_partial_collection(partial_path, collection, partial_spacer_template = nil, local_assigns = {}) #:nodoc:
127
+ return " " if collection.empty?
145
128
 
146
- return " " if collection_of_partials.empty?
129
+ local_assigns = local_assigns ? local_assigns.clone : {}
130
+ spacer = partial_spacer_template ? render(:partial => partial_spacer_template) : ''
147
131
 
148
- if partial_spacer_template
149
- spacer_path, spacer_name = partial_pieces(partial_spacer_template)
150
- collection_of_partials.join(render("#{spacer_path}/_#{spacer_name}"))
132
+ if partial_path.nil?
133
+ render_partial_collection_with_unknown_partial_path(collection, local_assigns)
151
134
  else
152
- collection_of_partials.join
153
- end
135
+ render_partial_collection_with_known_partial_path(collection, partial_path, local_assigns)
136
+ end.join(spacer)
154
137
  end
155
138
 
156
- alias_method :render_collection_of_partials, :render_partial_collection
157
-
158
- def partial_pieces(partial_path)
159
- if partial_path.include?('/')
160
- return File.dirname(partial_path), File.basename(partial_path)
161
- else
162
- return controller.class.controller_path, partial_path
139
+ def render_partial_collection_with_known_partial_path(collection, partial_path, local_assigns)
140
+ template = ActionView::PartialTemplate.new(self, partial_path, nil, local_assigns)
141
+ collection.map do |element|
142
+ template.render_member(element)
163
143
  end
164
144
  end
165
145
 
166
- def partial_counter_name(partial_name)
167
- "#{partial_variable_name(partial_name)}_counter".intern
168
- end
169
-
170
- def partial_variable_name(partial_name)
171
- partial_name.split('/').last.split('.').first.intern
172
- end
173
-
174
- def extracting_object(partial_name, object_assigns)
175
- variable_name = partial_variable_name(partial_name)
176
- if object_assigns.nil?
177
- controller.instance_variable_get("@#{variable_name}")
178
- else
179
- object_assigns
146
+ def render_partial_collection_with_unknown_partial_path(collection, local_assigns)
147
+ templates = Hash.new
148
+ i = 0
149
+ collection.map do |element|
150
+ partial_path = ActionController::RecordIdentifier.partial_path(element, controller.class.controller_path)
151
+ template = templates[partial_path] ||= ActionView::PartialTemplate.new(self, partial_path, nil, local_assigns)
152
+ template.counter = i
153
+ i += 1
154
+ template.render_member(element)
180
155
  end
181
156
  end
182
-
183
- def add_counter_to_local_assigns!(partial_name, local_assigns)
184
- counter_name = partial_counter_name(partial_name)
185
- local_assigns[counter_name] = 1 unless local_assigns.has_key?(counter_name)
186
- end
187
-
188
- def add_object_to_local_assigns!(partial_name, local_assigns, object)
189
- variable_name = partial_variable_name(partial_name)
190
-
191
- local_assigns[:object] ||=
192
- local_assigns[variable_name] ||=
193
- if object.is_a?(ActionView::Base::ObjectWrapper)
194
- object.value
195
- else
196
- object
197
- end || controller.instance_variable_get("@#{variable_name}")
198
- end
199
157
  end
200
158
  end
@@ -0,0 +1,127 @@
1
+ module ActionView #:nodoc:
2
+ class Template #:nodoc:
3
+
4
+ attr_accessor :locals
5
+ attr_reader :handler, :path, :extension, :filename, :path_without_extension, :method
6
+
7
+ def initialize(view, path, use_full_path, locals = {})
8
+ @view = view
9
+ @finder = @view.finder
10
+
11
+ # Clear the forward slash at the beginning if exists
12
+ @path = use_full_path ? path.sub(/^\//, '') : path
13
+ @view.first_render ||= @path
14
+ @source = nil # Don't read the source until we know that it is required
15
+ set_extension_and_file_name(use_full_path)
16
+
17
+ @locals = locals || {}
18
+ @handler = self.class.handler_class_for_extension(@extension).new(@view)
19
+ end
20
+
21
+ def render_template
22
+ render
23
+ rescue Exception => e
24
+ raise e unless filename
25
+ if TemplateError === e
26
+ e.sub_template_of(filename)
27
+ raise e
28
+ else
29
+ raise TemplateError.new(self, @view.assigns, e)
30
+ end
31
+ end
32
+
33
+ def render
34
+ prepare!
35
+ @handler.render(self)
36
+ end
37
+
38
+ def source
39
+ @source ||= File.read(self.filename)
40
+ end
41
+
42
+ def method_key
43
+ @filename
44
+ end
45
+
46
+ def base_path_for_exception
47
+ @finder.find_base_path_for("#{@path_without_extension}.#{@extension}") || @finder.view_paths.first
48
+ end
49
+
50
+ def prepare!
51
+ @view.send :evaluate_assigns
52
+ @view.current_render_extension = @extension
53
+
54
+ if @handler.compilable?
55
+ @handler.compile_template(self) # compile the given template, if necessary
56
+ @method = @view.method_names[method_key] # Set the method name for this template and run it
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def set_extension_and_file_name(use_full_path)
63
+ @path_without_extension, @extension = @finder.path_and_extension(@path)
64
+ if use_full_path
65
+ if @extension
66
+ @filename = @finder.pick_template(@path_without_extension, @extension)
67
+ else
68
+ @extension = @finder.pick_template_extension(@path).to_s
69
+ raise_missing_template_exception unless @extension
70
+
71
+ @filename = @finder.pick_template(@path, @extension)
72
+ @extension = @extension.gsub(/^.+\./, '') # strip off any formats
73
+ end
74
+ else
75
+ @filename = @path
76
+ end
77
+
78
+ raise_missing_template_exception if @filename.blank?
79
+ end
80
+
81
+ def raise_missing_template_exception
82
+ full_template_path = @path.include?('.') ? @path : "#{@path}.#{@view.template_format}.erb"
83
+ display_paths = @finder.view_paths.join(':')
84
+ template_type = (@path =~ /layouts/i) ? 'layout' : 'template'
85
+ raise(MissingTemplate, "Missing #{template_type} #{full_template_path} in view path #{display_paths}")
86
+ end
87
+
88
+ # Template Handlers
89
+
90
+ @@template_handlers = HashWithIndifferentAccess.new
91
+ @@default_template_handlers = nil
92
+
93
+ # Register a class that knows how to handle template files with the given
94
+ # extension. This can be used to implement new template types.
95
+ # The constructor for the class must take the ActiveView::Base instance
96
+ # as a parameter, and the class must implement a +render+ method that
97
+ # takes the contents of the template to render as well as the Hash of
98
+ # local assigns available to the template. The +render+ method ought to
99
+ # return the rendered template as a string.
100
+ def self.register_template_handler(extension, klass)
101
+ @@template_handlers[extension.to_sym] = klass
102
+ TemplateFinder.update_extension_cache_for(extension.to_s)
103
+ end
104
+
105
+ def self.template_handler_extensions
106
+ @@template_handlers.keys.map(&:to_s).sort
107
+ end
108
+
109
+ def self.register_default_template_handler(extension, klass)
110
+ register_template_handler(extension, klass)
111
+ @@default_template_handlers = klass
112
+ end
113
+
114
+ def self.handler_class_for_extension(extension)
115
+ (extension && @@template_handlers[extension.to_sym]) || @@default_template_handlers
116
+ end
117
+
118
+ register_default_template_handler :erb, TemplateHandlers::ERB
119
+ register_template_handler :rjs, TemplateHandlers::RJS
120
+ register_template_handler :builder, TemplateHandlers::Builder
121
+
122
+ # TODO: Depreciate old template extensions
123
+ register_template_handler :rhtml, TemplateHandlers::ERB
124
+ register_template_handler :rxml, TemplateHandlers::Builder
125
+
126
+ end
127
+ end