actionpack 2.0.5 → 2.1.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 (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
@@ -8,47 +8,55 @@ module ActionView
8
8
  end
9
9
 
10
10
  module Helpers
11
- # The Active Record Helper makes it easier to create forms for records kept in instance variables. The most far-reaching is the form
11
+ # The Active Record Helper makes it easier to create forms for records kept in instance variables. The most far-reaching is the +form+
12
12
  # method that creates a complete form for all the basic content types of the record (not associations or aggregations, though). This
13
13
  # is a great way of making the record quickly available for editing, but likely to prove lackluster for a complicated real-world form.
14
- # In that case, it's better to use the input method and the specialized form methods in link:classes/ActionView/Helpers/FormHelper.html
14
+ # In that case, it's better to use the +input+ method and the specialized +form+ methods in link:classes/ActionView/Helpers/FormHelper.html
15
15
  module ActiveRecordHelper
16
- # Returns a default input tag for the type of object returned by the method. For example, let's say you have a model
17
- # that has an attribute +title+ of type VARCHAR column, and this instance holds "Hello World":
18
- # input("post", "title") =>
19
- # <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
16
+ # Returns a default input tag for the type of object returned by the method. For example, if <tt>@post</tt>
17
+ # has an attribute +title+ mapped to a +VARCHAR+ column that holds "Hello World":
18
+ #
19
+ # input("post", "title")
20
+ # # => <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
20
21
  def input(record_name, method, options = {})
21
22
  InstanceTag.new(record_name, method, self).to_tag(options)
22
23
  end
23
24
 
24
- # Returns an entire form with all needed input tags for a specified Active Record object. For example, let's say you
25
- # have a table model <tt>Post</tt> with attributes named <tt>title</tt> of type <tt>VARCHAR</tt> and <tt>body</tt> of type <tt>TEXT</tt>:
25
+ # Returns an entire form with all needed input tags for a specified Active Record object. For example, if <tt>@post</tt>
26
+ # has attributes named +title+ of type +VARCHAR+ and +body+ of type +TEXT+ then
27
+ #
26
28
  # form("post")
27
- # That line would yield a form like the following:
28
- # <form action='/post/create' method='post'>
29
- # <p>
30
- # <label for="post_title">Title</label><br />
31
- # <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
32
- # </p>
33
- # <p>
34
- # <label for="post_body">Body</label><br />
35
- # <textarea cols="40" id="post_body" name="post[body]" rows="20">
36
- # </textarea>
37
- # </p>
38
- # <input type='submit' value='Create' />
39
- # </form>
29
+ #
30
+ # would yield a form like the following (modulus formatting):
31
+ #
32
+ # <form action='/posts/create' method='post'>
33
+ # <p>
34
+ # <label for="post_title">Title</label><br />
35
+ # <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
36
+ # </p>
37
+ # <p>
38
+ # <label for="post_body">Body</label><br />
39
+ # <textarea cols="40" id="post_body" name="post[body]" rows="20"></textarea>
40
+ # </p>
41
+ # <input name="commit" type="submit" value="Create" />
42
+ # </form>
40
43
  #
41
44
  # It's possible to specialize the form builder by using a different action name and by supplying another
42
- # block renderer. For example, let's say you have a model <tt>Entry</tt> with an attribute <tt>message</tt> of type <tt>VARCHAR</tt>:
45
+ # block renderer. For example, if <tt>@entry</tt> has an attribute +message+ of type +VARCHAR+ then
43
46
  #
44
- # form("entry", :action => "sign", :input_block =>
45
- # Proc.new { |record, column| "#{column.human_name}: #{input(record, column.name)}<br />" }) =>
47
+ # form("entry",
48
+ # :action => "sign",
49
+ # :input_block => Proc.new { |record, column|
50
+ # "#{column.human_name}: #{input(record, column.name)}<br />"
51
+ # })
46
52
  #
47
- # <form action='/post/sign' method='post'>
48
- # Message:
49
- # <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /><br />
50
- # <input type='submit' value='Sign' />
51
- # </form>
53
+ # would yield a form like the following (modulus formatting):
54
+ #
55
+ # <form action="/entries/sign" method="post">
56
+ # Message:
57
+ # <input id="entry_message" name="entry[message]" size="30" type="text" /><br />
58
+ # <input name="commit" type="submit" value="Sign" />
59
+ # </form>
52
60
  #
53
61
  # It's also possible to add additional content to the form by giving it a block, such as:
54
62
  #
@@ -56,6 +64,14 @@ module ActionView
56
64
  # form << content_tag("b", "Department")
57
65
  # form << collection_select("department", "id", @departments, "id", "name")
58
66
  # end
67
+ #
68
+ # The following options are available:
69
+ #
70
+ # * <tt>:action</tt> - The action used when submitting the form (default: +create+ if a new record, otherwise +update+).
71
+ # * <tt>:input_block</tt> - Specialize the output using a different block, see above.
72
+ # * <tt>:method</tt> - The method used when submitting the form (default: +post+).
73
+ # * <tt>:multipart</tt> - Whether to change the enctype of the form to "multipart/form-data", used when uploading a file (default: +false+).
74
+ # * <tt>:submit_value</tt> - The text of the submit button (default: "Create" if a new record, otherwise "Update").
59
75
  def form(record_name, options = {})
60
76
  record = instance_variable_get("@#{record_name}")
61
77
 
@@ -65,29 +81,27 @@ module ActionView
65
81
 
66
82
  submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize
67
83
 
68
- contents = ''
84
+ contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil)
69
85
  contents << hidden_field(record_name, :id) unless record.new_record?
