actionview 4.2.11.1 → 7.0.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 actionview might be problematic. Click here for more details.

Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +229 -215
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -8
  5. data/lib/action_view/base.rb +116 -43
  6. data/lib/action_view/buffers.rb +20 -3
  7. data/lib/action_view/cache_expiry.rb +66 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
  10. data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
  11. data/lib/action_view/dependency_tracker.rb +21 -122
  12. data/lib/action_view/digestor.rb +92 -85
  13. data/lib/action_view/flows.rb +15 -16
  14. data/lib/action_view/gem_version.rb +6 -4
  15. data/lib/action_view/helpers/active_model_helper.rb +17 -12
  16. data/lib/action_view/helpers/asset_tag_helper.rb +356 -101
  17. data/lib/action_view/helpers/asset_url_helper.rb +180 -74
  18. data/lib/action_view/helpers/atom_feed_helper.rb +21 -19
  19. data/lib/action_view/helpers/cache_helper.rb +156 -43
  20. data/lib/action_view/helpers/capture_helper.rb +21 -14
  21. data/lib/action_view/helpers/controller_helper.rb +16 -5
  22. data/lib/action_view/helpers/csp_helper.rb +26 -0
  23. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  24. data/lib/action_view/helpers/date_helper.rb +288 -132
  25. data/lib/action_view/helpers/debug_helper.rb +9 -6
  26. data/lib/action_view/helpers/form_helper.rb +956 -173
  27. data/lib/action_view/helpers/form_options_helper.rb +178 -97
  28. data/lib/action_view/helpers/form_tag_helper.rb +220 -101
  29. data/lib/action_view/helpers/javascript_helper.rb +33 -19
  30. data/lib/action_view/helpers/number_helper.rb +88 -63
  31. data/lib/action_view/helpers/output_safety_helper.rb +38 -6
  32. data/lib/action_view/helpers/rendering_helper.rb +21 -10
  33. data/lib/action_view/helpers/sanitize_helper.rb +31 -32
  34. data/lib/action_view/helpers/tag_helper.rb +332 -71
  35. data/lib/action_view/helpers/tags/base.rb +123 -99
  36. data/lib/action_view/helpers/tags/check_box.rb +21 -20
  37. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  38. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -34
  39. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  40. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  41. data/lib/action_view/helpers/tags/collection_select.rb +5 -3
  42. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  43. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  44. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  45. data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
  46. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  47. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  48. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  49. data/lib/action_view/helpers/tags/file_field.rb +18 -0
  50. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  51. data/lib/action_view/helpers/tags/hidden_field.rb +6 -0
  52. data/lib/action_view/helpers/tags/label.rb +7 -2
  53. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  54. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  55. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  56. data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
  57. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  58. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  59. data/lib/action_view/helpers/tags/search_field.rb +14 -9
  60. data/lib/action_view/helpers/tags/select.rb +11 -10
  61. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  62. data/lib/action_view/helpers/tags/text_area.rb +4 -2
  63. data/lib/action_view/helpers/tags/text_field.rb +8 -8
  64. data/lib/action_view/helpers/tags/time_field.rb +12 -2
  65. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  66. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  67. data/lib/action_view/helpers/tags/translator.rb +15 -16
  68. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  69. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  70. data/lib/action_view/helpers/tags/weekday_select.rb +28 -0
  71. data/lib/action_view/helpers/tags.rb +5 -2
  72. data/lib/action_view/helpers/text_helper.rb +80 -51
  73. data/lib/action_view/helpers/translation_helper.rb +120 -69
  74. data/lib/action_view/helpers/url_helper.rb +398 -171
  75. data/lib/action_view/helpers.rb +29 -27
  76. data/lib/action_view/layouts.rb +68 -63
  77. data/lib/action_view/log_subscriber.rb +77 -10
  78. data/lib/action_view/lookup_context.rb +137 -113
  79. data/lib/action_view/model_naming.rb +4 -2
  80. data/lib/action_view/path_set.rb +28 -32
  81. data/lib/action_view/railtie.rb +74 -13
  82. data/lib/action_view/record_identifier.rb +53 -26
  83. data/lib/action_view/render_parser.rb +188 -0
  84. data/lib/action_view/renderer/abstract_renderer.rb +152 -15
  85. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  86. data/lib/action_view/renderer/object_renderer.rb +34 -0
  87. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  88. data/lib/action_view/renderer/partial_renderer.rb +51 -333
  89. data/lib/action_view/renderer/renderer.rb +68 -11
  90. data/lib/action_view/renderer/streaming_template_renderer.rb +60 -56
  91. data/lib/action_view/renderer/template_renderer.rb +87 -74
  92. data/lib/action_view/rendering.rb +73 -47
  93. data/lib/action_view/ripper_ast_parser.rb +198 -0
  94. data/lib/action_view/routing_url_for.rb +35 -24
  95. data/lib/action_view/tasks/cache_digests.rake +25 -0
  96. data/lib/action_view/template/error.rb +151 -41
  97. data/lib/action_view/template/handlers/builder.rb +12 -13
  98. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  99. data/lib/action_view/template/handlers/erb.rb +29 -89
  100. data/lib/action_view/template/handlers/html.rb +11 -0
  101. data/lib/action_view/template/handlers/raw.rb +4 -4
  102. data/lib/action_view/template/handlers.rb +14 -10
  103. data/lib/action_view/template/html.rb +12 -13
  104. data/lib/action_view/template/inline.rb +22 -0
  105. data/lib/action_view/template/raw_file.rb +25 -0
  106. data/lib/action_view/template/renderable.rb +24 -0
  107. data/lib/action_view/template/resolver.rb +139 -300
  108. data/lib/action_view/template/sources/file.rb +17 -0
  109. data/lib/action_view/template/sources.rb +13 -0
  110. data/lib/action_view/template/text.rb +10 -12
  111. data/lib/action_view/template/types.rb +28 -26
  112. data/lib/action_view/template.rb +123 -91
  113. data/lib/action_view/template_details.rb +66 -0
  114. data/lib/action_view/template_path.rb +64 -0
  115. data/lib/action_view/test_case.rb +70 -53
  116. data/lib/action_view/testing/resolvers.rb +25 -35
  117. data/lib/action_view/unbound_template.rb +57 -0
  118. data/lib/action_view/version.rb +3 -1
  119. data/lib/action_view/view_paths.rb +73 -58
  120. data/lib/action_view.rb +16 -11
  121. data/lib/assets/compiled/rails-ujs.js +746 -0
  122. metadata +52 -32
  123. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  124. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,12 +1,15 @@
