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
@@ -26,17 +26,17 @@ module ActionView
26
26
  # :url => { :action => "reload" },
27
27
  # :complete => visual_effect(:highlight, "posts", :duration => 0.5)
28
28
  #
29
- # If no element_id is given, it assumes "element" which should be a local
29
+ # If no +element_id+ is given, it assumes "element" which should be a local
30
30
  # variable in the generated JavaScript execution context. This can be
31
- # used for example with drop_receiving_element:
31
+ # used for example with +drop_receiving_element+:
32
32
  #
33
33
  # <%= drop_receiving_element (...), :loading => visual_effect(:fade) %>
34
34
  #
35
35
  # This would fade the element that was dropped on the drop receiving
36
36
  # element.
37
37
  #
38
- # For toggling visual effects, you can use :toggle_appear, :toggle_slide, and
39
- # :toggle_blind which will alternate between appear/fade, slidedown/slideup, and
38
+ # For toggling visual effects, you can use <tt>:toggle_appear</tt>, <tt>:toggle_slide</tt>, and
39
+ # <tt>:toggle_blind</tt> which will alternate between appear/fade, slidedown/slideup, and
40
40
  # blinddown/blindup respectively.
41
41
  #
42
42
  # You can change the behaviour with various options, see
@@ -67,6 +67,7 @@ module ActionView
67
67
  # element as parameters.
68
68
  #
69
69
  # Example:
70
+ #
70
71
  # <%= sortable_element("my_list", :url => { :action => "order" }) %>
71
72
  #
72
73
  # In the example, the action gets a "my_list" array parameter
@@ -79,60 +80,56 @@ module ActionView
79
80
  #
80
81
  # Additional +options+ are:
81
82
  #
