activesupport 5.2.5 → 6.0.0

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 (138) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +327 -423
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -2
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/backtrace_cleaner.rb +28 -1
  7. data/lib/active_support/cache/file_store.rb +22 -22
  8. data/lib/active_support/cache/mem_cache_store.rb +16 -2
  9. data/lib/active_support/cache/memory_store.rb +7 -2
  10. data/lib/active_support/cache/null_store.rb +5 -0
  11. data/lib/active_support/cache/redis_cache_store.rb +47 -25
  12. data/lib/active_support/cache.rb +45 -23
  13. data/lib/active_support/callbacks.rb +16 -5
  14. data/lib/active_support/concern.rb +24 -1
  15. data/lib/active_support/configurable.rb +7 -11
  16. data/lib/active_support/core_ext/array/access.rb +18 -6
  17. data/lib/active_support/core_ext/array/extract.rb +21 -0
  18. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
  19. data/lib/active_support/core_ext/array.rb +1 -1
  20. data/lib/active_support/core_ext/class/attribute.rb +11 -16
  21. data/lib/active_support/core_ext/class/subclasses.rb +1 -1
  22. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  23. data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -47
  24. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  25. data/lib/active_support/core_ext/enumerable.rb +97 -73
  26. data/lib/active_support/core_ext/hash/compact.rb +2 -26
  27. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  28. data/lib/active_support/core_ext/hash/except.rb +1 -1
  29. data/lib/active_support/core_ext/hash/keys.rb +0 -29
  30. data/lib/active_support/core_ext/hash/slice.rb +3 -25
  31. data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
  32. data/lib/active_support/core_ext/hash.rb +1 -2
  33. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  34. data/lib/active_support/core_ext/kernel.rb +0 -1
  35. data/lib/active_support/core_ext/load_error.rb +1 -1
  36. data/lib/active_support/core_ext/module/attribute_accessors.rb +7 -10
  37. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +13 -19
  38. data/lib/active_support/core_ext/module/delegation.rb +33 -7
  39. data/lib/active_support/core_ext/module/introspection.rb +37 -13
  40. data/lib/active_support/core_ext/module/reachable.rb +1 -6
  41. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  42. data/lib/active_support/core_ext/module.rb +0 -1
  43. data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
  44. data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
  45. data/lib/active_support/core_ext/numeric.rb +0 -1
  46. data/lib/active_support/core_ext/object/blank.rb +1 -2
  47. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  48. data/lib/active_support/core_ext/object/json.rb +1 -0
  49. data/lib/active_support/core_ext/object/try.rb +15 -7
  50. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  51. data/lib/active_support/core_ext/range/compare_range.rb +22 -13
  52. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  53. data/lib/active_support/core_ext/range/include_range.rb +6 -0
  54. data/lib/active_support/core_ext/regexp.rb +0 -4
  55. data/lib/active_support/core_ext/securerandom.rb +23 -3
  56. data/lib/active_support/core_ext/string/access.rb +8 -0
  57. data/lib/active_support/core_ext/string/filters.rb +42 -1
  58. data/lib/active_support/core_ext/string/inflections.rb +7 -2
  59. data/lib/active_support/core_ext/string/multibyte.rb +4 -3
  60. data/lib/active_support/core_ext/string/output_safety.rb +61 -5
  61. data/lib/active_support/core_ext/string/strip.rb +3 -1
  62. data/lib/active_support/core_ext/time/calculations.rb +31 -2
  63. data/lib/active_support/core_ext/uri.rb +1 -0
  64. data/lib/active_support/current_attributes.rb +8 -0
  65. data/lib/active_support/dependencies/zeitwerk_integration.rb +110 -0
  66. data/lib/active_support/dependencies.rb +69 -16
  67. data/lib/active_support/deprecation/behaviors.rb +1 -1
  68. data/lib/active_support/deprecation/method_wrappers.rb +8 -20
  69. data/lib/active_support/deprecation/proxy_wrappers.rb +24 -5
  70. data/lib/active_support/deprecation.rb +1 -1
  71. data/lib/active_support/descendants_tracker.rb +56 -9
  72. data/lib/active_support/duration/iso8601_parser.rb +2 -3
  73. data/lib/active_support/duration/iso8601_serializer.rb +3 -4
  74. data/lib/active_support/duration.rb +12 -15
  75. data/lib/active_support/encrypted_configuration.rb +0 -4
  76. data/lib/active_support/encrypted_file.rb +2 -1
  77. data/lib/active_support/evented_file_update_checker.rb +39 -9
  78. data/lib/active_support/execution_wrapper.rb +1 -0
  79. data/lib/active_support/gem_version.rb +3 -3
  80. data/lib/active_support/hash_with_indifferent_access.rb +22 -18
  81. data/lib/active_support/i18n.rb +1 -0
  82. data/lib/active_support/i18n_railtie.rb +9 -1
  83. data/lib/active_support/inflector/inflections.rb +1 -4
  84. data/lib/active_support/inflector/methods.rb +15 -27
  85. data/lib/active_support/inflector/transliterate.rb +47 -18
  86. data/lib/active_support/json/decoding.rb +23 -23
  87. data/lib/active_support/json/encoding.rb +6 -2
  88. data/lib/active_support/key_generator.rb +0 -32
  89. data/lib/active_support/lazy_load_hooks.rb +5 -1
  90. data/lib/active_support/locale/en.rb +31 -0
  91. data/lib/active_support/log_subscriber.rb +31 -8
  92. data/lib/active_support/logger.rb +0 -15
  93. data/lib/active_support/logger_silence.rb +28 -12
  94. data/lib/active_support/logger_thread_safe_level.rb +26 -4
  95. data/lib/active_support/message_encryptor.rb +3 -5
  96. data/lib/active_support/message_verifier.rb +3 -3
  97. data/lib/active_support/multibyte/chars.rb +29 -48
  98. data/lib/active_support/multibyte/unicode.rb +44 -281
  99. data/lib/active_support/notifications/fanout.rb +98 -13
  100. data/lib/active_support/notifications/instrumenter.rb +79 -8
  101. data/lib/active_support/notifications.rb +41 -4
  102. data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -2
  103. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -1
  104. data/lib/active_support/number_helper/number_to_human_converter.rb +3 -1
  105. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -1
  106. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  107. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -0
  108. data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -3
  109. data/lib/active_support/number_helper.rb +7 -0
  110. data/lib/active_support/ordered_options.rb +1 -1
  111. data/lib/active_support/parameter_filter.rb +129 -0
  112. data/lib/active_support/rails.rb +0 -6
  113. data/lib/active_support/reloader.rb +4 -5
  114. data/lib/active_support/security_utils.rb +1 -1
  115. data/lib/active_support/subscriber.rb +65 -26
  116. data/lib/active_support/tagged_logging.rb +13 -4
  117. data/lib/active_support/test_case.rb +91 -0
  118. data/lib/active_support/testing/assertions.rb +15 -1
  119. data/lib/active_support/testing/deprecation.rb +0 -1
  120. data/lib/active_support/testing/file_fixtures.rb +2 -0
  121. data/lib/active_support/testing/isolation.rb +2 -2
  122. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  123. data/lib/active_support/testing/parallelization.rb +128 -0
  124. data/lib/active_support/testing/stream.rb +1 -1
  125. data/lib/active_support/testing/time_helpers.rb +7 -7
  126. data/lib/active_support/time_with_zone.rb +15 -5
  127. data/lib/active_support/values/time_zone.rb +12 -7
  128. data/lib/active_support/xml_mini/jdom.rb +2 -2
  129. data/lib/active_support/xml_mini/libxml.rb +2 -2
  130. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  131. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  132. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  133. data/lib/active_support/xml_mini/rexml.rb +2 -2
  134. data/lib/active_support/xml_mini.rb +2 -9
  135. data/lib/active_support.rb +2 -1
  136. metadata +34 -9
  137. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  138. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -110,7 +110,7 @@ module ActiveSupport
