activesupport 4.2.0 → 5.0.0.1

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

Potentially problematic release.


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

Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +630 -220
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +2 -3
  5. data/lib/active_support/array_inquirer.rb +44 -0
  6. data/lib/active_support/backtrace_cleaner.rb +1 -1
  7. data/lib/active_support/benchmarkable.rb +1 -1
  8. data/lib/active_support/cache/file_store.rb +36 -22
  9. data/lib/active_support/cache/mem_cache_store.rb +63 -54
  10. data/lib/active_support/cache/memory_store.rb +16 -21
  11. data/lib/active_support/cache/null_store.rb +1 -4
  12. data/lib/active_support/cache/strategy/local_cache.rb +31 -20
  13. data/lib/active_support/cache.rb +73 -89
  14. data/lib/active_support/callbacks.rb +195 -155
  15. data/lib/active_support/concern.rb +2 -2
  16. data/lib/active_support/concurrency/latch.rb +7 -15
  17. data/lib/active_support/concurrency/share_lock.rb +186 -0
  18. data/lib/active_support/configurable.rb +1 -0
  19. data/lib/active_support/core_ext/array/access.rb +27 -1
  20. data/lib/active_support/core_ext/array/conversions.rb +6 -4
  21. data/lib/active_support/core_ext/array/grouping.rb +9 -18
  22. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  23. data/lib/active_support/core_ext/array/wrap.rb +5 -4
  24. data/lib/active_support/core_ext/array.rb +1 -0
  25. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -10
  26. data/lib/active_support/core_ext/class/attribute.rb +10 -9
  27. data/lib/active_support/core_ext/class/subclasses.rb +3 -4
  28. data/lib/active_support/core_ext/class.rb +0 -1
  29. data/lib/active_support/core_ext/date/blank.rb +12 -0
  30. data/lib/active_support/core_ext/date/calculations.rb +1 -1
  31. data/lib/active_support/core_ext/date/conversions.rb +13 -6
  32. data/lib/active_support/core_ext/date.rb +1 -1
  33. data/lib/active_support/core_ext/date_and_time/calculations.rb +109 -25
  34. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -0
  35. data/lib/active_support/core_ext/date_and_time/zones.rb +3 -4
  36. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  37. data/lib/active_support/core_ext/date_time/calculations.rb +36 -10
  38. data/lib/active_support/core_ext/date_time/compatibility.rb +5 -0
  39. data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
  40. data/lib/active_support/core_ext/date_time.rb +2 -1
  41. data/lib/active_support/core_ext/enumerable.rb +49 -5
  42. data/lib/active_support/core_ext/file/atomic.rb +30 -25
  43. data/lib/active_support/core_ext/hash/conversions.rb +23 -4
  44. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -1
  45. data/lib/active_support/core_ext/hash/except.rb +9 -8
  46. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
  47. data/lib/active_support/core_ext/hash/keys.rb +23 -19
  48. data/lib/active_support/core_ext/hash/slice.rb +1 -1
  49. data/lib/active_support/core_ext/hash/transform_values.rb +11 -5
  50. data/lib/active_support/core_ext/integer/time.rb +1 -16
  51. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  52. data/lib/active_support/core_ext/kernel/debugger.rb +3 -10
  53. data/lib/active_support/core_ext/kernel/reporting.rb +2 -83
  54. data/lib/active_support/core_ext/kernel.rb +0 -1
  55. data/lib/active_support/core_ext/load_error.rb +4 -2
  56. data/lib/active_support/core_ext/marshal.rb +12 -11
  57. data/lib/active_support/core_ext/module/aliasing.rb +6 -1
  58. data/lib/active_support/core_ext/module/anonymous.rb +10 -1
  59. data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
  60. data/lib/active_support/core_ext/module/attribute_accessors.rb +15 -15
  61. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  62. data/lib/active_support/core_ext/module/concerning.rb +4 -4
  63. data/lib/active_support/core_ext/module/delegation.rb +35 -25
  64. data/lib/active_support/core_ext/module/deprecation.rb +2 -2
  65. data/lib/active_support/core_ext/module/introspection.rb +4 -0
  66. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -11
  67. data/lib/active_support/core_ext/module/qualified_const.rb +30 -12
  68. data/lib/active_support/core_ext/module/remove_method.rb +23 -0
  69. data/lib/active_support/core_ext/module.rb +1 -0
  70. data/lib/active_support/core_ext/name_error.rb +15 -2
  71. data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
  72. data/lib/active_support/core_ext/numeric/conversions.rb +74 -64
  73. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  74. data/lib/active_support/core_ext/numeric/time.rb +24 -19
  75. data/lib/active_support/core_ext/numeric.rb +1 -0
  76. data/lib/active_support/core_ext/object/blank.rb +17 -5
  77. data/lib/active_support/core_ext/object/deep_dup.rb +10 -3
  78. data/lib/active_support/core_ext/object/duplicable.rb +8 -13
  79. data/lib/active_support/core_ext/object/inclusion.rb +2 -2
  80. data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
  81. data/lib/active_support/core_ext/object/json.rb +15 -7
  82. data/lib/active_support/core_ext/object/to_query.rb +1 -1
  83. data/lib/active_support/core_ext/object/try.rb +68 -22
  84. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  85. data/lib/active_support/core_ext/object.rb +0 -1
  86. data/lib/active_support/core_ext/range/conversions.rb +18 -6
  87. data/lib/active_support/core_ext/range/each.rb +16 -18
  88. data/lib/active_support/core_ext/range/include_range.rb +20 -20
  89. data/lib/active_support/core_ext/securerandom.rb +23 -0
  90. data/lib/active_support/core_ext/string/access.rb +1 -1
  91. data/lib/active_support/core_ext/string/behavior.rb +1 -1
  92. data/lib/active_support/core_ext/string/conversions.rb +4 -3
  93. data/lib/active_support/core_ext/string/filters.rb +5 -5
  94. data/lib/active_support/core_ext/string/inflections.rb +32 -5
  95. data/lib/active_support/core_ext/string/multibyte.rb +11 -7
  96. data/lib/active_support/core_ext/string/output_safety.rb +18 -16
  97. data/lib/active_support/core_ext/string/strip.rb +3 -6
  98. data/lib/active_support/core_ext/struct.rb +3 -6
  99. data/lib/active_support/core_ext/time/calculations.rb +36 -11
  100. data/lib/active_support/core_ext/time/compatibility.rb +5 -0
  101. data/lib/active_support/core_ext/time/conversions.rb +4 -2
  102. data/lib/active_support/core_ext/time/marshal.rb +2 -29
  103. data/lib/active_support/core_ext/time/zones.rb +36 -4
  104. data/lib/active_support/core_ext/time.rb +1 -1
  105. data/lib/active_support/core_ext/uri.rb +1 -3
  106. data/lib/active_support/core_ext.rb +2 -1
  107. data/lib/active_support/dependencies/interlock.rb +51 -0
  108. data/lib/active_support/dependencies.rb +87 -95
  109. data/lib/active_support/deprecation/behaviors.rb +16 -2
  110. data/lib/active_support/deprecation/method_wrappers.rb +42 -16
  111. data/lib/active_support/deprecation/proxy_wrappers.rb +47 -24
  112. data/lib/active_support/deprecation/reporting.rb +23 -5
  113. data/lib/active_support/deprecation.rb +1 -1
  114. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  115. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  116. data/lib/active_support/duration.rb +55 -10
  117. data/lib/active_support/evented_file_update_checker.rb +194 -0
  118. data/lib/active_support/execution_wrapper.rb +117 -0
  119. data/lib/active_support/executor.rb +6 -0
  120. data/lib/active_support/file_update_checker.rb +23 -3
  121. data/lib/active_support/gem_version.rb +4 -4
  122. data/lib/active_support/hash_with_indifferent_access.rb +46 -13
  123. data/lib/active_support/i18n_railtie.rb +25 -4
  124. data/lib/active_support/inflector/inflections.rb +36 -5
  125. data/lib/active_support/inflector/methods.rb +97 -90
  126. data/lib/active_support/inflector/transliterate.rb +36 -21
  127. data/lib/active_support/json/decoding.rb +11 -10
  128. data/lib/active_support/json/encoding.rb +4 -49
  129. data/lib/active_support/key_generator.rb +7 -9
  130. data/lib/active_support/locale/en.yml +2 -0
  131. data/lib/active_support/log_subscriber/test_helper.rb +3 -3
  132. data/lib/active_support/log_subscriber.rb +1 -1
  133. data/lib/active_support/logger.rb +50 -1
  134. data/lib/active_support/logger_silence.rb +8 -4
  135. data/lib/active_support/logger_thread_safe_level.rb +31 -0
  136. data/lib/active_support/message_encryptor.rb +4 -4
  137. data/lib/active_support/message_verifier.rb +70 -8
  138. data/lib/active_support/multibyte/chars.rb +13 -4
  139. data/lib/active_support/multibyte/unicode.rb +44 -21
  140. data/lib/active_support/notifications/fanout.rb +6 -6
  141. data/lib/active_support/notifications/instrumenter.rb +20 -2
  142. data/lib/active_support/notifications.rb +2 -2
  143. data/lib/active_support/number_helper/number_to_currency_converter.rb +7 -9
  144. data/lib/active_support/number_helper/number_to_delimited_converter.rb +8 -3
  145. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -4
  146. data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -2
  147. data/lib/active_support/number_helper/number_to_percentage_converter.rb +1 -1
  148. data/lib/active_support/number_helper/number_to_phone_converter.rb +11 -2
  149. data/lib/active_support/number_helper/number_to_rounded_converter.rb +30 -25
  150. data/lib/active_support/number_helper.rb +90 -67
  151. data/lib/active_support/ordered_hash.rb +1 -1
  152. data/lib/active_support/ordered_options.rb +15 -1
  153. data/lib/active_support/per_thread_registry.rb +8 -3
  154. data/lib/active_support/rails.rb +2 -2
  155. data/lib/active_support/railtie.rb +6 -1
  156. data/lib/active_support/reloader.rb +129 -0
  157. data/lib/active_support/rescuable.rb +93 -47
  158. data/lib/active_support/security_utils.rb +7 -0
  159. data/lib/active_support/string_inquirer.rb +1 -1
  160. data/lib/active_support/subscriber.rb +5 -10
  161. data/lib/active_support/tagged_logging.rb +3 -1
  162. data/lib/active_support/test_case.rb +15 -29
  163. data/lib/active_support/testing/assertions.rb +15 -13
  164. data/lib/active_support/testing/autorun.rb +8 -1
  165. data/lib/active_support/testing/deprecation.rb +9 -8
  166. data/lib/active_support/testing/file_fixtures.rb +34 -0
  167. data/lib/active_support/testing/isolation.rb +22 -8
  168. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  169. data/lib/active_support/testing/stream.rb +42 -0
  170. data/lib/active_support/testing/time_helpers.rb +13 -10
  171. data/lib/active_support/time_with_zone.rb +135 -46
  172. data/lib/active_support/values/time_zone.rb +95 -47
  173. data/lib/active_support/values/unicode_tables.dat +0 -0
  174. data/lib/active_support/xml_mini/jdom.rb +7 -6
  175. data/lib/active_support/xml_mini/libxml.rb +2 -2
  176. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  177. data/lib/active_support/xml_mini/rexml.rb +7 -8
  178. data/lib/active_support/xml_mini.rb +22 -14
  179. data/lib/active_support.rb +20 -6
  180. metadata +33 -35
  181. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -14
  182. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  183. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  184. data/lib/active_support/core_ext/object/itself.rb +0 -15
  185. data/lib/active_support/core_ext/thread.rb +0 -86
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/object/try'
2
+
1
3
  module DateAndTime
