actionpack 3.2.22.5 → 5.2.4

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 (271) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +279 -603
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -297
  5. data/lib/abstract_controller/asset_paths.rb +4 -2
  6. data/lib/abstract_controller/base.rb +82 -52
  7. data/lib/abstract_controller/caching/fragments.rb +166 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +117 -103
  10. data/lib/abstract_controller/collector.rb +18 -7
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +65 -38
  13. data/lib/abstract_controller/logger.rb +3 -2
  14. data/lib/abstract_controller/railties/routes_helpers.rb +5 -3
  15. data/lib/abstract_controller/rendering.rb +77 -129
  16. data/lib/abstract_controller/translation.rb +21 -3
  17. data/lib/abstract_controller/url_for.rb +9 -7
  18. data/lib/abstract_controller.rb +12 -13
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/base.rb +81 -40
  22. data/lib/action_controller/caching.rb +22 -62
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +30 -18
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +190 -47
  27. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  28. data/lib/action_controller/metal/cookies.rb +3 -3
  29. data/lib/action_controller/metal/data_streaming.rb +40 -65
  30. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  31. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  32. data/lib/action_controller/metal/exceptions.rb +19 -12
  33. data/lib/action_controller/metal/flash.rb +42 -9
  34. data/lib/action_controller/metal/force_ssl.rb +79 -19
  35. data/lib/action_controller/metal/head.rb +35 -10
  36. data/lib/action_controller/metal/helpers.rb +31 -21
  37. data/lib/action_controller/metal/http_authentication.rb +182 -134
  38. data/lib/action_controller/metal/implicit_render.rb +62 -8
  39. data/lib/action_controller/metal/instrumentation.rb +28 -26
  40. data/lib/action_controller/metal/live.rb +312 -0
  41. data/lib/action_controller/metal/mime_responds.rb +159 -163
  42. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  43. data/lib/action_controller/metal/params_wrapper.rb +146 -93
  44. data/lib/action_controller/metal/redirecting.rb +80 -56
  45. data/lib/action_controller/metal/renderers.rb +119 -47
  46. data/lib/action_controller/metal/rendering.rb +89 -32
  47. data/lib/action_controller/metal/request_forgery_protection.rb +373 -41
  48. data/lib/action_controller/metal/rescue.rb +9 -16
  49. data/lib/action_controller/metal/streaming.rb +39 -45
  50. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  51. data/lib/action_controller/metal/testing.rb +8 -29
  52. data/lib/action_controller/metal/url_for.rb +43 -32
  53. data/lib/action_controller/metal.rb +112 -106
  54. data/lib/action_controller/railtie.rb +56 -18
  55. data/lib/action_controller/railties/helpers.rb +24 -0
  56. data/lib/action_controller/renderer.rb +117 -0
  57. data/lib/action_controller/template_assertions.rb +11 -0
  58. data/lib/action_controller/test_case.rb +402 -347
  59. data/lib/action_controller.rb +31 -30
  60. data/lib/action_dispatch/http/cache.rb +133 -34
  61. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  62. data/lib/action_dispatch/http/filter_parameters.rb +40 -24
  63. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  64. data/lib/action_dispatch/http/headers.rb +117 -16
  65. data/lib/action_dispatch/http/mime_negotiation.rb +98 -33
  66. data/lib/action_dispatch/http/mime_type.rb +198 -146
  67. data/lib/action_dispatch/http/mime_types.rb +22 -7
  68. data/lib/action_dispatch/http/parameter_filter.rb +61 -49
  69. data/lib/action_dispatch/http/parameters.rb +94 -51
  70. data/lib/action_dispatch/http/rack_cache.rb +4 -3
  71. data/lib/action_dispatch/http/request.rb +262 -117
  72. data/lib/action_dispatch/http/response.rb +400 -86
  73. data/lib/action_dispatch/http/upload.rb +66 -29
  74. data/lib/action_dispatch/http/url.rb +232 -60
  75. data/lib/action_dispatch/journey/formatter.rb +189 -0
  76. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  79. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  80. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  81. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  82. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  83. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  84. data/lib/action_dispatch/journey/parser.rb +199 -0
  85. data/lib/action_dispatch/journey/parser.y +50 -0
  86. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  87. data/lib/action_dispatch/journey/path/pattern.rb +199 -0
  88. data/lib/action_dispatch/journey/route.rb +203 -0
  89. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  90. data/lib/action_dispatch/journey/router.rb +156 -0
  91. data/lib/action_dispatch/journey/routes.rb +82 -0
  92. data/lib/action_dispatch/journey/scanner.rb +64 -0
  93. data/lib/action_dispatch/journey/visitors.rb +268 -0
  94. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  95. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  96. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  97. data/lib/action_dispatch/journey.rb +7 -0
  98. data/lib/action_dispatch/middleware/callbacks.rb +17 -13
  99. data/lib/action_dispatch/middleware/cookies.rb +494 -162
  100. data/lib/action_dispatch/middleware/debug_exceptions.rb +176 -53
  101. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  102. data/lib/action_dispatch/middleware/exception_wrapper.rb +103 -38
  103. data/lib/action_dispatch/middleware/executor.rb +21 -0
  104. data/lib/action_dispatch/middleware/flash.rb +128 -91
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +43 -16
  106. data/lib/action_dispatch/middleware/reloader.rb +6 -83
  107. data/lib/action_dispatch/middleware/remote_ip.rb +151 -49
  108. data/lib/action_dispatch/middleware/request_id.rb +19 -15
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +38 -34
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +14 -9
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +94 -44
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -4
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +36 -61
  114. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  115. data/lib/action_dispatch/middleware/stack.rb +33 -41
  116. data/lib/action_dispatch/middleware/static.rb +92 -48
  117. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +134 -5
  128. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  136. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  138. data/lib/action_dispatch/railtie.rb +29 -8
  139. data/lib/action_dispatch/request/session.rb +234 -0
  140. data/lib/action_dispatch/request/utils.rb +78 -0
  141. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  142. data/lib/action_dispatch/routing/inspector.rb +225 -0
  143. data/lib/action_dispatch/routing/mapper.rb +1329 -582
  144. data/lib/action_dispatch/routing/polymorphic_routes.rb +237 -94
  145. data/lib/action_dispatch/routing/redirection.rb +120 -50
  146. data/lib/action_dispatch/routing/route_set.rb +545 -322
  147. data/lib/action_dispatch/routing/routes_proxy.rb +37 -7
  148. data/lib/action_dispatch/routing/url_for.rb +103 -34
  149. data/lib/action_dispatch/routing.rb +66 -99
  150. data/lib/action_dispatch/system_test_case.rb +147 -0
  151. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  152. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  153. data/lib/action_dispatch/system_testing/server.rb +31 -0
  154. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  157. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  158. data/lib/action_dispatch/testing/assertions/response.rb +53 -42
  159. data/lib/action_dispatch/testing/assertions/routing.rb +79 -74
  160. data/lib/action_dispatch/testing/assertions.rb +15 -9
  161. data/lib/action_dispatch/testing/integration.rb +361 -207
  162. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  163. data/lib/action_dispatch/testing/test_process.rb +28 -19
  164. data/lib/action_dispatch/testing/test_request.rb +30 -33
  165. data/lib/action_dispatch/testing/test_response.rb +35 -11
  166. data/lib/action_dispatch.rb +42 -32
  167. data/lib/action_pack/gem_version.rb +17 -0
  168. data/lib/action_pack/version.rb +7 -7
  169. data/lib/action_pack.rb +4 -2
  170. metadata +116 -175
  171. data/lib/abstract_controller/layouts.rb +0 -423
  172. data/lib/abstract_controller/view_paths.rb +0 -96
  173. data/lib/action_controller/caching/actions.rb +0 -185
  174. data/lib/action_controller/caching/fragments.rb +0 -127
  175. data/lib/action_controller/caching/pages.rb +0 -187
  176. data/lib/action_controller/caching/sweeping.rb +0 -97
  177. data/lib/action_controller/deprecated/integration_test.rb +0 -2
  178. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  179. data/lib/action_controller/deprecated.rb +0 -3
  180. data/lib/action_controller/metal/compatibility.rb +0 -65
  181. data/lib/action_controller/metal/hide_actions.rb +0 -41
  182. data/lib/action_controller/metal/rack_delegation.rb +0 -26
  183. data/lib/action_controller/metal/responder.rb +0 -286
  184. data/lib/action_controller/metal/session_management.rb +0 -14
  185. data/lib/action_controller/middleware.rb +0 -39
  186. data/lib/action_controller/railties/paths.rb +0 -25
  187. data/lib/action_controller/record_identifier.rb +0 -85
  188. data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
  189. data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
  190. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
  191. data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
  192. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
  193. data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
  194. data/lib/action_controller/vendor/html-scanner.rb +0 -20
  195. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  196. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  197. data/lib/action_dispatch/middleware/head.rb +0 -18
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -75
  199. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  200. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
  201. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
  202. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
  203. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
  204. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
  205. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
  206. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
  207. data/lib/action_dispatch/testing/assertions/dom.rb +0 -37
  208. data/lib/action_dispatch/testing/assertions/selector.rb +0 -435
  209. data/lib/action_dispatch/testing/assertions/tag.rb +0 -138
  210. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  211. data/lib/action_view/asset_paths.rb +0 -142
  212. data/lib/action_view/base.rb +0 -220
  213. data/lib/action_view/buffers.rb +0 -43
  214. data/lib/action_view/context.rb +0 -36
  215. data/lib/action_view/flows.rb +0 -79
  216. data/lib/action_view/helpers/active_model_helper.rb +0 -50
  217. data/lib/action_view/helpers/asset_paths.rb +0 -7
  218. data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
  219. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  220. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  221. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  222. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  223. data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
  224. data/lib/action_view/helpers/cache_helper.rb +0 -64
  225. data/lib/action_view/helpers/capture_helper.rb +0 -203
  226. data/lib/action_view/helpers/controller_helper.rb +0 -25
  227. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  228. data/lib/action_view/helpers/date_helper.rb +0 -1062
  229. data/lib/action_view/helpers/debug_helper.rb +0 -40
  230. data/lib/action_view/helpers/form_helper.rb +0 -1486
  231. data/lib/action_view/helpers/form_options_helper.rb +0 -658
  232. data/lib/action_view/helpers/form_tag_helper.rb +0 -685
  233. data/lib/action_view/helpers/javascript_helper.rb +0 -110
  234. data/lib/action_view/helpers/number_helper.rb +0 -622
  235. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  236. data/lib/action_view/helpers/record_tag_helper.rb +0 -111
  237. data/lib/action_view/helpers/rendering_helper.rb +0 -92
  238. data/lib/action_view/helpers/sanitize_helper.rb +0 -259
  239. data/lib/action_view/helpers/tag_helper.rb +0 -167
  240. data/lib/action_view/helpers/text_helper.rb +0 -426
  241. data/lib/action_view/helpers/translation_helper.rb +0 -91
  242. data/lib/action_view/helpers/url_helper.rb +0 -693
  243. data/lib/action_view/helpers.rb +0 -60
  244. data/lib/action_view/locale/en.yml +0 -160
  245. data/lib/action_view/log_subscriber.rb +0 -28
  246. data/lib/action_view/lookup_context.rb +0 -258
  247. data/lib/action_view/path_set.rb +0 -101
  248. data/lib/action_view/railtie.rb +0 -55
  249. data/lib/action_view/renderer/abstract_renderer.rb +0 -41
  250. data/lib/action_view/renderer/partial_renderer.rb +0 -415
  251. data/lib/action_view/renderer/renderer.rb +0 -61
  252. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
  253. data/lib/action_view/renderer/template_renderer.rb +0 -95
  254. data/lib/action_view/template/error.rb +0 -128
  255. data/lib/action_view/template/handlers/builder.rb +0 -26
  256. data/lib/action_view/template/handlers/erb.rb +0 -125
  257. data/lib/action_view/template/handlers.rb +0 -50
  258. data/lib/action_view/template/resolver.rb +0 -298
  259. data/lib/action_view/template/text.rb +0 -30
  260. data/lib/action_view/template.rb +0 -337
  261. data/lib/action_view/test_case.rb +0 -246
  262. data/lib/action_view/testing/resolvers.rb +0 -49
  263. data/lib/action_view.rb +0 -84
  264. data/lib/sprockets/assets.rake +0 -99
  265. data/lib/sprockets/bootstrap.rb +0 -37
  266. data/lib/sprockets/compressors.rb +0 -83
  267. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  268. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  269. data/lib/sprockets/helpers.rb +0 -6
  270. data/lib/sprockets/railtie.rb +0 -62
  271. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,1062 +0,0 @@