70
86
  contents << all_input_tags(record, record_name, options)
71
87
  yield contents if block_given?
72
88
  contents << submit_tag(submit_value)
73
-
74
- content_tag('form', contents, :action => action, :method => 'post', :enctype => options[:multipart] ? 'multipart/form-data': nil)
89
+ contents << '</form>'
75
90
  end
76
91
 
77
92
  # Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
78
93
  # This error message is wrapped in a <tt>DIV</tt> tag, which can be extended to include a +prepend_text+ and/or +append_text+
79
94
  # (to properly explain the error), and a +css_class+ to style it accordingly. +object+ should either be the name of an instance variable or
80
- # the actual object. As an example, let's say you have a model
81
- # +post+ that has an error message on the +title+ attribute:
95
+ # the actual object. As an example, let's say you have a model <tt>@post</tt> that has an error message on the +title+ attribute:
82
96
  #
83
- # <%= error_message_on "post", "title" %> =>
84
- # <div class="formError">can't be empty</div>
97
+ # <%= error_message_on "post", "title" %>
98
+ # # => <div class="formError">can't be empty</div>
85
99
  #
86
- # <%= error_message_on @post, "title" %> =>
87
- # <div class="formError">can't be empty</div>
100
+ # <%= error_message_on @post, "title" %>
101
+ # # => <div class="formError">can't be empty</div>
88
102
  #
89
- # <%= error_message_on "post", "title", "Title simply ", " (or it won't work).", "inputError" %> =>
90
- # <div class="inputError">Title simply can't be empty (or it won't work).</div>
103
+ # <%= error_message_on "post", "title", "Title simply ", " (or it won't work).", "inputError" %>
104
+ # # => <div class="inputError">Title simply can't be empty (or it won't work).</div>
91
105
  def error_message_on(object, method, prepend_text = "", append_text = "", css_class = "formError")
92
106
  if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
93
107
  (errors = obj.errors.on(method))
@@ -103,30 +117,37 @@ module ActionView
103
117
  #
104
118
  # This <tt>DIV</tt> can be tailored by the following options:
105
119
  #