110
110
  base.instance_variable_set(:@_dependencies, [])
111
111
  end
112
112
 
113
- def append_features(base)
113
+ def append_features(base) #:nodoc:
114
114
  if base.instance_variable_defined?(:@_dependencies)
115
115
  base.instance_variable_get(:@_dependencies) << self
116
116
  false
@@ -123,6 +123,9 @@ module ActiveSupport
123
123
  end
124
124
  end
125
125
 
126
+ # Evaluate given block in context of base class,
127
+ # so that you can write class macros here.
128
+ # When you define more than one +included+ block, it raises an exception.
126
129
  def included(base = nil, &block)
127
130
  if base.nil?
128
131
  if instance_variable_defined?(:@_included_block)
@@ -137,6 +140,26 @@ module ActiveSupport
137
140
  end
138
141
  end
139
142
 
143
+ # Define class methods from given block.
144
+ # You can define private class methods as well.
145
+ #
146
+ # module Example
147
+ # extend ActiveSupport::Concern
148
+ #
149
+ # class_methods do
150
+ # def foo; puts 'foo'; end
151
+ #
152
+ # private
153
+ # def bar; puts 'bar'; end
154
+ # end
155
+ # end
156
+ #
157
+ # class Buzz
158
+ # include Example
159
+ # end
160
+ #
161
+ # Buzz.foo # => "foo"
162
+ # Buzz.bar # => private method 'bar' called for Buzz:Class(NoMethodError)
140
163
  def class_methods(&class_methods_module_definition)