1
- require 'date'
2
- require 'action_view/helpers/tag_helper'
3
- require 'active_support/core_ext/array/extract_options'
4
- require 'active_support/core_ext/date/conversions'
5
- require 'active_support/core_ext/hash/slice'
6
- require 'active_support/core_ext/object/with_options'
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"
7
10
 
8
11
  module ActionView
9
- module Helpers
12
+ module Helpers # :nodoc:
10
13
  # = Action View Date Helpers
11
14
  #
12
15
  # The Date Helper primarily creates select/option tags for different kinds of dates and times or date and time
@@ -68,71 +71,88 @@ module ActionView
68
71
  # distance_of_time_in_words(from_time, to_time, include_seconds: true) # => about 6 years
69
72
  # distance_of_time_in_words(to_time, from_time, include_seconds: true) # => about 6 years
70
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"
71
95
  def distance_of_time_in_words(from_time, to_time = 0, options = {})
72
96
  options = {
73
97
  scope: :'datetime.distance_in_words'
74
98
  }.merge!(options)
75
99
 
76
- from_time = from_time.to_time if from_time.respond_to?(:to_time)
77
- to_time = to_time.to_time if to_time.respond_to?(:to_time)
100
+ from_time = normalize_distance_of_time_argument_to_time(from_time)
101
+ to_time = normalize_distance_of_time_argument_to_time(to_time)
78
102
  from_time, to_time = to_time, from_time if from_time > to_time
79
- distance_in_minutes = ((to_time - from_time)/60.0).round
103
+ distance_in_minutes = ((to_time - from_time) / 60.0).round
80
104
  distance_in_seconds = (to_time - from_time).round
81
105
 
82
- I18n.with_options :locale => options[:locale], :scope => options[:scope] do |locale|
106
+ I18n.with_options locale: options[:locale], scope: options[:scope] do |locale|
83
107
  case distance_in_minutes
84
- when 0..1
85
- return distance_in_minutes == 0 ?
86
- locale.t(:less_than_x_minutes, :count => 1) :
87
- locale.t(:x_minutes, :count => distance_in_minutes) unless options[:include_seconds]
88
-
89
- case distance_in_seconds
90
- when 0..4 then locale.t :less_than_x_seconds, :count => 5
91
- when 5..9 then locale.t :less_than_x_seconds, :count => 10
92
- when 10..19 then locale.t :less_than_x_seconds, :count => 20
93
- when 20..39 then locale.t :half_a_minute
94
- when 40..59 then locale.t :less_than_x_minutes, :count => 1
95
- else locale.t :x_minutes, :count => 1
96
- end
97
-
98
- when 2...45 then locale.t :x_minutes, :count => distance_in_minutes
99
- when 45...90 then locale.t :about_x_hours, :count => 1
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
100
124
  # 90 mins up to 24 hours