82
- # <tt>:format</tt>:: A regular expression to determine what to send
83
- # as the serialized id to the server (the default
84
- # is <tt>/^[^_]*_(.*)$/</tt>).
85
- #
86
- # <tt>:constraint</tt>:: Whether to constrain the dragging to either <tt>:horizontal</tt>
87
- # or <tt>:vertical</tt> (or false to make it unconstrained).
88
- #
89
- # <tt>:overlap</tt>:: Calculate the item overlap in the <tt>:horizontal</tt> or
90
- # <tt>:vertical</tt> direction.
91
- #
92
- # <tt>:tag</tt>:: Which children of the container element to treat as
93
- # sortable (default is <tt>li</tt>).
94
- #
95
- # <tt>:containment</tt>:: Takes an element or array of elements to treat as
96
- # potential drop targets (defaults to the original
97
- # target element).
98
- #
99
- # <tt>:only</tt>:: A CSS class name or arry of class names used to filter
100
- # out child elements as candidates.
101
- #
102
- # <tt>:scroll</tt>:: Determines whether to scroll the list during drag
103
- # operationsif the list runs past the visual border.
104
- #
105
- # <tt>:tree</tt>:: Determines whether to treat nested lists as part of the
106
- # main sortable list. This means that you can create multi-
107
- # layer lists, and not only sort items at the same level,
108
- # but drag and sort items between levels.
109
- #
110
- # <tt>:hoverclass</tt>:: If set, the Droppable will have this additional CSS class
111
- # when an accepted Draggable is hovered over it.
112
- #
113
- # <tt>:handle</tt>:: Sets whether the element should only be draggable by an
114
- # embedded handle. The value may be a string referencing a
115
- # CSS class value (as of script.aculo.us V1.5). The first
116
- # child/grandchild/etc. element found within the element
117
- # that has this CSS class value will be used as the handle.
118
- #
119
- # <tt>:ghosting</tt>:: Clones the element and drags the clone, leaving the original
120
- # in place until the clone is dropped (defaut is <tt>false</tt>).
121
- #
122
- # <tt>:dropOnEmpty</tt>:: If set to true, the Sortable container will be made into
123
- # a Droppable, that can receive a Draggable (as according to
124
- # the containment rules) as a child element when there are no
125
- # more elements inside (defaut is <tt>false</tt>).
126
- #
127
- # <tt>:onChange</tt>:: Called whenever the sort order changes while dragging. When
128
- # dragging from one Sortable to another, the callback is
129
- # called once on each Sortable. Gets the affected element as
130
- # its parameter.
131
- #
132
- # <tt>:onUpdate</tt>:: Called when the drag ends and the Sortable's order is
133
- # changed in any way. When dragging from one Sortable to
134
- # another, the callback is called once on each Sortable. Gets
135
- # the container as its parameter.
83
+ # * <tt>:format</tt> - A regular expression to determine what to send as the
84
+ # serialized id to the server (the default is <tt>/^[^_]*_(.*)$/</tt>).
85
+ #
86
+ # * <tt>:constraint</tt> - Whether to constrain the dragging to either
87
+ # <tt>:horizontal</tt> or <tt>:vertical</tt> (or false to make it unconstrained).
88
+ #
89
+ # * <tt>:overlap</tt> - Calculate the item overlap in the <tt>:horizontal</tt>
90
+ # or <tt>:vertical</tt> direction.
91
+ #
92
+ # * <tt>:tag</tt> - Which children of the container element to treat as
93
+ # sortable (default is <tt>li</tt>).
94
+ #
95
+ # * <tt>:containment</tt> - Takes an element or array of elements to treat as
96
+ # potential drop targets (defaults to the original target element).
97
+ #
98
+ # * <tt>:only</tt> - A CSS class name or arry of class names used to filter
99
+ # out child elements as candidates.
100
+ #
101
+ # * <tt>:scroll</tt> - Determines whether to scroll the list during drag
102
+ # operations if the list runs past the visual border.
103
+ #
104
+ # * <tt>:tree</tt> - Determines whether to treat nested lists as part of the
105
+ # main sortable list. This means that you can create multi-layer lists,
106
+ # and not only sort items at the same level, but drag and sort items
107
+ # between levels.
108
+ #
109
+ # * <tt>:hoverclass</tt> - If set, the Droppable will have this additional CSS class
110
+ # when an accepted Draggable is hovered over it.
111
+ #
112
+ # * <tt>:handle</tt> - Sets whether the element should only be draggable by an
113
+ # embedded handle. The value may be a string referencing a CSS class value
114
+ # (as of script.aculo.us V1.5). The first child/grandchild/etc. element
115
+ # found within the element that has this CSS class value will be used as
116
+ # the handle.
117
+ #
118
+ # * <tt>:ghosting</tt> - Clones the element and drags the clone, leaving
119
+ # the original in place until the clone is dropped (default is <tt>false</tt>).
120
+ #
121
+ # * <tt>:dropOnEmpty</tt> - If true the Sortable container will be made into
122
+ # a Droppable, that can receive a Draggable (as according to the containment
123
+ # rules) as a child element when there are no more elements inside (default
124
+ # is <tt>false</tt>).
125
+ #
126
+ # * <tt>:onChange</tt> - Called whenever the sort order changes while dragging. When
127
+ # dragging from one Sortable to another, the callback is called once on each
128
+ # Sortable. Gets the affected element as its parameter.
129
+ #
130
+ # * <tt>:onUpdate</tt> - Called when the drag ends and the Sortable's order is
131
+ # changed in any way. When dragging from one Sortable to another, the callback
132
+ # is called once on each Sortable. Gets the container as its parameter.
136
133
  #
137
134
  # See http://script.aculo.us for more documentation.
138
135
  def sortable_element(element_id, options = {})
@@ -170,8 +167,8 @@ module ActionView
170
167
  end
171
168
 
172
169
  # Makes the element with the DOM ID specified by +element_id+ receive
173
- # dropped draggable elements (created by draggable_element).
174
- # and make an AJAX call By default, the action called gets the DOM ID
170
+ # dropped draggable elements (created by +draggable_element+).
171
+ # and make an AJAX call. By default, the action called gets the DOM ID
175
172
  # of the element as parameter.
176
173
  #
177
174
  # Example:
@@ -180,6 +177,32 @@ module ActionView
180
177
  #
181
178
  # You can change the behaviour with various options, see
182
179
  # http://script.aculo.us for more documentation.