141
164
  mod = const_defined?(:ClassMethods, false) ?
142
165
  const_get(:ClassMethods) :
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "active_support/concern"
4
4
  require "active_support/ordered_options"
5
- require "active_support/core_ext/array/extract_options"
6
- require "active_support/core_ext/regexp"
7
5
 
8
6
  module ActiveSupport
9
7
  # Configurable provides a <tt>config</tt> method to store and retrieve
@@ -69,8 +67,8 @@ module ActiveSupport
69
67
  # end
70
68
  # # => NameError: invalid config attribute name
71
69
  #
72
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
73
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
70
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
71
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
74
72
  #
75
73
  # class User
76
74
  # include ActiveSupport::Configurable
@@ -83,7 +81,7 @@ module ActiveSupport
83
81
  # User.new.allowed_access = true # => NoMethodError
84
82
  # User.new.allowed_access # => NoMethodError
85
83
  #
86
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
84
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
87
85
  #
88
86
  # class User
89
87
  # include ActiveSupport::Configurable
@@ -106,9 +104,7 @@ module ActiveSupport
106
104
  # end
107
105
  #
108
106
  # User.hair_colors # => [:brown, :black, :blonde, :red]
109
- def config_accessor(*names)
110
- options = names.extract_options!
111
-
107
+ def config_accessor(*names, instance_reader: true, instance_writer: true, instance_accessor: true) # :doc:
112
108
  names.each do |name|
113
109
  raise NameError.new("invalid config attribute name") unless /\A[_A-Za-z]\w*\z/.match?(name)
114
110
 
@@ -118,9 +114,9 @@ module ActiveSupport
118
114
  singleton_class.class_eval reader, __FILE__, reader_line
119
115
  singleton_class.class_eval writer, __FILE__, writer_line
120
116
 
121
- unless options[:instance_accessor] == false
122
- class_eval reader, __FILE__, reader_line unless options[:instance_reader] == false
123
- class_eval writer, __FILE__, writer_line unless options[:instance_writer] == false
117
+ if instance_accessor
118
+ class_eval reader, __FILE__, reader_line if instance_reader
119
+ class_eval writer, __FILE__, writer_line if instance_writer
124
120
  end
125
121
  send("#{name}=", yield) if block_given?
126
122
  end
@@ -29,16 +29,28 @@ class Array
29
29
  end
30
30
  end
31
31
 
32
- # Returns a copy of the Array without the specified elements.
32
+ # Returns a new array that includes the passed elements.
33
33
  #