101
- when 90...1440 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
125
+ when 90...1440 then locale.t :about_x_hours, count: (distance_in_minutes.to_f / 60.0).round
102
126
  # 24 hours up to 42 hours
103
- when 1440...2520 then locale.t :x_days, :count => 1
127
+ when 1440...2520 then locale.t :x_days, count: 1
104
128
  # 42 hours up to 30 days
105
- when 2520...43200 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
129
+ when 2520...43200 then locale.t :x_days, count: (distance_in_minutes.to_f / 1440.0).round
106
130
  # 30 days up to 60 days
107
- when 43200...86400 then locale.t :about_x_months, :count => (distance_in_minutes.to_f / 43200.0).round
131
+ when 43200...86400 then locale.t :about_x_months, count: (distance_in_minutes.to_f / 43200.0).round
108
132
  # 60 days up to 365 days
109
- when 86400...525600 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
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)
110
153
  else
111
- if from_time.acts_like?(:time) && to_time.acts_like?(:time)
112
- fyear = from_time.year
113
- fyear += 1 if from_time.month >= 3
114
- tyear = to_time.year
115
- tyear -= 1 if to_time.month < 3
116
- leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count{|x| Date.leap?(x)}
117
- minute_offset_for_leap_year = leap_years * 1440
118
- # Discount the leap year days when calculating year distance.
119
- # e.g. if there are 20 leap year days between 2 dates having the same day
120
- # and month then the based on 365 days calculation
121
- # the distance in years will come out to over 80 years when in written
122
- # English it would read better as about 80 years.
123
- minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
124
- else
125
- minutes_with_offset = distance_in_minutes
126
- end
127
- remainder = (minutes_with_offset % MINUTES_IN_YEAR)
128
- distance_in_years = (minutes_with_offset.div MINUTES_IN_YEAR)
129
- if remainder < MINUTES_IN_QUARTER_YEAR
130
- locale.t(:about_x_years, :count => distance_in_years)
131
- elsif remainder < MINUTES_IN_THREE_QUARTERS_YEAR
132
- locale.t(:over_x_years, :count => distance_in_years)
133
- else
134
- locale.t(:almost_x_years, :count => distance_in_years + 1)
135
- end
154
+ locale.t(:almost_x_years, count: distance_in_years + 1)
155
+ end
136
156
  end
137
157
  end
138
158
  end
@@ -177,12 +197,16 @@ module ActionView
177
197
  # and +:name+ (string). A format string would be something like "%{name} (%<number>02d)" for example.
178
198
  # See <tt>Kernel.sprintf</tt> for documentation on format sequences.
179
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; ".
180
202
  # * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Date.today.year - 5</tt> if
181
203
  # you are creating new record. While editing existing record, <tt>:start_year</tt> defaults to
182
204
  # the current selected year minus 5.
183
205
  # * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Date.today.year + 5</tt> if
184
206
  # you are creating new record. While editing existing record, <tt>:end_year</tt> defaults to
185
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.
186
210
  # * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
187
211
  # as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
188
212
  # first of the given month in order to not create invalid dates like 31 February.
@@ -196,15 +220,18 @@ module ActionView
196
220
  # the respective locale (e.g. [:year, :month, :day] in the en locale that ships with Rails).
197
221
  # * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
198
222
  # dates.
199
- # * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
223
+ # * <tt>:default</tt> - Set a default date if the affected date isn't set or is +nil+.
200
224
  # * <tt>:selected</tt> - Set a date that overrides the actual value.
201
225
  # * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
202
226
  # * <tt>:prompt</tt> - Set to true (for a generic prompt), a prompt string or a hash of prompt strings
203
227
  # for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
204
228
  # Setting this option prepends a select option with a generic prompt (Day, Month, Year, Hour, Minute, Seconds)
205
229
  # or the given prompt string.
206
- # * <tt>:with_css_classes</tt> - Set to true if you want assign different styles for 'select' tags. This option
207
- # automatically set classes 'year', 'month', 'day', 'hour', 'minute' and 'second' for your 'select' tags.
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.
208
235
  #
209
236
  # If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
210
237
  #
@@ -240,7 +267,7 @@ module ActionView
240
267
  # date_select("article", "written_on", default: 3.days.from_now)
241
268
  #
242
269
  # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