106
- # * <tt>header_tag</tt> - Used for the header of the error div (default: h2)
107
- # * <tt>id</tt> - The id of the error div (default: errorExplanation)
108
- # * <tt>class</tt> - The class of the error div (default: errorExplanation)
109
- # * <tt>object</tt> - The object (or array of objects) for which to display errors, if you need to escape the instance variable convention
110
- # * <tt>object_name</tt> - The object name to use in the header, or any text that you prefer. If <tt>object_name</tt> is not set, the name of the first object will be used.
111
- # * <tt>header_message</tt> - The message in the header of the error div. Pass +nil+ or an empty string to avoid the header message altogether. (default: X errors prohibited this object from being saved)
112
- # * <tt>message</tt> - The explanation message after the header message and before the error list. Pass +nil+ or an empty string to avoid the explanation message altogether. (default: There were problems with the following fields:)
120
+ # * <tt>:header_tag</tt> - Used for the header of the error div (default: "h2").
121
+ # * <tt>:id</tt> - The id of the error div (default: "errorExplanation").
122
+ # * <tt>:class</tt> - The class of the error div (default: "errorExplanation").
123
+ # * <tt>:object</tt> - The object (or array of objects) for which to display errors,
124
+ # if you need to escape the instance variable convention.
125
+ # * <tt>:object_name</tt> - The object name to use in the header, or any text that you prefer.
126
+ # If <tt>:object_name</tt> is not set, the name of the first object will be used.
127
+ # * <tt>:header_message</tt> - The message in the header of the error div. Pass +nil+
128
+ # or an empty string to avoid the header message altogether. (Default: "X errors
129
+ # prohibited this object from being saved").
130
+ # * <tt>:message</tt> - The explanation message after the header message and before
131
+ # the error list. Pass +nil+ or an empty string to avoid the explanation message
132
+ # altogether. (Default: "There were problems with the following fields:").
113
133
  #
114
- # To specify the display for one object, you simply provide its name as a parameter. For example, for the +User+ model:
134
+ # To specify the display for one object, you simply provide its name as a parameter.
135
+ # For example, for the <tt>@user</tt> model:
115
136
  #
116
137
  # error_messages_for 'user'
117
138
  #
118
- # To specify more than one object, you simply list them; optionally, you can add an extra +object_name+ parameter, which
119
- # will be the name used in the header message.
139
+ # To specify more than one object, you simply list them; optionally, you can add an extra <tt>:object_name</tt> parameter, which
140
+ # will be the name used in the header message:
120
141
  #
121
142
  # error_messages_for 'user_common', 'user', :object_name => 'user'
122
143
  #
123
- # If the objects cannot be located as instance variables, you can add an extra +object+ paremeter which gives the actual
124
- # object (or array of objects to use)
144
+ # If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> paremeter which gives the actual
145
+ # object (or array of objects to use):
125
146
  #
126
147
  # error_messages_for 'user', :object => @question.user
127
148
  #
128
149
  # NOTE: This is a pre-packaged presentation of the errors with embedded strings and a certain HTML structure. If what
129
- # you need is significantly different from the default presentation, it makes plenty of sense to access the object.errors
150
+ # you need is significantly different from the default presentation, it makes plenty of sense to access the <tt>object.errors</tt>
130
151
  # instance yourself and set it up. View the source of this method to see how easy it is.
131
152
  def error_messages_for(*params)
132
153
  options = params.extract_options!.symbolize_keys
@@ -213,29 +234,29 @@ module ActionView
213
234
  end
214
235
 
215
236
  alias_method :to_date_select_tag_without_error_wrapping, :to_date_select_tag
216
- def to_date_select_tag(options = {})
237
+ def to_date_select_tag(options = {}, html_options = {})
217
238
  if object.respond_to?("errors") && object.errors.respond_to?("on")
218
- error_wrapping(to_date_select_tag_without_error_wrapping(options), object.errors.on(@method_name))
239
+ error_wrapping(to_date_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name))
219
240
  else
220
- to_date_select_tag_without_error_wrapping(options)
241
+ to_date_select_tag_without_error_wrapping(options, html_options)
221
242
  end
222
243
  end
223
244
 
224
245
  alias_method :to_datetime_select_tag_without_error_wrapping, :to_datetime_select_tag
225
- def to_datetime_select_tag(options = {})
246
+ def to_datetime_select_tag(options = {}, html_options = {})
226
247
  if object.respond_to?("errors") && object.errors.respond_to?("on")