180
+ #
181
+ # Some of these +options+ include:
182
+ # * <tt>:accept</tt> - Set this to a string or an array of strings describing the
183
+ # allowable CSS classes that the +draggable_element+ must have in order
184
+ # to be accepted by this +drop_receiving_element+.
185
+ #
186
+ # * <tt>:confirm</tt> - Adds a confirmation dialog. Example:
187
+ #
188
+ # :confirm => "Are you sure you want to do this?"
189
+ #
190
+ # * <tt>:hoverclass</tt> - If set, the +drop_receiving_element+ will have
191
+ # this additional CSS class when an accepted +draggable_element+ is
192
+ # hovered over it.
193
+ #
194
+ # * <tt>:onDrop</tt> - Called when a +draggable_element+ is dropped onto
195
+ # this element. Override this callback with a JavaScript expression to
196
+ # change the default drop behavour. Example:
197
+ #
198
+ # :onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }"
199
+ #
200
+ # This callback gets three parameters: The Draggable element, the Droppable
201
+ # element and the Event object. You can extract additional information about
202
+ # the drop - like if the Ctrl or Shift keys were pressed - from the Event object.
203
+ #
204
+ # * <tt>:with</tt> - A JavaScript expression specifying the parameters for
205
+ # the XMLHttpRequest. Any expressions should return a valid URL query string.
183
206
  def drop_receiving_element(element_id, options = {})
184
207
  javascript_tag(drop_receiving_element_js(element_id, options).chop!)
185
208
  end
@@ -192,6 +215,9 @@ module ActionView
192
215
  options[:accept] = array_or_string_for_javascript(options[:accept]) if options[:accept]
193
216
  options[:hoverclass] = "'#{options[:hoverclass]}'" if options[:hoverclass]
194
217
 