243
- # # which is set in the form with todays date, regardless of the value in the Active Record object.
270
+ # # which is set in the form with today's date, regardless of the value in the Active Record object.
244
271
  # date_select("article", "written_on", selected: Date.today)
245
272
  #
246
273
  # # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
@@ -250,6 +277,12 @@ module ActionView
250
277
  # # Generates a date select with custom prompts.
251
278
  # date_select("article", "written_on", prompt: { day: 'Select day', month: 'Select month', year: 'Select year' })
252
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
+ #
253
286
  # The selects are prepared for multi-parameter assignment to an Active Record object.
254
287
  #
255
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
@@ -276,16 +309,16 @@ module ActionView
276
309
  # # the sunrise attribute.
277
310
  # time_select("article", "start_time", include_seconds: true)
278
311
  #
279
- # # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30 and 45.
280
- # time_select 'game', 'game_time', {minute_step: 15}
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 }
281
314
  #
282
315
  # # Creates a time select tag with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
283
- # time_select("article", "written_on", prompt: {hour: 'Choose hour', minute: 'Choose minute', second: 'Choose seconds'})
284
- # time_select("article", "written_on", prompt: {hour: true}) # generic prompt for hours
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
285
318
  # time_select("article", "written_on", prompt: true) # generic prompts for all
286
319
  #
287
320
  # # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM.
288
- # time_select 'game', 'game_time', {ampm: true}
321
+ # time_select 'game', 'game_time', { ampm: true }
289
322
  #
290
323
  # The selects are prepared for multi-parameter assignment to an Active Record object.
291
324
  #
@@ -321,8 +354,8 @@ module ActionView
321
354
  # datetime_select("article", "written_on", discard_type: true)
322
355
  #
323
356
  # # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
324
- # datetime_select("article", "written_on", prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
325
- # datetime_select("article", "written_on", prompt: {hour: true}) # generic prompt for hours
357
+ # datetime_select("article", "written_on", prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
358
+ # datetime_select("article", "written_on", prompt: { hour: true }) # generic prompt for hours
326
359
  # datetime_select("article", "written_on", prompt: true) # generic prompts for all
327
360
  #
328
361
  # The selects are prepared for multi-parameter assignment to an Active Record object.
@@ -372,8 +405,8 @@ module ActionView
372
405
  # select_datetime(my_date_time, prefix: 'payday')
373
406
  #
374
407
  # # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
375
- # select_datetime(my_date_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
376
- # select_datetime(my_date_time, prompt: {hour: true}) # generic prompt for hours
408
+ # select_datetime(my_date_time, prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
409
+ # select_datetime(my_date_time, prompt: { hour: true }) # generic prompt for hours
377
410
  # select_datetime(my_date_time, prompt: true) # generic prompts for all
378
411
  def select_datetime(datetime = Time.current, options = {}, html_options = {})
379
412
  DateTimeSelector.new(datetime, options, html_options).select_datetime
@@ -411,8 +444,8 @@ module ActionView
411
444
  # select_date(my_date, prefix: 'payday')
412
445
  #
413
446
  # # Generates a date select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
414
- # select_date(my_date, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
415
- # select_date(my_date, prompt: {hour: true}) # generic prompt for hours
447
+ # select_date(my_date, prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
448
+ # select_date(my_date, prompt: { hour: true }) # generic prompt for hours
416
449
  # select_date(my_date, prompt: true) # generic prompts for all
417
450
  def select_date(date = Date.current, options = {}, html_options = {})
418
451
  DateTimeSelector.new(date, options, html_options).select_date
@@ -451,8 +484,8 @@ module ActionView
451
484
  # select_time(my_time, start_hour: 2, end_hour: 14)
452
485
  #
453
486
  # # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts.
454
- # select_time(my_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
455
- # select_time(my_time, prompt: {hour: true}) # generic prompt for hours
487
+ # select_time(my_time, prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
488
+ # select_time(my_time, prompt: { hour: true }) # generic prompt for hours
456
489
  # select_time(my_time, prompt: true) # generic prompts for all
457
490
  def select_time(datetime = Time.current, options = {}, html_options = {})
458
491
  DateTimeSelector.new(datetime, options, html_options).select_time
@@ -462,7 +495,7 @@ module ActionView
462
495
  # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
463
496
  # Override the field name using the <tt>:field_name</tt> option, 'second' by default.
464
497
  #
465
- # my_time = Time.now + 16.minutes
498
+ # my_time = Time.now + 16.seconds
466
499
  #
467
500
  # # Generates a select field for seconds that defaults to the seconds for the time in my_time.
468
501
  # select_second(my_time)
@@ -486,7 +519,7 @@ module ActionView
486
519
  # selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
