actionpack 3.2.22.5 → 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (265) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +641 -418
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -288
  5. data/lib/abstract_controller.rb +1 -8
  6. data/lib/abstract_controller/asset_paths.rb +2 -2
  7. data/lib/abstract_controller/base.rb +39 -37
  8. data/lib/abstract_controller/callbacks.rb +101 -82
  9. data/lib/abstract_controller/collector.rb +7 -3
  10. data/lib/abstract_controller/helpers.rb +23 -11
  11. data/lib/abstract_controller/layouts.rb +68 -73
  12. data/lib/abstract_controller/logger.rb +1 -2
  13. data/lib/abstract_controller/rendering.rb +22 -13
  14. data/lib/abstract_controller/translation.rb +16 -1
  15. data/lib/abstract_controller/url_for.rb +6 -6
  16. data/lib/abstract_controller/view_paths.rb +1 -1
  17. data/lib/action_controller.rb +15 -6
  18. data/lib/action_controller/base.rb +46 -22
  19. data/lib/action_controller/caching.rb +46 -33
  20. data/lib/action_controller/caching/fragments.rb +23 -53
  21. data/lib/action_controller/deprecated.rb +5 -1
  22. data/lib/action_controller/deprecated/integration_test.rb +3 -0
  23. data/lib/action_controller/log_subscriber.rb +11 -8
  24. data/lib/action_controller/metal.rb +16 -30
  25. data/lib/action_controller/metal/conditional_get.rb +76 -32
  26. data/lib/action_controller/metal/data_streaming.rb +20 -26
  27. data/lib/action_controller/metal/exceptions.rb +19 -6
  28. data/lib/action_controller/metal/flash.rb +24 -9
  29. data/lib/action_controller/metal/force_ssl.rb +32 -9
  30. data/lib/action_controller/metal/head.rb +25 -4
  31. data/lib/action_controller/metal/helpers.rb +6 -9
  32. data/lib/action_controller/metal/hide_actions.rb +1 -2
  33. data/lib/action_controller/metal/http_authentication.rb +105 -87
  34. data/lib/action_controller/metal/implicit_render.rb +1 -1
  35. data/lib/action_controller/metal/instrumentation.rb +2 -1
  36. data/lib/action_controller/metal/live.rb +141 -0
  37. data/lib/action_controller/metal/mime_responds.rb +161 -47
  38. data/lib/action_controller/metal/params_wrapper.rb +112 -74
  39. data/lib/action_controller/metal/rack_delegation.rb +9 -3
  40. data/lib/action_controller/metal/redirecting.rb +15 -20
  41. data/lib/action_controller/metal/renderers.rb +11 -9
  42. data/lib/action_controller/metal/rendering.rb +8 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +112 -19
  44. data/lib/action_controller/metal/responder.rb +20 -19
  45. data/lib/action_controller/metal/streaming.rb +12 -18
  46. data/lib/action_controller/metal/strong_parameters.rb +516 -0
  47. data/lib/action_controller/metal/testing.rb +13 -18
  48. data/lib/action_controller/metal/url_for.rb +27 -25
  49. data/lib/action_controller/model_naming.rb +12 -0
  50. data/lib/action_controller/railtie.rb +33 -17
  51. data/lib/action_controller/railties/helpers.rb +22 -0
  52. data/lib/action_controller/record_identifier.rb +18 -72
  53. data/lib/action_controller/test_case.rb +215 -123
  54. data/lib/action_controller/vendor/html-scanner.rb +4 -19
  55. data/lib/action_dispatch.rb +27 -19
  56. data/lib/action_dispatch/http/cache.rb +63 -11
  57. data/lib/action_dispatch/http/filter_parameters.rb +18 -8
  58. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  59. data/lib/action_dispatch/http/headers.rb +27 -19
  60. data/lib/action_dispatch/http/mime_negotiation.rb +25 -2
  61. data/lib/action_dispatch/http/mime_type.rb +145 -113
  62. data/lib/action_dispatch/http/mime_types.rb +1 -1
  63. data/lib/action_dispatch/http/parameter_filter.rb +44 -46
  64. data/lib/action_dispatch/http/parameters.rb +12 -5
  65. data/lib/action_dispatch/http/rack_cache.rb +2 -3
  66. data/lib/action_dispatch/http/request.rb +49 -18
  67. data/lib/action_dispatch/http/response.rb +129 -35
  68. data/lib/action_dispatch/http/upload.rb +60 -17
  69. data/lib/action_dispatch/http/url.rb +53 -31
  70. data/lib/action_dispatch/journey.rb +5 -0
  71. data/lib/action_dispatch/journey/backwards.rb +5 -0
  72. data/lib/action_dispatch/journey/formatter.rb +146 -0
  73. data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
  74. data/lib/action_dispatch/journey/gtg/simulator.rb +44 -0
  75. data/lib/action_dispatch/journey/gtg/transition_table.rb +156 -0
  76. data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
  77. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  78. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  79. data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
  80. data/lib/action_dispatch/journey/nodes/node.rb +124 -0
  81. data/lib/action_dispatch/journey/parser.rb +206 -0
  82. data/lib/action_dispatch/journey/parser.y +47 -0
  83. data/lib/action_dispatch/journey/parser_extras.rb +23 -0
  84. data/lib/action_dispatch/journey/path/pattern.rb +196 -0
  85. data/lib/action_dispatch/journey/route.rb +116 -0
  86. data/lib/action_dispatch/journey/router.rb +164 -0
  87. data/lib/action_dispatch/journey/router/strexp.rb +24 -0
  88. data/lib/action_dispatch/journey/router/utils.rb +54 -0
  89. data/lib/action_dispatch/journey/routes.rb +75 -0
  90. data/lib/action_dispatch/journey/scanner.rb +61 -0
  91. data/lib/action_dispatch/journey/visitors.rb +189 -0
  92. data/lib/action_dispatch/journey/visualizer/fsm.css +34 -0
  93. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  94. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  95. data/lib/action_dispatch/middleware/callbacks.rb +9 -4
  96. data/lib/action_dispatch/middleware/cookies.rb +168 -57
  97. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -17
  98. data/lib/action_dispatch/middleware/exception_wrapper.rb +27 -3
  99. data/lib/action_dispatch/middleware/flash.rb +58 -58
  100. data/lib/action_dispatch/middleware/params_parser.rb +14 -29
  101. data/lib/action_dispatch/middleware/public_exceptions.rb +31 -14
  102. data/lib/action_dispatch/middleware/reloader.rb +6 -6
  103. data/lib/action_dispatch/middleware/remote_ip.rb +145 -39
  104. data/lib/action_dispatch/middleware/request_id.rb +2 -6
  105. data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
  106. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  107. data/lib/action_dispatch/middleware/session/cookie_store.rb +81 -7
  108. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
  109. data/lib/action_dispatch/middleware/show_exceptions.rb +12 -45
  110. data/lib/action_dispatch/middleware/ssl.rb +70 -0
  111. data/lib/action_dispatch/middleware/stack.rb +6 -1
  112. data/lib/action_dispatch/middleware/static.rb +5 -24
  113. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +14 -11
  114. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +25 -0
  115. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
  116. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +15 -9
  117. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -5
  118. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +7 -2
  119. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +30 -15
  120. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +39 -13
  121. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +6 -2
  122. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  123. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +144 -0
  124. data/lib/action_dispatch/railtie.rb +16 -6
  125. data/lib/action_dispatch/request/session.rb +181 -0
  126. data/lib/action_dispatch/routing.rb +41 -40
  127. data/lib/action_dispatch/routing/inspector.rb +240 -0
  128. data/lib/action_dispatch/routing/mapper.rb +501 -273
  129. data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -20
  130. data/lib/action_dispatch/routing/redirection.rb +46 -29
  131. data/lib/action_dispatch/routing/route_set.rb +203 -164
  132. data/lib/action_dispatch/routing/routes_proxy.rb +2 -0
  133. data/lib/action_dispatch/routing/url_for.rb +48 -33
  134. data/lib/action_dispatch/testing/assertions/dom.rb +3 -13
  135. data/lib/action_dispatch/testing/assertions/response.rb +32 -40
  136. data/lib/action_dispatch/testing/assertions/routing.rb +40 -39
  137. data/lib/action_dispatch/testing/assertions/selector.rb +15 -20
  138. data/lib/action_dispatch/testing/assertions/tag.rb +20 -23
  139. data/lib/action_dispatch/testing/integration.rb +41 -22
  140. data/lib/action_dispatch/testing/test_process.rb +9 -6
  141. data/lib/action_dispatch/testing/test_request.rb +7 -3
  142. data/lib/action_pack.rb +1 -1
  143. data/lib/action_pack/version.rb +4 -4
  144. data/lib/action_view.rb +17 -8
  145. data/lib/action_view/base.rb +15 -34
  146. data/lib/action_view/buffers.rb +1 -1
  147. data/lib/action_view/context.rb +4 -4
  148. data/lib/action_view/dependency_tracker.rb +91 -0
  149. data/lib/action_view/digestor.rb +85 -0
  150. data/lib/action_view/flows.rb +1 -4
  151. data/lib/action_view/helpers.rb +2 -4
  152. data/lib/action_view/helpers/active_model_helper.rb +3 -4
  153. data/lib/action_view/helpers/asset_tag_helper.rb +211 -353
  154. data/lib/action_view/helpers/asset_url_helper.rb +354 -0
  155. data/lib/action_view/helpers/atom_feed_helper.rb +13 -10
  156. data/lib/action_view/helpers/cache_helper.rb +150 -18
  157. data/lib/action_view/helpers/capture_helper.rb +42 -29
  158. data/lib/action_view/helpers/csrf_helper.rb +0 -2
  159. data/lib/action_view/helpers/date_helper.rb +268 -247
  160. data/lib/action_view/helpers/debug_helper.rb +10 -11
  161. data/lib/action_view/helpers/form_helper.rb +904 -547
  162. data/lib/action_view/helpers/form_options_helper.rb +341 -166
  163. data/lib/action_view/helpers/form_tag_helper.rb +188 -88
  164. data/lib/action_view/helpers/javascript_helper.rb +23 -16
  165. data/lib/action_view/helpers/number_helper.rb +148 -354
  166. data/lib/action_view/helpers/output_safety_helper.rb +3 -3
  167. data/lib/action_view/helpers/record_tag_helper.rb +17 -22
  168. data/lib/action_view/helpers/rendering_helper.rb +2 -4
  169. data/lib/action_view/helpers/sanitize_helper.rb +3 -6
  170. data/lib/action_view/helpers/tag_helper.rb +43 -37
  171. data/lib/action_view/helpers/tags.rb +39 -0
  172. data/lib/action_view/helpers/tags/base.rb +148 -0
  173. data/lib/action_view/helpers/tags/check_box.rb +64 -0
  174. data/lib/action_view/helpers/tags/checkable.rb +16 -0
  175. data/lib/action_view/helpers/tags/collection_check_boxes.rb +43 -0
  176. data/lib/action_view/helpers/tags/collection_helpers.rb +83 -0
  177. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
  178. data/lib/action_view/helpers/tags/collection_select.rb +28 -0
  179. data/lib/action_view/helpers/tags/color_field.rb +25 -0
  180. data/lib/action_view/helpers/tags/date_field.rb +13 -0
  181. data/lib/action_view/helpers/tags/date_select.rb +72 -0
  182. data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
  183. data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
  184. data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
  185. data/lib/action_view/helpers/tags/email_field.rb +8 -0
  186. data/lib/action_view/helpers/tags/file_field.rb +8 -0
  187. data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
  188. data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
  189. data/lib/action_view/helpers/tags/label.rb +65 -0
  190. data/lib/action_view/helpers/tags/month_field.rb +13 -0
  191. data/lib/action_view/helpers/tags/number_field.rb +18 -0
  192. data/lib/action_view/helpers/tags/password_field.rb +12 -0
  193. data/lib/action_view/helpers/tags/radio_button.rb +31 -0
  194. data/lib/action_view/helpers/tags/range_field.rb +8 -0
  195. data/lib/action_view/helpers/tags/search_field.rb +24 -0
  196. data/lib/action_view/helpers/tags/select.rb +41 -0
  197. data/lib/action_view/helpers/tags/tel_field.rb +8 -0
  198. data/lib/action_view/helpers/tags/text_area.rb +18 -0
  199. data/lib/action_view/helpers/tags/text_field.rb +29 -0
  200. data/lib/action_view/helpers/tags/time_field.rb +13 -0
  201. data/lib/action_view/helpers/tags/time_select.rb +8 -0
  202. data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
  203. data/lib/action_view/helpers/tags/url_field.rb +8 -0
  204. data/lib/action_view/helpers/tags/week_field.rb +13 -0
  205. data/lib/action_view/helpers/text_helper.rb +126 -113
  206. data/lib/action_view/helpers/translation_helper.rb +32 -16
  207. data/lib/action_view/helpers/url_helper.rb +200 -271
  208. data/lib/action_view/locale/en.yml +1 -105
  209. data/lib/action_view/log_subscriber.rb +6 -4
  210. data/lib/action_view/lookup_context.rb +15 -39
  211. data/lib/action_view/model_naming.rb +12 -0
  212. data/lib/action_view/path_set.rb +9 -39
  213. data/lib/action_view/railtie.rb +6 -22
  214. data/lib/action_view/record_identifier.rb +84 -0
  215. data/lib/action_view/renderer/abstract_renderer.rb +10 -19
  216. data/lib/action_view/renderer/partial_renderer.rb +144 -81
  217. data/lib/action_view/renderer/renderer.rb +2 -19
  218. data/lib/action_view/renderer/streaming_template_renderer.rb +2 -5
  219. data/lib/action_view/renderer/template_renderer.rb +14 -13
  220. data/lib/action_view/routing_url_for.rb +107 -0
  221. data/lib/action_view/template.rb +22 -21
  222. data/lib/action_view/template/error.rb +22 -12
  223. data/lib/action_view/template/handlers.rb +12 -9
  224. data/lib/action_view/template/handlers/builder.rb +1 -1
  225. data/lib/action_view/template/handlers/erb.rb +11 -16
  226. data/lib/action_view/template/handlers/raw.rb +11 -0
  227. data/lib/action_view/template/resolver.rb +111 -83
  228. data/lib/action_view/template/text.rb +12 -8
  229. data/lib/action_view/template/types.rb +57 -0
  230. data/lib/action_view/test_case.rb +66 -43
  231. data/lib/action_view/testing/resolvers.rb +3 -2
  232. data/lib/action_view/vendor/html-scanner.rb +20 -0
  233. data/lib/{action_controller → action_view}/vendor/html-scanner/html/document.rb +0 -0
  234. data/lib/{action_controller → action_view}/vendor/html-scanner/html/node.rb +12 -12
  235. data/lib/{action_controller → action_view}/vendor/html-scanner/html/sanitizer.rb +18 -7
  236. data/lib/{action_controller → action_view}/vendor/html-scanner/html/selector.rb +1 -1
  237. data/lib/{action_controller → action_view}/vendor/html-scanner/html/tokenizer.rb +1 -1
  238. data/lib/{action_controller → action_view}/vendor/html-scanner/html/version.rb +0 -0
  239. metadata +135 -125
  240. data/lib/action_controller/caching/actions.rb +0 -185
  241. data/lib/action_controller/caching/pages.rb +0 -187
  242. data/lib/action_controller/caching/sweeping.rb +0 -97
  243. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  244. data/lib/action_controller/metal/compatibility.rb +0 -65
  245. data/lib/action_controller/metal/session_management.rb +0 -14
  246. data/lib/action_controller/railties/paths.rb +0 -25
  247. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  248. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  249. data/lib/action_dispatch/middleware/head.rb +0 -18
  250. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  251. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  252. data/lib/action_view/asset_paths.rb +0 -142
  253. data/lib/action_view/helpers/asset_paths.rb +0 -7
  254. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  255. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  256. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  257. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  258. data/lib/sprockets/assets.rake +0 -99
  259. data/lib/sprockets/bootstrap.rb +0 -37
  260. data/lib/sprockets/compressors.rb +0 -83
  261. data/lib/sprockets/helpers.rb +0 -6
  262. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  263. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  264. data/lib/sprockets/railtie.rb +0 -62
  265. data/lib/sprockets/static_compiler.rb +0 -56