218
+ # Confirmation happens during the onDrop callback, so it can be removed from the options
219
+ options.delete(:confirm) if options[:confirm]
220
+
195
221
  %(Droppables.add(#{element_id.to_json}, #{options_for_javascript(options)});)
196
222
  end
197
223
  end
@@ -3,19 +3,19 @@ require 'html/document'
3
3
 
4
4
  module ActionView
5
5
  module Helpers #:nodoc:
6
- # The TextHelper module provides a set of methods for filtering, formatting
7
- # and transforming strings, which can reduce the amount of inline Ruby code in
8
- # your views. These helper methods extend ActionView making them callable
6
+ # The TextHelper module provides a set of methods for filtering, formatting
7
+ # and transforming strings, which can reduce the amount of inline Ruby code in
8
+ # your views. These helper methods extend ActionView making them callable
9
9
  # within your template files.
10
- module TextHelper
11
- # The preferred method of outputting text in your views is to use the
12
- # <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
13
- # do not operate as expected in an eRuby code block. If you absolutely must
10
+ module TextHelper
11
+ # The preferred method of outputting text in your views is to use the
12
+ # <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
13
+ # do not operate as expected in an eRuby code block. If you absolutely must
14
14
  # output text within a non-output code block (i.e., <% %>), you can use the concat method.
15
15
  #
16
16
  # ==== Examples
17
- # <%
18
- # concat "hello", binding
17
+ # <%
18
+ # concat "hello", binding
19
19
  # # is the equivalent of <%= "hello" %>
20
20
  #
21
21
  # if (logged_in == true):
@@ -29,43 +29,54 @@ module ActionView
29
29
  eval(ActionView::Base.erb_variable, binding) << string
30
30
  end
31
31
 
32
- # If +text+ is longer than +length+, +text+ will be truncated to the length of
33
- # +length+ (defaults to 30) and the last characters will be replaced with the +truncate_string+
34
- # (defaults to "...").
35
- #
36
- # ==== Examples
37
- # truncate("Once upon a time in a world far far away", 14)
38
- # # => Once upon a...
39
- #
40
- # truncate("Once upon a time in a world far far away")
41
- # # => Once upon a time in a world f...
42
- #
43
- # truncate("And they found that many people were sleeping better.", 25, "(clipped)")
44
- # # => And they found that many (clipped)
45
- #
46
- # truncate("And they found that many people were sleeping better.", 15, "... (continued)")
47
- # # => And they found... (continued)
48
- def truncate(text, length = 30, truncate_string = "...")
49
- if text.nil? then return end
50
- l = length - truncate_string.chars.length
51
- (text.chars.length > length ? text.chars[0...l] + truncate_string : text).to_s
32
+ if RUBY_VERSION < '1.9'
33
+ # If +text+ is longer than +length+, +text+ will be truncated to the length of
34
+ # +length+ (defaults to 30) and the last characters will be replaced with the +truncate_string+
35
+ # (defaults to "...").
36
+ #
37
+ # ==== Examples
38
+ # truncate("Once upon a time in a world far far away", 14)
39
+ # # => Once upon a...
40
+ #
41
+ # truncate("Once upon a time in a world far far away")
42
+ # # => Once upon a time in a world f...
43
+ #
44
+ # truncate("And they found that many people were sleeping better.", 25, "(clipped)")
45
+ # # => And they found that many (clipped)
46
+ #
47
+ # truncate("And they found that many people were sleeping better.", 15, "... (continued)")
48
+ # # => And they found... (continued)
49
+ def truncate(text, length = 30, truncate_string = "...")
50
+ if text
51
+ l = length - truncate_string.chars.length
52
+ chars = text.chars
53
+ (chars.length > length ? chars[0...l] + truncate_string : text).to_s
54
+ end
55
+ end
56
+ else
57
+ def truncate(text, length = 30, truncate_string = "...") #:nodoc:
58
+ if text
59
+ l = length - truncate_string.length
60
+ (text.length > length ? text[0...l] + truncate_string : text).to_s
61
+ end
62
+ end
52
63
  end
53
64
 
54
65
  # Highlights one or more +phrases+ everywhere in +text+ by inserting it into
55
- # a +highlighter+ string. The highlighter can be specialized by passing +highlighter+
66
+ # a +highlighter+ string. The highlighter can be specialized by passing +highlighter+
56
67
  # as a single-quoted string with \1 where the phrase is to be inserted (defaults to
57
68
  # '<strong class="highlight">\1</strong>')
58
69
  #
59
70
  # ==== Examples
60
- # highlight('You searched for: rails', 'rails')
71
+ # highlight('You searched for: rails', 'rails')
61
72
  # # => You searched for: <strong class="highlight">rails</strong>
62
73
  #
63
74
  # highlight('You searched for: ruby, rails, dhh', 'actionpack')
64
- # # => You searched for: ruby, rails, dhh
75
+ # # => You searched for: ruby, rails, dhh
65
76
  #
66
- # highlight('You searched for: rails', ['for', 'rails'], '<em>\1</em>')
77
+ # highlight('You searched for: rails', ['for', 'rails'], '<em>\1</em>')
67
78
  # # => You searched <em>for</em>: <em>rails</em>
68
- #
79
+ #
69
80
  # highlight('You searched for: rails', 'rails', "<a href='search?q=\1'>\1</a>")
70
81
  # # => You searched for: <a href='search?q=rails>rails</a>
71
82
  def highlight(text, phrases, highlighter = '<strong class="highlight">\1</strong>')
@@ -77,71 +88,83 @@ module ActionView
77
88
  end
78
89
  end
79
90
 
80
- # Extracts an excerpt from +text+ that matches the first instance of +phrase+.
81
- # The +radius+ expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
82
- # defined in +radius+ (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
83
- # then the +excerpt_string+ will be prepended/appended accordingly. If the +phrase+
84
- # isn't found, nil is returned.
85
- #
86
- # ==== Examples
87
- # excerpt('This is an example', 'an', 5)
88
- # # => "...s is an examp..."
89
- #
90
- # excerpt('This is an example', 'is', 5)
91
- # # => "This is an..."
92
- #
93
- # excerpt('This is an example', 'is')
94
- # # => "This is an example"
95
- #
96
- # excerpt('This next thing is an example', 'ex', 2)
97
- # # => "...next t..."
98
- #
99
- # excerpt('This is also an example', 'an', 8, '<chop> ')
100
- # # => "<chop> is also an example"
101
- def excerpt(text, phrase, radius = 100, excerpt_string = "...")
102
- if text.nil? || phrase.nil? then return end
103
- phrase = Regexp.escape(phrase)
91
+ if RUBY_VERSION < '1.9'
92
+ # Extracts an excerpt from +text+ that matches the first instance of +phrase+.
93
+ # The +radius+ expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
94
+ # defined in +radius+ (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
95
+ # then the +excerpt_string+ will be prepended/appended accordingly. The resulting string will be stripped in any case.
96
+ # If the +phrase+ isn't found, nil is returned.
97
+ #
98
+ # ==== Examples
99
+ # excerpt('This is an example', 'an', 5)
100
+ # # => "...s is an exam..."
101
+ #
102
+ # excerpt('This is an example', 'is', 5)
103
+ # # => "This is a..."
104
+ #
105
+ # excerpt('This is an example', 'is')
106
+ # # => "This is an example"
107
+ #
108
+ # excerpt('This next thing is an example', 'ex', 2)
109
+ # # => "...next..."
110
+ #
111
+ # excerpt('This is also an example', 'an', 8, '<chop> ')
112
+ # # => "<chop> is also an example"
113
+ def excerpt(text, phrase, radius = 100, excerpt_string = "...")
114
+ if text && phrase
115
+ phrase = Regexp.escape(phrase)
104
116
 
105
- if found_pos = text.chars =~ /(#{phrase})/i
106
- start_pos = [ found_pos - radius, 0 ].max
107
- end_pos = [ found_pos + phrase.chars.length + radius, text.chars.length ].min
117
+ if found_pos = text.chars =~ /(#{phrase})/i
118
+ start_pos = [ found_pos - radius, 0 ].max
119
+ end_pos = [ [ found_pos + phrase.chars.length + radius - 1, 0].max, text.chars.length ].min
108
120
 
109
- prefix = start_pos > 0 ? excerpt_string : ""
110
- postfix = end_pos < text.chars.length ? excerpt_string : ""
121
+ prefix = start_pos > 0 ? excerpt_string : ""
122
+ postfix = end_pos < text.chars.length - 1 ? excerpt_string : ""
111
123
 
112
- prefix + text.chars[start_pos..end_pos].strip + postfix
113
- else
114
- nil
124
+ prefix + text.chars[start_pos..end_pos].strip + postfix
125
+ else
126
+ nil
127
+ end
128
+ end
129
+ end
130
+ else
131
+ def excerpt(text, phrase, radius = 100, excerpt_string = "...") #:nodoc:
132
+ if text && phrase
133
+ phrase = Regexp.escape(phrase)
134
+
135
+ if found_pos = text =~ /(#{phrase})/i
136
+ start_pos = [ found_pos - radius, 0 ].max
137
+ end_pos = [ [ found_pos + phrase.length + radius - 1, 0].max, text.length ].min
138
+
139
+ prefix = start_pos > 0 ? excerpt_string : ""
140
+ postfix = end_pos < text.length - 1 ? excerpt_string : ""
141
+
142
+ prefix + text[start_pos..end_pos].strip + postfix
143
+ else
144
+ nil
145
+ end
146
+ end
115
147
  end
116
148
  end
117
149
 
118
- # Attempts to pluralize the +singular+ word unless +count+ is 1. If +plural+
119
- # is supplied, it will use that when count is > 1, if the ActiveSupport Inflector
120
- # is loaded, it will use the Inflector to determine the plural form, otherwise
121
- # it will just add an 's' to the +singular+ word.
150
+ # Attempts to pluralize the +singular+ word unless +count+ is 1. If
151
+ # +plural+ is supplied, it will use that when count is > 1, otherwise
152
+ # it will use the Inflector to determine the plural form
122
153
  #
123
154
  # ==== Examples
124
- # pluralize(1, 'person')
155
+ # pluralize(1, 'person')
125
156
  # # => 1 person
126
157
  #
127
- # pluralize(2, 'person')
158
+ # pluralize(2, 'person')
128
159
  # # => 2 people
129
160
  #
130
- # pluralize(3, 'person', 'users')
161
+ # pluralize(3, 'person', 'users')
131
162
  # # => 3 users
132
163
  #
133
164
  # pluralize(0, 'person')
134
165
  # # => 0 people
135
166
  def pluralize(count, singular, plural = nil)
136
- "#{count || 0} " + if count == 1 || count == '1'
137
- singular
138
- elsif plural
139
- plural
140
- elsif Object.const_defined?("Inflector")
141
- Inflector.pluralize(singular)
142
- else
143
- singular + "s"
144
- end
167
+ "#{count || 0} " + ((count == 1 || count == '1') ? singular : (plural || singular.pluralize))
145
168
  end
146
169
 
147
170
  # Wraps the +text+ into lines no longer than +line_width+ width. This method
@@ -197,7 +220,7 @@ module ActionView
197
220
  end
198
221
  end
199
222
 
200
- # Returns the text with all the Textile codes turned into HTML tags,
223
+ # Returns the text with all the Textile codes turned into HTML tags,
201
224
  # but without the bounding <p> tag that RedCloth adds.
202
225
  #
203
226
  # You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile].
@@ -241,57 +264,62 @@ module ActionView
241
264
  # # => "<p>We like to <em>write</em> <code>code</code>, not just <em>read</em> it!</p>"
242
265
  #
243
266
  # markdown("The [Markdown website](http://daringfireball.net/projects/markdown/) has more information.")
244
- # # => "<p>The <a href="http://daringfireball.net/projects/markdown/">Markdown website</a>
267
+ # # => "<p>The <a href="http://daringfireball.net/projects/markdown/">Markdown website</a>
245
268
  # # has more information.</p>"
246
269
  #
247
270
  # markdown('![The ROR logo](http://rubyonrails.com/images/rails.png "Ruby on Rails")')
248
- # # => '<p><img src="http://rubyonrails.com/images/rails.png" alt="The ROR logo" title="Ruby on Rails" /></p>'
271
+ # # => '<p><img src="http://rubyonrails.com/images/rails.png" alt="The ROR logo" title="Ruby on Rails" /></p>'
249
272
  def markdown(text)
250
273
  text.blank? ? "" : BlueCloth.new(text).to_html
251
274
  end
252
275
  rescue LoadError
253
276
  # We can't really help what's not there
254
277
  end
255
-
278
+
256
279
  # Returns +text+ transformed into HTML using simple formatting rules.
257
- # Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
280
+ # Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
258
281
  # paragraph and wrapped in <tt><p></tt> tags. One newline (<tt>\n</tt>) is
259
282
  # considered as a linebreak and a <tt><br /></tt> tag is appended. This
260
- # method does not remove the newlines from the +text+.
283
+ # method does not remove the newlines from the +text+.
261
284
  #
285
+ # You can pass any HTML attributes into <tt>html_options</tt>. These
286
+ # will be added to all created paragraphs.
262
287
  # ==== Examples
263
- # my_text = """Here is some basic text...
264
- # ...with a line break."""
288
+ # my_text = "Here is some basic text...\n...with a line break."
265
289
  #
266
290
  # simple_format(my_text)
267
- # # => "<p>Here is some basic text...<br />...with a line break.</p>"
291
+ # # => "<p>Here is some basic text...\n<br />...with a line break.</p>"
268
292
  #
269
- # more_text = """We want to put a paragraph...
270
- #
271
- # ...right there."""
293
+ # more_text = "We want to put a paragraph...\n\n...right there."
272
294
  #
273
295
  # simple_format(more_text)
274
- # # => "<p>We want to put a paragraph...</p><p>...right there.</p>"
275
- def simple_format(text)
276
- content_tag 'p', text.to_s.
277
- gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
278
- gsub(/\n\n+/, "</p>\n\n<p>"). # 2+ newline -> paragraph
279
- gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
296
+ # # => "<p>We want to put a paragraph...</p>\n\n<p>...right there.</p>"
297
+ #
298
+ # simple_format("Look ma! A class!", :class => 'description')
299
+ # # => "<p class='description'>Look ma! A class!</p>"
300
+ def simple_format(text, html_options={})
301
+ start_tag = tag('p', html_options, true)
302
+ text = text.to_s.dup
303
+ text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n
304
+ text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph
305
+ text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
306
+ text.insert 0, start_tag
307
+ text << "</p>"
280
308
  end
281
309
 
282
- # Turns all URLs and e-mail addresses into clickable links. The +link+ parameter
310
+ # Turns all URLs and e-mail addresses into clickable links. The +link+ parameter
283
311
  # will limit what should be linked. You can add HTML attributes to the links using
284
- # +href_options+. Options for +link+ are <tt>:all</tt> (default),
285
- # <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
312
+ # +href_options+. Options for +link+ are <tt>:all</tt> (default),
313
+ # <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
286
314
  # e-mail address is yielded and the result is used as the link text.
287
315
  #
288
316
  # ==== Examples
289
- # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
317
+ # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
290
318
  # # => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
291
319
  # # say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
292
320
  #
293
321
  # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :urls)
294
- # # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
322
+ # # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
295
323
  # # or e-mail david@loudthinking.com"
296
324
  #
297
325
  # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :email_addresses)
@@ -301,9 +329,9 @@ module ActionView
301
329
  # auto_link(post_body, :all, :target => '_blank') do |text|
302
330
  # truncate(text, 15)
303
331
  # end
304
- # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
332
+ # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
305
333
  # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
306
- #
334
+ #
307
335
  def auto_link(text, link = :all, href_options = {}, &block)
308
336
  return '' if text.blank?
309
337
  case link
@@ -312,15 +340,15 @@ module ActionView
312
340
  when :urls then auto_link_urls(text, href_options, &block)
313
341
  end
314
342
  end
315
-
343
+
316
344
  # Creates a Cycle object whose _to_s_ method cycles through elements of an
317
- # array every time it is called. This can be used for example, to alternate
318
- # classes for table rows. You can use named cycles to allow nesting in loops.
319
- # Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
320
- # named cycle. You can manually reset a cycle by calling reset_cycle and passing the
345
+ # array every time it is called. This can be used for example, to alternate
346
+ # classes for table rows. You can use named cycles to allow nesting in loops.
347
+ # Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
348
+ # named cycle. You can manually reset a cycle by calling reset_cycle and passing the
321
349
  # name of the cycle.
322
350
  #
323
- # ==== Examples
351
+ # ==== Examples
324
352
  # # Alternate CSS classes for even and odd numbers...
325
353
  # @items = [1,2,3,4]
326
354
  # <table>
@@ -333,8 +361,8 @@ module ActionView
333
361
  #
334
362
  #
335
363
  # # Cycle CSS classes for rows, and text colors for values within each row
336
- # @items = x = [{:first => 'Robert', :middle => 'Daniel', :last => 'James'},
337
- # {:first => 'Emily', :middle => 'Shannon', :maiden => 'Pike', :last => 'Hicks'},
364
+ # @items = x = [{:first => 'Robert', :middle => 'Daniel', :last => 'James'},
365
+ # {:first => 'Emily', :middle => 'Shannon', :maiden => 'Pike', :last => 'Hicks'},
338
366
  # {:first => 'June', :middle => 'Dae', :last => 'Jones'}]
339
367
  # <% @items.each do |item| %>
340
368
  # <tr class="<%= cycle("even", "odd", :name => "row_class") -%>">
@@ -364,8 +392,8 @@ module ActionView
364
392
  end
365
393
  return cycle.to_s
366
394
  end
367
-
368
- # Resets a cycle so that it starts from the first element the next time
395
+
396
+ # Resets a cycle so that it starts from the first element the next time
369
397
  # it is called. Pass in +name+ to reset a named cycle.
370
398
  #
371
399
  # ==== Example
@@ -391,12 +419,12 @@ module ActionView
391
419
 
392
420
  class Cycle #:nodoc:
393
421
  attr_reader :values
394
-
422
+
395
423
  def initialize(first_value, *values)
396
424
  @values = values.unshift(first_value)
397
425
  reset
398
426
  end
399
-
427
+
400
428
  def reset
401
429
  @index = 0
402
430
  end
@@ -416,7 +444,7 @@ module ActionView
416
444
  @_cycles = Hash.new unless defined?(@_cycles)
417
445
  return @_cycles[name]
418
446
  end
419
-
447
+
420
448
  def set_cycle(name, cycle_object)
421
449
  @_cycles = Hash.new unless defined?(@_cycles)
422
450
  @_cycles[name] = cycle_object
@@ -425,22 +453,22 @@ module ActionView
425
453
  AUTO_LINK_RE = %r{
426
454
  ( # leading text
427
455
  <\w+.*?>| # leading HTML tag, or
428
- [^=!:'"/]| # leading punctuation, or
456
+ [^=!:'"/]| # leading punctuation, or
429
457
  ^ # beginning of line
430
458
  )
431
459
  (
432
460
  (?:https?://)| # protocol spec, or
433
461
  (?:www\.) # www.*
434
- )
462
+ )
435
463
  (
436
464
  [-\w]+ # subdomain or domain
437
465
  (?:\.[-\w]+)* # remaining subdomains or domain
438
466
  (?::\d+)? # port
439
- (?:/(?:(?:[~\w\+@%=-]|(?:[,.;:][^\s$]))+)?)* # path
467
+ (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:][^\s$]))+)?)* # path
440
468
  (?:\?[\w\+@%&=.;-]+)? # query string
441
469
  (?:\#[\w\-]*)? # trailing anchor
442
470
  )
443
- ([[:punct:]]|\s|<|$) # trailing text
471
+ ([[:punct:]]|<|$|) # trailing text
444
472
  }x unless const_defined?(:AUTO_LINK_RE)
445
473
 
446
474
  # Turns all urls into clickable links. If a block is given, each url
@@ -465,7 +493,7 @@ module ActionView
465
493
  body = text.dup
466
494
  text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
467
495
  text = $1
468
-
496
+
469
497
  if body.match(/<a\b[^>]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/)
470
498
  text
471
499
  else