487
520
  # Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
488
521
  #
489
- # my_time = Time.now + 6.hours
522
+ # my_time = Time.now + 10.minutes
490
523
  #
491
524
  # # Generates a select field for minutes that defaults to the minutes for the time in my_time.
492
525
  # select_minute(my_time)
@@ -643,8 +676,6 @@ module ActionView
643
676
  # <time datetime="2010-11-04T17:55:45+01:00">November 04, 2010 17:55</time>
644
677
  # time_tag Date.yesterday, 'Yesterday' # =>
645
678
  # <time datetime="2010-11-03">Yesterday</time>
646
- # time_tag Date.today, pubdate: true # =>
647
- # <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
648
679
  # time_tag Date.today, datetime: Date.today.strftime('%G-W%V') # =>
649
680
  # <time datetime="2010-W44">November 04, 2010</time>
650
681
  #
@@ -655,19 +686,29 @@ module ActionView
655
686
  def time_tag(date_or_time, *args, &block)
656
687
  options = args.extract_options!
657
688
  format = options.delete(:format) || :long
658
- content = args.first || I18n.l(date_or_time, :format => format)
659
- datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.iso8601
689
+ content = args.first || I18n.l(date_or_time, format: format)
660
690
 
661
- content_tag(:time, content, options.reverse_merge(:datetime => datetime), &block)
691
+ content_tag("time", content, options.reverse_merge(datetime: date_or_time.iso8601), &block)
662
692
  end
693
+
694
+ private
695
+ def normalize_distance_of_time_argument_to_time(value)
696
+ if value.is_a?(Numeric)
697
+ Time.at(value)
698
+ elsif value.respond_to?(:to_time)
699
+ value.to_time
700
+ else
701
+ raise ArgumentError, "#{value.inspect} can't be converted to a Time value"
702
+ end
703
+ end
663
704
  end
664
705
 
665
- class DateTimeSelector #:nodoc:
706
+ class DateTimeSelector # :nodoc:
666
707
  include ActionView::Helpers::TagHelper
667
708
 
668
- DEFAULT_PREFIX = 'date'.freeze
709
+ DEFAULT_PREFIX = "date"
669
710
  POSITION = {
670
- :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6
711
+ year: 1, month: 2, day: 3, hour: 4, minute: 5, second: 6
671
712
  }.freeze
672
713
 
673
714
  AMPM_TRANSLATION = Hash[
@@ -683,8 +724,8 @@ module ActionView
683
724
  @options = options.dup
684
725
  @html_options = html_options.dup
685
726
  @datetime = datetime
686
- @options[:datetime_separator] ||= ' &mdash; '
687
- @options[:time_separator] ||= ' : '
727
+ @options[:datetime_separator] ||= " &mdash; "
728
+ @options[:time_separator] ||= " : "
688
729
  end
689
730
 
690
731
  def select_datetime
@@ -754,7 +795,7 @@ module ActionView
754
795
  if @options[:use_hidden] || @options[:discard_minute]
755
796
  build_hidden(:minute, min)
756
797
  else
757
- build_options_and_select(:minute, min, :step => @options[:minute_step])
798
+ build_options_and_select(:minute, min, step: @options[:minute_step])
758
799
  end
759
800
  end
760
801
 
@@ -774,7 +815,7 @@ module ActionView
774
815
  if @options[:use_hidden] || @options[:discard_day]
775
816
  build_hidden(:day, day || 1)
776
817
  else
777
- build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false, :use_two_digit_numbers => @options[:use_two_digit_numbers])
818
+ build_select(:day, build_day_options(day))
778
819
  end
779
820
  end
780
821
 
@@ -784,17 +825,17 @@ module ActionView
784
825
  else
785
826
  month_options = []
786
827
  1.upto(12) do |month_number|
787
- options = { :value => month_number }
828
+ options = { value: month_number }
788
829
  options[:selected] = "selected" if month == month_number
789
- month_options << content_tag(:option, month_name(month_number), options) + "\n"
830
+ month_options << content_tag("option", month_name(month_number), options) + "\n"
790
831
  end
791
832
  build_select(:month, month_options.join)
792
833
  end
793
834
  end
794
835
 
795
836
  def select_year
796
- if !@datetime || @datetime == 0
797
- val = '1'
837
+ if !year || @datetime == 0
838
+ val = "1"
798
839
  middle_year = Date.today.year
799
840
  else
800
841
  val = middle_year = year
@@ -814,14 +855,19 @@ module ActionView
814
855
  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."
815
856
  end
816
857
 