@@ -0,0 +1,8 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class FileField < TextField #:nodoc:
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,29 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class GroupedCollectionSelect < Base #:nodoc:
5
+ def initialize(object_name, method_name, template_object, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
6
+ @collection = collection
7
+ @group_method = group_method
8
+ @group_label_method = group_label_method
9
+ @option_key_method = option_key_method
10
+ @option_value_method = option_value_method
11
+ @html_options = html_options
12
+
13
+ super(object_name, method_name, template_object, options)
14
+ end
15
+
16
+ def render
17
+ option_tags_options = {
18
+ :selected => @options.fetch(:selected) { value(@object) },
19
+ :disabled => @options[:disabled]
20
+ }
21
+
22
+ select_content_tag(
23
+ option_groups_from_collection_for_select(@collection, @group_method, @group_label_method, @option_key_method, @option_value_method, option_tags_options), @options, @html_options
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,8 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class HiddenField < TextField #:nodoc:
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,65 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class Label < Base #:nodoc:
5
+ def initialize(object_name, method_name, template_object, content_or_options = nil, options = nil)
6
+ options ||= {}
7
+
8
+ content_is_options = content_or_options.is_a?(Hash)
9
+ if content_is_options
10
+ options.merge! content_or_options
11
+ @content = nil
12
+ else
13
+ @content = content_or_options
14
+ end
15
+
16
+ super(object_name, method_name, template_object, options)
17
+ end
18
+
19
+ def render(&block)
20
+ options = @options.stringify_keys
21
+ tag_value = options.delete("value")
22
+ name_and_id = options.dup
23
+
24
+ if name_and_id["for"]
25
+ name_and_id["id"] = name_and_id["for"]
26
+ else
27
+ name_and_id.delete("id")
28
+ end
29
+
30
+ add_default_name_and_id_for_value(tag_value, name_and_id)
31
+ options.delete("index")
32
+ options.delete("namespace")
33
+ options["for"] = name_and_id["id"] unless options.key?("for")
34
+
35
+ if block_given?
36
+ content = @template_object.capture(&block)
37
+ else
38
+ content = if @content.blank?
39
+ @object_name.gsub!(/\[(.*)_attributes\]\[\d\]/, '.\1')
40
+ method_and_value = tag_value.present? ? "#{@method_name}.#{tag_value}" : @method_name
41
+
42
+ if object.respond_to?(:to_model)
43
+ key = object.class.model_name.i18n_key
44
+ i18n_default = ["#{key}.#{method_and_value}".to_sym, ""]
45
+ end
46
+
47
+ i18n_default ||= ""
48
+ I18n.t("#{@object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.label").presence
49
+ else
50
+ @content.to_s
51
+ end
52
+
53
+ content ||= if object && object.class.respond_to?(:human_attribute_name)
54
+ object.class.human_attribute_name(@method_name)
55
+ end
56
+
57
+ content ||= @method_name.humanize
58
+ end
59
+
60
+ label_tag(name_and_id["id"], content, options)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,13 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class MonthField < DatetimeField #:nodoc:
5
+ private
6
+
7
+ def format_date(value)
8
+ value.try(:strftime, "%Y-%m")
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class NumberField < TextField #:nodoc:
5
+ def render
6
+ options = @options.stringify_keys
7
+
8
+ if range = options.delete("in") || options.delete("within")
9
+ options.update("min" => range.min, "max" => range.max)
10
+ end
11
+
12
+ @options = options
13
+ super
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class PasswordField < TextField #:nodoc:
5
+ def render
6
+ @options = {:value => nil}.merge!(@options)
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,31 @@
1
+ require 'action_view/helpers/tags/checkable'
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module Tags
6
+ class RadioButton < Base #:nodoc:
7
+ include Checkable
8
+
9
+ def initialize(object_name, method_name, template_object, tag_value, options)
10
+ @tag_value = tag_value
11
+ super(object_name, method_name, template_object, options)
12
+ end
13
+
14
+ def render
15
+ options = @options.stringify_keys
16
+ options["type"] = "radio"
17
+ options["value"] = @tag_value
18
+ options["checked"] = "checked" if input_checked?(object, options)
19
+ add_default_name_and_id_for_value(@tag_value, options)
20
+ tag("input", options)
21
+ end
22
+
23
+ private
24
+
25
+ def checked?(value)
26
+ value.to_s == @tag_value.to_s
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class RangeField < NumberField #:nodoc:
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class SearchField < TextField #:nodoc:
5
+ def render
6
+ options = @options.stringify_keys
7
+
8
+ if options["autosave"]
9
+ if options["autosave"] == true
10
+ options["autosave"] = request.host.split(".").reverse.join(".")
11
+ end
12
+ options["results"] ||= 10
13
+ end
14
+
15
+ if options["onsearch"]
16
+ options["incremental"] = true unless options.has_key?("incremental")
17
+ end
18
+
19
+ super
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,41 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class Select < Base #:nodoc:
5
+ def initialize(object_name, method_name, template_object, choices, options, html_options)
6
+ @choices = choices
7
+ @choices = @choices.to_a if @choices.is_a?(Range)
8
+ @html_options = html_options
9
+
10
+ super(object_name, method_name, template_object, options)
11
+ end
12
+
13
+ def render
14
+ option_tags_options = {
15
+ :selected => @options.fetch(:selected) { value(@object) },
16
+ :disabled => @options[:disabled]
17
+ }
18
+
19
+ option_tags = if grouped_choices?
20
+ grouped_options_for_select(@choices, option_tags_options)
21
+ else
22
+ options_for_select(@choices, option_tags_options)
23
+ end
24
+
25
+ select_content_tag(option_tags, @options, @html_options)
26
+ end
27
+
28
+ private
29
+
30
+ # Grouped choices look like this:
31
+ #
32
+ # [nil, []]
33
+ # { nil => [] }
34
+ #
35
+ def grouped_choices?
36
+ !@choices.empty? && @choices.first.respond_to?(:last) && Array === @choices.first.last
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,8 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class TelField < TextField #:nodoc:
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class TextArea < Base #:nodoc:
5
+ def render
6
+ options = @options.stringify_keys
7
+ add_default_name_and_id(options)
8
+
9
+ if size = options.delete("size")
10
+ options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
11
+ end
12
+
13
+ content_tag("textarea", options.delete('value') || value_before_type_cast(object), options)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class TextField < Base #:nodoc:
5
+ def render
6
+ options = @options.stringify_keys
7
+ options["size"] = options["maxlength"] unless options.key?("size")
8
+ options["type"] ||= field_type
9
+ options["value"] = options.fetch("value"){ value_before_type_cast(object) } unless field_type == "file"
10
+ options["value"] &&= ERB::Util.html_escape(options["value"])
11
+ add_default_name_and_id(options)
12
+ tag("input", options)
13
+ end
14
+
15
+ class << self
16
+ def field_type
17
+ @field_type ||= self.name.split("::").last.sub("Field", "").downcase
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def field_type
24
+ self.class.field_type
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class TimeField < DatetimeField #:nodoc:
5
+ private
6
+
7
+ def format_date(value)
8
+ value.try(:strftime, "%T.%L")
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class TimeSelect < DateSelect #:nodoc:
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,20 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class TimeZoneSelect < Base #:nodoc:
5
+ def initialize(object_name, method_name, template_object, priority_zones, options, html_options)
6
+ @priority_zones = priority_zones
7
+ @html_options = html_options
8
+
9
+ super(object_name, method_name, template_object, options)
10
+ end
11
+
12
+ def render
13
+ select_content_tag(
14
+ time_zone_options_for_select(value(@object) || @options[:default], @priority_zones, @options[:model] || ActiveSupport::TimeZone), @options, @html_options
15
+ )
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,8 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class UrlField < TextField #:nodoc:
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags
4
+ class WeekField < DatetimeField #:nodoc:
5
+ private
6
+
7
+ def format_date(value)
8
+ value.try(:strftime, "%Y-W%W")
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,5 @@
1
- require 'active_support/core_ext/object/blank'
2
1
  require 'active_support/core_ext/string/filters'
2
+ require 'active_support/core_ext/array/extract_options'
3
3
 
4
4
  module ActionView
5
5
  # = Action View Text Helpers
@@ -36,7 +36,6 @@ module ActionView
36
36
  # do not operate as expected in an eRuby code block. If you absolutely must
37
37
  # output text within a non-output code block (i.e., <% %>), you can use the concat method.
38
38
  #
39
- # ==== Examples
40
39
  # <%
41
40
  # concat "hello"
42
41
  # # is the equivalent of <%= "hello" %>
@@ -44,7 +43,7 @@ module ActionView
44
43
  # if logged_in
45
44
  # concat "Logged in!"
46
45
  # else
47
- # concat link_to('login', :action => login)
46
+ # concat link_to('login', action: :login)
48
47
  # end
49
48
  # # will either display "Logged in!" or a login link
50
49
  # %>
@@ -62,128 +61,121 @@ module ActionView
62
61
  #
63
62
  # Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
64
63
  #
65
- # The result is not marked as HTML-safe, so will be subject to the default escaping when
66
- # used in views, unless wrapped by <tt>raw()</tt>. Care should be taken if +text+ contains HTML tags
67
- # or entities, because truncation may produce invalid HTML (such as unbalanced or incomplete tags).
64
+ # Pass a block if you want to show extra content when the text is truncated.
68
65
  #
69
- # ==== Examples
66
+ # The result is marked as HTML-safe, but it is escaped by default, unless <tt>:escape</tt> is
67
+ # +false+. Care should be taken if +text+ contains HTML tags or entities, because truncation
68
+ # may produce invalid HTML (such as unbalanced or incomplete tags).
70
69
  #
71
70
  # truncate("Once upon a time in a world far far away")
72
71
  # # => "Once upon a time in a world..."
73
72
  #
74
- # truncate("Once upon a time in a world far far away", :length => 17)
73
+ # truncate("Once upon a time in a world far far away", length: 17)
75
74
  # # => "Once upon a ti..."
76
75
  #
77
- # truncate("Once upon a time in a world far far away", :length => 17, :separator => ' ')
76
+ # truncate("Once upon a time in a world far far away", length: 17, separator: ' ')
78
77
  # # => "Once upon a..."
79
78
  #
80
- # truncate("And they found that many people were sleeping better.", :length => 25, :omission => '... (continued)')
79
+ # truncate("And they found that many people were sleeping better.", length: 25, omission: '... (continued)')
81
80
  # # => "And they f... (continued)"
82
81
  #
83
82
  # truncate("<p>Once upon a time in a world far far away</p>")
84
83
  # # => "<p>Once upon a time in a wo..."
85
- def truncate(text, options = {})
86
- options.reverse_merge!(:length => 30)
87
- text.truncate(options.delete(:length), options) if text
84
+ #
85
+ # truncate("Once upon a time in a world far far away") { link_to "Continue", "#" }
86
+ # # => "Once upon a time in a wo...<a href="#">Continue</a>"
87
+ def truncate(text, options = {}, &block)
88
+ if text
89
+ length = options.fetch(:length, 30)
90
+
91
+ content = text.truncate(length, options)
92
+ content = options[:escape] == false ? content.html_safe : ERB::Util.html_escape(content)
93
+ content << capture(&block) if block_given? && text.length > length
94
+ content
95
+ end
88
96
  end
89
97
 
90
98
  # Highlights one or more +phrases+ everywhere in +text+ by inserting it into
91
99
  # a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
92
- # as a single-quoted string with \1 where the phrase is to be inserted (defaults to
93
- # '<strong class="highlight">\1</strong>')
100
+ # as a single-quoted string with <tt>\1</tt> where the phrase is to be inserted (defaults to
101
+ # '<mark>\1</mark>')
94
102
  #
95
- # ==== Examples
96
103
  # highlight('You searched for: rails', 'rails')
97
- # # => You searched for: <strong class="highlight">rails</strong>
104
+ # # => You searched for: <mark>rails</mark>
98
105
  #
99
106
  # highlight('You searched for: ruby, rails, dhh', 'actionpack')
100
107
  # # => You searched for: ruby, rails, dhh
101
108
  #
102
- # highlight('You searched for: rails', ['for', 'rails'], :highlighter => '<em>\1</em>')
109
+ # highlight('You searched for: rails', ['for', 'rails'], highlighter: '<em>\1</em>')
103
110
  # # => You searched <em>for</em>: <em>rails</em>
104
111
  #
105
- # highlight('You searched for: rails', 'rails', :highlighter => '<a href="search?q=\1">\1</a>')
112
+ # highlight('You searched for: rails', 'rails', highlighter: '<a href="search?q=\1">\1</a>')
106
113
  # # => You searched for: <a href="search?q=rails">rails</a>
107
- #
108
- # You can still use <tt>highlight</tt> with the old API that accepts the
109
- # +highlighter+ as its optional third parameter:
110
- # highlight('You searched for: rails', 'rails', '<a href="search?q=\1">\1</a>') # => You searched for: <a href="search?q=rails">rails</a>
111
- def highlight(text, phrases, *args)
112
- options = args.extract_options!
113
- unless args.empty?
114
- ActiveSupport::Deprecation.warn "Calling highlight with a highlighter as an argument is deprecated. " \
115
- "Please call with :highlighter => '#{args[0]}' instead.", caller
114
+ def highlight(text, phrases, options = {})
115
+ text = sanitize(text) if options.fetch(:sanitize, true)
116
116
 
117
- options[:highlighter] = args[0] || '<strong class="highlight">\1</strong>'
118
- end
119
- options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
120
-
121
- text = sanitize(text) unless options[:sanitize] == false
122
117
  if text.blank? || phrases.blank?
123
118
  text
124
119
  else
120
+ highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
125
121
  match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
126
- text.gsub(/(#{match})(?![^<]*?>)/i, options[:highlighter])
122
+ text.gsub(/(#{match})(?![^<]*?>)/i, highlighter)
127
123
  end.html_safe
128
124
  end
129
125
 
130
126
  # Extracts an excerpt from +text+ that matches the first instance of +phrase+.
131
127
  # The <tt>:radius</tt> option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
132
128
  # defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
133
- # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. The resulting string
134
- # will be stripped in any case. If the +phrase+ isn't found, nil is returned.
129
+ # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. The
130
+ # <tt>:separator</tt> enable to choose the delimation. The resulting string will be stripped in any case. If the +phrase+
131
+ # isn't found, nil is returned.
135
132
  #
136
- # ==== Examples
137
- # excerpt('This is an example', 'an', :radius => 5)
133
+ # excerpt('This is an example', 'an', radius: 5)
138
134
  # # => ...s is an exam...
139
135
  #
140
- # excerpt('This is an example', 'is', :radius => 5)
136
+ # excerpt('This is an example', 'is', radius: 5)
141
137
  # # => This is a...
142
138
  #
143
139
  # excerpt('This is an example', 'is')
144
140
  # # => This is an example
145
141
  #
146
- # excerpt('This next thing is an example', 'ex', :radius => 2)
142
+ # excerpt('This next thing is an example', 'ex', radius: 2)
147
143
  # # => ...next...
148
144
  #
149
- # excerpt('This is also an example', 'an', :radius => 8, :omission => '<chop> ')
145
+ # excerpt('This is also an example', 'an', radius: 8, omission: '<chop> ')
150
146
  # # => <chop> is also an example
151
147
  #
152
- # You can still use <tt>excerpt</tt> with the old API that accepts the
153
- # +radius+ as its optional third and the +ellipsis+ as its
154
- # optional forth parameter:
155
- # excerpt('This is an example', 'an', 5) # => ...s is an exam...
156
- # excerpt('This is also an example', 'an', 8, '<chop> ') # => <chop> is also an example
157
- def excerpt(text, phrase, *args)
148
+ # excerpt('This is a very beautiful morning', 'very', separator: ' ', radius: 1)
149
+ # # => ...a very beautiful...
150
+ def excerpt(text, phrase, options = {})
158
151
  return unless text && phrase
159
152
 
160
- options = args.extract_options!
161
- unless args.empty?
162
- ActiveSupport::Deprecation.warn "Calling excerpt with radius and omission as arguments is deprecated. " \
163
- "Please call with :radius => #{args[0]}#{", :omission => '#{args[1]}'" if args[1]} instead.", caller
153
+ separator = options.fetch(:separator, "")
154
+ phrase = Regexp.escape(phrase)
155
+ regex = /#{phrase}/i
164
156
 
165
- options[:radius] = args[0] || 100
166
- options[:omission] = args[1] || "..."
167
- end
168
- options.reverse_merge!(:radius => 100, :omission => "...")
157
+ return unless matches = text.match(regex)
158
+ phrase = matches[0]
169
159
 
170
- phrase = Regexp.escape(phrase)
171
- return unless found_pos = text.mb_chars =~ /(#{phrase})/i
160
+ text.split(separator).each do |value|
161
+ if value.match(regex)
162
+ regex = phrase = value
163
+ break
164
+ end
165
+ end
172
166
 
173
- start_pos = [ found_pos - options[:radius], 0 ].max
174
- end_pos = [ [ found_pos + phrase.mb_chars.length + options[:radius] - 1, 0].max, text.mb_chars.length ].min
167
+ first_part, second_part = text.split(regex, 2)
175
168
 
176
- prefix = start_pos > 0 ? options[:omission] : ""
177
- postfix = end_pos < text.mb_chars.length - 1 ? options[:omission] : ""
169
+ prefix, first_part = cut_excerpt_part(:first, first_part, separator, options)
170
+ postfix, second_part = cut_excerpt_part(:second, second_part, separator, options)
178
171
 
179
- prefix + text.mb_chars[start_pos..end_pos].strip + postfix
172
+ prefix + (first_part + separator + phrase + separator + second_part).strip + postfix
180
173
  end
181
174
 
182
175
  # Attempts to pluralize the +singular+ word unless +count+ is 1. If
183
176
  # +plural+ is supplied, it will use that when count is > 1, otherwise
184
- # it will use the Inflector to determine the plural form
177
+ # it will use the Inflector to determine the plural form.
185
178
  #
186
- # ==== Examples
187
179
  # pluralize(1, 'person')
188
180
  # # => 1 person
189
181
  #
@@ -196,42 +188,35 @@ module ActionView
196
188
  # pluralize(0, 'person')
197
189
  # # => 0 people
198
190
  def pluralize(count, singular, plural = nil)
199
- "#{count || 0} " + ((count == 1 || count =~ /^1(\.0+)?$/) ? singular : (plural || singular.pluralize))
191
+ word = if (count == 1 || count =~ /^1(\.0+)?$/)
192
+ singular
193
+ else
194
+ plural || singular.pluralize
195
+ end
196
+
197
+ "#{count || 0} #{word}"
200
198
  end
201
199
 
202
200
  # Wraps the +text+ into lines no longer than +line_width+ width. This method
203
201
  # breaks on the first whitespace character that does not exceed +line_width+
204
202
  # (which is 80 by default).
205
203
  #
206
- # ==== Examples
207
- #
208
204
  # word_wrap('Once upon a time')
209
205
  # # => Once upon a time
210
206
  #
211
207
  # word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...')
212
- # # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\n a successor to the throne turned out to be more trouble than anyone could have\n imagined...
208
+ # # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\na successor to the throne turned out to be more trouble than anyone could have\nimagined...
213
209
  #
214
- # word_wrap('Once upon a time', :line_width => 8)
215
- # # => Once upon\na time
210
+ # word_wrap('Once upon a time', line_width: 8)
211
+ # # => Once\nupon a\ntime
216
212
  #
217
- # word_wrap('Once upon a time', :line_width => 1)
213
+ # word_wrap('Once upon a time', line_width: 1)
218
214
  # # => Once\nupon\na\ntime
219
- #
220
- # You can still use <tt>word_wrap</tt> with the old API that accepts the
221
- # +line_width+ as its optional second parameter:
222
- # word_wrap('Once upon a time', 8) # => Once upon\na time
223
- def word_wrap(text, *args)
224
- options = args.extract_options!
225
- unless args.blank?
226
- ActiveSupport::Deprecation.warn "Calling word_wrap with line_width as an argument is deprecated. " \
227
- "Please call with :line_width => #{args[0]} instead.", caller
228
-
229
- options[:line_width] = args[0] || 80
230
- end
231
- options.reverse_merge!(:line_width => 80)
215
+ def word_wrap(text, options = {})
216
+ line_width = options.fetch(:line_width, 80)
232
217
 
233
218
  text.split("\n").collect do |line|
234
- line.length > options[:line_width] ? line.gsub(/(.{1,#{options[:line_width]}})(\s+|$)/, "\\1\n").strip : line
219
+ line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
235
220
  end * "\n"
236
221
  end
237
222
 
@@ -246,6 +231,7 @@ module ActionView
246
231
  #
247
232
  # ==== Options
248
233
  # * <tt>:sanitize</tt> - If +false+, does not sanitize +text+.
234
+ # * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt>
249
235
  #
250
236
  # ==== Examples
251
237
  # my_text = "Here is some basic text...\n...with a line break."
@@ -253,27 +239,32 @@ module ActionView
253
239
  # simple_format(my_text)
254
240
  # # => "<p>Here is some basic text...\n<br />...with a line break.</p>"
255
241
  #
242
+ # simple_format(my_text, {}, wrapper_tag: "div")
243
+ # # => "<div>Here is some basic text...\n<br />...with a line break.</div>"
244
+ #
256
245
  # more_text = "We want to put a paragraph...\n\n...right there."
257
246
  #
258
247
  # simple_format(more_text)
259
248
  # # => "<p>We want to put a paragraph...</p>\n\n<p>...right there.</p>"
260
249
  #
261
- # simple_format("Look ma! A class!", :class => 'description')
250
+ # simple_format("Look ma! A class!", class: 'description')
262
251
  # # => "<p class='description'>Look ma! A class!</p>"
263
252
  #
264
- # simple_format("<span>I'm allowed!</span> It's true.", {}, :sanitize => false)
253
+ # simple_format("<span>I'm allowed!</span> It's true.", {}, sanitize: false)
265
254
  # # => "<p><span>I'm allowed!</span> It's true.</p>"
266
- def simple_format(text, html_options={}, options={})
267
- text = '' if text.nil?
268
- text = text.dup
269
- start_tag = tag('p', html_options, true)
270
- text = sanitize(text) unless options[:sanitize] == false
271
- text = text.to_str
272
- text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n
273
- text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph
274
- text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
275
- text.insert 0, start_tag
276
- text.html_safe.safe_concat("</p>")
255
+ def simple_format(text, html_options = {}, options = {})
256
+ wrapper_tag = options.fetch(:wrapper_tag, :p)
257
+
258
+ text = sanitize(text) if options.fetch(:sanitize, true)
259
+ paragraphs = split_paragraphs(text)
260
+
261
+ if paragraphs.empty?
262
+ content_tag(wrapper_tag, nil, html_options)
263
+ else
264
+ paragraphs.map { |paragraph|
265
+ content_tag(wrapper_tag, paragraph, html_options, options[:sanitize])
266
+ }.join("\n\n").html_safe
267
+ end
277
268
  end
278
269
 
279
270
  # Creates a Cycle object whose _to_s_ method cycles through elements of an
@@ -285,7 +276,6 @@ module ActionView
285
276
  # and passing the name of the cycle. The current cycle string can be obtained
286
277
  # anytime using the current_cycle method.
287
278
  #
288
- # ==== Examples
289
279
  # # Alternate CSS classes for even and odd numbers...
290
280
  # @items = [1,2,3,4]
291
281
  # <table>
@@ -298,15 +288,15 @@ module ActionView
298
288
  #
299
289
  #
300
290
  # # Cycle CSS classes for rows, and text colors for values within each row
301
- # @items = x = [{:first => 'Robert', :middle => 'Daniel', :last => 'James'},
302
- # {:first => 'Emily', :middle => 'Shannon', :maiden => 'Pike', :last => 'Hicks'},
303
- # {:first => 'June', :middle => 'Dae', :last => 'Jones'}]
291
+ # @items = x = [{first: 'Robert', middle: 'Daniel', last: 'James'},
292
+ # {first: 'Emily', middle: 'Shannon', maiden: 'Pike', last: 'Hicks'},
293
+ # {first: 'June', middle: 'Dae', last: 'Jones'}]
304
294
  # <% @items.each do |item| %>
305
- # <tr class="<%= cycle("odd", "even", :name => "row_class") -%>">
295
+ # <tr class="<%= cycle("odd", "even", name: "row_class") -%>">
306
296
  # <td>
307
297
  # <% item.values.each do |value| %>
308
298
  # <%# Create a named cycle "colors" %>
309
- # <span style="color:<%= cycle("red", "green", "blue", :name => "colors") -%>">
299
+ # <span style="color:<%= cycle("red", "green", "blue", name: "colors") -%>">
310
300
  # <%= value %>
311
301
  # </span>
312
302
  # <% end %>
@@ -315,12 +305,9 @@ module ActionView
315
305
  # </tr>
316
306
  # <% end %>
317
307
  def cycle(first_value, *values)
318
- if (values.last.instance_of? Hash)
319
- params = values.pop
320
- name = params[:name]
321
- else
322
- name = "default"
323
- end
308
+ options = values.extract_options!
309
+ name = options.fetch(:name, 'default')
310
+
324
311
  values.unshift(first_value)
325
312
 
326
313
  cycle = get_cycle(name)
@@ -334,7 +321,6 @@ module ActionView
334
321
  # for complex table highlighting or any other design need which requires
335
322
  # the current cycle string in more than one place.
336
323
  #
337
- # ==== Example
338
324
  # # Alternate background colors
339
325
  # @items = [1,2,3,4]
340
326
  # <% @items.each do |item| %>
@@ -350,14 +336,13 @@ module ActionView
350
336
  # Resets a cycle so that it starts from the first element the next time
351
337
  # it is called. Pass in +name+ to reset a named cycle.
352
338
  #
353
- # ==== Example
354
339
  # # Alternate CSS classes for even and odd numbers...
355
340
  # @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]]
356
341
  # <table>
357
342
  # <% @items.each do |item| %>
358
343
  # <tr class="<%= cycle("even", "odd") -%>">
359
344
  # <% item.each do |value| %>
360
- # <span style="color:<%= cycle("#333", "#666", "#999", :name => "colors") -%>">
345
+ # <span style="color:<%= cycle("#333", "#666", "#999", name: "colors") -%>">
361
346
  # <%= value %>
362
347
  # </span>
363
348
  # <% end %>
@@ -421,6 +406,34 @@ module ActionView
421
406
  @_cycles = Hash.new unless defined?(@_cycles)
422
407
  @_cycles[name] = cycle_object
423
408
  end
409
+
410
+ def split_paragraphs(text)
411
+ return [] if text.blank?
412
+
413
+ text.to_str.gsub(/\r\n?/, "\n").split(/\n\n+/).map! do |t|
414
+ t.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') || t
415
+ end
416
+ end
417
+
418
+ def cut_excerpt_part(part_position, part, separator, options)
419
+ return "", "" unless part
420
+
421
+ radius = options.fetch(:radius, 100)
422
+ omission = options.fetch(:omission, "...")
423
+
424
+ part = part.split(separator)
425
+ part.delete("")
426
+ affix = part.size > radius ? omission : ""
427
+
428
+ part = if part_position == :first
429
+ drop_index = [part.length - radius, 0].max
430
+ part.drop(drop_index)
431
+ else
432
+ part.first(radius)
433
+ end
434
+
435
+ return affix, part.join(separator)
436
+ end
424
437
  end
425
438
  end
426
439
  end