2
4
  module Calculations
3
5
  DAYS_INTO_WEEK = {
@@ -9,15 +11,26 @@ module DateAndTime
9
11
  :saturday => 5,
10
12
  :sunday => 6
11
13
  }
14
+ WEEKEND_DAYS = [ 6, 0 ]
12
15
 
13
16
  # Returns a new date/time representing yesterday.
14
17
  def yesterday
15
- advance(:days => -1)
18
+ advance(days: -1)
19
+ end
20
+
21
+ # Returns a new date/time representing the previous day.
22
+ def prev_day
23
+ advance(days: -1)
16
24
  end
17
25
 
18
26
  # Returns a new date/time representing tomorrow.
19
27
  def tomorrow
20
- advance(:days => 1)
28
+ advance(days: 1)
29
+ end
30
+
31
+ # Returns a new date/time representing the next day.
32
+ def next_day
33
+ advance(days: 1)
21
34
  end
22
35
 
23
36
  # Returns true if the date/time is today.
@@ -35,6 +48,16 @@ module DateAndTime
35
48
  self > self.class.current
36
49
  end
37
50
 
51
+ # Returns true if the date/time falls on a Saturday or Sunday.
52
+ def on_weekend?
53
+ WEEKEND_DAYS.include?(wday)
54
+ end
55
+
56
+ # Returns true if the date/time does not fall on a Saturday or Sunday.
57
+ def on_weekday?
58
+ !WEEKEND_DAYS.include?(wday)
59
+ end
60
+
38
61
  # Returns a new date/time the specified number of days ago.