227
- error_wrapping(to_datetime_select_tag_without_error_wrapping(options), object.errors.on(@method_name))
248
+ error_wrapping(to_datetime_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name))
228
249
  else
229
- to_datetime_select_tag_without_error_wrapping(options)
250
+ to_datetime_select_tag_without_error_wrapping(options, html_options)
230
251
  end
231
252
  end
232
253
 
233
254
  alias_method :to_time_select_tag_without_error_wrapping, :to_time_select_tag
234
- def to_time_select_tag(options = {})
255
+ def to_time_select_tag(options = {}, html_options = {})
235
256
  if object.respond_to?("errors") && object.errors.respond_to?("on")
236
- error_wrapping(to_time_select_tag_without_error_wrapping(options), object.errors.on(@method_name))
257
+ error_wrapping(to_time_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name))
237
258
  else
238
- to_time_select_tag_without_error_wrapping(options)
259
+ to_time_select_tag_without_error_wrapping(options, html_options)
239
260
  end
240
261
  end
241
262
 
@@ -11,24 +11,24 @@ module ActionView
11
11
  # === Using asset hosts
12
12
  # By default, Rails links to these assets on the current host in the public
13
13
  # folder, but you can direct Rails to link to assets from a dedicated assets server by
14
- # setting ActionController::Base.asset_host in your environment.rb. For example,
15
- # let's say your asset host is assets.example.com.
14
+ # setting ActionController::Base.asset_host in your <tt>config/environment.rb</tt>. For example,
15
+ # let's say your asset host is <tt>assets.example.com</tt>.
16
16
  #
17
17
  # ActionController::Base.asset_host = "assets.example.com"
18
18
  # image_tag("rails.png")
19
19
  # => <img src="http://assets.example.com/images/rails.png" alt="Rails" />
20
- # stylesheet_include_tag("application")
20
+ # stylesheet_link_tag("application")
21
21
  # => <link href="http://assets.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
22
22
  #
23
23
  # This is useful since browsers typically open at most two connections to a single host,
24
24
  # which means your assets often wait in single file for their turn to load. You can
25
- # alleviate this by using a %d wildcard in <tt>asset_host</tt> (for example, "assets%d.example.com")
26
- # to automatically distribute asset requests among four hosts (e.g., assets0.example.com through assets3.example.com)
25
+ # alleviate this by using a <tt>%d</tt> wildcard in <tt>asset_host</tt> (for example, "assets%d.example.com")
26
+ # to automatically distribute asset requests among four hosts (e.g., "assets0.example.com" through "assets3.example.com")
27
27
  # so browsers will open eight connections rather than two.
28
28
  #
29
29
  # image_tag("rails.png")
30
30
  # => <img src="http://assets0.example.com/images/rails.png" alt="Rails" />
31
- # stylesheet_include_tag("application")
31
+ # stylesheet_link_tag("application")
32
32
  # => <link href="http://assets3.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
33
33
  #
34
34
  # To do this, you can either setup 4 actual hosts, or you can use wildcard DNS to CNAME
@@ -47,11 +47,13 @@ module ActionView
47
47
  # ActionController::Base.asset_host = Proc.new { |source| "http://assets#{rand(2) + 1}.example.com" }
48
48
  # image_tag("rails.png")
49
49
  # => <img src="http://assets2.example.com/images/rails.png" alt="Rails" />
50
- # stylesheet_include_tag("application")
50
+ # stylesheet_link_tag("application")
51
51
  # => <link href="http://assets1.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
52
52
  #
53
- # The proc takes a single <tt>source</tt> parameter which is the path of the source asset. This can be used to
54
- # generate a particular asset host depending on the asset path.
53
+ # The proc takes a <tt>source</tt> parameter (which is the path of the source asset) and an optional
54
+ # <tt>request</tt> parameter (which is an entire instance of an <tt>ActionController::AbstractRequest</tt>
55
+ # subclass). This can be used to generate a particular asset host depending on the asset path and the particular
56
+ # request.
55
57
  #
56
58
  # ActionController::Base.asset_host = Proc.new { |source|
57
59
  # if source.starts_with?('/images')
@@ -62,9 +64,22 @@ module ActionView
62
64
  # }
