activesupport 5.2.4 → 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 +331 -396
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -2
  5. data/lib/active_support.rb +2 -1
  6. data/lib/active_support/actionable_error.rb +48 -0
  7. data/lib/active_support/backtrace_cleaner.rb +28 -1
  8. data/lib/active_support/cache.rb +45 -23
  9. data/lib/active_support/cache/file_store.rb +22 -22
  10. data/lib/active_support/cache/mem_cache_store.rb +5 -0
  11. data/lib/active_support/cache/memory_store.rb +7 -2
  12. data/lib/active_support/cache/null_store.rb +5 -0
  13. data/lib/active_support/cache/redis_cache_store.rb +36 -9
  14. data/lib/active_support/callbacks.rb +16 -5
  15. data/lib/active_support/concern.rb +24 -1
  16. data/lib/active_support/configurable.rb +7 -11
  17. data/lib/active_support/core_ext/array.rb +1 -1
  18. data/lib/active_support/core_ext/array/access.rb +18 -6
  19. data/lib/active_support/core_ext/array/extract.rb +21 -0
  20. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
  21. data/lib/active_support/core_ext/class/attribute.rb +11 -16
  22. data/lib/active_support/core_ext/class/subclasses.rb +1 -1
  23. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  24. data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -47
  25. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  26. data/lib/active_support/core_ext/enumerable.rb +97 -73
  27. data/lib/active_support/core_ext/hash.rb +1 -2
  28. data/lib/active_support/core_ext/hash/compact.rb +2 -26
  29. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  30. data/lib/active_support/core_ext/hash/except.rb +1 -1
  31. data/lib/active_support/core_ext/hash/keys.rb +0 -29
  32. data/lib/active_support/core_ext/hash/slice.rb +3 -25
  33. data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
  34. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  35. data/lib/active_support/core_ext/kernel.rb +0 -1
  36. data/lib/active_support/core_ext/load_error.rb +1 -1
  37. data/lib/active_support/core_ext/module.rb +0 -1
  38. data/lib/active_support/core_ext/module/attribute_accessors.rb +7 -10
  39. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +13 -19
  40. data/lib/active_support/core_ext/module/delegation.rb +33 -7
  41. data/lib/active_support/core_ext/module/introspection.rb +37 -13
  42. data/lib/active_support/core_ext/module/reachable.rb +1 -6
  43. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  44. data/lib/active_support/core_ext/numeric.rb +0 -1
  45. data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
  46. data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
  47. data/lib/active_support/core_ext/object/blank.rb +1 -2
  48. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  49. data/lib/active_support/core_ext/object/json.rb +1 -0
  50. data/lib/active_support/core_ext/object/try.rb +15 -7
  51. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  52. data/lib/active_support/core_ext/range/compare_range.rb +22 -13
  53. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  54. data/lib/active_support/core_ext/range/include_range.rb +6 -0
  55. data/lib/active_support/core_ext/regexp.rb +0 -4
  56. data/lib/active_support/core_ext/securerandom.rb +23 -3
  57. data/lib/active_support/core_ext/string/access.rb +8 -0
  58. data/lib/active_support/core_ext/string/filters.rb +42 -1
  59. data/lib/active_support/core_ext/string/inflections.rb +7 -2
  60. data/lib/active_support/core_ext/string/multibyte.rb +4 -3
  61. data/lib/active_support/core_ext/string/output_safety.rb +61 -5
  62. data/lib/active_support/core_ext/string/strip.rb +3 -1
  63. data/lib/active_support/core_ext/time/calculations.rb +31 -2
  64. data/lib/active_support/core_ext/uri.rb +1 -0
  65. data/lib/active_support/current_attributes.rb +8 -0
  66. data/lib/active_support/dependencies.rb +69 -16
  67. data/lib/active_support/dependencies/zeitwerk_integration.rb +110 -0
  68. data/lib/active_support/deprecation.rb +1 -1
  69. data/lib/active_support/deprecation/behaviors.rb +1 -1
  70. data/lib/active_support/deprecation/method_wrappers.rb +8 -20
  71. data/lib/active_support/deprecation/proxy_wrappers.rb +24 -5
  72. data/lib/active_support/descendants_tracker.rb +56 -9
  73. data/lib/active_support/duration.rb +4 -3
  74. data/lib/active_support/duration/iso8601_parser.rb +2 -3
  75. data/lib/active_support/duration/iso8601_serializer.rb +3 -4
  76. data/lib/active_support/encrypted_configuration.rb +0 -4
  77. data/lib/active_support/encrypted_file.rb +2 -1
  78. data/lib/active_support/evented_file_update_checker.rb +39 -9
  79. data/lib/active_support/execution_wrapper.rb +1 -0
  80. data/lib/active_support/gem_version.rb +3 -3
  81. data/lib/active_support/hash_with_indifferent_access.rb +22 -18
  82. data/lib/active_support/i18n.rb +1 -0
  83. data/lib/active_support/i18n_railtie.rb +9 -1
  84. data/lib/active_support/inflector/inflections.rb +1 -4
  85. data/lib/active_support/inflector/methods.rb +15 -27
  86. data/lib/active_support/inflector/transliterate.rb +47 -18
  87. data/lib/active_support/json/decoding.rb +23 -23
  88. data/lib/active_support/json/encoding.rb +6 -2
  89. data/lib/active_support/key_generator.rb +0 -32
  90. data/lib/active_support/lazy_load_hooks.rb +5 -1
  91. data/lib/active_support/locale/en.rb +31 -0
  92. data/lib/active_support/log_subscriber.rb +31 -8
  93. data/lib/active_support/logger.rb +0 -15
  94. data/lib/active_support/logger_silence.rb +28 -12
  95. data/lib/active_support/logger_thread_safe_level.rb +26 -4
  96. data/lib/active_support/message_encryptor.rb +3 -5
  97. data/lib/active_support/message_verifier.rb +3 -3
  98. data/lib/active_support/multibyte/chars.rb +29 -48
  99. data/lib/active_support/multibyte/unicode.rb +44 -281
  100. data/lib/active_support/notifications.rb +41 -4
  101. data/lib/active_support/notifications/fanout.rb +98 -13
  102. data/lib/active_support/notifications/instrumenter.rb +79 -8
  103. data/lib/active_support/number_helper.rb +7 -0
  104. data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -2
  105. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -1
  106. data/lib/active_support/number_helper/number_to_human_converter.rb +3 -1
  107. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -1
  108. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  109. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -0
  110. data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -3
  111. data/lib/active_support/ordered_options.rb +1 -1
  112. data/lib/active_support/parameter_filter.rb +129 -0
  113. data/lib/active_support/rails.rb +0 -6
  114. data/lib/active_support/reloader.rb +4 -5
  115. data/lib/active_support/security_utils.rb +1 -1
  116. data/lib/active_support/subscriber.rb +65 -26
  117. data/lib/active_support/tagged_logging.rb +13 -4
  118. data/lib/active_support/test_case.rb +91 -0
  119. data/lib/active_support/testing/assertions.rb +15 -1
  120. data/lib/active_support/testing/deprecation.rb +0 -1
  121. data/lib/active_support/testing/file_fixtures.rb +2 -0
  122. data/lib/active_support/testing/isolation.rb +2 -2
  123. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  124. data/lib/active_support/testing/parallelization.rb +128 -0
  125. data/lib/active_support/testing/stream.rb +1 -1
  126. data/lib/active_support/testing/time_helpers.rb +7 -7
  127. data/lib/active_support/time_with_zone.rb +15 -5
  128. data/lib/active_support/values/time_zone.rb +12 -7
  129. data/lib/active_support/xml_mini.rb +2 -9
  130. data/lib/active_support/xml_mini/jdom.rb +2 -2
  131. data/lib/active_support/xml_mini/libxml.rb +2 -2
  132. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  133. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  134. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  135. data/lib/active_support/xml_mini/rexml.rb +2 -2
  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