1
- require 'date'
2
- require 'action_view/helpers/tag_helper'
3
- require 'active_support/core_ext/date/conversions'
4
- require 'active_support/core_ext/hash/slice'
5
- require 'active_support/core_ext/object/with_options'
6
-
7
- module ActionView
8
- module Helpers
9
- # = Action View Date Helpers
10
- #
11
- # The Date Helper primarily creates select/option tags for different kinds of dates and times or date and time
12
- # elements. All of the select-type methods share a number of common options that are as follows:
13
- #
14
- # * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday"
15
- # would give birthday[month] instead of date[month] if passed to the <tt>select_month</tt> method.
16
- # * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date.
17
- # * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true,
18
- # the <tt>select_month</tt> method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead
19
- # of "date[month]".
20
- module DateHelper
21
- # Reports the approximate distance in time between two Time, Date or DateTime objects or integers as seconds.
22
- # Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs.
23
- # Distances are reported based on the following table:
24
- #
25
- # 0 <-> 29 secs # => less than a minute
26
- # 30 secs <-> 1 min, 29 secs # => 1 minute
27
- # 1 min, 30 secs <-> 44 mins, 29 secs # => [2..44] minutes
28
- # 44 mins, 30 secs <-> 89 mins, 29 secs # => about 1 hour
29
- # 89 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
30
- # 23 hrs, 59 mins, 30 secs <-> 41 hrs, 59 mins, 29 secs # => 1 day
31
- # 41 hrs, 59 mins, 30 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
32
- # 29 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 1 month
33
- # 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months
34
- # 1 yr <-> 1 yr, 3 months # => about 1 year
35
- # 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year
36
- # 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years
37
- # 2 yrs <-> max time or date # => (same rules as 1 yr)
38
- #
39
- # With <tt>include_seconds</tt> = true and the difference < 1 minute 29 seconds:
40
- # 0-4 secs # => less than 5 seconds
41
- # 5-9 secs # => less than 10 seconds
42
- # 10-19 secs # => less than 20 seconds
43
- # 20-39 secs # => half a minute
44
- # 40-59 secs # => less than a minute
45
- # 60-89 secs # => 1 minute
46
- #
47
- # ==== Examples
48
- # from_time = Time.now
49
- # distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
50
- # distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
51
- # distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
52
- # distance_of_time_in_words(from_time, from_time + 15.seconds, true) # => less than 20 seconds
53
- # distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years
54
- # distance_of_time_in_words(from_time, from_time + 60.hours) # => 3 days
55
- # distance_of_time_in_words(from_time, from_time + 45.seconds, true) # => less than a minute
56
- # distance_of_time_in_words(from_time, from_time - 45.seconds, true) # => less than a minute
57
- # distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
58
- # distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
59
- # distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years
60
- # distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years
61
- #
62
- # to_time = Time.now + 6.years + 19.days
63
- # distance_of_time_in_words(from_time, to_time, true) # => about 6 years
64
- # distance_of_time_in_words(to_time, from_time, true) # => about 6 years
65
- # distance_of_time_in_words(Time.now, Time.now) # => less than a minute
66
- #
67
- def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {})
68
- options = {
69
- :scope => :'datetime.distance_in_words',
70
- }.merge!(options)
71
-
72
- from_time = from_time.to_time if from_time.respond_to?(:to_time)
73
- to_time = to_time.to_time if to_time.respond_to?(:to_time)
74
- distance = (to_time.to_f - from_time.to_f).abs
75
- distance_in_minutes = (distance / 60.0).round
76
- distance_in_seconds = distance.round
77
-
78
- I18n.with_options :locale => options[:locale], :scope => options[:scope] do |locale|
79
- case distance_in_minutes
80
- when 0..1
81
- return distance_in_minutes == 0 ?
82
- locale.t(:less_than_x_minutes, :count => 1) :
83
- locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds
84
-
85
- case distance_in_seconds
86
- when 0..4 then locale.t :less_than_x_seconds, :count => 5
87
- when 5..9 then locale.t :less_than_x_seconds, :count => 10
88
- when 10..19 then locale.t :less_than_x_seconds, :count => 20
89
- when 20..39 then locale.t :half_a_minute
90
- when 40..59 then locale.t :less_than_x_minutes, :count => 1
91
- else locale.t :x_minutes, :count => 1
92
- end
93
-
94
- when 2..44 then locale.t :x_minutes, :count => distance_in_minutes
95
- when 45..89 then locale.t :about_x_hours, :count => 1
96
- when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
97
- when 1440..2519 then locale.t :x_days, :count => 1
98
- when 2520..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
99
- when 43200..86399 then locale.t :about_x_months, :count => 1
100
- when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
101
- else
102
- fyear = from_time.year
103
- fyear += 1 if from_time.month >= 3
104
- tyear = to_time.year
105
- tyear -= 1 if to_time.month < 3
106
- leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count{|x| Date.leap?(x)}
107
- minute_offset_for_leap_year = leap_years * 1440
108
- # Discount the leap year days when calculating year distance.
109
- # e.g. if there are 20 leap year days between 2 dates having the same day
110
- # and month then the based on 365 days calculation
111
- # the distance in years will come out to over 80 years when in written
112
- # english it would read better as about 80 years.
113
- minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
114
- remainder = (minutes_with_offset % 525600)
115
- distance_in_years = (minutes_with_offset.div 525600)
116
- if remainder < 131400
117
- locale.t(:about_x_years, :count => distance_in_years)
118
- elsif remainder < 394200
119
- locale.t(:over_x_years, :count => distance_in_years)
120
- else
121
- locale.t(:almost_x_years, :count => distance_in_years + 1)
122
- end
123
- end
124
- end
125
- end
126
-
127
- # Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
128
- #
129
- # ==== Examples
130
- # time_ago_in_words(3.minutes.from_now) # => 3 minutes
131
- # time_ago_in_words(Time.now - 15.hours) # => about 15 hours
132
- # time_ago_in_words(Time.now) # => less than a minute
133
- #
134
- # from_time = Time.now - 3.days - 14.minutes - 25.seconds
135
- # time_ago_in_words(from_time) # => 3 days
136
- #
137
- def time_ago_in_words(from_time, include_seconds = false, options = {})
138
- distance_of_time_in_words(from_time, Time.now, include_seconds, options)
139
- end
140
-
141
- alias_method :distance_of_time_in_words_to_now, :time_ago_in_words
142
-
143
- # Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based
144
- # attribute (identified by +method+) on an object assigned to the template (identified by +object+).
145
- #
146
- #
147
- # ==== Options
148
- # * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
149
- # "2" instead of "February").
150
- # * <tt>:use_short_month</tt> - Set to true if you want to use abbreviated month names instead of full
151
- # month names (e.g. "Feb" instead of "February").
152
- # * <tt>:add_month_numbers</tt> - Set to true if you want to use both month numbers and month names (e.g.
153
- # "2 - February" instead of "February").
154
- # * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names.
155
- # Note: You can also use Rails' i18n functionality for this.
156
- # * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
157
- # * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>.
158
- # * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>.
159
- # * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
160
- # as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
161
- # first of the given month in order to not create invalid dates like 31 February.
162
- # * <tt>:discard_month</tt> - Set to true if you don't want to show a month select. This includes the month
163
- # as a hidden field instead of showing a select field. Also note that this implicitly sets :discard_day to true.
164
- # * <tt>:discard_year</tt> - Set to true if you don't want to show a year select. This includes the year
165
- # as a hidden field instead of showing a select field.
166
- # * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> to
167
- # customize the order in which the select fields are shown. If you leave out any of the symbols, the respective
168
- # select will not be shown (like when you set <tt>:discard_xxx => true</tt>. Defaults to the order defined in
169
- # the respective locale (e.g. [:year, :month, :day] in the en locale that ships with Rails).
170
- # * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
171
- # dates.
172
- # * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
173
- # * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
174
- # * <tt>:prompt</tt> - Set to true (for a generic prompt), a prompt string or a hash of prompt strings
175
- # for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
176
- # Setting this option prepends a select option with a generic prompt (Day, Month, Year, Hour, Minute, Seconds)
177
- # or the given prompt string.
178
- #
179
- # If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
180
- #
181
- # NOTE: Discarded selects will default to 1. So if no month select is available, January will be assumed.
182
- #
183
- # ==== Examples
184
- # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute.
185
- # date_select("article", "written_on")
186
- #
187
- # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
188
- # # with the year in the year drop down box starting at 1995.
189
- # date_select("article", "written_on", :start_year => 1995)
190
- #
191
- # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
192
- # # with the year in the year drop down box starting at 1995, numbers used for months instead of words,
193
- # # and without a day select box.
194
- # date_select("article", "written_on", :start_year => 1995, :use_month_numbers => true,
195
- # :discard_day => true, :include_blank => true)
196
- #
197
- # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
198
- # # with the fields ordered as day, month, year rather than month, day, year.
199
- # date_select("article", "written_on", :order => [:day, :month, :year])
200
- #
201
- # # Generates a date select that when POSTed is stored in the user variable, in the birthday attribute
202
- # # lacking a year field.
203
- # date_select("user", "birthday", :order => [:month, :day])
204
- #
205
- # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
206
- # # which is initially set to the date 3 days from the current date
207
- # date_select("article", "written_on", :default => 3.days.from_now)
208
- #
209
- # # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
210
- # # that will have a default day of 20.
211
- # date_select("credit_card", "bill_due", :default => { :day => 20 })
212
- #
213
- # # Generates a date select with custom prompts.
214
- # date_select("article", "written_on", :prompt => { :day => 'Select day', :month => 'Select month', :year => 'Select year' })
215
- #
216
- # The selects are prepared for multi-parameter assignment to an Active Record object.
217
- #
218
- # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
219
- # all month choices are valid.
220
- def date_select(object_name, method, options = {}, html_options = {})
221
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_date_select_tag(options, html_options)
222
- end
223
-
224
- # Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a
225
- # specified time-based attribute (identified by +method+) on an object assigned to the template (identified by
226
- # +object+). You can include the seconds with <tt>:include_seconds</tt>. You can get hours in the AM/PM format
227
- # with <tt>:ampm</tt> option.
228
- #
229
- # This method will also generate 3 input hidden tags, for the actual year, month and day unless the option
230
- # <tt>:ignore_date</tt> is set to +true+. If you set the <tt>:ignore_date</tt> to +true+, you must have a
231
- # +date_select+ on the same method within the form otherwise an exception will be raised.
232
- #
233
- # If anything is passed in the html_options hash it will be applied to every select tag in the set.
234
- #
235
- # ==== Examples
236
- # # Creates a time select tag that, when POSTed, will be stored in the article variable in the sunrise attribute.
237
- # time_select("article", "sunrise")
238
- #
239
- # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the article variables in
240
- # # the sunrise attribute.
241
- # time_select("article", "start_time", :include_seconds => true)
242
- #
243
- # # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30 and 45.
244
- # time_select 'game', 'game_time', {:minute_step => 15}
245
- #
246
- # # Creates a time select tag with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
247
- # time_select("article", "written_on", :prompt => {:hour => 'Choose hour', :minute => 'Choose minute', :second => 'Choose seconds'})
248
- # time_select("article", "written_on", :prompt => {:hour => true}) # generic prompt for hours
249
- # time_select("article", "written_on", :prompt => true) # generic prompts for all
250
- #
251
- # # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM.
252
- # time_select 'game', 'game_time', {:ampm => true}
253
- #
254
- # The selects are prepared for multi-parameter assignment to an Active Record object.
255
- #
256
- # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
257
- # all month choices are valid.
258
- def time_select(object_name, method, options = {}, html_options = {})
259
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_time_select_tag(options, html_options)
260
- end
261
-
262
- # Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a
263
- # specified datetime-based attribute (identified by +method+) on an object assigned to the template (identified
264
- # by +object+).
265
- #
266
- # If anything is passed in the html_options hash it will be applied to every select tag in the set.
267
- #
268
- # ==== Examples
269
- # # Generates a datetime select that, when POSTed, will be stored in the article variable in the written_on
270
- # # attribute.
271
- # datetime_select("article", "written_on")
272
- #
273
- # # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the
274
- # # article variable in the written_on attribute.
275
- # datetime_select("article", "written_on", :start_year => 1995)
276
- #
277
- # # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will
278
- # # be stored in the trip variable in the departing attribute.
279
- # datetime_select("trip", "departing", :default => 3.days.from_now)
280
- #
281
- # # Generate a datetime select with hours in the AM/PM format
282
- # datetime_select("article", "written_on", :ampm => true)
283
- #
284
- # # Generates a datetime select that discards the type that, when POSTed, will be stored in the article variable
285
- # # as the written_on attribute.
286
- # datetime_select("article", "written_on", :discard_type => true)
287
- #
288
- # # Generates a datetime select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
289
- # datetime_select("article", "written_on", :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
290
- # datetime_select("article", "written_on", :prompt => {:hour => true}) # generic prompt for hours
291
- # datetime_select("article", "written_on", :prompt => true) # generic prompts for all
292
- #
293
- # The selects are prepared for multi-parameter assignment to an Active Record object.
294
- def datetime_select(object_name, method, options = {}, html_options = {})
295
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_datetime_select_tag(options, html_options)
296
- end
297
-
298
- # Returns a set of html select-tags (one for year, month, day, hour, minute, and second) pre-selected with the
299
- # +datetime+. It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with
300
- # an array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not
301
- # supply a Symbol, it will be appended onto the <tt>:order</tt> passed in. You can also add
302
- # <tt>:date_separator</tt>, <tt>:datetime_separator</tt> and <tt>:time_separator</tt> keys to the +options+ to
303
- # control visual display of the elements.
304
- #
305
- # If anything is passed in the html_options hash it will be applied to every select tag in the set.
306
- #
307
- # ==== Examples
308
- # my_date_time = Time.now + 4.days
309
- #
310
- # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today).
311
- # select_datetime(my_date_time)
312
- #
313
- # # Generates a datetime select that defaults to today (no specified datetime)
314
- # select_datetime()
315
- #
316
- # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
317
- # # with the fields ordered year, month, day rather than month, day, year.
318
- # select_datetime(my_date_time, :order => [:year, :month, :day])
319
- #
320
- # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
321
- # # with a '/' between each date field.
322
- # select_datetime(my_date_time, :date_separator => '/')
323
- #
324
- # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
325
- # # with a date fields separated by '/', time fields separated by '' and the date and time fields
326
- # # separated by a comma (',').
327
- # select_datetime(my_date_time, :date_separator => '/', :time_separator => '', :datetime_separator => ',')
328
- #
329
- # # Generates a datetime select that discards the type of the field and defaults to the datetime in
330
- # # my_date_time (four days after today)
331
- # select_datetime(my_date_time, :discard_type => true)
332
- #
333
- # # Generate a datetime field with hours in the AM/PM format
334
- # select_datetime(my_date_time, :ampm => true)
335
- #
336
- # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
337
- # # prefixed with 'payday' rather than 'date'
338
- # select_datetime(my_date_time, :prefix => 'payday')
339
- #
340
- # # Generates a datetime select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
341
- # select_datetime(my_date_time, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
342
- # select_datetime(my_date_time, :prompt => {:hour => true}) # generic prompt for hours
343
- # select_datetime(my_date_time, :prompt => true) # generic prompts for all
344
- #
345
- def select_datetime(datetime = Time.current, options = {}, html_options = {})
346
- DateTimeSelector.new(datetime, options, html_options).select_datetime
347
- end
348
-
349
- # Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
350
- # It's possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
351
- # symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order.
352
- # If the array passed to the <tt>:order</tt> option does not contain all the three symbols, all tags will be hidden.
353
- #
354
- # If anything is passed in the html_options hash it will be applied to every select tag in the set.
355
- #
356
- # ==== Examples
357
- # my_date = Time.now + 6.days
358
- #
359
- # # Generates a date select that defaults to the date in my_date (six days after today).
360
- # select_date(my_date)
361
- #
362
- # # Generates a date select that defaults to today (no specified date).
363
- # select_date()
364
- #
365
- # # Generates a date select that defaults to the date in my_date (six days after today)
366
- # # with the fields ordered year, month, day rather than month, day, year.
367
- # select_date(my_date, :order => [:year, :month, :day])
368
- #
369
- # # Generates a date select that discards the type of the field and defaults to the date in
370
- # # my_date (six days after today).
371
- # select_date(my_date, :discard_type => true)
372
- #
373
- # # Generates a date select that defaults to the date in my_date,
374
- # # which has fields separated by '/'.
375
- # select_date(my_date, :date_separator => '/')
376
- #
377
- # # Generates a date select that defaults to the datetime in my_date (six days after today)
378
- # # prefixed with 'payday' rather than 'date'.
379
- # select_date(my_date, :prefix => 'payday')
380
- #
381
- # # Generates a date select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
382
- # select_date(my_date, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
383
- # select_date(my_date, :prompt => {:hour => true}) # generic prompt for hours
384
- # select_date(my_date, :prompt => true) # generic prompts for all
385
- #
386
- def select_date(date = Date.current, options = {}, html_options = {})
387
- DateTimeSelector.new(date, options, html_options).select_date
388
- end
389
-
390
- # Returns a set of html select-tags (one for hour and minute).
391
- # You can set <tt>:time_separator</tt> key to format the output, and
392
- # the <tt>:include_seconds</tt> option to include an input for seconds.
393
- #
394
- # If anything is passed in the html_options hash it will be applied to every select tag in the set.
395
- #
396
- # ==== Examples
397
- # my_time = Time.now + 5.days + 7.hours + 3.minutes + 14.seconds
398
- #
399
- # # Generates a time select that defaults to the time in my_time.
400
- # select_time(my_time)
401
- #
402
- # # Generates a time select that defaults to the current time (no specified time).
403
- # select_time()
404
- #
405
- # # Generates a time select that defaults to the time in my_time,
406
- # # which has fields separated by ':'.
407
- # select_time(my_time, :time_separator => ':')
408
- #
409
- # # Generates a time select that defaults to the time in my_time,
410
- # # that also includes an input for seconds.
411
- # select_time(my_time, :include_seconds => true)
412
- #
413
- # # Generates a time select that defaults to the time in my_time, that has fields
414
- # # separated by ':' and includes an input for seconds.
415
- # select_time(my_time, :time_separator => ':', :include_seconds => true)
416
- #
417
- # # Generate a time select field with hours in the AM/PM format
418
- # select_time(my_time, :ampm => true)
419
- #
420
- # # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts.
421
- # select_time(my_time, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
422
- # select_time(my_time, :prompt => {:hour => true}) # generic prompt for hours
423
- # select_time(my_time, :prompt => true) # generic prompts for all
424
- #
425
- def select_time(datetime = Time.current, options = {}, html_options = {})
426
- DateTimeSelector.new(datetime, options, html_options).select_time
427
- end
428
-
429
- # Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
430
- # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
431
- # Override the field name using the <tt>:field_name</tt> option, 'second' by default.
432
- #
433
- # ==== Examples
434
- # my_time = Time.now + 16.minutes
435
- #
436
- # # Generates a select field for seconds that defaults to the seconds for the time in my_time.
437
- # select_second(my_time)
438
- #
439
- # # Generates a select field for seconds that defaults to the number given.
440
- # select_second(33)
441
- #
442
- # # Generates a select field for seconds that defaults to the seconds for the time in my_time
443
- # # that is named 'interval' rather than 'second'.
444
- # select_second(my_time, :field_name => 'interval')
445
- #
446
- # # Generates a select field for seconds with a custom prompt. Use <tt>:prompt => true</tt> for a
447
- # # generic prompt.
448
- # select_second(14, :prompt => 'Choose seconds')
449
- #
450
- def select_second(datetime, options = {}, html_options = {})
451
- DateTimeSelector.new(datetime, options, html_options).select_second
452
- end
453
-
454
- # Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
455
- # Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
456
- # selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
457
- # Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
458
- #
459
- # ==== Examples
460
- # my_time = Time.now + 6.hours
461
- #
462
- # # Generates a select field for minutes that defaults to the minutes for the time in my_time.
463
- # select_minute(my_time)
464
- #
465
- # # Generates a select field for minutes that defaults to the number given.
466
- # select_minute(14)
467
- #
468
- # # Generates a select field for minutes that defaults to the minutes for the time in my_time
469
- # # that is named 'moment' rather than 'minute'.
470
- # select_minute(my_time, :field_name => 'moment')
471
- #
472
- # # Generates a select field for minutes with a custom prompt. Use <tt>:prompt => true</tt> for a
473
- # # generic prompt.
474
- # select_minute(14, :prompt => 'Choose minutes')
475
- #
476
- def select_minute(datetime, options = {}, html_options = {})
477
- DateTimeSelector.new(datetime, options, html_options).select_minute
478
- end
479
-
480
- # Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
481
- # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
482
- # Override the field name using the <tt>:field_name</tt> option, 'hour' by default.
483
- #
484
- # ==== Examples
485
- # my_time = Time.now + 6.hours
486
- #
487
- # # Generates a select field for hours that defaults to the hour for the time in my_time.
488
- # select_hour(my_time)
489
- #
490
- # # Generates a select field for hours that defaults to the number given.
491
- # select_hour(13)
492
- #
493
- # # Generates a select field for hours that defaults to the hour for the time in my_time
494
- # # that is named 'stride' rather than 'hour'.
495
- # select_hour(my_time, :field_name => 'stride')
496
- #
497
- # # Generates a select field for hours with a custom prompt. Use <tt>:prompt => true</tt> for a
498
- # # generic prompt.
499
- # select_hour(13, :prompt => 'Choose hour')
500
- #
501
- # # Generate a select field for hours in the AM/PM format
502
- # select_hour(my_time, :ampm => true)
503
- #
504
- def select_hour(datetime, options = {}, html_options = {})
505
- DateTimeSelector.new(datetime, options, html_options).select_hour
506
- end
507
-
508
- # Returns a select tag with options for each of the days 1 through 31 with the current day selected.
509
- # The <tt>date</tt> can also be substituted for a day number.
510
- # Override the field name using the <tt>:field_name</tt> option, 'day' by default.
511
- #
512
- # ==== Examples
513
- # my_date = Time.now + 2.days
514
- #
515
- # # Generates a select field for days that defaults to the day for the date in my_date.
516
- # select_day(my_time)
517
- #
518
- # # Generates a select field for days that defaults to the number given.
519
- # select_day(5)
520
- #
521
- # # Generates a select field for days that defaults to the day for the date in my_date
522
- # # that is named 'due' rather than 'day'.
523
- # select_day(my_time, :field_name => 'due')
524
- #
525
- # # Generates a select field for days with a custom prompt. Use <tt>:prompt => true</tt> for a
526
- # # generic prompt.
527
- # select_day(5, :prompt => 'Choose day')
528
- #
529
- def select_day(date, options = {}, html_options = {})
530
- DateTimeSelector.new(date, options, html_options).select_day
531
- end
532
-
533
- # Returns a select tag with options for each of the months January through December with the current month
534
- # selected. The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are
535
- # used as values (what's submitted to the server). It's also possible to use month numbers for the presentation
536
- # instead of names -- set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you
537
- # want both numbers and names, set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer
538
- # to show month names as abbreviations, set the <tt>:use_short_month</tt> key in +options+ to true. If you want
539
- # to use your own month names, set the <tt>:use_month_names</tt> key in +options+ to an array of 12 month names.
540
- # Override the field name using the <tt>:field_name</tt> option, 'month' by default.
541
- #
542
- # ==== Examples
543
- # # Generates a select field for months that defaults to the current month that
544
- # # will use keys like "January", "March".
545
- # select_month(Date.today)
546
- #
547
- # # Generates a select field for months that defaults to the current month that
548
- # # is named "start" rather than "month".
549
- # select_month(Date.today, :field_name => 'start')
550
- #
551
- # # Generates a select field for months that defaults to the current month that
552
- # # will use keys like "1", "3".
553
- # select_month(Date.today, :use_month_numbers => true)
554
- #
555
- # # Generates a select field for months that defaults to the current month that
556
- # # will use keys like "1 - January", "3 - March".
557
- # select_month(Date.today, :add_month_numbers => true)
558
- #
559
- # # Generates a select field for months that defaults to the current month that
560
- # # will use keys like "Jan", "Mar".
561
- # select_month(Date.today, :use_short_month => true)
562
- #
563
- # # Generates a select field for months that defaults to the current month that
564
- # # will use keys like "Januar", "Marts."
565
- # select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...))
566
- #
567
- # # Generates a select field for months with a custom prompt. Use <tt>:prompt => true</tt> for a
568
- # # generic prompt.
569
- # select_month(14, :prompt => 'Choose month')
570
- #
571
- def select_month(date, options = {}, html_options = {})
572
- DateTimeSelector.new(date, options, html_options).select_month
573
- end
574
-
575
- # Returns a select tag with options for each of the five years on each side of the current, which is selected.
576
- # The five year radius can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the
577
- # +options+. Both ascending and descending year lists are supported by making <tt>:start_year</tt> less than or
578
- # greater than <tt>:end_year</tt>. The <tt>date</tt> can also be substituted for a year given as a number.
579
- # Override the field name using the <tt>:field_name</tt> option, 'year' by default.
580
- #
581
- # ==== Examples
582
- # # Generates a select field for years that defaults to the current year that
583
- # # has ascending year values.
584
- # select_year(Date.today, :start_year => 1992, :end_year => 2007)
585
- #
586
- # # Generates a select field for years that defaults to the current year that
587
- # # is named 'birth' rather than 'year'.
588
- # select_year(Date.today, :field_name => 'birth')
589
- #
590
- # # Generates a select field for years that defaults to the current year that
591
- # # has descending year values.
592
- # select_year(Date.today, :start_year => 2005, :end_year => 1900)
593
- #
594
- # # Generates a select field for years that defaults to the year 2006 that
595
- # # has ascending year values.
596
- # select_year(2006, :start_year => 2000, :end_year => 2010)
597
- #
598
- # # Generates a select field for years with a custom prompt. Use <tt>:prompt => true</tt> for a
599
- # # generic prompt.
600
- # select_year(14, :prompt => 'Choose year')
601
- #
602
- def select_year(date, options = {}, html_options = {})
603
- DateTimeSelector.new(date, options, html_options).select_year
604
- end
605
-
606
- # Returns an html time tag for the given date or time.
607
- #
608
- # ==== Examples
609
- # time_tag Date.today # =>
610
- # <time datetime="2010-11-04">November 04, 2010</time>
611
- # time_tag Time.now # =>
612
- # <time datetime="2010-11-04T17:55:45+01:00">November 04, 2010 17:55</time>
613
- # time_tag Date.yesterday, 'Yesterday' # =>
614
- # <time datetime="2010-11-03">Yesterday</time>
615
- # time_tag Date.today, :pubdate => true # =>
616
- # <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
617
- #
618
- def time_tag(date_or_time, *args)
619
- options = args.extract_options!
620
- format = options.delete(:format) || :long
621
- content = args.first || I18n.l(date_or_time, :format => format)
622
- datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.rfc3339
623
-
624
- content_tag(:time, content, options.reverse_merge(:datetime => datetime))
625
- end
626
- end
627
-
628
- class DateTimeSelector #:nodoc:
629
- include ActionView::Helpers::TagHelper
630
-
631
- DEFAULT_PREFIX = 'date'.freeze
632
- POSITION = {
633
- :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6
634
- }.freeze
635
-
636
- AMPM_TRANSLATION = Hash[
637
- [[0, "12 AM"], [1, "01 AM"], [2, "02 AM"], [3, "03 AM"],
638
- [4, "04 AM"], [5, "05 AM"], [6, "06 AM"], [7, "07 AM"],
639
- [8, "08 AM"], [9, "09 AM"], [10, "10 AM"], [11, "11 AM"],
640
- [12, "12 PM"], [13, "01 PM"], [14, "02 PM"], [15, "03 PM"],
641
- [16, "04 PM"], [17, "05 PM"], [18, "06 PM"], [19, "07 PM"],
642
- [20, "08 PM"], [21, "09 PM"], [22, "10 PM"], [23, "11 PM"]]
643
- ].freeze
644
-
645
- def initialize(datetime, options = {}, html_options = {})
646
- @options = options.dup
647
- @html_options = html_options.dup
648
- @datetime = datetime
649
- @options[:datetime_separator] ||= ' &mdash; '
650
- @options[:time_separator] ||= ' : '
651
- end
652
-
653
- def select_datetime
654
- order = date_order.dup
655
- order -= [:hour, :minute, :second]
656
- @options[:discard_year] ||= true unless order.include?(:year)
657
- @options[:discard_month] ||= true unless order.include?(:month)
658
- @options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
659
- @options[:discard_minute] ||= true if @options[:discard_hour]
660
- @options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
661
-
662
- # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
663
- # valid (otherwise it could be 31 and February wouldn't be a valid date)
664
- if @datetime && @options[:discard_day] && !@options[:discard_month]
665
- @datetime = @datetime.change(:day => 1)
666
- end
667
-
668
- if @options[:tag] && @options[:ignore_date]
669
- select_time
670
- else
671
- [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
672
- order += [:hour, :minute, :second] unless @options[:discard_hour]
673
-
674
- build_selects_from_types(order)
675
- end
676
- end
677
-
678
- def select_date
679
- order = date_order.dup
680
-
681
- @options[:discard_hour] = true
682
- @options[:discard_minute] = true
683
- @options[:discard_second] = true
684
-
685
- @options[:discard_year] ||= true unless order.include?(:year)
686
- @options[:discard_month] ||= true unless order.include?(:month)
687
- @options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
688
-
689
- # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
690
- # valid (otherwise it could be 31 and February wouldn't be a valid date)
691
- if @datetime && @options[:discard_day] && !@options[:discard_month]
692
- @datetime = @datetime.change(:day => 1)
693
- end
694
-
695
- [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
696
-
697
- build_selects_from_types(order)
698
- end
699
-
700
- def select_time
701
- order = []
702
-
703
- @options[:discard_month] = true
704
- @options[:discard_year] = true
705
- @options[:discard_day] = true
706
- @options[:discard_second] ||= true unless @options[:include_seconds]
707
-
708
- order += [:year, :month, :day] unless @options[:ignore_date]
709
-
710
- order += [:hour, :minute]
711
- order << :second if @options[:include_seconds]
712
-
713
- build_selects_from_types(order)
714
- end
715
-
716
- def select_second
717
- if @options[:use_hidden] || @options[:discard_second]
718
- build_hidden(:second, sec) if @options[:include_seconds]
719
- else
720
- build_options_and_select(:second, sec)
721
- end
722
- end
723
-
724
- def select_minute
725
- if @options[:use_hidden] || @options[:discard_minute]
726
- build_hidden(:minute, min)
727
- else
728
- build_options_and_select(:minute, min, :step => @options[:minute_step])
729
- end
730
- end
731
-
732
- def select_hour
733
- if @options[:use_hidden] || @options[:discard_hour]
734
- build_hidden(:hour, hour)
735
- else
736
- build_options_and_select(:hour, hour, :end => 23, :ampm => @options[:ampm])
737
- end
738
- end
739
-
740
- def select_day
741
- if @options[:use_hidden] || @options[:discard_day]
742
- build_hidden(:day, day || 1)
743
- else
744
- build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false, :use_two_digit_numbers => @options[:use_two_digit_numbers])
745
- end
746
- end
747
-
748
- def select_month
749
- if @options[:use_hidden] || @options[:discard_month]
750
- build_hidden(:month, month || 1)
751
- else
752
- month_options = []
753
- 1.upto(12) do |month_number|
754
- options = { :value => month_number }
755
- options[:selected] = "selected" if month == month_number
756
- month_options << content_tag(:option, month_name(month_number), options) + "\n"
757
- end
758
- build_select(:month, month_options.join)
759
- end
760
- end
761
-
762
- def select_year
763
- if !@datetime || @datetime == 0
764
- val = '1'
765
- middle_year = Date.today.year
766
- else
767
- val = middle_year = year
768
- end
769
-
770
- if @options[:use_hidden] || @options[:discard_year]
771
- build_hidden(:year, val)
772
- else
773
- options = {}
774
- options[:start] = @options[:start_year] || middle_year - 5
775
- options[:end] = @options[:end_year] || middle_year + 5
776
- options[:step] = options[:start] < options[:end] ? 1 : -1
777
- options[:leading_zeros] = false
778
- options[:max_years_allowed] = @options[:max_years_allowed] || 1000
779
-
780
- if (options[:end] - options[:start]).abs > options[:max_years_allowed]
781
- raise ArgumentError, "There're too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter"
782
- end
783
-
784
- build_options_and_select(:year, val, options)
785
- end
786
- end
787
-
788
- private
789
- %w( sec min hour day month year ).each do |method|
790
- define_method(method) do
791
- @datetime.kind_of?(Numeric) ? @datetime : @datetime.send(method) if @datetime
792
- end
793
- end
794
-
795
- # Returns translated month names, but also ensures that a custom month
796
- # name array has a leading nil element.
797
- def month_names
798
- @month_names ||= begin
799
- month_names = @options[:use_month_names] || translated_month_names
800
- month_names.unshift(nil) if month_names.size < 13
801
- month_names
802
- end
803
- end
804
-
805
- # Returns translated month names.
806
- # => [nil, "January", "February", "March",
807
- # "April", "May", "June", "July",
808
- # "August", "September", "October",
809
- # "November", "December"]
810
- #
811
- # If <tt>:use_short_month</tt> option is set
812
- # => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
813
- # "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
814
- def translated_month_names
815
- key = @options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
816
- I18n.translate(key, :locale => @options[:locale])
817
- end
818
-
819
- # Lookup month name for number.
820
- # month_name(1) => "January"
821
- #
822
- # If <tt>:use_month_numbers</tt> option is passed
823
- # month_name(1) => 1
824
- #
825
- # If <tt>:add_month_numbers</tt> option is passed
826
- # month_name(1) => "1 - January"
827
- def month_name(number)
828
- if @options[:use_month_numbers]
829
- number
830
- elsif @options[:use_two_digit_numbers]
831
- sprintf "%02d", number
832
- elsif @options[:add_month_numbers]
833
- "#{number} - #{month_names[number]}"
834
- else
835
- month_names[number]
836
- end
837
- end
838
-
839
- def date_order
840
- @date_order ||= @options[:order] || translated_date_order
841
- end
842
-
843
- def translated_date_order
844
- I18n.translate(:'date.order', :locale => @options[:locale]) || []
845
- end
846
-
847
- # Build full select tag from date type and options.
848
- def build_options_and_select(type, selected, options = {})
849
- build_select(type, build_options(selected, options))
850
- end
851
-
852
- # Build select option html from date value and options.
853
- # build_options(15, :start => 1, :end => 31)
854
- # => "<option value="1">1</option>
855
- # <option value="2">2</option>
856
- # <option value="3">3</option>..."
857
- #
858
- # If <tt>:step</tt> options is passed
859
- # build_options(15, :start => 1, :end => 31, :step => 2)
860
- # => "<option value="1">1</option>
861
- # <option value="3">3</option>
862
- # <option value="5">5</option>..."
863
- def build_options(selected, options = {})
864
- start = options.delete(:start) || 0
865
- stop = options.delete(:end) || 59
866
- step = options.delete(:step) || 1
867
- options.reverse_merge!({:leading_zeros => true, :ampm => false, :use_two_digit_numbers => false})
868
- leading_zeros = options.delete(:leading_zeros)
869
-
870
- select_options = []
871
- start.step(stop, step) do |i|
872
- value = leading_zeros ? sprintf("%02d", i) : i
873
- tag_options = { :value => value }
874
- tag_options[:selected] = "selected" if selected == i
875
- text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
876
- text = options[:ampm] ? AMPM_TRANSLATION[i] : text
877
- select_options << content_tag(:option, text, tag_options)
878
- end
879
- (select_options.join("\n") + "\n").html_safe
880
- end
881
-
882
- # Builds select tag from date type and html select options.
883
- # build_select(:month, "<option value="1">January</option>...")
884
- # => "<select id="post_written_on_2i" name="post[written_on(2i)]">
885
- # <option value="1">January</option>...
886
- # </select>"
887
- def build_select(type, select_options_as_html)
888
- select_options = {
889
- :id => input_id_from_type(type),
890
- :name => input_name_from_type(type)
891
- }.merge(@html_options)
892
- select_options.merge!(:disabled => 'disabled') if @options[:disabled]
893
-
894
- select_html = "\n"
895
- select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
896
- select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
897
- select_html << select_options_as_html
898
-
899
- (content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe
900
- end
901
-
902
- # Builds a prompt option tag with supplied options or from default options.
903
- # prompt_option_tag(:month, :prompt => 'Select month')
904
- # => "<option value="">Select month</option>"
905
- def prompt_option_tag(type, options)
906
- prompt = case options
907
- when Hash
908
- default_options = {:year => false, :month => false, :day => false, :hour => false, :minute => false, :second => false}
909
- default_options.merge!(options)[type.to_sym]
910
- when String
911
- options
912
- else
913
- I18n.translate(:"datetime.prompts.#{type}", :locale => @options[:locale])
914
- end
915
-
916
- prompt ? content_tag(:option, prompt, :value => '') : ''
917
- end
918
-
919
- # Builds hidden input tag for date part and value.
920
- # build_hidden(:year, 2008)
921
- # => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
922
- def build_hidden(type, value)
923
- select_options = {
924
- :type => "hidden",
925
- :id => input_id_from_type(type),
926
- :name => input_name_from_type(type),
927
- :value => value
928
- }.merge(@html_options.slice(:disabled))
929
- select_options.merge!(:disabled => 'disabled') if @options[:disabled]
930
-
931
- tag(:input, select_options) + "\n".html_safe
932
- end
933
-
934
- # Returns the name attribute for the input tag.
935
- # => post[written_on(1i)]
936
- def input_name_from_type(type)
937
- prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX
938
- prefix += "[#{@options[:index]}]" if @options.has_key?(:index)
939
-
940
- field_name = @options[:field_name] || type
941
- if @options[:include_position]
942
- field_name += "(#{ActionView::Helpers::DateTimeSelector::POSITION[type]}i)"
943
- end
944
-
945
- @options[:discard_type] ? prefix : "#{prefix}[#{field_name}]"
946
- end
947
-
948
- # Returns the id attribute for the input tag.
949
- # => "post_written_on_1i"
950
- def input_id_from_type(type)
951
- id = input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
952
- id = @options[:namespace] + '_' + id if @options[:namespace]
953
-
954
- id
955
- end
956
-
957
- # Given an ordering of datetime components, create the selection HTML
958
- # and join them with their appropriate separators.
959
- def build_selects_from_types(order)
960
- select = ''
961
- first_visible = order.find { |type| !@options[:"discard_#{type}"] }
962
- order.reverse.each do |type|
963
- separator = separator(type) unless type == first_visible # don't add before first visible field
964
- select.insert(0, separator.to_s + send("select_#{type}").to_s)
965
- end
966
- select.html_safe
967
- end
968
-
969
- # Returns the separator for a given datetime component.
970
- def separator(type)
971
- case type
972
- when :year
973
- @options[:discard_year] ? "" : @options[:date_separator]
974
- when :month
975
- @options[:discard_month] ? "" : @options[:date_separator]
976
- when :day
977
- @options[:discard_day] ? "" : @options[:date_separator]
978
- when :hour
979
- (@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
980
- when :minute
981
- @options[:discard_minute] ? "" : @options[:time_separator]
982
- when :second
983
- @options[:include_seconds] ? @options[:time_separator] : ""
984
- end
985
- end
986
- end
987
-
988
- module DateHelperInstanceTag
989
- def to_date_select_tag(options = {}, html_options = {})
990
- datetime_selector(options, html_options).select_date.html_safe
991
- end
992
-
993
- def to_time_select_tag(options = {}, html_options = {})
994
- datetime_selector(options, html_options).select_time.html_safe
995
- end
996
-
997
- def to_datetime_select_tag(options = {}, html_options = {})
998
- datetime_selector(options, html_options).select_datetime.html_safe
999
- end
1000
-
1001
- private
1002
- def datetime_selector(options, html_options)
1003
- datetime = value(object) || default_datetime(options)
1004
- @auto_index ||= nil
1005
-
1006
- options = options.dup
1007
- options[:field_name] = @method_name
1008
- options[:include_position] = true
1009
- options[:prefix] ||= @object_name
1010
- options[:index] = @auto_index if @auto_index && !options.has_key?(:index)
1011
-
1012
- DateTimeSelector.new(datetime, options, html_options)
1013
- end
1014
-
1015
- def default_datetime(options)
1016
- return if options[:include_blank] || options[:prompt]
1017
-
1018
- case options[:default]
1019
- when nil
1020
- Time.current
1021
- when Date, Time
1022
- options[:default]
1023
- else
1024
- default = options[:default].dup
1025
-
1026
- # Rename :minute and :second to :min and :sec
1027
- default[:min] ||= default[:minute]
1028
- default[:sec] ||= default[:second]
1029
-
1030
- time = Time.current
1031
-
1032
- [:year, :month, :day, :hour, :min, :sec].each do |key|
1033
- default[key] ||= time.send(key)
1034
- end
1035
-
1036
- Time.utc_time(
1037
- default[:year], default[:month], default[:day],
1038
- default[:hour], default[:min], default[:sec]
1039
- )
1040
- end
1041
- end
1042
- end
1043
-
1044
- class InstanceTag #:nodoc:
1045
- include DateHelperInstanceTag
1046
- end
1047
-
1048
- class FormBuilder
1049
- def date_select(method, options = {}, html_options = {})
1050
- @template.date_select(@object_name, method, objectify_options(options), html_options)
1051
- end
1052
-
1053
- def time_select(method, options = {}, html_options = {})
1054
- @template.time_select(@object_name, method, objectify_options(options), html_options)
1055
- end
1056
-
1057
- def datetime_select(method, options = {}, html_options = {})
1058
- @template.datetime_select(@object_name, method, objectify_options(options), html_options)
1059
- end
1060
- end
1061
- end
1062
- end