63
65
  # image_tag("rails.png")
64
66
  # => <img src="http://images.example.com/images/rails.png" alt="Rails" />
65
- # stylesheet_include_tag("application")
67
+ # stylesheet_link_tag("application")
66
68
  # => <link href="http://assets.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
67
69
  #
70
+ # The optional <tt>request</tt> parameter to the proc is useful in particular for serving assets from an
71
+ # SSL-protected page. The example proc below disables asset hosting for HTTPS connections, while still sending
72
+ # assets for plain HTTP requests from asset hosts. This is useful for avoiding mixed media warnings when serving
73
+ # non-HTTP assets from HTTPS web pages when you don't have an SSL certificate for each of the asset hosts.
74
+ #
75
+ # ActionController::Base.asset_host = Proc.new { |source, request|
76
+ # if request.ssl?
77
+ # "#{request.protocol}#{request.host_with_port}"
78
+ # else
79
+ # "#{request.protocol}assets.example.com"
80
+ # end
81
+ # }
82
+ #
68
83
  # === Using asset timestamps
69
84
  #
70
85
  # By default, Rails will append all asset paths with that asset's timestamp. This allows you to set a cache-expiration date for the
@@ -86,7 +101,7 @@ module ActionView
86
101
  # something like Live HTTP Headers for Firefox to verify that the cache is indeed working (and that the assets are not being
87
102
  # requested over and over).
88
103
  module AssetTagHelper
89
- ASSETS_DIR = defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/public" : "public"
104
+ ASSETS_DIR = defined?(Rails.public_path) ? Rails.public_path : "public"
90
105
  JAVASCRIPTS_DIR = "#{ASSETS_DIR}/javascripts"
91
106
  STYLESHEETS_DIR = "#{ASSETS_DIR}/stylesheets"
92
107
 
@@ -140,7 +155,8 @@ module ActionView
140
155
  alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
141
156
 
142
157
  JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'] unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
143
- @@javascript_default_sources = JAVASCRIPT_DEFAULT_SOURCES.dup
158
+ @@javascript_expansions = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup }
159
+ @@stylesheet_expansions = {}
144
160
 
145
161
  # Returns an html script tag for each of the +sources+ provided. You
146
162
  # can pass in the filename (.js extension is optional) of javascript files
@@ -148,7 +164,7 @@ module ActionView
148
164
  # current page or you can pass the full path relative to your document
149
165
  # root. To include the Prototype and Scriptaculous javascript libraries in
150
166
  # your application, pass <tt>:defaults</tt> as the source. When using
151
- # :defaults, if an <tt>application.js</tt> file exists in your public
167
+ # <tt>:defaults</tt>, if an application.js file exists in your public
152
168
  # javascripts directory, it will be included as well. You can modify the
153
169
  # html attributes of the script tag by passing a hash as the last argument.
154
170
  #
@@ -234,22 +250,52 @@ module ActionView
234
250
  end
235
251
  end
236
252
 
253
+ # Register one or more javascript files to be included when <tt>symbol</tt>
254
+ # is passed to <tt>javascript_include_tag</tt>. This method is typically intended
255
+ # to be called from plugin initialization to register javascript files
256
+ # that the plugin installed in <tt>public/javascripts</tt>.
257
+ #
258
+ # ActionView::Helpers::AssetTagHelper.register_javascript_expansion :monkey => ["head", "body", "tail"]
259
+ #
260
+ # javascript_include_tag :monkey # =>
261
+ # <script type="text/javascript" src="/javascripts/head.js"></script>
262
+ # <script type="text/javascript" src="/javascripts/body.js"></script>
263
+ # <script type="text/javascript" src="/javascripts/tail.js"></script>
264
+ def self.register_javascript_expansion(expansions)
265
+ @@javascript_expansions.merge!(expansions)
266
+ end
267
+
268
+ # Register one or more stylesheet files to be included when <tt>symbol</tt>
269
+ # is passed to <tt>stylesheet_link_tag</tt>. This method is typically intended
270
+ # to be called from plugin initialization to register stylesheet files
271
+ # that the plugin installed in <tt>public/stylesheets</tt>.
272
+ #
273
+ # ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion :monkey => ["head", "body", "tail"]
274
+ #
275
+ # stylesheet_link_tag :monkey # =>
276
+ # <link href="/stylesheets/head.css" media="screen" rel="stylesheet" type="text/css" />
277
+ # <link href="/stylesheets/body.css" media="screen" rel="stylesheet" type="text/css" />
278
+ # <link href="/stylesheets/tail.css" media="screen" rel="stylesheet" type="text/css" />
279
+ def self.register_stylesheet_expansion(expansions)
280
+ @@stylesheet_expansions.merge!(expansions)
281
+ end
282
+
237
283
  # Register one or more additional JavaScript files to be included when