39
62
  def days_ago(days)
40
63
  advance(:days => -days)
@@ -76,15 +99,28 @@ module DateAndTime
76
99
  end
77
100
 
78
101
  # Returns a new date/time at the start of the month.
79
- # DateTime objects will have a time set to 0:00.
102
+ #
103
+ # today = Date.today # => Thu, 18 Jun 2015
104
+ # today.beginning_of_month # => Mon, 01 Jun 2015
105
+ #
106
+ # +DateTime+ objects will have a time set to 0:00.
107
+ #
108
+ # now = DateTime.current # => Thu, 18 Jun 2015 15:23:13 +0000
109
+ # now.beginning_of_month # => Mon, 01 Jun 2015 00:00:00 +0000
80
110
  def beginning_of_month
81
111
  first_hour(change(:day => 1))
82
112
  end
83
113
  alias :at_beginning_of_month :beginning_of_month
84
114
 
85
115
  # Returns a new date/time at the start of the quarter.
86
- # Example: 1st January, 1st July, 1st October.
87
- # DateTime objects will have a time set to 0:00.
116
+ #
117
+ # today = Date.today # => Fri, 10 Jul 2015
118
+ # today.beginning_of_quarter # => Wed, 01 Jul 2015
119
+ #
120
+ # +DateTime+ objects will have a time set to 0:00.
121
+ #
122
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
123
+ # now.beginning_of_quarter # => Wed, 01 Jul 2015 00:00:00 +0000
88
124
  def beginning_of_quarter