817
- build_options_and_select(:year, val, options)
858
+ build_select(:year, build_year_options(val, options))
818
859
  end
819
860
  end
820
861
 
821
862
  private
822
863
  %w( sec min hour day month year ).each do |method|
823
864
  define_method(method) do
824
- @datetime.kind_of?(Numeric) ? @datetime : @datetime.send(method) if @datetime
865
+ case @datetime
866
+ when Hash then @datetime[method.to_sym]
867
+ when Numeric then @datetime
868
+ when nil then nil
869
+ else @datetime.send(method)
870
+ end
825
871
  end
826
872
  end
827
873
 
@@ -829,12 +875,12 @@ module ActionView
829
875
  # valid. Otherwise, February 31st or February 29th, 2011 can be selected, which are invalid.
830
876
  def set_day_if_discarded
831
877
  if @datetime && @options[:discard_day]
832
- @datetime = @datetime.change(:day => 1)
878
+ @datetime = @datetime.change(day: 1)
833
879
  end
834
880
  end
835
881
 
836
882
  # Returns translated month names, but also ensures that a custom month
837
- # name array has a leading nil element.
883
+ # name array has a leading +nil+ element.
838
884
  def month_names
839
885
  @month_names ||= begin
840
886
  month_names = @options[:use_month_names] || translated_month_names
@@ -854,7 +900,28 @@ module ActionView
854
900
  # "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
855
901
  def translated_month_names
856
902
  key = @options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
857
- I18n.translate(key, :locale => @options[:locale])
903
+ I18n.translate(key, locale: @options[:locale])
904
+ end
905
+
906
+ # Looks up day names by number.
907
+ #
908
+ # day_name(1) # => 1
909
+ #
910
+ # If the <tt>use_two_digit_numbers: true</tt> option is passed to DateTimeSelector:
911
+ #
912
+ # day_name(1) # => "01"
913
+ #
914
+ # If the <tt>day_format: ->(day) { day.ordinalize }</tt> option is passed to DateTimeSelector:
915
+ #
916
+ # day_name(1) # => "1st"
917
+ def day_name(number)
918
+ if day_format_lambda = @options[:day_format]
919
+ day_format_lambda.call(number)
920
+ elsif @options[:use_two_digit_numbers]
921
+ "%02d" % number
922
+ else
923
+ number
924
+ end
858
925
  end
859
926
 
860
927
  # Looks up month names by number (1-based):
@@ -882,23 +949,38 @@ module ActionView
882
949
  if @options[:use_month_numbers]
883
950
  number
884
951
  elsif @options[:use_two_digit_numbers]
885
- '%02d' % number
952
+ "%02d" % number
886
953
  elsif @options[:add_month_numbers]
887
954
  "#{number} - #{month_names[number]}"
888
955
  elsif format_string = @options[:month_format_string]
889
- format_string % {number: number, name: month_names[number]}
956
+ format_string % { number: number, name: month_names[number] }
890
957
  else
891
958
  month_names[number]
892
959
  end
893
960
  end
894
961
 
962
+ # Looks up year names by number.
963
+ #
964
+ # year_name(1998) # => 1998
965
+ #
966
+ # If the <tt>:year_format</tt> option is passed:
967
+ #
968
+ # year_name(1998) # => "Heisei 10"
969
+ def year_name(number)
970
+ if year_format_lambda = @options[:year_format]
971
+ year_format_lambda.call(number)
972
+ else
973
+ number
974
+ end
975
+ end
976
+
895
977
  def date_order
896
978
  @date_order ||= @options[:order] || translated_date_order
897
979
  end
898
980
 
899
981
  def translated_date_order
900
- date_order = I18n.translate(:'date.order', :locale => @options[:locale], :default => [])
901
- date_order = date_order.map { |element| element.to_sym }
982
+ date_order = I18n.translate(:'date.order', locale: @options[:locale], default: [])
983
+ date_order = date_order.map(&:to_sym)
902
984
 
903
985
  forbidden_elements = date_order - [:year, :month, :day]
904
986
  if forbidden_elements.any?
@@ -944,11 +1026,68 @@ module ActionView
944
1026
  select_options = []
945
1027
  start.step(stop, step) do |i|
946
1028
  value = leading_zeros ? sprintf("%02d", i) : i
947
- tag_options = { :value => value }
1029
+ tag_options = { value: value }
948
1030
  tag_options[:selected] = "selected" if selected == i
949
1031
  text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
950
1032
  text = options[:ampm] ? AMPM_TRANSLATION[i] : text