34
- # people = ["David", "Rafael", "Aaron", "Todd"]
35
- # people.without "Aaron", "Todd"
36
- # # => ["David", "Rafael"]
34
+ # [ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
35
+ # [ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ]
36
+ def including(*elements)
37
+ self + elements.flatten(1)
38
+ end
39
+
40
+ # Returns a copy of the Array excluding the specified elements.
41
+ #
42
+ # ["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
43
+ # [ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) # => [ [ 0, 1 ] ]
37
44
  #
38
- # Note: This is an optimization of <tt>Enumerable#without</tt> that uses <tt>Array#-</tt>
45
+ # Note: This is an optimization of <tt>Enumerable#excluding</tt> that uses <tt>Array#-</tt>
39
46
  # instead of <tt>Array#reject</tt> for performance reasons.
47
+ def excluding(*elements)
48
+ self - elements.flatten(1)
49
+ end
50
+
51
+ # Alias for #excluding.
40
52
  def without(*elements)
41
- self - elements
53
+ excluding(*elements)
42
54
  end
43
55
 
44
56
  # Equal to <tt>self[1]</tt>.
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Array
4
+ # Removes and returns the elements for which the block returns a true value.
5
+ # If no block is given, an Enumerator is returned instead.
6
+ #
7
+ # numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
8
+ # odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
9
+ # numbers # => [0, 2, 4, 6, 8]
10
+ def extract!
11
+ return to_enum(:extract!) { size } unless block_given?
12
+
13
+ extracted_elements = []
14
+
15
+ reject! do |element|
16
+ extracted_elements << element if yield(element)
17
+ end
18
+
19
+ extracted_elements
20
+ end
21
+ end
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Array
4
- # The human way of thinking about adding stuff to the end of a list is with append.
5
- alias_method :append, :push unless [].respond_to?(:append)
3
+ require "active_support/deprecation"
6
4
 
7
- # The human way of thinking about adding stuff to the beginning of a list is with prepend.
8
- alias_method :prepend, :unshift unless [].respond_to?(:prepend)
9
- end
5
+ ActiveSupport::Deprecation.warn "Ruby 2.5+ (required by Rails 6) provides Array#append and Array#prepend natively, so requiring active_support/core_ext/array/prepend_and_append is no longer necessary. Requiring it will raise LoadError in Rails 6.1."
@@ -3,7 +3,7 @@
3
3
  require "active_support/core_ext/array/wrap"
4
4
  require "active_support/core_ext/array/access"
5
5
  require "active_support/core_ext/array/conversions"
6
+ require "active_support/core_ext/array/extract"
6
7
  require "active_support/core_ext/array/extract_options"
7
8
  require "active_support/core_ext/array/grouping"
8
- require "active_support/core_ext/array/prepend_and_append"
9
9
  require "active_support/core_ext/array/inquiry"
@@ -84,27 +84,26 @@ class Class
84
84
  # To set a default value for the attribute, pass <tt>default:</tt>, like so:
85
85
  #
86
86
  # class_attribute :settings, default: {}
87
- def class_attribute(*attrs)
88
- options = attrs.extract_options!
89
- instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
90
- instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
91
- instance_predicate = options.fetch(:instance_predicate, true)
92
- default_value = options.fetch(:default, nil)
93
-
87
+ def class_attribute(
88
+ *attrs,
89
+ instance_accessor: true,
90
+ instance_reader: instance_accessor,
91
+ instance_writer: instance_accessor,
92
+ instance_predicate: true,
93
+ default: nil
94
+ )
94
95
  attrs.each do |name|
95
96
  singleton_class.silence_redefinition_of_method(name)
96
- define_singleton_method(name) { nil }
97
+ define_singleton_method(name) { default }
97
98
 
98
99
  singleton_class.silence_redefinition_of_method("#{name}?")
99
100
  define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
100
101
 
101
- ivar = "@#{name}"
102
+ ivar = "@#{name}".to_sym
102
103
 
103
104
  singleton_class.silence_redefinition_of_method("#{name}=")
104
105
  define_singleton_method("#{name}=") do |val|
105
- singleton_class.class_eval do
106
- redefine_method(name) { val }
107
- end
106
+ redefine_singleton_method(name) { val }
108
107
 