89
125
  first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
90
126
  beginning_of_month.change(:month => first_quarter_month)
@@ -92,28 +128,62 @@ module DateAndTime
92
128
  alias :at_beginning_of_quarter :beginning_of_quarter
93
129
 
94
130
  # Returns a new date/time at the end of the quarter.
95
- # Example: 31st March, 30th June, 30th September.
96
- # DateTime objects will have a time set to 23:59:59.
131
+ #
132
+ # today = Date.today # => Fri, 10 Jul 2015
133
+ # today.end_of_quarter # => Wed, 30 Sep 2015
134
+ #
135
+ # +DateTime+ objects will have a time set to 23:59:59.
136
+ #
137
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
138
+ # now.end_of_quarter # => Wed, 30 Sep 2015 23:59:59 +0000
97
139
  def end_of_quarter
98
140
  last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
99
141
  beginning_of_month.change(:month => last_quarter_month).end_of_month
100
142
  end
101
143
  alias :at_end_of_quarter :end_of_quarter
102
144
 
103
- # Return a new date/time at the beginning of the year.
104
- # Example: 1st January.
105
- # DateTime objects will have a time set to 0:00.
145
+ # Returns a new date/time at the beginning of the year.
146
+ #
147
+ # today = Date.today # => Fri, 10 Jul 2015
148
+ # today.beginning_of_year # => Thu, 01 Jan 2015
149
+ #
150
+ # +DateTime+ objects will have a time set to 0:00.
151
+ #
152
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
153
+ # now.beginning_of_year # => Thu, 01 Jan 2015 00:00:00 +0000
106
154
  def beginning_of_year
107
155
  change(:month => 1).beginning_of_month
108
156
  end
109
157
  alias :at_beginning_of_year :beginning_of_year
110
158
 
111
159
  # Returns a new date/time representing the given day in the next week.
160
+ #
161
+ # today = Date.today # => Thu, 07 May 2015
162
+ # today.next_week # => Mon, 11 May 2015
163
+ #
112
164
  # The +given_day_in_next_week+ defaults to the beginning of the week
113
165
  # which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+
114
- # when set. +DateTime+ objects have their time set to 0:00.
115
- def next_week(given_day_in_next_week = Date.beginning_of_week)
116
- first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)))
166
+ # when set.
167
+ #
168
+ # today = Date.today # => Thu, 07 May 2015
169
+ # today.next_week(:friday) # => Fri, 15 May 2015
170
+ #
171
+ # +DateTime+ objects have their time set to 0:00 unless +same_time+ is true.
172
+ #
173
+ # now = DateTime.current # => Thu, 07 May 2015 13:31:16 +0000
174
+ # now.next_week # => Mon, 11 May 2015 00:00:00 +0000
175
+ def next_week(given_day_in_next_week = Date.beginning_of_week, same_time: false)
176
+ result = first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)))
177
+ same_time ? copy_time_to(result) : result
178
+ end
179
+
180
+ # Returns a new date/time representing the next weekday.
181
+ def next_weekday
182
+ if next_day.on_weekend?
183
+ next_week(:monday, same_time: true)
184
+ else
185
+ next_day
186
+ end
117
187
  end
118
188
 
119
189
  # Short-hand for months_since(1).
@@ -134,12 +204,23 @@ module DateAndTime
134
204
  # Returns a new date/time representing the given day in the previous week.
135
205
  # Week is assumed to start on +start_day+, default is
136
206
  # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
137
- # DateTime objects have their time set to 0:00.
138
- def prev_week(start_day = Date.beginning_of_week)
139
- first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day)))
207
+ # DateTime objects have their time set to 0:00 unless +same_time+ is true.
208
+ def prev_week(start_day = Date.beginning_of_week, same_time: false)
209
+ result = first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day)))
210
+ same_time ? copy_time_to(result) : result
140
211
  end