951
- select_options << content_tag(:option, text, tag_options)
1033
+ select_options << content_tag("option", text, tag_options)
1034
+ end
1035
+
1036
+ (select_options.join("\n") + "\n").html_safe
1037
+ end
1038
+
1039
+ # Build select option HTML for day.
1040
+ # build_day_options(2)
1041
+ # => "<option value="1">1</option>
1042
+ # <option value="2" selected="selected">2</option>
1043
+ # <option value="3">3</option>..."
1044
+ #
1045
+ # If <tt>day_format: ->(day) { day.ordinalize }</tt> option is passed to DateTimeSelector
1046
+ # build_day_options(2)
1047
+ # => "<option value="1">1st</option>
1048
+ # <option value="2" selected="selected">2nd</option>
1049
+ # <option value="3">3rd</option>..."
1050
+ #
1051
+ # If <tt>use_two_digit_numbers: true</tt> option is passed to DateTimeSelector
1052
+ # build_day_options(2)
1053
+ # => "<option value="1">01</option>
1054
+ # <option value="2" selected="selected">02</option>
1055
+ # <option value="3">03</option>..."
1056
+ def build_day_options(selected)
1057
+ select_options = []
1058
+ (1..31).each do |value|
1059
+ tag_options = { value: value }
1060
+ tag_options[:selected] = "selected" if selected == value
1061
+ text = day_name(value)
1062
+ select_options << content_tag("option", text, tag_options)
1063
+ end
1064
+
1065
+ (select_options.join("\n") + "\n").html_safe
1066
+ end
1067
+
1068
+ # Build select option HTML for year.
1069
+ # If <tt>year_format</tt> option is not passed
1070
+ # build_year_options(1998, start: 1998, end: 2000)
1071
+ # => "<option value="1998" selected="selected">1998</option>
1072
+ # <option value="1999">1999</option>
1073
+ # <option value="2000">2000</option>"
1074
+ #
1075
+ # If <tt>year_format</tt> option is passed
1076
+ # build_year_options(1998, start: 1998, end: 2000, year_format: ->year { "Heisei #{ year - 1988 }" })
1077
+ # => "<option value="1998" selected="selected">Heisei 10</option>
1078
+ # <option value="1999">Heisei 11</option>
1079
+ # <option value="2000">Heisei 12</option>"
1080
+ def build_year_options(selected, options = {})
1081
+ start = options.delete(:start)
1082
+ stop = options.delete(:end)
1083
+ step = options.delete(:step)
1084
+
1085
+ select_options = []
1086
+ start.step(stop, step) do |value|
1087
+ tag_options = { value: value }
1088
+ tag_options[:selected] = "selected" if selected == value
1089
+ text = year_name(value)
1090
+ select_options << content_tag("option", text, tag_options)
952
1091
  end
953
1092
 
954
1093
  (select_options.join("\n") + "\n").html_safe
@@ -961,48 +1100,65 @@ module ActionView
961
1100
  # </select>"
962
1101
  def build_select(type, select_options_as_html)
963
1102
  select_options = {
964
- :id => input_id_from_type(type),
965
- :name => input_name_from_type(type)
1103
+ id: input_id_from_type(type),
1104
+ name: input_name_from_type(type)
966
1105
  }.merge!(@html_options)
967
- select_options[:disabled] = 'disabled' if @options[:disabled]
968
- select_options[:class] = [select_options[:class], type].compact.join(' ') if @options[:with_css_classes]
1106
+ select_options[:disabled] = "disabled" if @options[:disabled]
1107
+ select_options[:class] = css_class_attribute(type, select_options[:class], @options[:with_css_classes]) if @options[:with_css_classes]
969
1108
 
970
- select_html = "\n"
971
- select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
1109
+ select_html = +"\n"
1110
+ select_html << content_tag("option", "", value: "", label: " ") + "\n" if @options[:include_blank]
972
1111
  select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
973
1112
  select_html << select_options_as_html
974
1113
 