@@ -12,6 +12,11 @@ module ActiveSupport
12
12
  class NullStore < Store
13
13
  prepend Strategy::LocalCache
14
14
 
15
+ # Advertise cache versioning support.
16
+ def self.supports_cache_versioning?
17
+ true
18
+ end
19
+
15
20
  def clear(options = nil)
16
21
  end
17
22
 
@@ -17,7 +17,6 @@ end
17
17
 
18
18
  require "digest/sha2"
19
19
  require "active_support/core_ext/marshal"
20
- require "active_support/core_ext/hash/transform_values"
21
20
 
22
21
  module ActiveSupport
23
22
  module Cache
@@ -67,6 +66,11 @@ module ActiveSupport
67
66
  SCAN_BATCH_SIZE = 1000
68
67
  private_constant :SCAN_BATCH_SIZE
69
68
 
69
+ # Advertise cache versioning support.
70
+ def self.supports_cache_versioning?
71
+ true
72
+ end
73
+
70
74
  # Support raw values in the local cache strategy.
71
75
  module LocalCacheWithRaw # :nodoc:
72
76
  private
@@ -148,15 +152,17 @@ module ActiveSupport
148
152
 
149
153
  # Creates a new Redis cache store.
150
154
  #
151
- # Handles three options: block provided to instantiate, single URL
152
- # provided, and multiple URLs provided.
155
+ # Handles four options: :redis block, :redis instance, single :url
156
+ # string, and multiple :url strings.
153
157
  #
154
- # :redis Proc -> options[:redis].call
155
- # :url String -> Redis.new(url: …)
156
- # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
158
+ # Option Class Result
159
+ # :redis Proc -> options[:redis].call
160
+ # :redis Object -> options[:redis]
161
+ # :url String -> Redis.new(url: …)
162
+ # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
157
163
  #