109
108
  if singleton_class?
110
109
  class_eval do
@@ -137,10 +136,6 @@ class Class
137
136
  instance_variable_set ivar, val
138
137
  end
139
138
  end
140
-
141
- unless default_value.nil?
142
- self.send("#{name}=", default_value)
143
- end
144
139
  end
145
140
  end
146
141
  end
@@ -3,7 +3,7 @@
3
3
  class Class
4
4
  begin
5
5
  # Test if this Ruby supports each_object against singleton_class
6
- ObjectSpace.each_object(Numeric.singleton_class) {}
6
+ ObjectSpace.each_object(Numeric.singleton_class) { }
7
7
 
8
8
  # Returns an array with all classes that are < than its receiver.
9
9
  #
@@ -110,12 +110,13 @@ class Date
110
110
  # Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
111
111
  # any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
112
112
  def advance(options)
113
- options = options.dup
114
113
  d = self
115
- d = d >> options.delete(:years) * 12 if options[:years]
116
- d = d >> options.delete(:months) if options[:months]
117
- d = d + options.delete(:weeks) * 7 if options[:weeks]
118
- d = d + options.delete(:days) if options[:days]
114
+
115
+ d = d >> options[:years] * 12 if options[:years]
116
+ d = d >> options[:months] if options[:months]
117
+ d = d + options[:weeks] * 7 if options[:weeks]
118
+ d = d + options[:days] if options[:days]
119
+
119
120
  d
120
121
  end
121
122
 
@@ -5,13 +5,13 @@ require "active_support/core_ext/object/try"
5
5
  module DateAndTime
6
6
  module Calculations
7
7
  DAYS_INTO_WEEK = {
8
- monday: 0,
9
- tuesday: 1,
10
- wednesday: 2,
11
- thursday: 3,
12
- friday: 4,
13
- saturday: 5,
14
- sunday: 6
8
+ sunday: 0,
9
+ monday: 1,
10
+ tuesday: 2,
11
+ wednesday: 3,
12
+ thursday: 4,
13
+ friday: 5,
14
+ saturday: 6
15
15
  }
16
16
  WEEKEND_DAYS = [ 6, 0 ]
17
17
 
@@ -20,21 +20,11 @@ module DateAndTime
20
20
  advance(days: -1)
21
21
  end
22
22
 
23
- # Returns a new date/time the specified number of days ago.
24
- def prev_day(days = 1)
25
- advance(days: -days)
26
- end
27
-
28
23
  # Returns a new date/time representing tomorrow.
29
24
  def tomorrow
30
25
  advance(days: 1)
31
26
  end
32
27
 
33
- # Returns a new date/time the specified number of days in the future.
34
- def next_day(days = 1)
35
- advance(days: days)
36
- end
37
-
38
28
  # Returns true if the date/time is today.
39
29
  def today?
40
30
  to_date == ::Date.current
@@ -60,6 +50,16 @@ module DateAndTime
60
50
  !WEEKEND_DAYS.include?(wday)
61
51
  end
62
52
 
53
+ # Returns true if the date/time falls before <tt>date_or_time</tt>.
54
+ def before?(date_or_time)
55
+ self < date_or_time
56
+ end
57
+
58
+ # Returns true if the date/time falls after <tt>date_or_time</tt>.
59
+ def after?(date_or_time)
60
+ self > date_or_time
61
+ end
62
+
63
63
  # Returns a new date/time the specified number of days ago.
64
64
  def days_ago(days)
65
65
  advance(days: -days)
@@ -124,7 +124,7 @@ module DateAndTime
124
124
  # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
125
125
  # now.beginning_of_quarter # => Wed, 01 Jul 2015 00:00:00 +0000
126
126
  def beginning_of_quarter
127
- first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
127
+ first_quarter_month = month - (2 + month) % 3
128
128
  beginning_of_month.change(month: first_quarter_month)
129
129
  end
130
130
  alias :at_beginning_of_quarter :beginning_of_quarter
@@ -139,7 +139,7 @@ module DateAndTime
139
139
  # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