975
- (content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe
1114
+ (content_tag("select", select_html.html_safe, select_options) + "\n").html_safe
1115
+ end
1116
+
1117
+ # Builds the CSS class value for the select element
1118
+ # css_class_attribute(:year, 'date optional', { year: 'my-year' })
1119
+ # => "date optional my-year"
1120
+ def css_class_attribute(type, html_options_class, options) # :nodoc:
1121
+ css_class = \
1122
+ case options
1123
+ when Hash
1124
+ options[type.to_sym]
1125
+ else
1126
+ type
1127
+ end
1128
+
1129
+ [html_options_class, css_class].compact.join(" ")
976
1130
  end
977
1131
 
978
1132
  # Builds a prompt option tag with supplied options or from default options.
979
1133
  # prompt_option_tag(:month, prompt: 'Select month')
980
1134
  # => "<option value="">Select month</option>"
981
1135
  def prompt_option_tag(type, options)
982
- prompt = case options
1136
+ prompt = \
1137
+ case options
983
1138
  when Hash
984
- default_options = {:year => false, :month => false, :day => false, :hour => false, :minute => false, :second => false}
1139
+ default_options = { year: false, month: false, day: false, hour: false, minute: false, second: false }
985
1140
  default_options.merge!(options)[type.to_sym]
986
1141
  when String
987
1142
  options
988
1143
  else
989
- I18n.translate(:"datetime.prompts.#{type}", :locale => @options[:locale])
990
- end
1144
+ I18n.translate(:"datetime.prompts.#{type}", locale: @options[:locale])
1145
+ end
991
1146
 
992
- prompt ? content_tag(:option, prompt, :value => '') : ''
1147
+ prompt ? content_tag("option", prompt, value: "") : ""
993
1148
  end
994
1149
 
995
1150
  # Builds hidden input tag for date part and value.
996
1151
  # build_hidden(:year, 2008)
997
- # => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
1152
+ # => "<input type="hidden" id="date_year" name="date[year]" value="2008" autocomplete="off" />"
998
1153
  def build_hidden(type, value)
999
1154
  select_options = {
1000
- :type => "hidden",
1001
- :id => input_id_from_type(type),
1002
- :name => input_name_from_type(type),
1003
- :value => value
1155
+ type: "hidden",
1156
+ id: input_id_from_type(type),
1157
+ name: input_name_from_type(type),
1158
+ value: value,
1159
+ autocomplete: "off"
1004
1160
  }.merge!(@html_options.slice(:disabled))
1005
- select_options[:disabled] = 'disabled' if @options[:disabled]
1161
+ select_options[:disabled] = "disabled" if @options[:disabled]
1006
1162
 
1007
1163
  tag(:input, select_options) + "\n".html_safe
1008
1164
  end
@@ -1013,7 +1169,7 @@ module ActionView
1013
1169
  prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX
1014
1170
  prefix += "[#{@options[:index]}]" if @options.has_key?(:index)
1015
1171
 
1016
- field_name = @options[:field_name] || type
1172
+ field_name = @options[:field_name] || type.to_s
1017
1173
  if @options[:include_position]
1018
1174
  field_name += "(#{ActionView::Helpers::DateTimeSelector::POSITION[type]}i)"
1019
1175
  end
@@ -1024,8 +1180,8 @@ module ActionView
1024
1180
  # Returns the id attribute for the input tag.
1025
1181
  # => "post_written_on_1i"
1026
1182
  def input_id_from_type(type)
1027
- id = input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
1028
- id = @options[:namespace] + '_' + id if @options[:namespace]
1183
+ id = input_name_from_type(type).gsub(/([\[(])|(\]\[)/, "_").gsub(/[\])]/, "")
1184
+ id = @options[:namespace] + "_" + id if @options[:namespace]
1029
1185
 
1030
1186
  id
1031
1187
  end
@@ -1033,11 +1189,11 @@ module ActionView
1033
1189
  # Given an ordering of datetime components, create the selection HTML
1034
1190
  # and join them with their appropriate separators.
1035
1191
  def build_selects_from_types(order)
1036
- select = ''
1192
+ select = +""
1037
1193
  first_visible = order.find { |type| !@options[:"discard_#{type}"] }
1038
1194
  order.reverse_each do |type|
1039
1195
  separator = separator(type) unless type == first_visible # don't add before first visible field
1040
- select.insert(0, separator.to_s + send("select_#{type}").to_s)
1196
+ select.insert(0, separator.to_s + public_send("select_#{type}").to_s)
1041
1197
  end
1042
1198
  select.html_safe
1043
1199
  end
@@ -1047,12 +1203,12 @@ module ActionView
1047
1203
  return "" if @options[:use_hidden]
1048
1204
 
1049
1205
  case type
1050
- when :year, :month, :day
1051
- @options[:"discard_#{type}"] ? "" : @options[:date_separator]
1052
- when :hour
1053
- (@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
1054
- when :minute, :second
1055
- @options[:"discard_#{type}"] ? "" : @options[:time_separator]
1206
+ when :year, :month, :day
1207
+ @options[:"discard_#{type}"] ? "" : @options[:date_separator]
1208
+ when :hour
1209
+ (@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
1210
+ when :minute, :second
1211
+ @options[:"discard_#{type}"] ? "" : @options[:time_separator]
1056
1212
  end
1057
1213
  end
1058
1214
  end