238
284
  # <tt>javascript_include_tag :defaults</tt> is called. This method is
239
285
  # typically intended to be called from plugin initialization to register additional
240
286
  # .js files that the plugin installed in <tt>public/javascripts</tt>.
241
287
  def self.register_javascript_include_default(*sources)
242
- @@javascript_default_sources.concat(sources)
288
+ @@javascript_expansions[:defaults].concat(sources)
243
289
  end
244
290
 
245
291
  def self.reset_javascript_include_default #:nodoc:
246
- @@javascript_default_sources = JAVASCRIPT_DEFAULT_SOURCES.dup
292
+ @@javascript_expansions[:defaults] = JAVASCRIPT_DEFAULT_SOURCES.dup
247
293
  end
248
294
 
249
295
  # Computes the path to a stylesheet asset in the public stylesheets directory.
250
- # If the +source+ filename has no extension, .css will be appended.
296
+ # If the +source+ filename has no extension, <tt>.css</tt> will be appended.
251
297
  # Full paths from the document root will be passed through.
252
- # Used internally by stylesheet_link_tag to build the stylesheet path.
298
+ # Used internally by +stylesheet_link_tag+ to build the stylesheet path.
253
299
  #
254
300
  # ==== Examples
255
301
  # stylesheet_path "style" # => /stylesheets/style.css
@@ -263,7 +309,7 @@ module ActionView
263
309
  alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
264
310
 
265
311
  # Returns a stylesheet link tag for the sources specified as arguments. If
266
- # you don't specify an extension, .css will be appended automatically.
312
+ # you don't specify an extension, <tt>.css</tt> will be appended automatically.
267
313
  # You can modify the link attributes by passing a hash as the last argument.
268
314
  #
269
315
  # ==== Examples
@@ -286,7 +332,7 @@ module ActionView
286
332
  # <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" type="text/css" />
287
333
  # <link href="/css/stylish.css" media="screen" rel="stylesheet" type="text/css" />
288
334
  #
289
- # You can also include all styles in the stylesheet directory using :all as the source:
335
+ # You can also include all styles in the stylesheet directory using <tt>:all</tt> as the source:
290
336
  #
291
337
  # stylesheet_link_tag :all # =>
292
338
  # <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" type="text/css" />
@@ -333,7 +379,7 @@ module ActionView
333
379
 
334
380
  # Computes the path to an image asset in the public images directory.
335
381
  # Full paths from the document root will be passed through.
336
- # Used internally by image_tag to build the image path.
382
+ # Used internally by +image_tag+ to build the image path.
337
383
  #
338
384
  # ==== Examples
339
385
  # image_path("edit") # => /images/edit
@@ -383,7 +429,7 @@ module ActionView
383
429
  options.symbolize_keys!
384
430
 
385
431
  options[:src] = path_to_image(source)
386
- options[:alt] ||= File.basename(options[:src], '.*').split('.').first.capitalize
432
+ options[:alt] ||= File.basename(options[:src], '.*').split('.').first.to_s.capitalize
387
433
 
388
434
  if size = options.delete(:size)
389
435
  options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
@@ -408,8 +454,8 @@ module ActionView
408
454
  end
409
455
  end
410
456
 
411
- # Add the .ext if not present. Return full URLs otherwise untouched.
412
- # Prefix with /dir/ if lacking a leading /. Account for relative URL
457
+ # Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
458
+ # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
413
459
  # roots. Rewrite the asset path for cache-busting asset ids. Include