140
140
  # now.end_of_quarter # => Wed, 30 Sep 2015 23:59:59 +0000
141
141
  def end_of_quarter
142
- last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
142
+ last_quarter_month = month + (12 - month) % 3
143
143
  beginning_of_month.change(month: last_quarter_month).end_of_month
144
144
  end
145
145
  alias :at_end_of_quarter :end_of_quarter
@@ -188,21 +188,11 @@ module DateAndTime
188
188
  end
189
189
  end
190
190
 
191
- # Returns a new date/time the specified number of months in the future.
192
- def next_month(months = 1)
193
- advance(months: months)
194
- end
195
-
196
191
  # Short-hand for months_since(3)
197
192
  def next_quarter
198
193
  months_since(3)
199
194
  end
200
195
 
201
- # Returns a new date/time the specified number of years in the future.
202
- def next_year(years = 1)
203
- advance(years: years)
204
- end
205
-
206
196
  # Returns a new date/time representing the given day in the previous week.
207
197
  # Week is assumed to start on +start_day+, default is
208
198
  # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
@@ -223,11 +213,6 @@ module DateAndTime
223
213
  end
224
214
  alias_method :last_weekday, :prev_weekday
225
215
 
226
- # Returns a new date/time the specified number of months ago.
227
- def prev_month(months = 1)
228
- advance(months: -months)
229
- end
230
-
231
216
  # Short-hand for months_ago(1).
232
217
  def last_month
233
218
  months_ago(1)
@@ -239,11 +224,6 @@ module DateAndTime
239
224
  end
240
225
  alias_method :last_quarter, :prev_quarter
241
226
 
242
- # Returns a new date/time the specified number of years ago.
243
- def prev_year(years = 1)
244
- advance(years: -years)
245
- end
246
-
247
227
  # Short-hand for years_ago(1).
248
228
  def last_year
249
229
  years_ago(1)
@@ -253,9 +233,8 @@ module DateAndTime
253
233
  # Week is assumed to start on +start_day+, default is
254
234
  # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
255
235
  def days_to_week_start(start_day = Date.beginning_of_week)
256
- start_day_number = DAYS_INTO_WEEK[start_day]
257
- current_day_number = wday != 0 ? wday - 1 : 6
258
- (current_day_number - start_day_number) % 7
236
+ start_day_number = DAYS_INTO_WEEK.fetch(start_day)
237
+ (wday - start_day_number) % 7
259
238
  end
260
239
 
261
240
  # Returns a new date/time representing the start of this week on the given day.
@@ -336,8 +315,7 @@ module DateAndTime
336
315
  # today.next_occurring(:monday) # => Mon, 18 Dec 2017
337
316
  # today.next_occurring(:thursday) # => Thu, 21 Dec 2017
338
317
  def next_occurring(day_of_week)
339
- current_day_number = wday != 0 ? wday - 1 : 6
340
- from_now = DAYS_INTO_WEEK.fetch(day_of_week) - current_day_number
318
+ from_now = DAYS_INTO_WEEK.fetch(day_of_week) - wday
341
319
  from_now += 7 unless from_now > 0
342
320
  advance(days: from_now)
343
321
  end
@@ -348,8 +326,7 @@ module DateAndTime
348
326
  # today.prev_occurring(:monday) # => Mon, 11 Dec 2017
349
327
  # today.prev_occurring(:thursday) # => Thu, 07 Dec 2017
350
328
  def prev_occurring(day_of_week)
351
- current_day_number = wday != 0 ? wday - 1 : 6
352
- ago = current_day_number - DAYS_INTO_WEEK.fetch(day_of_week)
329
+ ago = wday - DAYS_INTO_WEEK.fetch(day_of_week)
353
330
  ago += 7 unless ago > 0
354
331
  advance(days: -ago)
355
332
  end
@@ -364,7 +341,7 @@ module DateAndTime
364
341
  end
365
342
 
366
343
  def days_span(day)
