omg-actionview 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +25 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +40 -0
  5. data/app/assets/javascripts/rails-ujs.esm.js +686 -0
  6. data/app/assets/javascripts/rails-ujs.js +630 -0
  7. data/lib/action_view/base.rb +316 -0
  8. data/lib/action_view/buffers.rb +165 -0
  9. data/lib/action_view/cache_expiry.rb +69 -0
  10. data/lib/action_view/context.rb +32 -0
  11. data/lib/action_view/dependency_tracker/erb_tracker.rb +159 -0
  12. data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
  13. data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
  14. data/lib/action_view/dependency_tracker.rb +41 -0
  15. data/lib/action_view/deprecator.rb +7 -0
  16. data/lib/action_view/digestor.rb +130 -0
  17. data/lib/action_view/flows.rb +75 -0
  18. data/lib/action_view/gem_version.rb +17 -0
  19. data/lib/action_view/helpers/active_model_helper.rb +54 -0
  20. data/lib/action_view/helpers/asset_tag_helper.rb +680 -0
  21. data/lib/action_view/helpers/asset_url_helper.rb +473 -0
  22. data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
  23. data/lib/action_view/helpers/cache_helper.rb +315 -0
  24. data/lib/action_view/helpers/capture_helper.rb +236 -0
  25. data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
  26. data/lib/action_view/helpers/controller_helper.rb +42 -0
  27. data/lib/action_view/helpers/csp_helper.rb +26 -0
  28. data/lib/action_view/helpers/csrf_helper.rb +35 -0
  29. data/lib/action_view/helpers/date_helper.rb +1266 -0
  30. data/lib/action_view/helpers/debug_helper.rb +38 -0
  31. data/lib/action_view/helpers/form_helper.rb +2765 -0
  32. data/lib/action_view/helpers/form_options_helper.rb +927 -0
  33. data/lib/action_view/helpers/form_tag_helper.rb +1088 -0
  34. data/lib/action_view/helpers/javascript_helper.rb +96 -0
  35. data/lib/action_view/helpers/number_helper.rb +165 -0
  36. data/lib/action_view/helpers/output_safety_helper.rb +70 -0
  37. data/lib/action_view/helpers/rendering_helper.rb +218 -0
  38. data/lib/action_view/helpers/sanitize_helper.rb +201 -0
  39. data/lib/action_view/helpers/tag_helper.rb +621 -0
  40. data/lib/action_view/helpers/tags/base.rb +138 -0
  41. data/lib/action_view/helpers/tags/check_box.rb +65 -0
  42. data/lib/action_view/helpers/tags/checkable.rb +18 -0
  43. data/lib/action_view/helpers/tags/collection_check_boxes.rb +37 -0
  44. data/lib/action_view/helpers/tags/collection_helpers.rb +118 -0
  45. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
  46. data/lib/action_view/helpers/tags/collection_select.rb +33 -0
  47. data/lib/action_view/helpers/tags/color_field.rb +26 -0
  48. data/lib/action_view/helpers/tags/date_field.rb +14 -0
  49. data/lib/action_view/helpers/tags/date_select.rb +75 -0
  50. data/lib/action_view/helpers/tags/datetime_field.rb +39 -0
  51. data/lib/action_view/helpers/tags/datetime_local_field.rb +29 -0
  52. data/lib/action_view/helpers/tags/datetime_select.rb +10 -0
  53. data/lib/action_view/helpers/tags/email_field.rb +10 -0
  54. data/lib/action_view/helpers/tags/file_field.rb +26 -0
  55. data/lib/action_view/helpers/tags/grouped_collection_select.rb +34 -0
  56. data/lib/action_view/helpers/tags/hidden_field.rb +14 -0
  57. data/lib/action_view/helpers/tags/label.rb +84 -0
  58. data/lib/action_view/helpers/tags/month_field.rb +14 -0
  59. data/lib/action_view/helpers/tags/number_field.rb +20 -0
  60. data/lib/action_view/helpers/tags/password_field.rb +14 -0
  61. data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
  62. data/lib/action_view/helpers/tags/radio_button.rb +32 -0
  63. data/lib/action_view/helpers/tags/range_field.rb +10 -0
  64. data/lib/action_view/helpers/tags/search_field.rb +27 -0
  65. data/lib/action_view/helpers/tags/select.rb +45 -0
  66. data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
  67. data/lib/action_view/helpers/tags/tel_field.rb +10 -0
  68. data/lib/action_view/helpers/tags/text_area.rb +24 -0
  69. data/lib/action_view/helpers/tags/text_field.rb +33 -0
  70. data/lib/action_view/helpers/tags/time_field.rb +23 -0
  71. data/lib/action_view/helpers/tags/time_select.rb +10 -0
  72. data/lib/action_view/helpers/tags/time_zone_select.rb +25 -0
  73. data/lib/action_view/helpers/tags/translator.rb +39 -0
  74. data/lib/action_view/helpers/tags/url_field.rb +10 -0
  75. data/lib/action_view/helpers/tags/week_field.rb +14 -0
  76. data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
  77. data/lib/action_view/helpers/tags.rb +47 -0
  78. data/lib/action_view/helpers/text_helper.rb +568 -0
  79. data/lib/action_view/helpers/translation_helper.rb +161 -0
  80. data/lib/action_view/helpers/url_helper.rb +812 -0
  81. data/lib/action_view/helpers.rb +68 -0
  82. data/lib/action_view/layouts.rb +434 -0
  83. data/lib/action_view/locale/en.yml +56 -0
  84. data/lib/action_view/log_subscriber.rb +132 -0
  85. data/lib/action_view/lookup_context.rb +299 -0
  86. data/lib/action_view/model_naming.rb +14 -0
  87. data/lib/action_view/path_registry.rb +57 -0
  88. data/lib/action_view/path_set.rb +84 -0
  89. data/lib/action_view/railtie.rb +132 -0
  90. data/lib/action_view/record_identifier.rb +118 -0
  91. data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
  92. data/lib/action_view/render_parser/ripper_render_parser.rb +350 -0
  93. data/lib/action_view/render_parser.rb +40 -0
  94. data/lib/action_view/renderer/abstract_renderer.rb +186 -0
  95. data/lib/action_view/renderer/collection_renderer.rb +204 -0
  96. data/lib/action_view/renderer/object_renderer.rb +34 -0
  97. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +120 -0
  98. data/lib/action_view/renderer/partial_renderer.rb +267 -0
  99. data/lib/action_view/renderer/renderer.rb +107 -0
  100. data/lib/action_view/renderer/streaming_template_renderer.rb +107 -0
  101. data/lib/action_view/renderer/template_renderer.rb +115 -0
  102. data/lib/action_view/rendering.rb +190 -0
  103. data/lib/action_view/routing_url_for.rb +149 -0
  104. data/lib/action_view/tasks/cache_digests.rake +25 -0
  105. data/lib/action_view/template/error.rb +264 -0
  106. data/lib/action_view/template/handlers/builder.rb +25 -0
  107. data/lib/action_view/template/handlers/erb/erubi.rb +85 -0
  108. data/lib/action_view/template/handlers/erb.rb +157 -0
  109. data/lib/action_view/template/handlers/html.rb +11 -0
  110. data/lib/action_view/template/handlers/raw.rb +11 -0
  111. data/lib/action_view/template/handlers.rb +66 -0
  112. data/lib/action_view/template/html.rb +33 -0
  113. data/lib/action_view/template/inline.rb +22 -0
  114. data/lib/action_view/template/raw_file.rb +25 -0
  115. data/lib/action_view/template/renderable.rb +30 -0
  116. data/lib/action_view/template/resolver.rb +212 -0
  117. data/lib/action_view/template/sources/file.rb +17 -0
  118. data/lib/action_view/template/sources.rb +13 -0
  119. data/lib/action_view/template/text.rb +32 -0
  120. data/lib/action_view/template/types.rb +50 -0
  121. data/lib/action_view/template.rb +580 -0
  122. data/lib/action_view/template_details.rb +66 -0
  123. data/lib/action_view/template_path.rb +66 -0
  124. data/lib/action_view/test_case.rb +449 -0
  125. data/lib/action_view/testing/resolvers.rb +44 -0
  126. data/lib/action_view/unbound_template.rb +67 -0
  127. data/lib/action_view/version.rb +10 -0
  128. data/lib/action_view/view_paths.rb +117 -0
  129. data/lib/action_view.rb +104 -0
  130. metadata +275 -0