141
212
  alias_method :last_week, :prev_week
142
213
 
214
+ # Returns a new date/time representing the previous weekday.
215
+ def prev_weekday
216
+ if prev_day.on_weekend?
217
+ copy_time_to(beginning_of_week(:friday))
218
+ else
219
+ prev_day
220
+ end
221
+ end
222
+ alias_method :last_weekday, :prev_weekday
223
+
143
224
  # Short-hand for months_ago(1).
144
225
  def prev_month
145
226
  months_ago(1)
@@ -235,17 +316,20 @@ module DateAndTime
235
316
  end
236
317
 
237
318
  private
319
+ def first_hour(date_or_time)
320
+ date_or_time.acts_like?(:time) ? date_or_time.beginning_of_day : date_or_time
321
+ end
238
322
 
239
- def first_hour(date_or_time)
240
- date_or_time.acts_like?(:time) ? date_or_time.beginning_of_day : date_or_time
241
- end
323
+ def last_hour(date_or_time)
324
+ date_or_time.acts_like?(:time) ? date_or_time.end_of_day : date_or_time
325
+ end
242
326
 
243
- def last_hour(date_or_time)
244
- date_or_time.acts_like?(:time) ? date_or_time.end_of_day : date_or_time
245
- end
327
+ def days_span(day)
328
+ (DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
329
+ end
246
330
 
247
- def days_span(day)
248
- (DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
249
- end
331
+ def copy_time_to(other)
332
+ other.change(hour: hour, min: min, sec: sec, usec: try(:usec))
333
+ end
250
334
  end
251
335
  end
@@ -0,0 +1,18 @@
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
+
3
+ module DateAndTime
4
+ module Compatibility
5
+ # If true, +to_time+ preserves the timezone offset of receiver.
6
+ #
7
+ # NOTE: With Ruby 2.4+ the default for +to_time+ changed from
8
+ # converting to the local system time, to preserving the offset
9
+ # of the receiver. For backwards compatibility we're overriding
10
+ # this behavior, but new apps will have an initializer that sets
11
+ # this to true, because the new behavior is preferred.
12
+ mattr_accessor(:preserve_timezone, instance_writer: false) { false }
13
+
14
+ def to_time
15
+ preserve_timezone ? getlocal(utc_offset) : getlocal
16
+ end
17
+ end
18
+ end
@@ -4,8 +4,8 @@ module DateAndTime
4
4
  # if Time.zone_default is set. Otherwise, it returns the current time.
5
5
  #
6
6
  # Time.zone = 'Hawaii' # => 'Hawaii'
7
- # DateTime.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
8
- # Date.new(2000).in_time_zone # => Sat, 01 Jan 2000 00:00:00 HST -10:00
7
+ # Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
8
+ # Date.new(2000).in_time_zone # => Sat, 01 Jan 2000 00:00:00 HST -10:00
9
9
  #
10
10
  # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
11
11
  # instead of the operating system's time zone.
@@ -14,8 +14,7 @@ module DateAndTime
14
14
  # and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
15
15
  #
16
16
  # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
17
- # DateTime.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
18
- # Date.new(2000).in_time_zone('Alaska') # => Sat, 01 Jan 2000 00:00:00 AKST -09:00
17
+ # Date.new(2000).in_time_zone('Alaska') # => Sat, 01 Jan 2000 00:00:00 AKST -09:00
19
18
  def in_time_zone(zone = ::Time.zone)
20
19
  time_zone = ::Time.find_zone! zone
21
20
  time = acts_like?(:time) ? self : nil
@@ -0,0 +1,12 @@
1
+ require 'date'
2
+
3
+ class DateTime #:nodoc:
4
+ # No DateTime is ever blank:
5
+ #
6
+ # DateTime.now.blank? # => false
7
+ #
8
+ # @return [false]
9
+ def blank?
10
+ false
11
+ end
12
+ end
@@ -10,7 +10,11 @@ class DateTime
10
10
  end
11
11
  end
12
12
 
13
- # Seconds since midnight: DateTime.now.seconds_since_midnight.
13
+ # Returns the number of seconds since 00:00:00.
14
+ #
15
+ # DateTime.new(2012, 8, 29, 0, 0, 0).seconds_since_midnight # => 0
16
+ # DateTime.new(2012, 8, 29, 12, 34, 56).seconds_since_midnight # => 45296
17
+ # DateTime.new(2012, 8, 29, 23, 59, 59).seconds_since_midnight # => 86399
14
18
  def seconds_since_midnight
15
19
  sec + (min * 60) + (hour * 3600)
16
20
  end
@@ -24,6 +28,13 @@ class DateTime
24
28
  end_of_day.to_i - to_i
25
29
  end
26
30
 
31
+ # Returns the fraction of a second as a +Rational+
32
+ #
33
+ # DateTime.new(2012, 8, 29, 0, 0, 0.5).subsec # => (1/2)
34
+ def subsec
35
+ sec_fraction
36
+ end
37
+
27
38
  # Returns a new DateTime where one or more of the elements have been changed
28
39
  # according to the +options+ parameter. The time options (<tt>:hour</tt>,
29
40
  # <tt>:min</tt>, <tt>:sec</tt>) reset cascadingly, so if only the hour is
@@ -139,14 +150,32 @@ class DateTime
139
150
  end
140
151
  alias :at_end_of_minute :end_of_minute
141
152
 
142
- # Adjusts DateTime to UTC by adding its offset value; offset is set to 0.
153
+ # Returns a <tt>Time</tt> instance of the simultaneous time in the system timezone.
154
+ def localtime(utc_offset = nil)
155
+ utc = new_offset(0)
156
+
157
+ Time.utc(
158
+ utc.year, utc.month, utc.day,
159
+ utc.hour, utc.min, utc.sec + utc.sec_fraction
160
+ ).getlocal(utc_offset)
161
+ end
162
+ alias_method :getlocal, :localtime
163
+
164
+ # Returns a <tt>Time</tt> instance of the simultaneous time in the UTC timezone.
143
165
  #
144
166
  # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
145
- # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 +0000
167
+ # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 UTC
146
168
  def utc
147
- new_offset(0)
169
+ utc = new_offset(0)
170
+
171
+ Time.utc(
172
+ utc.year, utc.month, utc.day,
173
+ utc.hour, utc.min, utc.sec + utc.sec_fraction
174
+ )
148
175
  end
176
+ alias_method :getgm, :utc
149
177
  alias_method :getutc, :utc
178
+ alias_method :gmtime, :utc
150
179
 
151
180
  # Returns +true+ if <tt>offset == 0</tt>.
152
181
  def utc?
@@ -161,13 +190,10 @@ class DateTime
161
190
  # Layers additional behavior on DateTime#<=> so that Time and
162
191
  # ActiveSupport::TimeWithZone instances can be compared with a DateTime.
163
192
  def <=>(other)
164
- if other.kind_of?(Infinity)
165
- super
166
- elsif other.respond_to? :to_datetime
167
- super other.to_datetime
193
+ if other.respond_to? :to_datetime
194
+ super other.to_datetime rescue nil
168
195
  else
169
- nil
196
+ super
170
197
  end
171
198
  end
172
-
173
199
  end
@@ -0,0 +1,5 @@
1
+ require 'active_support/core_ext/date_and_time/compatibility'
2
+
3
+ class DateTime
4
+ prepend DateAndTime::Compatibility
5
+ end
@@ -40,6 +40,8 @@ class DateTime
40
40
  alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s)
41
41
  alias_method :to_s, :to_formatted_s
42
42
 
43
+ # Returns a formatted string of the offset from UTC, or an alternative
44
+ # string if the time zone is already UTC.
43
45
  #
44
46
  # datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24))
45
47
  # datetime.formatted_offset # => "-06:00"
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext/date_time/acts_like'
2
+ require 'active_support/core_ext/date_time/blank'
2
3
  require 'active_support/core_ext/date_time/calculations'
4
+ require 'active_support/core_ext/date_time/compatibility'
3
5
  require 'active_support/core_ext/date_time/conversions'
4
- require 'active_support/core_ext/date_time/zones'
@@ -17,11 +17,12 @@ module Enumerable
17
17
  # The default sum of an empty list is zero. You can override this default:
18
18
  #
19
19
  # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
20
- def sum(identity = 0, &block)
20
+ def sum(identity = nil, &block)
21
21
  if block_given?
22
22
  map(&block).sum(identity)
23
23
  else
24
- inject { |sum, element| sum + element } || identity
24
+ sum = identity ? inject(identity, :+) : inject(:+)
25
+ sum || identity || 0
25
26
  end
26
27
  end
27
28
 
@@ -60,21 +61,64 @@ module Enumerable
60
61
  def exclude?(object)
61
62
  !include?(object)
62
63
  end
64
+
65
+ # Returns a copy of the enumerable without the specified elements.
66
+ #
67
+ # ["David", "Rafael", "Aaron", "Todd"].without "Aaron", "Todd"
68
+ # => ["David", "Rafael"]
69
+ #
70
+ # {foo: 1, bar: 2, baz: 3}.without :bar
71
+ # => {foo: 1, baz: 3}
72
+ def without(*elements)
73
+ reject { |element| elements.include?(element) }
74
+ end
75
+
76
+ # Convert an enumerable to an array based on the given key.
77
+ #
78
+ # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
79
+ # => ["David", "Rafael", "Aaron"]
80
+ #
81
+ # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
82
+ # => [[1, "David"], [2, "Rafael"]]
83
+ def pluck(*keys)
84
+ if keys.many?
85
+ map { |element| keys.map { |key| element[key] } }
86
+ else
87
+ map { |element| element[keys.first] }
88
+ end
89
+ end
63
90
  end
64
91
 
65
92
  class Range #:nodoc:
66
93
  # Optimize range sum to use arithmetic progression if a block is not given and
67
94
  # we have a range of numeric values.
68
- def sum(identity = 0)
95
+ def sum(identity = nil)
69
96
  if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
70
97
  super
71
98
  else
72
99
  actual_last = exclude_end? ? (last - 1) : last
73
100
  if actual_last >= first
74
- (actual_last - first + 1) * (actual_last + first) / 2
101
+ sum = identity || 0
102
+ sum + (actual_last - first + 1) * (actual_last + first) / 2
75
103
  else
76
- identity
104
+ identity || 0
77
105
  end
78
106
  end
79
107
  end
80
108
  end
109
+
110
+ # Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
111
+ #
112
+ # We tried shimming it to attempt the fast native method, rescue TypeError,
113
+ # and fall back to the compatible implementation, but that's much slower than
114
+ # just calling the compat method in the first place.
115
+ if Array.instance_methods(false).include?(:sum) && !(%w[a].sum rescue false)
116
+ class Array
117
+ remove_method :sum
118
+
119
+ def sum(*args) #:nodoc:
120
+ # Use Enumerable#sum instead.
121
+ super
122
+ end
123
+ end
124
+ end
@@ -8,40 +8,45 @@ class File
8
8
  # file.write('hello')
9
9
  # end
10
10
  #
11
- # If your temp directory is not on the same filesystem as the file you're
12
- # trying to write, you can provide a different temporary directory.
11
+ # This method needs to create a temporary file. By default it will create it
12
+ # in the same directory as the destination file. If you don't like this
13
+ # behavior you can provide a different directory but it must be on the
14
+ # same physical filesystem as the file you're trying to write.
13
15
  #
14
16
  # File.atomic_write('/data/something.important', '/data/tmp') do |file|
15
17
  # file.write('hello')
16
18
  # end
17
- def self.atomic_write(file_name, temp_dir = Dir.tmpdir)
19
+ def self.atomic_write(file_name, temp_dir = dirname(file_name))
18
20
  require 'tempfile' unless defined?(Tempfile)
19
- require 'fileutils' unless defined?(FileUtils)
20
21
 
21
- temp_file = Tempfile.new(basename(file_name), temp_dir)
22
- temp_file.binmode
23
- yield temp_file
24
- temp_file.close
22
+ Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file|
23
+ temp_file.binmode
24
+ return_val = yield temp_file
25
+ temp_file.close
25
26
 
26
- if File.exist?(file_name)
27
- # Get original file permissions
28
- old_stat = stat(file_name)
29
- else
30
- # If not possible, probe which are the default permissions in the
31
- # destination directory.
32
- old_stat = probe_stat_in(dirname(file_name))
33
- end
27
+ old_stat = if exist?(file_name)
28
+ # Get original file permissions
29
+ stat(file_name)
30
+ elsif temp_dir != dirname(file_name)
31
+ # If not possible, probe which are the default permissions in the
32
+ # destination directory.
33
+ probe_stat_in(dirname(file_name))
34
+ end
34
35
 
35
- # Overwrite original file with temp file
36
- FileUtils.mv(temp_file.path, file_name)
36
+ if old_stat
37
+ # Set correct permissions on new file
38
+ begin
39
+ chown(old_stat.uid, old_stat.gid, temp_file.path)
40
+ # This operation will affect filesystem ACL's
41
+ chmod(old_stat.mode, temp_file.path)
42
+ rescue Errno::EPERM, Errno::EACCES
43
+ # Changing file ownership failed, moving on.
44
+ end
45
+ end
37
46
 
38
- # Set correct permissions on new file
39
- begin
40
- chown(old_stat.uid, old_stat.gid, file_name)
41
- # This operation will affect filesystem ACL's
42
- chmod(old_stat.mode, file_name)
43
- rescue Errno::EPERM, Errno::EACCES
44
- # Changing file ownership failed, moving on.
47
+ # Overwrite original file with temp file
48
+ rename(temp_file.path, file_name)
49
+ return_val
45
50
  end
46
51
  end
47
52
 
@@ -31,7 +31,7 @@ class Hash
31
31
  # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
32
32
  # callable can add nodes by using <tt>options[:builder]</tt>.
33
33
  #
34
- # 'foo'.to_xml(lambda { |options, key| options[:builder].b(key) })
34
+ # {foo: lambda { |options, key| options[:builder].b(key) }}.to_xml
35
35
  # # => "<b>foo</b>"
36
36
  #
37
37
  # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
@@ -55,8 +55,7 @@ class Hash
55
55
  #
56
56
  # XML_TYPE_NAMES = {
57
57
  # "Symbol" => "symbol",
58
- # "Fixnum" => "integer",
59
- # "Bignum" => "integer",
58
+ # "Integer" => "integer",
60
59
  # "BigDecimal" => "decimal",
61
60
  # "Float" => "float",
62
61
  # "TrueClass" => "boolean",
@@ -106,7 +105,25 @@ class Hash
106
105
  # # => {"hash"=>{"foo"=>1, "bar"=>2}}
107
106
  #
108
107
  # +DisallowedType+ is raised if the XML contains attributes with <tt>type="yaml"</tt> or
109
- # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to parse this XML.
108
+ # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to
109
+ # parse this XML.
110
+ #
111
+ # Custom +disallowed_types+ can also be passed in the form of an
112
+ # array.
113
+ #
114
+ # xml = <<-XML
115
+ # <?xml version="1.0" encoding="UTF-8"?>
116
+ # <hash>
117
+ # <foo type="integer">1</foo>
118
+ # <bar type="string">"David"</bar>
119
+ # </hash>
120
+ # XML
121
+ #
122
+ # hash = Hash.from_xml(xml, ['integer'])
123
+ # # => ActiveSupport::XMLConverter::DisallowedType: Disallowed type attribute: "integer"
124
+ #
125
+ # Note that passing custom disallowed types will override the default types,
126
+ # which are Symbol and YAML.
110
127
  def from_xml(xml, disallowed_types = nil)
111
128
  ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
112
129
  end
@@ -120,6 +137,8 @@ end
120
137
 
121
138
  module ActiveSupport
122
139
  class XMLConverter # :nodoc:
140
+ # Raised if the XML contains attributes with type="yaml" or
141
+ # type="symbol". Read Hash#from_xml for more details.
123
142
  class DisallowedType < StandardError
124
143
  def initialize(type)
125
144
  super "Disallowed type attribute: #{type.inspect}"
@@ -4,7 +4,7 @@ class Hash
4
4
  # h1 = { a: true, b: { c: [1, 2, 3] } }
5
5
  # h2 = { a: false, b: { x: [3, 4, 5] } }
6
6
  #
7
- # h1.deep_merge(h2) #=> { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
7
+ # h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
8
8
  #
9
9
  # Like with Hash#merge in the standard library, a block can be provided
10
10
  # to merge values:
@@ -1,8 +1,9 @@
1
1
  class Hash
2
- # Returns a hash that includes everything but the given keys.
3
- # hash = { a: true, b: false, c: nil}
4
- # hash.except(:c) # => { a: true, b: false}
5
- # hash # => { a: true, b: false, c: nil}
2
+ # Returns a hash that includes everything except given keys.
3
+ # hash = { a: true, b: false, c: nil }
4
+ # hash.except(:c) # => { a: true, b: false }
5
+ # hash.except(:a, :b) # => { c: nil }
6
+ # hash # => { a: true, b: false, c: nil }
6
7
  #
7
8
  # This is useful for limiting a set of parameters to everything but a few known toggles:
8
9
  # @person.update(params[:person].except(:admin))
@@ -10,10 +11,10 @@ class Hash
10
11
  dup.except!(*keys)
11
12
  end
12
13
 
13
- # Replaces the hash without the given keys.
14
- # hash = { a: true, b: false, c: nil}
15
- # hash.except!(:c) # => { a: true, b: false}
16
- # hash # => { a: true, b: false }
14
+ # Removes the given keys from hash and returns it.
15
+ # hash = { a: true, b: false, c: nil }
16
+ # hash.except!(:c) # => { a: true, b: false }
17
+ # hash # => { a: true, b: false }
17
18
  def except!(*keys)
18
19
  keys.each { |key| delete(key) }
19
20
  self
@@ -6,7 +6,7 @@ class Hash
6
6
  #
7
7
  # { a: 1 }.with_indifferent_access['a'] # => 1
8
8
  def with_indifferent_access
9
- ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
9
+ ActiveSupport::HashWithIndifferentAccess.new(self)
10
10
  end
11
11
 
12
12
  # Called when object is nested under an object that receives