367
- (DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
344
+ (DAYS_INTO_WEEK.fetch(day) - DAYS_INTO_WEEK.fetch(Date.beginning_of_week)) % 7
368
345
  end
369
346
 
370
347
  def copy_time_to(other)
@@ -110,7 +110,7 @@ class DateTime
110
110
  # instance time. Do not use this method in combination with x.months, use
111
111
  # months_since instead!
112
112
  def since(seconds)
113
- self + Rational(seconds.round, 86400)
113
+ self + Rational(seconds, 86400)
114
114
  end
115
115
  alias :in :since
116
116
 
@@ -1,64 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Enumerable
4
+ INDEX_WITH_DEFAULT = Object.new
5
+ private_constant :INDEX_WITH_DEFAULT
6
+
4
7
  # Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
5
8
  # when we omit an identity.
9
+
10
+ # :stopdoc:
11
+
12
+ # We can't use Refinements here because Refinements with Module which will be prepended
13
+ # doesn't work well https://bugs.ruby-lang.org/issues/13446
14
+ alias :_original_sum_with_required_identity :sum
15
+ private :_original_sum_with_required_identity
16
+
17
+ # :startdoc:
18
+
19
+ # Calculates a sum from the elements.
6
20
  #
7
- # We tried shimming it to attempt the fast native method, rescue TypeError,
8
- # and fall back to the compatible implementation, but that's much slower than
9
- # just calling the compat method in the first place.
10
- if Enumerable.instance_methods(false).include?(:sum) && !((?a..?b).sum rescue false)
11
- # :stopdoc:
12
-
13
- # We can't use Refinements here because Refinements with Module which will be prepended
14
- # doesn't work well https://bugs.ruby-lang.org/issues/13446
15
- alias :_original_sum_with_required_identity :sum
16
- private :_original_sum_with_required_identity
17
-
18
- # :startdoc:
19
-
20
- # Calculates a sum from the elements.
21
- #
22
- # payments.sum { |p| p.price * p.tax_rate }
23
- # payments.sum(&:price)
24
- #
25
- # The latter is a shortcut for:
26
- #
27
- # payments.inject(0) { |sum, p| sum + p.price }
28
- #
29
- # It can also calculate the sum without the use of a block.
30
- #
31
- # [5, 15, 10].sum # => 30
32
- # ['foo', 'bar'].sum # => "foobar"
33
- # [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
34
- #
35
- # The default sum of an empty list is zero. You can override this default:
36
- #
37
- # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
38
- def sum(identity = nil, &block)
39
- if identity
40
- _original_sum_with_required_identity(identity, &block)
41
- elsif block_given?
42
- map(&block).sum(identity)
43
- else
44
- inject(:+) || 0
45
- end
46
- end
47
- else
48
- def sum(identity = nil, &block)
49
- if block_given?
50
- map(&block).sum(identity)
51
- else
52
- sum = identity ? inject(identity, :+) : inject(:+)
53
- sum || identity || 0
54
- end
21
+ # payments.sum { |p| p.price * p.tax_rate }
22
+ # payments.sum(&:price)
23
+ #
24
+ # The latter is a shortcut for:
25
+ #
26
+ # payments.inject(0) { |sum, p| sum + p.price }
27
+ #
28
+ # It can also calculate the sum without the use of a block.
29
+ #
30
+ # [5, 15, 10].sum # => 30
31
+ # ['foo', 'bar'].sum # => "foobar"
32
+ # [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
33
+ #
34
+ # The default sum of an empty list is zero. You can override this default:
35
+ #
36
+ # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
37
+ def sum(identity = nil, &block)
38
+ if identity
39
+ _original_sum_with_required_identity(identity, &block)
40
+ elsif block_given?
41
+ map(&block).sum(identity)
42
+ else
43
+ inject(:+) || 0
55
44
  end
56
45
  end
57
46
 
58
- # Convert an enumerable to a hash.
47
+ # Convert an enumerable to a hash keying it by the block return value.
59
48
  #
60
49
  # people.index_by(&:login)
61
50
  # # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
51
+ #
62
52
  # people.index_by { |person| "#{person.first_name} #{person.last_name}" }
63
53
  # # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
64
54
  def index_by
@@ -71,6 +61,26 @@ module Enumerable
71
61
  end
72
62
  end
73
63
 
64
+ # Convert an enumerable to a hash keying it with the enumerable items and with the values returned in the block.
65
+ #
66
+ # post = Post.new(title: "hey there", body: "what's up?")
67
+ #
68
+ # %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
69
+ # # => { title: "hey there", body: "what's up?" }
70
+ def index_with(default = INDEX_WITH_DEFAULT)
71
+ if block_given?
72
+ result = {}
73
+ each { |elem| result[elem] = yield(elem) }
74
+ result
75
+ elsif default != INDEX_WITH_DEFAULT
76
+ result = {}
77
+ each { |elem| result[elem] = default }
78
+ result
79
+ else
80
+ to_enum(:index_with) { size if respond_to?(:size) }
81
+ end
82
+ end
83
+
74
84
  # Returns +true+ if the enumerable has more than 1 element. Functionally
75
85
  # equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
76
86
  # much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
@@ -87,23 +97,43 @@ module Enumerable
87
97
  end
88
98
  end
89
99
 
100
+ # Returns a new array that includes the passed elements.
101
+ #
102
+ # [ 1, 2, 3 ].including(4, 5)
103
+ # # => [ 1, 2, 3, 4, 5 ]
104
+ #
105
+ # ["David", "Rafael"].including %w[ Aaron Todd ]
106
+ # # => ["David", "Rafael", "Aaron", "Todd"]
107
+ def including(*elements)
108
+ to_a.including(*elements)
109
+ end
110
+
90
111
  # The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
91
112
  # collection does not include the object.
92
113
  def exclude?(object)
93
114
  !include?(object)
94
115
  end
95
116
 
96
- # Returns a copy of the enumerable without the specified elements.
117
+ # Returns a copy of the enumerable excluding the specified elements.
118
+ #
119
+ # ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
120
+ # # => ["David", "Rafael"]
97
121
  #
98
- # ["David", "Rafael", "Aaron", "Todd"].without "Aaron", "Todd"
122
+ # ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
99
123
  # # => ["David", "Rafael"]
100
124
  #
101
- # {foo: 1, bar: 2, baz: 3}.without :bar
125
+ # {foo: 1, bar: 2, baz: 3}.excluding :bar
102
126
  # # => {foo: 1, baz: 3}
103
- def without(*elements)
127
+ def excluding(*elements)
128
+ elements.flatten!(1)
104
129
  reject { |element| elements.include?(element) }
105
130
  end
106
131
 
132
+ # Alias for #excluding.
133
+ def without(*elements)
134
+ excluding(*elements)
135
+ end
136
+
107
137
  # Convert an enumerable to an array based on the given key.
108
138
  #
109
139
  # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
@@ -138,27 +168,21 @@ class Range #:nodoc:
138
168
  end
139
169
  end
140
170
 
141
- # Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
142
- #
143
- # We tried shimming it to attempt the fast native method, rescue TypeError,
144
- # and fall back to the compatible implementation, but that's much slower than
145
- # just calling the compat method in the first place.
146
- if Array.instance_methods(false).include?(:sum) && !(%w[a].sum rescue false)
147
- # Using Refinements here in order not to expose our internal method
148
- using Module.new {
149
- refine Array do
150
- alias :orig_sum :sum
151
- end
152
- }
171
+ # Using Refinements here in order not to expose our internal method
172
+ using Module.new {
173
+ refine Array do
174
+ alias :orig_sum :sum
175
+ end
176
+ }
153
177
 
154
- class Array
155
- def sum(init = nil, &block) #:nodoc:
156
- if init.is_a?(Numeric) || first.is_a?(Numeric)
157
- init ||= 0
158
- orig_sum(init, &block)
159
- else
160
- super
161
- end
178
+ class Array #:nodoc:
179
+ # Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
180
+ def sum(init = nil, &block)
181
+ if init.is_a?(Numeric) || first.is_a?(Numeric)
182
+ init ||= 0
183
+ orig_sum(init, &block)
184
+ else
185
+ super
162
186
  end
163
187
  end
164
188
  end