414
460
  # asset host, if configured, with the correct request protocol.
415
461
  def compute_public_path(source, dir, ext = nil, include_host = true)
@@ -428,7 +474,7 @@ module ActionView
428
474
 
429
475
  ActionView::Base.computed_public_paths[cache_key] ||=
430
476
  begin
431
- source += ".#{ext}" if File.extname(source).blank? && ext
477
+ source += ".#{ext}" if ext && File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))
432
478
 
433
479
  if source =~ %r{^[-a-z]+://}
434
480
  source
@@ -456,16 +502,21 @@ module ActionView
456
502
  end
457
503
  end
458
504
 
459
- # Pick an asset host for this source. Returns nil if no host is set,
505
+ # Pick an asset host for this source. Returns +nil+ if no host is set,
460
506
  # the host if no wildcard is set, the host interpolated with the
461
- # numbers 0-3 if it contains %d (the number is the source hash mod 4),
507
+ # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
462
508
  # or the value returned from invoking the proc if it's a proc.
463
509
  def compute_asset_host(source)
464
510
  if host = ActionController::Base.asset_host
465
511
  if host.is_a?(Proc)
466
- host.call(source)
512
+ case host.arity
513
+ when 2
514
+ host.call(source, @controller.request)
515
+ else
516
+ host.call(source)
517
+ end
467
518
  else
468
- host % (source.hash % 4)
519
+ (host =~ /%d/) ? host % (source.hash % 4) : host
469
520
  end
470
521
  end
471
522
  end
@@ -514,28 +565,34 @@ module ActionView
514
565
  end
515
566
 
516
567
  def expand_javascript_sources(sources)
517
- case
518
- when sources.include?(:all)
519
- all_javascript_files = Dir[File.join(JAVASCRIPTS_DIR, '*.js')].collect { |file| File.basename(file).split(".", 0).first }.sort
520
- sources = ((@@javascript_default_sources.dup & all_javascript_files) + all_javascript_files).uniq
521
-
522
- when sources.include?(:defaults)
523
- sources = sources[0..(sources.index(:defaults))] +
524
- @@javascript_default_sources.dup +
525
- sources[(sources.index(:defaults) + 1)..sources.length]
526
-
527
- sources.delete(:defaults)
528
- sources << "application" if file_exist?(File.join(JAVASCRIPTS_DIR, "application.js"))
568
+ if sources.include?(:all)
569
+ all_javascript_files = Dir[File.join(JAVASCRIPTS_DIR, '*.js')].collect { |file| File.basename(file).gsub(/\.\w+$/, '') }.sort
570
+ @@all_javascript_sources ||= ((determine_source(:defaults, @@javascript_expansions).dup & all_javascript_files) + all_javascript_files).uniq
571
+ else
572
+ expanded_sources = sources.collect do |source|
573
+ determine_source(source, @@javascript_expansions)
574
+ end.flatten
575
+ expanded_sources << "application" if sources.include?(:defaults) && file_exist?(File.join(JAVASCRIPTS_DIR, "application.js"))
576
+ expanded_sources
529
577
  end
530
-
531
- sources
532
578
  end
533
579
 
534
580
  def expand_stylesheet_sources(sources)
535
581
  if sources.first == :all
536
- @@all_stylesheet_sources ||= Dir[File.join(STYLESHEETS_DIR, '*.css')].collect { |file| File.basename(file).split(".", 1).first }.sort
582
+ @@all_stylesheet_sources ||= Dir[File.join(STYLESHEETS_DIR, '*.css')].collect { |file| File.basename(file).gsub(/\.\w+$/, '') }.sort
537
583
  else
538
- sources
584
+ sources.collect do |source|
585
+ determine_source(source, @@stylesheet_expansions)
586
+ end.flatten
587
+ end
588
+ end
589
+
590
+ def determine_source(source, collection)
591
+ case source
592
+ when Symbol
593
+ collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
594
+ else
595
+ source
539
596
  end
540
597
  end
541
598