@@ -0,0 +1,1266 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "action_view/helpers/tag_helper"
5
+ require "active_support/core_ext/array/extract_options"
6
+ require "active_support/core_ext/date/conversions"
7
+ require "active_support/core_ext/hash/slice"
8
+ require "active_support/core_ext/object/acts_like"
9
+ require "active_support/core_ext/object/with_options"
10
+
11
+ module ActionView
12
+ module Helpers # :nodoc:
13
+ # = Action View \Date \Helpers
14
+ #
15
+ # The Date Helper primarily creates select/option tags for different kinds of dates and times or date and time
16
+ # elements. All of the select-type methods share a number of common options that are as follows:
17
+ #
18
+ # * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday"
19
+ # would give \birthday[month] instead of \date[month] if passed to the <tt>select_month</tt> method.
20
+ # * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date.
21
+ # * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true,
22
+ # the <tt>select_month</tt> method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead
23
+ # of \date[month].
24
+ module DateHelper
25
+ MINUTES_IN_YEAR = 525600
26
+ MINUTES_IN_QUARTER_YEAR = 131400
27
+ MINUTES_IN_THREE_QUARTERS_YEAR = 394200
28
+
29
+ # Reports the approximate distance in time between two Time, Date, or DateTime objects or integers as seconds.
30
+ # Pass <tt>include_seconds: true</tt> if you want more detailed approximations when distance < 1 min, 29 secs.
31
+ # Distances are reported based on the following table:
32
+ #
33
+ # 0 <-> 29 secs # => less than a minute
34
+ # 30 secs <-> 1 min, 29 secs # => 1 minute
35
+ # 1 min, 30 secs <-> 44 mins, 29 secs # => [2..44] minutes
36
+ # 44 mins, 30 secs <-> 89 mins, 29 secs # => about 1 hour
37
+ # 89 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
38
+ # 23 hrs, 59 mins, 30 secs <-> 41 hrs, 59 mins, 29 secs # => 1 day
39
+ # 41 hrs, 59 mins, 30 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
40
+ # 29 days, 23 hrs, 59 mins, 30 secs <-> 44 days, 23 hrs, 59 mins, 29 secs # => about 1 month
41
+ # 44 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 2 months
42
+ # 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months
43
+ # 1 yr <-> 1 yr, 3 months # => about 1 year
44
+ # 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year
45
+ # 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years
46
+ # 2 yrs <-> max time or date # => (same rules as 1 yr)
47
+ #
48
+ # With <tt>include_seconds: true</tt> and the difference < 1 minute 29 seconds:
49
+ # 0-4 secs # => less than 5 seconds
50
+ # 5-9 secs # => less than 10 seconds
51
+ # 10-19 secs # => less than 20 seconds
52
+ # 20-39 secs # => half a minute
53
+ # 40-59 secs # => less than a minute
54
+ # 60-89 secs # => 1 minute
55
+ #
56
+ # from_time = Time.now
57
+ # distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
58
+ # distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
59
+ # distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
60
+ # distance_of_time_in_words(from_time, from_time + 15.seconds, include_seconds: true) # => less than 20 seconds
61
+ # distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years
62
+ # distance_of_time_in_words(from_time, from_time + 60.hours) # => 3 days
63
+ # distance_of_time_in_words(from_time, from_time + 45.seconds, include_seconds: true) # => less than a minute
64
+ # distance_of_time_in_words(from_time, from_time - 45.seconds, include_seconds: true) # => less than a minute
65
+ # distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
66
+ # distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
67
+ # distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years
68
+ # distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years
69
+ #
70
+ # to_time = Time.now + 6.years + 19.days
71
+ # distance_of_time_in_words(from_time, to_time, include_seconds: true) # => about 6 years
72
+ # distance_of_time_in_words(to_time, from_time, include_seconds: true) # => about 6 years
73
+ # distance_of_time_in_words(Time.now, Time.now) # => less than a minute
74
+ #
75
+ # With the <tt>scope</tt> option, you can define a custom scope for \Rails
76
+ # to look up the translation.
77
+ #
78
+ # For example you can define the following in your locale (e.g. en.yml).
79
+ #
80
+ # datetime:
81
+ # distance_in_words:
82
+ # short:
83
+ # about_x_hours:
84
+ # one: 'an hour'
85
+ # other: '%{count} hours'
86
+ #
87
+ # See https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/en.yml
88
+ # for more examples.
89
+ #
90
+ # Which will then result in the following:
91
+ #
92
+ # from_time = Time.now
93
+ # distance_of_time_in_words(from_time, from_time + 50.minutes, scope: 'datetime.distance_in_words.short') # => "an hour"
94
+ # distance_of_time_in_words(from_time, from_time + 3.hours, scope: 'datetime.distance_in_words.short') # => "3 hours"
95
+ def distance_of_time_in_words(from_time, to_time = 0, options = {})
96
+ options = {
97
+ scope: :'datetime.distance_in_words'
98
+ }.merge!(options)
99
+
100
+ from_time = normalize_distance_of_time_argument_to_time(from_time)
101
+ to_time = normalize_distance_of_time_argument_to_time(to_time)
102
+ from_time, to_time = to_time, from_time if from_time > to_time
103
+ distance_in_minutes = ((to_time - from_time) / 60.0).round
104
+ distance_in_seconds = (to_time - from_time).round
105
+
106
+ I18n.with_options locale: options[:locale], scope: options[:scope] do |locale|
107
+ case distance_in_minutes
108
+ when 0..1
109
+ return distance_in_minutes == 0 ?
110
+ locale.t(:less_than_x_minutes, count: 1) :
111
+ locale.t(:x_minutes, count: distance_in_minutes) unless options[:include_seconds]
112
+
113
+ case distance_in_seconds
114
+ when 0..4 then locale.t :less_than_x_seconds, count: 5
115
+ when 5..9 then locale.t :less_than_x_seconds, count: 10
116
+ when 10..19 then locale.t :less_than_x_seconds, count: 20
117
+ when 20..39 then locale.t :half_a_minute
118
+ when 40..59 then locale.t :less_than_x_minutes, count: 1
119
+ else locale.t :x_minutes, count: 1
120
+ end
121
+
122
+ when 2...45 then locale.t :x_minutes, count: distance_in_minutes
123
+ when 45...90 then locale.t :about_x_hours, count: 1
124
+ # 90 mins up to 24 hours
125
+ when 90...1440 then locale.t :about_x_hours, count: (distance_in_minutes.to_f / 60.0).round
126
+ # 24 hours up to 42 hours
127
+ when 1440...2520 then locale.t :x_days, count: 1
128
+ # 42 hours up to 30 days
129
+ when 2520...43200 then locale.t :x_days, count: (distance_in_minutes.to_f / 1440.0).round
130
+ # 30 days up to 60 days
131
+ when 43200...86400 then locale.t :about_x_months, count: (distance_in_minutes.to_f / 43200.0).round
132
+ # 60 days up to 365 days
133
+ when 86400...525600 then locale.t :x_months, count: (distance_in_minutes.to_f / 43200.0).round
134
+ else
135
+ from_year = from_time.year
136
+ from_year += 1 if from_time.month >= 3
137
+ to_year = to_time.year
138
+ to_year -= 1 if to_time.month < 3
139
+ leap_years = (from_year > to_year) ? 0 : (from_year..to_year).count { |x| Date.leap?(x) }
140
+ minute_offset_for_leap_year = leap_years * 1440
141
+ # Discount the leap year days when calculating year distance.
142
+ # e.g. if there are 20 leap year days between 2 dates having the same day
143
+ # and month then based on 365 days calculation
144
+ # the distance in years will come out to over 80 years when in written
145
+ # English it would read better as about 80 years.
146
+ minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
147
+ remainder = (minutes_with_offset % MINUTES_IN_YEAR)
148
+ distance_in_years = (minutes_with_offset.div MINUTES_IN_YEAR)
149
+ if remainder < MINUTES_IN_QUARTER_YEAR
150
+ locale.t(:about_x_years, count: distance_in_years)
151
+ elsif remainder < MINUTES_IN_THREE_QUARTERS_YEAR
152
+ locale.t(:over_x_years, count: distance_in_years)
153
+ else
154
+ locale.t(:almost_x_years, count: distance_in_years + 1)
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ # Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
161
+ #
162
+ # time_ago_in_words(3.minutes.from_now) # => 3 minutes
163
+ # time_ago_in_words(3.minutes.ago) # => 3 minutes
164
+ # time_ago_in_words(Time.now - 15.hours) # => about 15 hours
165
+ # time_ago_in_words(Time.now) # => less than a minute
166
+ # time_ago_in_words(Time.now, include_seconds: true) # => less than 5 seconds
167
+ #
168
+ # from_time = Time.now - 3.days - 14.minutes - 25.seconds
169
+ # time_ago_in_words(from_time) # => 3 days
170
+ #
171
+ # from_time = (3.days + 14.minutes + 25.seconds).ago
172
+ # time_ago_in_words(from_time) # => 3 days
173
+ #
174
+ # Note that you cannot pass a <tt>Numeric</tt> value to <tt>time_ago_in_words</tt>.
175
+ #
176
+ def time_ago_in_words(from_time, options = {})
177
+ distance_of_time_in_words(from_time, Time.now, options)
178
+ end
179
+
180
+ alias_method :distance_of_time_in_words_to_now, :time_ago_in_words
181
+
182
+ # Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based
183
+ # attribute (identified by +method+) on an object assigned to the template (identified by +object+).
184
+ #
185
+ # ==== Options
186
+ # * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
187
+ # "2" instead of "February").
188
+ # * <tt>:use_two_digit_numbers</tt> - Set to true if you want to display two digit month and day numbers (e.g.
189
+ # "02" instead of "February" and "08" instead of "8").
190
+ # * <tt>:use_short_month</tt> - Set to true if you want to use abbreviated month names instead of full
191
+ # month names (e.g. "Feb" instead of "February").
192
+ # * <tt>:add_month_numbers</tt> - Set to true if you want to use both month numbers and month names (e.g.
193
+ # "2 - February" instead of "February").
194
+ # * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names.
195
+ # Note: You can also use Rails' i18n functionality for this.
196
+ # * <tt>:month_format_string</tt> - Set to a format string. The string gets passed keys +:number+ (integer)
197
+ # and +:name+ (string). A format string would be something like "%{name} (%<number>02d)" for example.
198
+ # See <tt>Kernel.sprintf</tt> for documentation on format sequences.
199
+ # * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
200
+ # * <tt>:time_separator</tt> - Specifies a string to separate the time fields. Default is " : ".
201
+ # * <tt>:datetime_separator</tt>- Specifies a string to separate the date and time fields. Default is " &mdash; ".
202
+ # * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Date.today.year - 5</tt> if
203
+ # you are creating new record. While editing existing record, <tt>:start_year</tt> defaults to
204
+ # the current selected year minus 5.
205
+ # * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Date.today.year + 5</tt> if
206
+ # you are creating new record. While editing existing record, <tt>:end_year</tt> defaults to
207
+ # the current selected year plus 5.
208
+ # * <tt>:year_format</tt> - Set format of years for year select. Lambda should be passed.
209
+ # * <tt>:day_format</tt> - Set format of days for day select. Lambda should be passed.
210
+ # * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
211
+ # as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
212
+ # first of the given month in order to not create invalid dates like 31 February.
213
+ # * <tt>:discard_month</tt> - Set to true if you don't want to show a month select. This includes the month
214
+ # as a hidden field instead of showing a select field. Also note that this implicitly sets :discard_day to true.
215
+ # * <tt>:discard_year</tt> - Set to true if you don't want to show a year select. This includes the year
216
+ # as a hidden field instead of showing a select field.
217
+ # * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> to
218
+ # customize the order in which the select fields are shown. If you leave out any of the symbols, the respective
219
+ # select will not be shown (like when you set <tt>discard_xxx: true</tt>. Defaults to the order defined in
220
+ # the respective locale (e.g. [:year, :month, :day] in the en locale that ships with \Rails).
221
+ # * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
222
+ # dates.
223
+ # * <tt>:default</tt> - Set a default date if the affected date isn't set or is +nil+.
224
+ # * <tt>:selected</tt> - Set a date that overrides the actual value.
225
+ # * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
226
+ # * <tt>:prompt</tt> - Set to true (for a generic prompt), a prompt string or a hash of prompt strings
227
+ # for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
228
+ # Setting this option prepends a select option with a generic prompt (Day, Month, Year, Hour, Minute, Seconds)
229
+ # or the given prompt string.
230
+ # * <tt>:with_css_classes</tt> - Set to true or a hash of strings. Use true if you want to assign generic styles for
231
+ # select tags. This automatically set classes 'year', 'month', 'day', 'hour', 'minute' and 'second'. A hash of
232
+ # strings for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt>, <tt>:second</tt>
233
+ # will extend the select type with the given value. Use +html_options+ to modify every select tag in the set.
234
+ # * <tt>:use_hidden</tt> - Set to true if you only want to generate hidden input tags.
235
+ #
236
+ # If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
237
+ #
238
+ # NOTE: Discarded selects will default to 1. So if no month select is available, January will be assumed.
239
+ #
240
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute.
241
+ # date_select("article", "written_on")
242
+ #
243
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
244
+ # # with the year in the year drop down box starting at 1995.
245
+ # date_select("article", "written_on", start_year: 1995)
246
+ #
247
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
248
+ # # with the year in the year drop down box starting at 1995, numbers used for months instead of words,
249
+ # # and without a day select box.
250
+ # date_select("article", "written_on", start_year: 1995, use_month_numbers: true,
251
+ # discard_day: true, include_blank: true)
252
+ #
253
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
254
+ # # with two digit numbers used for months and days.
255
+ # date_select("article", "written_on", use_two_digit_numbers: true)
256
+ #
257
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
258
+ # # with the fields ordered as day, month, year rather than month, day, year.
259
+ # date_select("article", "written_on", order: [:day, :month, :year])
260
+ #
261
+ # # Generates a date select that when POSTed is stored in the user variable, in the birthday attribute
262
+ # # lacking a year field.
263
+ # date_select("user", "birthday", order: [:month, :day])
264
+ #
265
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
266
+ # # which is initially set to the date 3 days from the current date
267
+ # date_select("article", "written_on", default: 3.days.from_now)
268
+ #
269
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
270
+ # # which is set in the form with today's date, regardless of the value in the Active Record object.
271
+ # date_select("article", "written_on", selected: Date.today)
272
+ #
273
+ # # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
274
+ # # that will have a default day of 20.
275
+ # date_select("credit_card", "bill_due", default: { day: 20 })
276
+ #
277
+ # # Generates a date select with custom prompts.
278
+ # date_select("article", "written_on", prompt: { day: 'Select day', month: 'Select month', year: 'Select year' })
279
+ #
280
+ # # Generates a date select with custom year format.
281
+ # date_select("article", "written_on", year_format: ->(year) { "Heisei #{year - 1988}" })
282
+ #
283
+ # # Generates a date select with custom day format.
284
+ # date_select("article", "written_on", day_format: ->(day) { day.ordinalize })
285
+ #
286
+ # The selects are prepared for multi-parameter assignment to an Active Record object.
287
+ #
288
+ # 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
289
+ # all month choices are valid.
290
+ def date_select(object_name, method, options = {}, html_options = {})
291
+ Tags::DateSelect.new(object_name, method, self, options, html_options).render
292
+ end
293
+
294
+ # Returns a set of select tags (one for hour, minute, and optionally second) pre-selected for accessing a
295
+ # specified time-based attribute (identified by +method+) on an object assigned to the template (identified by
296
+ # +object+). You can include the seconds with <tt>:include_seconds</tt>. You can get hours in the AM/PM format
297
+ # with <tt>:ampm</tt> option.
298
+ #
299
+ # This method will also generate 3 input hidden tags, for the actual year, month, and day unless the option
300
+ # <tt>:ignore_date</tt> is set to +true+. If you set the <tt>:ignore_date</tt> to +true+, you must have a
301
+ # +date_select+ on the same method within the form otherwise an exception will be raised.
302
+ #
303
+ # If anything is passed in the html_options hash it will be applied to every select tag in the set.
304
+ #
305
+ # # Creates a time select tag that, when POSTed, will be stored in the article variable in the sunrise attribute.
306
+ # time_select("article", "sunrise")
307
+ #
308
+ # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the article variables in
309
+ # # the sunrise attribute.
310
+ # time_select("article", "start_time", include_seconds: true)
311
+ #
312
+ # # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30, and 45.
313
+ # time_select 'game', 'game_time', { minute_step: 15 }
314
+ #
315
+ # # Creates a time select tag with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
316
+ # time_select("article", "written_on", prompt: { hour: 'Choose hour', minute: 'Choose minute', second: 'Choose seconds' })
317
+ # time_select("article", "written_on", prompt: { hour: true }) # generic prompt for hours
318
+ # time_select("article", "written_on", prompt: true) # generic prompts for all
319
+ #
320
+ # # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM.
321
+ # time_select 'game', 'game_time', { ampm: true }
322
+ #
323
+ # # You can set :ignore_date option to true which will remove the hidden inputs for day,
324
+ # # month, and year that are set by default on this helper when you only want the time inputs
325
+ # time_select 'game', 'game_time', { ignore_date: true }
326
+ #
327
+ # The selects are prepared for multi-parameter assignment to an Active Record object.
328
+ #
329
+ # 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
330
+ # all month choices are valid.
331
+ def time_select(object_name, method, options = {}, html_options = {})
332
+ Tags::TimeSelect.new(object_name, method, self, options, html_options).render
333
+ end
334
+
335
+ # Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a
336
+ # specified datetime-based attribute (identified by +method+) on an object assigned to the template (identified
337
+ # by +object+).
338
+ #
339
+ # If anything is passed in the html_options hash it will be applied to every select tag in the set.
340
+ #
341
+ # # Generates a datetime select that, when POSTed, will be stored in the article variable in the written_on
342
+ # # attribute.
343
+ # datetime_select("article", "written_on")
344
+ #
345
+ # # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the
346
+ # # article variable in the written_on attribute.
347
+ # datetime_select("article", "written_on", start_year: 1995)
348
+ #
349
+ # # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will
350
+ # # be stored in the trip variable in the departing attribute.
351
+ # datetime_select("trip", "departing", default: 3.days.from_now)
352
+ #
353
+ # # Generate a datetime select with hours in the AM/PM format
354
+ # datetime_select("article", "written_on", ampm: true)
355
+ #
356
+ # # Generates a datetime select that discards the type that, when POSTed, will be stored in the article variable
357
+ # # as the written_on attribute.
358
+ # datetime_select("article", "written_on", discard_type: true)
359
+ #
360
+ # # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
361
+ # datetime_select("article", "written_on", prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
362
+ # datetime_select("article", "written_on", prompt: { hour: true }) # generic prompt for hours
363
+ # datetime_select("article", "written_on", prompt: true) # generic prompts for all
364
+ #
365
+ # The selects are prepared for multi-parameter assignment to an Active Record object.
366
+ def datetime_select(object_name, method, options = {}, html_options = {})
367
+ Tags::DatetimeSelect.new(object_name, method, self, options, html_options).render
368
+ end
369
+
370
+ # Returns a set of HTML select-tags (one for year, month, day, hour, minute, and second) pre-selected with the
371
+ # +datetime+. It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with
372
+ # an array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not
373
+ # supply a Symbol, it will be appended onto the <tt>:order</tt> passed in. You can also add
374
+ # <tt>:date_separator</tt>, <tt>:datetime_separator</tt> and <tt>:time_separator</tt> keys to the +options+ to
375
+ # control visual display of the elements.
376
+ #
377
+ # If anything is passed in the html_options hash it will be applied to every select tag in the set.
378
+ #
379
+ # my_date_time = Time.now + 4.days
380
+ #
381
+ # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today).
382
+ # select_datetime(my_date_time)
383
+ #
384
+ # # Generates a datetime select that defaults to today (no specified datetime)
385
+ # select_datetime()
386
+ #
387
+ # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
388
+ # # with the fields ordered year, month, day rather than month, day, year.
389
+ # select_datetime(my_date_time, order: [:year, :month, :day])
390
+ #
391
+ # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
392
+ # # with a '/' between each date field.
393
+ # select_datetime(my_date_time, date_separator: '/')
394
+ #
395
+ # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
396
+ # # with a date fields separated by '/', time fields separated by '' and the date and time fields
397
+ # # separated by a comma (',').
398
+ # select_datetime(my_date_time, date_separator: '/', time_separator: '', datetime_separator: ',')
399
+ #
400
+ # # Generates a datetime select that discards the type of the field and defaults to the datetime in
401
+ # # my_date_time (four days after today)
402
+ # select_datetime(my_date_time, discard_type: true)
403
+ #
404
+ # # Generate a datetime field with hours in the AM/PM format
405
+ # select_datetime(my_date_time, ampm: true)
406
+ #
407
+ # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
408
+ # # prefixed with 'payday' rather than 'date'
409
+ # select_datetime(my_date_time, prefix: 'payday')
410
+ #
411
+ # # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
412
+ # select_datetime(my_date_time, prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
413
+ # select_datetime(my_date_time, prompt: { hour: true }) # generic prompt for hours
414
+ # select_datetime(my_date_time, prompt: true) # generic prompts for all
415
+ def select_datetime(datetime = Time.current, options = {}, html_options = {})
416
+ DateTimeSelector.new(datetime, options, html_options).select_datetime
417
+ end
418
+
419
+ # Returns a set of HTML select-tags (one for year, month, and day) pre-selected with the +date+.
420
+ # It's possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
421
+ # symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order.
422
+ # If the array passed to the <tt>:order</tt> option does not contain all the three symbols, all tags will be hidden.
423
+ #
424
+ # If anything is passed in the html_options hash it will be applied to every select tag in the set.
425
+ #
426
+ # my_date = Time.now + 6.days
427
+ #
428
+ # # Generates a date select that defaults to the date in my_date (six days after today).
429
+ # select_date(my_date)
430
+ #
431
+ # # Generates a date select that defaults to today (no specified date).
432
+ # select_date()
433
+ #
434
+ # # Generates a date select that defaults to the date in my_date (six days after today)
435
+ # # with the fields ordered year, month, day rather than month, day, year.
436
+ # select_date(my_date, order: [:year, :month, :day])
437
+ #
438
+ # # Generates a date select that discards the type of the field and defaults to the date in
439
+ # # my_date (six days after today).
440
+ # select_date(my_date, discard_type: true)
441
+ #
442
+ # # Generates a date select that defaults to the date in my_date,
443
+ # # which has fields separated by '/'.
444
+ # select_date(my_date, date_separator: '/')
445
+ #
446
+ # # Generates a date select that defaults to the datetime in my_date (six days after today)
447
+ # # prefixed with 'payday' rather than 'date'.
448
+ # select_date(my_date, prefix: 'payday')
449
+ #
450
+ # # Generates a date select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
451
+ # select_date(my_date, prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
452
+ # select_date(my_date, prompt: { hour: true }) # generic prompt for hours
453
+ # select_date(my_date, prompt: true) # generic prompts for all
454
+ def select_date(date = Date.current, options = {}, html_options = {})
455
+ DateTimeSelector.new(date, options, html_options).select_date
456
+ end
457
+
458
+ # Returns a set of HTML select-tags (one for hour and minute).
459
+ # You can set <tt>:time_separator</tt> key to format the output, and
460
+ # the <tt>:include_seconds</tt> option to include an input for seconds.
461
+ #
462
+ # If anything is passed in the html_options hash it will be applied to every select tag in the set.
463
+ #
464
+ # my_time = Time.now + 5.days + 7.hours + 3.minutes + 14.seconds
465
+ #
466
+ # # Generates a time select that defaults to the time in my_time.
467
+ # select_time(my_time)
468
+ #
469
+ # # Generates a time select that defaults to the current time (no specified time).
470
+ # select_time()
471
+ #
472
+ # # Generates a time select that defaults to the time in my_time,
473
+ # # which has fields separated by ':'.
474
+ # select_time(my_time, time_separator: ':')
475
+ #
476
+ # # Generates a time select that defaults to the time in my_time,
477
+ # # that also includes an input for seconds.
478
+ # select_time(my_time, include_seconds: true)
479
+ #
480
+ # # Generates a time select that defaults to the time in my_time, that has fields
481
+ # # separated by ':' and includes an input for seconds.
482
+ # select_time(my_time, time_separator: ':', include_seconds: true)
483
+ #
484
+ # # Generate a time select field with hours in the AM/PM format
485
+ # select_time(my_time, ampm: true)
486
+ #
487
+ # # Generates a time select field with hours that range from 2 to 14
488
+ # select_time(my_time, start_hour: 2, end_hour: 14)
489
+ #
490
+ # # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts.
491
+ # select_time(my_time, prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
492
+ # select_time(my_time, prompt: { hour: true }) # generic prompt for hours
493
+ # select_time(my_time, prompt: true) # generic prompts for all
494
+ def select_time(datetime = Time.current, options = {}, html_options = {})
495
+ DateTimeSelector.new(datetime, options, html_options).select_time
496
+ end
497
+
498
+ # Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
499
+ # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
500
+ # Override the field name using the <tt>:field_name</tt> option, 'second' by default.
501
+ #
502
+ # my_time = Time.now + 16.seconds
503
+ #
504
+ # # Generates a select field for seconds that defaults to the seconds for the time in my_time.
505
+ # select_second(my_time)
506
+ #
507
+ # # Generates a select field for seconds that defaults to the number given.
508
+ # select_second(33)
509
+ #
510
+ # # Generates a select field for seconds that defaults to the seconds for the time in my_time
511
+ # # that is named 'interval' rather than 'second'.
512
+ # select_second(my_time, field_name: 'interval')
513
+ #
514
+ # # Generates a select field for seconds with a custom prompt. Use <tt>prompt: true</tt> for a
515
+ # # generic prompt.
516
+ # select_second(14, prompt: 'Choose seconds')
517
+ def select_second(datetime, options = {}, html_options = {})
518
+ DateTimeSelector.new(datetime, options, html_options).select_second
519
+ end
520
+
521
+ # Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
522
+ # Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
523
+ # selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
524
+ # Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
525
+ #
526
+ # my_time = Time.now + 10.minutes
527
+ #
528
+ # # Generates a select field for minutes that defaults to the minutes for the time in my_time.
529
+ # select_minute(my_time)
530
+ #
531
+ # # Generates a select field for minutes that defaults to the number given.
532
+ # select_minute(14)
533
+ #
534
+ # # Generates a select field for minutes that defaults to the minutes for the time in my_time
535
+ # # that is named 'moment' rather than 'minute'.
536
+ # select_minute(my_time, field_name: 'moment')
537
+ #
538
+ # # Generates a select field for minutes with a custom prompt. Use <tt>prompt: true</tt> for a
539
+ # # generic prompt.
540
+ # select_minute(14, prompt: 'Choose minutes')
541
+ def select_minute(datetime, options = {}, html_options = {})
542
+ DateTimeSelector.new(datetime, options, html_options).select_minute
543
+ end
544
+
545
+ # Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
546
+ # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
547
+ # Override the field name using the <tt>:field_name</tt> option, 'hour' by default.
548
+ #
549
+ # my_time = Time.now + 6.hours
550
+ #
551
+ # # Generates a select field for hours that defaults to the hour for the time in my_time.
552
+ # select_hour(my_time)
553
+ #
554
+ # # Generates a select field for hours that defaults to the number given.
555
+ # select_hour(13)
556
+ #
557
+ # # Generates a select field for hours that defaults to the hour for the time in my_time
558
+ # # that is named 'stride' rather than 'hour'.
559
+ # select_hour(my_time, field_name: 'stride')
560
+ #
561
+ # # Generates a select field for hours with a custom prompt. Use <tt>prompt: true</tt> for a
562
+ # # generic prompt.
563
+ # select_hour(13, prompt: 'Choose hour')
564
+ #
565
+ # # Generate a select field for hours in the AM/PM format
566
+ # select_hour(my_time, ampm: true)
567
+ #
568
+ # # Generates a select field that includes options for hours from 2 to 14.
569
+ # select_hour(my_time, start_hour: 2, end_hour: 14)
570
+ def select_hour(datetime, options = {}, html_options = {})
571
+ DateTimeSelector.new(datetime, options, html_options).select_hour
572
+ end
573
+
574
+ # Returns a select tag with options for each of the days 1 through 31 with the current day selected.
575
+ # The <tt>date</tt> can also be substituted for a day number.
576
+ # If you want to display days with a leading zero set the <tt>:use_two_digit_numbers</tt> key in +options+ to true.
577
+ # Override the field name using the <tt>:field_name</tt> option, 'day' by default.
578
+ #
579
+ # my_date = Time.now + 2.days
580
+ #
581
+ # # Generates a select field for days that defaults to the day for the date in my_date.
582
+ # select_day(my_date)
583
+ #
584
+ # # Generates a select field for days that defaults to the number given.
585
+ # select_day(5)
586
+ #
587
+ # # Generates a select field for days that defaults to the number given, but displays it with two digits.
588
+ # select_day(5, use_two_digit_numbers: true)
589
+ #
590
+ # # Generates a select field for days that defaults to the day for the date in my_date
591
+ # # that is named 'due' rather than 'day'.
592
+ # select_day(my_date, field_name: 'due')
593
+ #
594
+ # # Generates a select field for days with a custom prompt. Use <tt>prompt: true</tt> for a
595
+ # # generic prompt.
596
+ # select_day(5, prompt: 'Choose day')
597
+ def select_day(date, options = {}, html_options = {})
598
+ DateTimeSelector.new(date, options, html_options).select_day
599
+ end
600
+
601
+ # Returns a select tag with options for each of the months January through December with the current month
602
+ # selected. The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are
603
+ # used as values (what's submitted to the server). It's also possible to use month numbers for the presentation
604
+ # instead of names -- set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you
605
+ # want both numbers and names, set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer
606
+ # to show month names as abbreviations, set the <tt>:use_short_month</tt> key in +options+ to true. If you want
607
+ # to use your own month names, set the <tt>:use_month_names</tt> key in +options+ to an array of 12 month names.
608
+ # If you want to display months with a leading zero set the <tt>:use_two_digit_numbers</tt> key in +options+ to true.
609
+ # Override the field name using the <tt>:field_name</tt> option, 'month' by default.
610
+ #
611
+ # # Generates a select field for months that defaults to the current month that
612
+ # # will use keys like "January", "March".
613
+ # select_month(Date.today)
614
+ #
615
+ # # Generates a select field for months that defaults to the current month that
616
+ # # is named "start" rather than "month".
617
+ # select_month(Date.today, field_name: 'start')
618
+ #
619
+ # # Generates a select field for months that defaults to the current month that
620
+ # # will use keys like "1", "3".
621
+ # select_month(Date.today, use_month_numbers: true)
622
+ #
623
+ # # Generates a select field for months that defaults to the current month that
624
+ # # will use keys like "1 - January", "3 - March".
625
+ # select_month(Date.today, add_month_numbers: true)
626
+ #
627
+ # # Generates a select field for months that defaults to the current month that
628
+ # # will use keys like "Jan", "Mar".
629
+ # select_month(Date.today, use_short_month: true)
630
+ #
631
+ # # Generates a select field for months that defaults to the current month that
632
+ # # will use keys like "Januar", "Marts."
633
+ # select_month(Date.today, use_month_names: %w(Januar Februar Marts ...))
634
+ #
635
+ # # Generates a select field for months that defaults to the current month that
636
+ # # will use keys with two digit numbers like "01", "03".
637
+ # select_month(Date.today, use_two_digit_numbers: true)
638
+ #
639
+ # # Generates a select field for months with a custom prompt. Use <tt>prompt: true</tt> for a
640
+ # # generic prompt.
641
+ # select_month(14, prompt: 'Choose month')
642
+ def select_month(date, options = {}, html_options = {})
643
+ DateTimeSelector.new(date, options, html_options).select_month
644
+ end
645
+
646
+ # Returns a select tag with options for each of the five years on each side of the current, which is selected.
647
+ # The five year radius can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the
648
+ # +options+. Both ascending and descending year lists are supported by making <tt>:start_year</tt> less than or
649
+ # greater than <tt>:end_year</tt>. The <tt>date</tt> can also be substituted for a year given as a number.
650
+ # Override the field name using the <tt>:field_name</tt> option, 'year' by default.
651
+ #
652
+ # # Generates a select field for years that defaults to the current year that
653
+ # # has ascending year values.
654
+ # select_year(Date.today, start_year: 1992, end_year: 2007)
655
+ #
656
+ # # Generates a select field for years that defaults to the current year that
657
+ # # is named 'birth' rather than 'year'.
658
+ # select_year(Date.today, field_name: 'birth')
659
+ #
660
+ # # Generates a select field for years that defaults to the current year that
661
+ # # has descending year values.
662
+ # select_year(Date.today, start_year: 2005, end_year: 1900)
663
+ #
664
+ # # Generates a select field for years that defaults to the year 2006 that
665
+ # # has ascending year values.
666
+ # select_year(2006, start_year: 2000, end_year: 2010)
667
+ #
668
+ # # Generates a select field for years with a custom prompt. Use <tt>prompt: true</tt> for a
669
+ # # generic prompt.
670
+ # select_year(14, prompt: 'Choose year')
671
+ def select_year(date, options = {}, html_options = {})
672
+ DateTimeSelector.new(date, options, html_options).select_year
673
+ end
674
+
675
+ # Returns an HTML time tag for the given date or time.
676
+ #
677
+ # time_tag Date.today # =>
678
+ # <time datetime="2010-11-04">November 04, 2010</time>
679
+ # time_tag Time.now # =>
680
+ # <time datetime="2010-11-04T17:55:45+01:00">November 04, 2010 17:55</time>
681
+ # time_tag Date.yesterday, 'Yesterday' # =>
682
+ # <time datetime="2010-11-03">Yesterday</time>
683
+ # time_tag Date.today, datetime: Date.today.strftime('%G-W%V') # =>
684
+ # <time datetime="2010-W44">November 04, 2010</time>
685
+ #
686
+ # <%= time_tag Time.now do %>
687
+ # <span>Right now</span>
688
+ # <% end %>
689
+ # # => <time datetime="2010-11-04T17:55:45+01:00"><span>Right now</span></time>
690
+ def time_tag(date_or_time, *args, &block)
691
+ options = args.extract_options!
692
+ format = options.delete(:format) || :long
693
+ content = args.first || I18n.l(date_or_time, format: format)
694
+
695
+ content_tag("time", content, options.reverse_merge(datetime: date_or_time.iso8601), &block)
696
+ end
697
+
698
+ private
699
+ def normalize_distance_of_time_argument_to_time(value)
700
+ if value.is_a?(Numeric)
701
+ Time.at(value)
702
+ elsif value.respond_to?(:to_time)
703
+ value.to_time
704
+ else
705
+ raise ArgumentError, "#{value.inspect} can't be converted to a Time value"
706
+ end
707
+ end
708
+ end
709
+
710
+ class DateTimeSelector # :nodoc:
711
+ include ActionView::Helpers::TagHelper
712
+
713
+ DEFAULT_PREFIX = "date"
714
+ POSITION = {
715
+ year: 1, month: 2, day: 3, hour: 4, minute: 5, second: 6
716
+ }.freeze
717
+
718
+ AMPM_TRANSLATION = Hash[
719
+ [[0, "12 AM"], [1, "01 AM"], [2, "02 AM"], [3, "03 AM"],
720
+ [4, "04 AM"], [5, "05 AM"], [6, "06 AM"], [7, "07 AM"],
721
+ [8, "08 AM"], [9, "09 AM"], [10, "10 AM"], [11, "11 AM"],
722
+ [12, "12 PM"], [13, "01 PM"], [14, "02 PM"], [15, "03 PM"],
723
+ [16, "04 PM"], [17, "05 PM"], [18, "06 PM"], [19, "07 PM"],
724
+ [20, "08 PM"], [21, "09 PM"], [22, "10 PM"], [23, "11 PM"]]
725
+ ].freeze
726
+
727
+ def initialize(datetime, options = {}, html_options = {})
728
+ @options = options.dup
729
+ @html_options = html_options.dup
730
+ @datetime = datetime
731
+ @options[:datetime_separator] ||= " &mdash; "
732
+ @options[:time_separator] ||= " : "
733
+ end
734
+
735
+ def select_datetime
736
+ order = date_order.dup
737
+ order -= [:hour, :minute, :second]
738
+ @options[:discard_year] ||= true unless order.include?(:year)
739
+ @options[:discard_month] ||= true unless order.include?(:month)
740
+ @options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
741
+ @options[:discard_minute] ||= true if @options[:discard_hour]
742
+ @options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
743
+
744
+ set_day_if_discarded
745
+
746
+ if @options[:tag] && @options[:ignore_date]
747
+ select_time
748
+ else
749
+ [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
750
+ order += [:hour, :minute, :second] unless @options[:discard_hour]
751
+
752
+ build_selects_from_types(order)
753
+ end
754
+ end
755
+
756
+ def select_date
757
+ order = date_order.dup
758
+
759
+ @options[:discard_hour] = true
760
+ @options[:discard_minute] = true
761
+ @options[:discard_second] = true
762
+
763
+ @options[:discard_year] ||= true unless order.include?(:year)
764
+ @options[:discard_month] ||= true unless order.include?(:month)
765
+ @options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
766
+
767
+ set_day_if_discarded
768
+
769
+ [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
770
+
771
+ build_selects_from_types(order)
772
+ end
773
+
774
+ def select_time
775
+ order = []
776
+
777
+ @options[:discard_month] = true
778
+ @options[:discard_year] = true
779
+ @options[:discard_day] = true
780
+ @options[:discard_second] ||= true unless @options[:include_seconds]
781
+
782
+ order += [:year, :month, :day] unless @options[:ignore_date]
783
+
784
+ order += [:hour, :minute]
785
+ order << :second if @options[:include_seconds]
786
+
787
+ build_selects_from_types(order)
788
+ end
789
+
790
+ def select_second
791
+ if @options[:use_hidden] || @options[:discard_second]
792
+ build_hidden(:second, sec) if @options[:include_seconds]
793
+ else
794
+ build_options_and_select(:second, sec)
795
+ end
796
+ end
797
+
798
+ def select_minute
799
+ if @options[:use_hidden] || @options[:discard_minute]
800
+ build_hidden(:minute, min)
801
+ else
802
+ build_options_and_select(:minute, min, step: @options[:minute_step])
803
+ end
804
+ end
805
+
806
+ def select_hour
807
+ if @options[:use_hidden] || @options[:discard_hour]
808
+ build_hidden(:hour, hour)
809
+ else
810
+ options = {}
811
+ options[:ampm] = @options[:ampm] || false
812
+ options[:start] = @options[:start_hour] || 0
813
+ options[:end] = @options[:end_hour] || 23
814
+ build_options_and_select(:hour, hour, options)
815
+ end
816
+ end
817
+
818
+ def select_day
819
+ if @options[:use_hidden] || @options[:discard_day]
820
+ build_hidden(:day, day || 1)
821
+ else
822
+ build_select(:day, build_day_options(day))
823
+ end
824
+ end
825
+
826
+ def select_month
827
+ if @options[:use_hidden] || @options[:discard_month]
828
+ build_hidden(:month, month || 1)
829
+ else
830
+ month_options = []
831
+ 1.upto(12) do |month_number|
832
+ options = { value: month_number }
833
+ options[:selected] = "selected" if month == month_number
834
+ month_options << content_tag("option", month_name(month_number), options) + "\n"
835
+ end
836
+ build_select(:month, month_options.join)
837
+ end
838
+ end
839
+
840
+ def select_year
841
+ if !year || @datetime == 0
842
+ val = "1"
843
+ middle_year = Date.today.year
844
+ else
845
+ val = middle_year = year
846
+ end
847
+
848
+ if @options[:use_hidden] || @options[:discard_year]
849
+ build_hidden(:year, val)
850
+ else
851
+ options = {}
852
+ options[:start] = @options[:start_year] || middle_year - 5
853
+ options[:end] = @options[:end_year] || middle_year + 5
854
+ options[:step] = options[:start] < options[:end] ? 1 : -1
855
+
856
+ max_years_allowed = @options[:max_years_allowed] || 1000
857
+
858
+ if (options[:end] - options[:start]).abs > max_years_allowed
859
+ raise ArgumentError, "There are too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter."
860
+ end
861
+
862
+ build_select(:year, build_year_options(val, options))
863
+ end
864
+ end
865
+
866
+ private
867
+ %w( sec min hour day month year ).each do |method|
868
+ define_method(method) do
869
+ case @datetime
870
+ when Hash then @datetime[method.to_sym]
871
+ when Numeric then @datetime
872
+ when nil then nil
873
+ else @datetime.send(method)
874
+ end
875
+ end
876
+ end
877
+
878
+ def prompt_text(prompt, type)
879
+ prompt.kind_of?(String) ? prompt : I18n.translate(:"datetime.prompts.#{type}", locale: @options[:locale])
880
+ end
881
+
882
+ # If the day is hidden, the day should be set to the 1st so all month and year choices are
883
+ # valid. Otherwise, February 31st or February 29th, 2011 can be selected, which are invalid.
884
+ def set_day_if_discarded
885
+ if @datetime && @options[:discard_day]
886
+ @datetime = @datetime.change(day: 1)
887
+ end
888
+ end
889
+
890
+ # Returns translated month names, but also ensures that a custom month
891
+ # name array has a leading +nil+ element.
892
+ def month_names
893
+ @month_names ||= begin
894
+ month_names = @options[:use_month_names] || translated_month_names
895
+ month_names = [nil, *month_names] if month_names.size < 13
896
+ month_names
897
+ end
898
+ end
899
+
900
+ # Returns translated month names.
901
+ # => [nil, "January", "February", "March",
902
+ # "April", "May", "June", "July",
903
+ # "August", "September", "October",
904
+ # "November", "December"]
905
+ #
906
+ # If <tt>:use_short_month</tt> option is set
907
+ # => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
908
+ # "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
909
+ def translated_month_names
910
+ key = @options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
911
+ I18n.translate(key, locale: @options[:locale])
912
+ end
913
+
914
+ # Looks up day names by number.
915
+ #
916
+ # day_name(1) # => 1
917
+ #
918
+ # If the <tt>use_two_digit_numbers: true</tt> option is passed to DateTimeSelector:
919
+ #
920
+ # day_name(1) # => "01"
921
+ #
922
+ # If the <tt>day_format: ->(day) { day.ordinalize }</tt> option is passed to DateTimeSelector:
923
+ #
924
+ # day_name(1) # => "1st"
925
+ def day_name(number)
926
+ if day_format_lambda = @options[:day_format]
927
+ day_format_lambda.call(number)
928
+ elsif @options[:use_two_digit_numbers]
929
+ "%02d" % number
930
+ else
931
+ number
932
+ end
933
+ end
934
+
935
+ # Looks up month names by number (1-based):
936
+ #
937
+ # month_name(1) # => "January"
938
+ #
939
+ # If the <tt>:use_month_numbers</tt> option is passed:
940
+ #
941
+ # month_name(1) # => 1
942
+ #
943
+ # If the <tt>:use_two_digit_numbers</tt> option is passed:
944
+ #
945
+ # month_name(1) # => '01'
946
+ #
947
+ # If the <tt>:add_month_numbers</tt> option is passed:
948
+ #
949
+ # month_name(1) # => "1 - January"
950
+ #
951
+ # If the <tt>:month_format_string</tt> option is passed:
952
+ #
953
+ # month_name(1) # => "January (01)"
954
+ #
955
+ # depending on the format string.
956
+ def month_name(number)
957
+ if @options[:use_month_numbers]
958
+ number
959
+ elsif @options[:use_two_digit_numbers]
960
+ "%02d" % number
961
+ elsif @options[:add_month_numbers]
962
+ "#{number} - #{month_names[number]}"
963
+ elsif format_string = @options[:month_format_string]
964
+ format_string % { number: number, name: month_names[number] }
965
+ else
966
+ month_names[number]
967
+ end
968
+ end
969
+
970
+ # Looks up year names by number.
971
+ #
972
+ # year_name(1998) # => 1998
973
+ #
974
+ # If the <tt>:year_format</tt> option is passed:
975
+ #
976
+ # year_name(1998) # => "Heisei 10"
977
+ def year_name(number)
978
+ if year_format_lambda = @options[:year_format]
979
+ year_format_lambda.call(number)
980
+ else
981
+ number
982
+ end
983
+ end
984
+
985
+ def date_order
986
+ @date_order ||= @options[:order] || translated_date_order
987
+ end
988
+
989
+ def translated_date_order
990
+ date_order = I18n.translate(:'date.order', locale: @options[:locale], default: [])
991
+ date_order = date_order.map(&:to_sym)
992
+
993
+ forbidden_elements = date_order - [:year, :month, :day]
994
+ if forbidden_elements.any?
995
+ raise StandardError,
996
+ "#{@options[:locale]}.date.order only accepts :year, :month and :day"
997
+ end
998
+
999
+ date_order
1000
+ end
1001
+
1002
+ # Build full select tag from date type and options.
1003
+ def build_options_and_select(type, selected, options = {})
1004
+ build_select(type, build_options(selected, options))
1005
+ end
1006
+
1007
+ # Build select option HTML from date value and options.
1008
+ #
1009
+ # build_options(15, start: 1, end: 31)
1010
+ # => "<option value="1">1</option>
1011
+ # <option value="2">2</option>
1012
+ # <option value="3">3</option>..."
1013
+ #
1014
+ # If <tt>use_two_digit_numbers: true</tt> option is passed:
1015
+ #
1016
+ # build_options(15, start: 1, end: 31, use_two_digit_numbers: true)
1017
+ # => "<option value="1">01</option>
1018
+ # <option value="2">02</option>
1019
+ # <option value="3">03</option>..."
1020
+ #
1021
+ # If <tt>:step</tt> options is passed:
1022
+ #
1023
+ # build_options(15, start: 1, end: 31, step: 2)
1024
+ # => "<option value="1">1</option>
1025
+ # <option value="3">3</option>
1026
+ # <option value="5">5</option>..."
1027
+ def build_options(selected, options = {})
1028
+ options = {
1029
+ leading_zeros: true, ampm: false, use_two_digit_numbers: false
1030
+ }.merge!(options)
1031
+
1032
+ start = options.delete(:start) || 0
1033
+ stop = options.delete(:end) || 59
1034
+ step = options.delete(:step) || 1
1035
+ leading_zeros = options.delete(:leading_zeros)
1036
+
1037
+ select_options = []
1038
+ start.step(stop, step) do |i|
1039
+ value = leading_zeros ? sprintf("%02d", i) : i
1040
+ tag_options = { value: value }
1041
+ tag_options[:selected] = "selected" if selected == i
1042
+ text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
1043
+ text = options[:ampm] ? AMPM_TRANSLATION[i] : text
1044
+ select_options << content_tag("option", text, tag_options)
1045
+ end
1046
+
1047
+ (select_options.join("\n") + "\n").html_safe
1048
+ end
1049
+
1050
+ # Build select option HTML for day.
1051
+ #
1052
+ # build_day_options(2)
1053
+ # => "<option value="1">1</option>
1054
+ # <option value="2" selected="selected">2</option>
1055
+ # <option value="3">3</option>..."
1056
+ #
1057
+ # If <tt>day_format: ->(day) { day.ordinalize }</tt> option is passed to DateTimeSelector
1058
+ #
1059
+ # build_day_options(2)
1060
+ # => "<option value="1">1st</option>
1061
+ # <option value="2" selected="selected">2nd</option>
1062
+ # <option value="3">3rd</option>..."
1063
+ #
1064
+ # If <tt>use_two_digit_numbers: true</tt> option is passed to DateTimeSelector
1065
+ #
1066
+ # build_day_options(2)
1067
+ # => "<option value="1">01</option>
1068
+ # <option value="2" selected="selected">02</option>
1069
+ # <option value="3">03</option>..."
1070
+ def build_day_options(selected)
1071
+ select_options = []
1072
+ (1..31).each do |value|
1073
+ tag_options = { value: value }
1074
+ tag_options[:selected] = "selected" if selected == value
1075
+ text = day_name(value)
1076
+ select_options << content_tag("option", text, tag_options)
1077
+ end
1078
+
1079
+ (select_options.join("\n") + "\n").html_safe
1080
+ end
1081
+
1082
+ # Build select option HTML for year.
1083
+ #
1084
+ # build_year_options(1998, start: 1998, end: 2000)
1085
+ # => "<option value="1998" selected="selected">1998</option>
1086
+ # <option value="1999">1999</option>
1087
+ # <option value="2000">2000</option>"
1088
+ def build_year_options(selected, options = {})
1089
+ start = options.delete(:start)
1090
+ stop = options.delete(:end)
1091
+ step = options.delete(:step)
1092
+
1093
+ select_options = []
1094
+ start.step(stop, step) do |value|
1095
+ tag_options = { value: value }
1096
+ tag_options[:selected] = "selected" if selected == value
1097
+ text = year_name(value)
1098
+ select_options << content_tag("option", text, tag_options)
1099
+ end
1100
+
1101
+ (select_options.join("\n") + "\n").html_safe
1102
+ end
1103
+
1104
+ # Builds select tag from date type and HTML select options.
1105
+ #
1106
+ # build_select(:month, "<option value="1">January</option>...")
1107
+ # => "<select id="post_written_on_2i" name="post[written_on(2i)]">
1108
+ # <option value="1">January</option>...
1109
+ # </select>"
1110
+ def build_select(type, select_options_as_html)
1111
+ select_options = {
1112
+ id: input_id_from_type(type),
1113
+ name: input_name_from_type(type)
1114
+ }.merge!(@html_options)
1115
+ select_options[:disabled] = "disabled" if @options[:disabled]
1116
+ select_options[:class] = css_class_attribute(type, select_options[:class], @options[:with_css_classes]) if @options[:with_css_classes]
1117
+
1118
+ select_html = +"\n"
1119
+ select_html << content_tag("option", "", value: "", label: " ") + "\n" if @options[:include_blank]
1120
+ select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
1121
+ select_html << select_options_as_html
1122
+
1123
+ (content_tag("select", select_html.html_safe, select_options) + "\n").html_safe
1124
+ end
1125
+
1126
+ # Builds the CSS class value for the select element.
1127
+ #
1128
+ # css_class_attribute(:year, 'date optional', { year: 'my-year' })
1129
+ # => "date optional my-year"
1130
+ def css_class_attribute(type, html_options_class, options) # :nodoc:
1131
+ css_class = \
1132
+ case options
1133
+ when Hash
1134
+ options[type.to_sym]
1135
+ else
1136
+ type
1137
+ end
1138
+
1139
+ [html_options_class, css_class].compact.join(" ")
1140
+ end
1141
+
1142
+ # Builds a prompt option tag with supplied options or from default options.
1143
+ #
1144
+ # prompt_option_tag(:month, prompt: 'Select month')
1145
+ # => "<option value="">Select month</option>"
1146
+ def prompt_option_tag(type, options)
1147
+ prompt = \
1148
+ case options
1149
+ when Hash
1150
+ default_options = { year: false, month: false, day: false, hour: false, minute: false, second: false }
1151
+ default_options.merge!(options)[type.to_sym]
1152
+ when String
1153
+ options
1154
+ else
1155
+ I18n.translate(:"datetime.prompts.#{type}", locale: @options[:locale])
1156
+ end
1157
+
1158
+ prompt ? content_tag("option", prompt_text(prompt, type), value: "") : ""
1159
+ end
1160
+
1161
+ # Builds hidden input tag for date part and value.
1162
+ #
1163
+ # build_hidden(:year, 2008)
1164
+ # => "<input type="hidden" id="date_year" name="date[year]" value="2008" autocomplete="off" />"
1165
+ def build_hidden(type, value)
1166
+ select_options = {
1167
+ type: "hidden",
1168
+ id: input_id_from_type(type),
1169
+ name: input_name_from_type(type),
1170
+ value: value,
1171
+ autocomplete: "off"
1172
+ }.merge!(@html_options.slice(:disabled))
1173
+ select_options[:disabled] = "disabled" if @options[:disabled]
1174
+
1175
+ tag(:input, select_options) + "\n".html_safe
1176
+ end
1177
+
1178
+ # Returns the name attribute for the input tag.
1179
+ # => post[written_on(1i)]
1180
+ def input_name_from_type(type)
1181
+ prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX
1182
+ prefix += "[#{@options[:index]}]" if @options.has_key?(:index)
1183
+
1184
+ field_name = @options[:field_name] || type.to_s
1185
+ if @options[:include_position]
1186
+ field_name += "(#{ActionView::Helpers::DateTimeSelector::POSITION[type]}i)"
1187
+ end
1188
+
1189
+ @options[:discard_type] ? prefix : "#{prefix}[#{field_name}]"
1190
+ end
1191
+
1192
+ # Returns the id attribute for the input tag.
1193
+ # => "post_written_on_1i"
1194
+ def input_id_from_type(type)
1195
+ id = input_name_from_type(type).gsub(/([\[(])|(\]\[)/, "_").gsub(/[\])]/, "")
1196
+ id = @options[:namespace] + "_" + id if @options[:namespace]
1197
+
1198
+ id
1199
+ end
1200
+
1201
+ # Given an ordering of datetime components, create the selection HTML
1202
+ # and join them with their appropriate separators.
1203
+ def build_selects_from_types(order)
1204
+ select = +""
1205
+ first_visible = order.find { |type| !@options[:"discard_#{type}"] }
1206
+ order.reverse_each do |type|
1207
+ separator = separator(type) unless type == first_visible # don't add before first visible field
1208
+ select.insert(0, separator.to_s + public_send("select_#{type}").to_s)
1209
+ end
1210
+ select.html_safe
1211
+ end
1212
+
1213
+ # Returns the separator for a given datetime component.
1214
+ def separator(type)
1215
+ return "" if @options[:use_hidden]
1216
+
1217
+ case type
1218
+ when :year, :month, :day
1219
+ @options[:"discard_#{type}"] ? "" : @options[:date_separator]
1220
+ when :hour
1221
+ (@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
1222
+ when :minute, :second
1223
+ @options[:"discard_#{type}"] ? "" : @options[:time_separator]
1224
+ end
1225
+ end
1226
+ end
1227
+
1228
+ class FormBuilder
1229
+ # Wraps ActionView::Helpers::DateHelper#date_select for form builders:
1230
+ #
1231
+ # <%= form_for @person do |f| %>
1232
+ # <%= f.date_select :birth_date %>
1233
+ # <%= f.submit %>
1234
+ # <% end %>
1235
+ #
1236
+ # Please refer to the documentation of the base helper for details.
1237
+ def date_select(method, options = {}, html_options = {})
1238
+ @template.date_select(@object_name, method, objectify_options(options), html_options)
1239
+ end
1240
+
1241
+ # Wraps ActionView::Helpers::DateHelper#time_select for form builders:
1242
+ #
1243
+ # <%= form_for @race do |f| %>
1244
+ # <%= f.time_select :average_lap %>
1245
+ # <%= f.submit %>
1246
+ # <% end %>
1247
+ #
1248
+ # Please refer to the documentation of the base helper for details.
1249
+ def time_select(method, options = {}, html_options = {})
1250
+ @template.time_select(@object_name, method, objectify_options(options), html_options)
1251
+ end
1252
+
1253
+ # Wraps ActionView::Helpers::DateHelper#datetime_select for form builders:
1254
+ #
1255
+ # <%= form_for @person do |f| %>
1256
+ # <%= f.datetime_select :last_request_at %>
1257
+ # <%= f.submit %>
1258
+ # <% end %>
1259
+ #
1260
+ # Please refer to the documentation of the base helper for details.
1261
+ def datetime_select(method, options = {}, html_options = {})
1262
+ @template.datetime_select(@object_name, method, objectify_options(options), html_options)
1263
+ end
1264
+ end
1265
+ end
1266
+ end