158
164
  # No namespace is set by default. Provide one if the Redis cache
159
- # server is shared with other apps: <tt>namespace: 'myapp-cache'<tt>.
165
+ # server is shared with other apps: <tt>namespace: 'myapp-cache'</tt>.
160
166
  #
161
167
  # Compression is enabled by default with a 1kB threshold, so cached
162
168
  # values larger than 1kB are automatically compressed. Disable by
@@ -259,7 +265,14 @@ module ActiveSupport
259
265
  def increment(name, amount = 1, options = nil)
260
266
  instrument :increment, name, amount: amount do
261
267
  failsafe :increment do
262
- redis.with { |c| c.incrby normalize_key(name, options), amount }
268
+ options = merged_options(options)
269
+ key = normalize_key(name, options)
270
+
271
+ redis.with do |c|
272
+ c.incrby(key, amount).tap do
273
+ write_key_expiry(c, key, options)
274
+ end
275
+ end
263
276
  end
264
277
  end
265
278
  end
@@ -275,7 +288,14 @@ module ActiveSupport
275
288
  def decrement(name, amount = 1, options = nil)
276
289
  instrument :decrement, name, amount: amount do
277
290
  failsafe :decrement do
278
- redis.with { |c| c.decrby normalize_key(name, options), amount }
291
+ options = merged_options(options)
292
+ key = normalize_key(name, options)
293
+
294
+ redis.with do |c|
295
+ c.decrby(key, amount).tap do
296
+ write_key_expiry(c, key, options)
297
+ end
298
+ end
279
299
  end
280
300
  end
281
301
  end
@@ -343,6 +363,7 @@ module ActiveSupport
343
363
  def read_multi_mget(*names)
344
364
  options = names.extract_options!
345
365
  options = merged_options(options)
366
+ return {} if names == []
346
367
 
347
368
  keys = names.map { |name| normalize_key(name, options) }
348
369
 
@@ -386,6 +407,12 @@ module ActiveSupport
386
407
  end
387
408
  end
388
409
 
410
+ def write_key_expiry(client, key, options)
411
+ if options[:expires_in] && client.ttl(key).negative?
412
+ client.expire key, options[:expires_in].to_i
413
+ end
414
+ end
415
+
389
416
  # Delete an entry from the cache.
390
417
  def delete_entry(key, options)
391
418
  failsafe :delete_entry, returning: false do
@@ -23,6 +23,9 @@ module ActiveSupport
23
23
  # +ClassMethods.set_callback+), and run the installed callbacks at the
24
24
  # appropriate times (via +run_callbacks+).
25
25
  #
26
+ # By default callbacks are halted by throwing +:abort+.
27
+ # See +ClassMethods.define_callbacks+ for details.
28
+ #
26
29
  # Three kinds of callbacks are supported: before callbacks, run before a
27
30
  # certain event; after callbacks, run after the event; and around callbacks,
28
31
  # blocks that surround the event, triggering it when they yield. Callback code
@@ -497,9 +500,7 @@ module ActiveSupport
497
500
  arg.halted || !@user_conditions.all? { |c| c.call(arg.target, arg.value) }
498
501
  end
499
502
 
500
- def nested
501
- @nested
502
- end
503
+ attr_reader :nested
503
504
 
504
505
  def final?
505
506
  !@call_template
@@ -578,7 +579,7 @@ module ActiveSupport
578
579
  end
579
580
 
580
581
  protected
581
- def chain; @chain; end
582
+ attr_reader :chain
582
583
 
583
584
  private
584
585
 
@@ -659,9 +660,17 @@ module ActiveSupport
659
660
  # * <tt>:if</tt> - A symbol or an array of symbols, each naming an instance
660
661
  # method or a proc; the callback will be called only when they all return
661
662
  # a true value.
663
+ #
664
+ # If a proc is given, its body is evaluated in the context of the
665
+ # current object. It can also optionally accept the current object as
666
+ # an argument.
662
667
  # * <tt>:unless</tt> - A symbol or an array of symbols, each naming an
663
668
  # instance method or a proc; the callback will be called only when they
664
669
  # all return a false value.
670
+ #
671
+ # If a proc is given, its body is evaluated in the context of the
672
+ # current object. It can also optionally accept the current object as
673
+ # an argument.
665
674
  # * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
666
675
  # existing chain rather than appended.
667
676
  def set_callback(name, *filter_list, &block)
@@ -809,7 +818,9 @@ module ActiveSupport
809
818
  names.each do |name|
810
819
  name = name.to_sym
811
820
 
812
- set_callbacks name, CallbackChain.new(name, options)
821
+ ([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target|
822
+ target.set_callbacks name, CallbackChain.new(name, options)
823
+ end
813
824
 
814
825
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
815
826
  def _run_#{name}_callbacks(&block)
@@ -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
@@ -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"
@@ -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."
@@ -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)