activesupport 7.1.5.2 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +64 -1234
  3. data/lib/active_support/array_inquirer.rb +1 -1
  4. data/lib/active_support/backtrace_cleaner.rb +10 -3
  5. data/lib/active_support/benchmark.rb +21 -0
  6. data/lib/active_support/benchmarkable.rb +3 -2
  7. data/lib/active_support/broadcast_logger.rb +4 -4
  8. data/lib/active_support/cache/file_store.rb +27 -12
  9. data/lib/active_support/cache/mem_cache_store.rb +16 -74
  10. data/lib/active_support/cache/memory_store.rb +8 -3
  11. data/lib/active_support/cache/redis_cache_store.rb +21 -15
  12. data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
  13. data/lib/active_support/cache.rb +74 -77
  14. data/lib/active_support/callbacks.rb +75 -115
  15. data/lib/active_support/class_attribute.rb +26 -0
  16. data/lib/active_support/code_generator.rb +9 -0
  17. data/lib/active_support/concurrency/share_lock.rb +0 -1
  18. data/lib/active_support/configuration_file.rb +15 -6
  19. data/lib/active_support/core_ext/array/conversions.rb +0 -2
  20. data/lib/active_support/core_ext/benchmark.rb +6 -9
  21. data/lib/active_support/core_ext/class/attribute.rb +10 -19
  22. data/lib/active_support/core_ext/class/subclasses.rb +15 -35
  23. data/lib/active_support/core_ext/date/blank.rb +4 -0
  24. data/lib/active_support/core_ext/date/conversions.rb +2 -2
  25. data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
  26. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  27. data/lib/active_support/core_ext/date_time/conversions.rb +0 -4
  28. data/lib/active_support/core_ext/digest/uuid.rb +6 -0
  29. data/lib/active_support/core_ext/enumerable.rb +8 -3
  30. data/lib/active_support/core_ext/erb/util.rb +5 -0
  31. data/lib/active_support/core_ext/hash/except.rb +0 -12
  32. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  33. data/lib/active_support/core_ext/module/attr_internal.rb +16 -6
  34. data/lib/active_support/core_ext/module/delegation.rb +20 -163
  35. data/lib/active_support/core_ext/module/deprecation.rb +1 -4
  36. data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
  37. data/lib/active_support/core_ext/object/blank.rb +45 -1
  38. data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
  39. data/lib/active_support/core_ext/object/json.rb +16 -10
  40. data/lib/active_support/core_ext/object/with.rb +5 -3
  41. data/lib/active_support/core_ext/pathname/blank.rb +4 -0
  42. data/lib/active_support/core_ext/range/overlap.rb +1 -1
  43. data/lib/active_support/core_ext/securerandom.rb +8 -24
  44. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  45. data/lib/active_support/core_ext/string/filters.rb +1 -1
  46. data/lib/active_support/core_ext/string/multibyte.rb +1 -1
  47. data/lib/active_support/core_ext/string/output_safety.rb +0 -7
  48. data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
  49. data/lib/active_support/core_ext/time/calculations.rb +32 -30
  50. data/lib/active_support/core_ext/time/compatibility.rb +24 -0
  51. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  52. data/lib/active_support/core_ext/time/zones.rb +1 -1
  53. data/lib/active_support/core_ext.rb +0 -1
  54. data/lib/active_support/current_attributes.rb +38 -40
  55. data/lib/active_support/delegation.rb +200 -0
  56. data/lib/active_support/dependencies/autoload.rb +0 -12
  57. data/lib/active_support/dependencies.rb +0 -1
  58. data/lib/active_support/deprecation/constant_accessor.rb +47 -26
  59. data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
  60. data/lib/active_support/deprecation/reporting.rb +1 -15
  61. data/lib/active_support/deprecation.rb +8 -5
  62. data/lib/active_support/descendants_tracker.rb +9 -87
  63. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  64. data/lib/active_support/duration/iso8601_serializer.rb +1 -2
  65. data/lib/active_support/duration.rb +25 -16
  66. data/lib/active_support/encrypted_configuration.rb +20 -2
  67. data/lib/active_support/encrypted_file.rb +1 -1
  68. data/lib/active_support/error_reporter.rb +65 -3
  69. data/lib/active_support/evented_file_update_checker.rb +0 -2
  70. data/lib/active_support/execution_wrapper.rb +0 -1
  71. data/lib/active_support/file_update_checker.rb +1 -1
  72. data/lib/active_support/fork_tracker.rb +2 -38
  73. data/lib/active_support/gem_version.rb +4 -4
  74. data/lib/active_support/hash_with_indifferent_access.rb +21 -23
  75. data/lib/active_support/html_safe_translation.rb +3 -0
  76. data/lib/active_support/i18n_railtie.rb +19 -11
  77. data/lib/active_support/isolated_execution_state.rb +0 -2
  78. data/lib/active_support/json/encoding.rb +2 -2
  79. data/lib/active_support/log_subscriber.rb +0 -12
  80. data/lib/active_support/logger.rb +15 -2
  81. data/lib/active_support/logger_thread_safe_level.rb +0 -8
  82. data/lib/active_support/message_pack/extensions.rb +15 -2
  83. data/lib/active_support/message_verifier.rb +12 -0
  84. data/lib/active_support/multibyte/chars.rb +2 -2
  85. data/lib/active_support/notifications/fanout.rb +4 -8
  86. data/lib/active_support/notifications/instrumenter.rb +21 -18
  87. data/lib/active_support/notifications.rb +28 -27
  88. data/lib/active_support/number_helper/number_converter.rb +2 -2
  89. data/lib/active_support/number_helper.rb +22 -0
  90. data/lib/active_support/option_merger.rb +2 -2
  91. data/lib/active_support/ordered_options.rb +53 -15
  92. data/lib/active_support/railtie.rb +8 -11
  93. data/lib/active_support/string_inquirer.rb +1 -1
  94. data/lib/active_support/subscriber.rb +1 -0
  95. data/lib/active_support/tagged_logging.rb +5 -1
  96. data/lib/active_support/test_case.rb +3 -1
  97. data/lib/active_support/testing/assertions.rb +79 -21
  98. data/lib/active_support/testing/constant_stubbing.rb +30 -8
  99. data/lib/active_support/testing/deprecation.rb +5 -12
  100. data/lib/active_support/testing/isolation.rb +19 -9
  101. data/lib/active_support/testing/method_call_assertions.rb +2 -16
  102. data/lib/active_support/testing/parallelization/server.rb +3 -0
  103. data/lib/active_support/testing/strict_warnings.rb +8 -4
  104. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  105. data/lib/active_support/testing/time_helpers.rb +4 -3
  106. data/lib/active_support/time_with_zone.rb +29 -16
  107. data/lib/active_support/values/time_zone.rb +18 -16
  108. data/lib/active_support/xml_mini.rb +11 -2
  109. data/lib/active_support.rb +11 -3
  110. metadata +32 -22
  111. data/lib/active_support/deprecation/instance_delegator.rb +0 -65
  112. data/lib/active_support/proxy_object.rb +0 -17
  113. data/lib/active_support/ruby_features.rb +0 -7
@@ -19,11 +19,20 @@ module ActiveSupport
19
19
  end
20
20
 
21
21
  def parse(context: nil, **options)
22
- source = render(context)
23
- if YAML.respond_to?(:unsafe_load)
24
- YAML.unsafe_load(source, **options) || {}
22
+ source = @content.include?("<%") ? render(context) : @content
23
+
24
+ if source == @content
25
+ if YAML.respond_to?(:unsafe_load)
26
+ YAML.unsafe_load_file(@content_path, **options) || {}
27
+ else
28
+ YAML.load_file(@content_path, **options) || {}
29
+ end
25
30
  else
26
- YAML.load(source, **options) || {}
31
+ if YAML.respond_to?(:unsafe_load)
32
+ YAML.unsafe_load(source, **options) || {}
33
+ else
34
+ YAML.load(source, **options) || {}
35
+ end
27
36
  end
28
37
  rescue Psych::SyntaxError => error
29
38
  raise "YAML syntax error occurred while parsing #{@content_path}. " \
@@ -33,8 +42,7 @@ module ActiveSupport
33
42
 
34
43
  private
35
44
  def read(content_path)
36
- require "yaml"
37
- require "erb"
45
+ require "yaml" unless defined?(YAML)
38
46
 
39
47
  File.read(content_path).tap do |content|
40
48
  if content.include?("\u00A0")
@@ -44,6 +52,7 @@ module ActiveSupport
44
52
  end
45
53
 
46
54
  def render(context)
55
+ require "erb" unless defined?(ERB)
47
56
  erb = ERB.new(@content).tap { |e| e.filename = @content_path }
48
57
  context ? erb.result(context) : erb.result
49
58
  end
@@ -104,8 +104,6 @@ class Array
104
104
  end
105
105
  end
106
106
  alias_method :to_formatted_s, :to_fs
107
- alias_method :to_default_s, :to_s
108
- deprecate to_default_s: :to_s, deprecator: ActiveSupport.deprecator
109
107
 
110
108
  # Returns a string that represents the array in XML by invoking +to_xml+
111
109
  # on each element. Active Record collections delegate their representation
@@ -3,14 +3,11 @@
3
3
  require "benchmark"
4
4
 
5
5
  class << Benchmark
6
- # Benchmark realtime in milliseconds.
7
- #
8
- # Benchmark.realtime { User.all }
9
- # # => 8.0e-05
10
- #
11
- # Benchmark.ms { User.all }
12
- # # => 0.074
13
- def ms(&block)
14
- 1000 * realtime(&block)
6
+ def ms(&block) # :nodoc
7
+ # NOTE: Please also remove the Active Support `benchmark` dependency when removing this
8
+ ActiveSupport.deprecator.warn <<~TEXT
9
+ `Benchmark.ms` is deprecated and will be removed in Rails 8.1 without replacement.
10
+ TEXT
11
+ ActiveSupport::Benchmark.realtime(:float_millisecond, &block)
15
12
  end
16
13
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/module/redefine_method"
4
+ require "active_support/class_attribute"
4
5
 
5
6
  class Class
6
7
  # Declare a class-level attribute whose value is inheritable by subclasses.
@@ -91,24 +92,16 @@ class Class
91
92
  raise TypeError, "#{name.inspect} is not a symbol nor a string"
92
93
  end
93
94
 
94
- class_methods << <<~RUBY # In case the method exists and is not public
95
- silence_redefinition_of_method def #{name}
96
- end
97
- RUBY
98
-
99
- methods << <<~RUBY if instance_reader
100
- silence_redefinition_of_method def #{name}
101
- defined?(@#{name}) ? @#{name} : self.class.#{name}
102
- end
103
- RUBY
95
+ name = name.to_sym
96
+ ::ActiveSupport::ClassAttribute.redefine(self, name, default)
104
97
 
105
- class_methods << <<~RUBY
106
- silence_redefinition_of_method def #{name}=(value)
107
- redefine_method(:#{name}) { value } if singleton_class?
108
- redefine_singleton_method(:#{name}) { value }
109
- value
110
- end
111
- RUBY
98
+ unless singleton_class?
99
+ methods << <<~RUBY if instance_reader
100
+ silence_redefinition_of_method def #{name}
101
+ defined?(@#{name}) ? @#{name} : self.class.#{name}
102
+ end
103
+ RUBY
104
+ end
112
105
 
113
106
  methods << <<~RUBY if instance_writer
114
107
  silence_redefinition_of_method(:#{name}=)
@@ -125,7 +118,5 @@ class Class
125
118
 
126
119
  location = caller_locations(1, 1).first
127
120
  class_eval(["class << self", *class_methods, "end", *methods].join(";").tr("\n", ";"), location.path, location.lineno)
128
-
129
- attrs.each { |name| public_send("#{name}=", default) }
130
121
  end
131
122
  end
@@ -1,43 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/ruby_features"
4
3
  require "active_support/descendants_tracker"
5
4
 
6
5
  class Class
7
- if ActiveSupport::RubyFeatures::CLASS_SUBCLASSES
8
- # Returns an array with all classes that are < than its receiver.
9
- #
10
- # class C; end
11
- # C.descendants # => []
12
- #
13
- # class B < C; end
14
- # C.descendants # => [B]
15
- #
16
- # class A < B; end
17
- # C.descendants # => [B, A]
18
- #
19
- # class D < C; end
20
- # C.descendants # => [B, A, D]
21
- def descendants
22
- subclasses.concat(subclasses.flat_map(&:descendants))
23
- end
24
- else
25
- def descendants
26
- ObjectSpace.each_object(singleton_class).reject do |k|
27
- k.singleton_class? || k == self
28
- end
29
- end
30
-
31
- # Returns an array with the direct children of +self+.
32
- #
33
- # class Foo; end
34
- # class Bar < Foo; end
35
- # class Baz < Bar; end
36
- #
37
- # Foo.subclasses # => [Bar]
38
- def subclasses
39
- descendants.select { |descendant| descendant.superclass == self }
40
- end
6
+ # Returns an array with all classes that are < than its receiver.
7
+ #
8
+ # class C; end
9
+ # C.descendants # => []
10
+ #
11
+ # class B < C; end
12
+ # C.descendants # => [B]
13
+ #
14
+ # class A < B; end
15
+ # C.descendants # => [B, A]
16
+ #
17
+ # class D < C; end
18
+ # C.descendants # => [B, A, D]
19
+ def descendants
20
+ subclasses.concat(subclasses.flat_map(&:descendants))
41
21
  end
42
22
 
43
23
  prepend ActiveSupport::DescendantsTracker::ReloadedClassesFiltering
@@ -11,4 +11,8 @@ class Date # :nodoc:
11
11
  def blank?
12
12
  false
13
13
  end
14
+
15
+ def present?
16
+ true
17
+ end
14
18
  end
@@ -17,6 +17,7 @@ class Date
17
17
  date.strftime("%B #{day_format}, %Y") # => "April 25th, 2007"
18
18
  },
19
19
  rfc822: "%d %b %Y",
20
+ rfc2822: "%d %b %Y",
20
21
  iso8601: lambda { |date| date.iso8601 }
21
22
  }
22
23
 
@@ -34,6 +35,7 @@ class Date
34
35
  # date.to_fs(:long) # => "November 10, 2007"
35
36
  # date.to_fs(:long_ordinal) # => "November 10th, 2007"
36
37
  # date.to_fs(:rfc822) # => "10 Nov 2007"
38
+ # date.to_fs(:rfc2822) # => "10 Nov 2007"
37
39
  # date.to_fs(:iso8601) # => "2007-11-10"
38
40
  #
39
41
  # == Adding your own date formats to to_fs
@@ -56,8 +58,6 @@ class Date
56
58
  end
57
59
  end
58
60
  alias_method :to_formatted_s, :to_fs
59
- alias_method :to_default_s, :to_s
60
- deprecate to_default_s: :to_s, deprecator: ActiveSupport.deprecator
61
61
 
62
62
  # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
63
63
  def readable_inspect
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/module/attribute_accessors"
4
+ require "active_support/core_ext/module/redefine_method"
4
5
 
5
6
  module DateAndTime
6
7
  module Compatibility
@@ -11,7 +12,33 @@ module DateAndTime
11
12
  # of the receiver. For backwards compatibility we're overriding
12
13
  # this behavior, but new apps will have an initializer that sets
13
14
  # this to true, because the new behavior is preferred.
14
- mattr_accessor :preserve_timezone, instance_writer: false, default: false
15
+ mattr_accessor :preserve_timezone, instance_accessor: false, default: nil
16
+
17
+ singleton_class.silence_redefinition_of_method :preserve_timezone
18
+
19
+ #--
20
+ # This re-implements the behaviour of the mattr_reader, instead
21
+ # of prepending on to it, to avoid overcomplicating a module that
22
+ # is in turn included in several places. This will all go away in
23
+ # Rails 8.0 anyway.
24
+ def self.preserve_timezone # :nodoc:
25
+ if @@preserve_timezone.nil?
26
+ # Only warn once, the first time the value is used (which should
27
+ # be the first time #to_time is called).
28
+ ActiveSupport.deprecator.warn(
29
+ "`to_time` will always preserve the receiver timezone rather than system local time in Rails 8.1." \
30
+ "To opt in to the new behavior, set `config.active_support.to_time_preserves_timezone = :zone`."
31
+ )
32
+
33
+ @@preserve_timezone = false
34
+ end
35
+
36
+ @@preserve_timezone
37
+ end
38
+
39
+ def preserve_timezone # :nodoc:
40
+ Compatibility.preserve_timezone
41
+ end
15
42
 
16
43
  # Change the output of <tt>ActiveSupport::TimeZone.utc_to_local</tt>.
17
44
  #
@@ -11,4 +11,8 @@ class DateTime # :nodoc:
11
11
  def blank?
12
12
  false
13
13
  end
14
+
15
+ def present?
16
+ true
17
+ end
14
18
  end
@@ -40,10 +40,6 @@ class DateTime
40
40
  end
41
41
  end
42
42
  alias_method :to_formatted_s, :to_fs
43
- if instance_methods(false).include?(:to_s)
44
- alias_method :to_default_s, :to_s
45
- deprecate to_default_s: :to_s, deprecator: ActiveSupport.deprecator
46
- end
47
43
 
48
44
 
49
45
  # Returns a formatted string of the offset from UTC, or an alternative
@@ -53,6 +53,12 @@ module Digest
53
53
  SecureRandom.uuid
54
54
  end
55
55
 
56
+ # Returns the nil UUID. This is a special form of UUID that is specified to
57
+ # have all 128 bits set to zero.
58
+ def self.nil_uuid
59
+ "00000000-0000-0000-0000-000000000000"
60
+ end
61
+
56
62
  def self.pack_uuid_namespace(namespace)
57
63
  if [DNS_NAMESPACE, OID_NAMESPACE, URL_NAMESPACE, X500_NAMESPACE].include?(namespace)
58
64
  namespace
@@ -192,9 +192,14 @@ module Enumerable
192
192
  # # => [ Person.find(1), Person.find(5), Person.find(3) ]
193
193
  #
194
194
  # If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored.
195
- # If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result.
196
- def in_order_of(key, series)
197
- group_by(&key).values_at(*series).flatten(1).compact
195
+ # If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result, unless
196
+ # the +filter+ option is set to +false+.
197
+ def in_order_of(key, series, filter: true)
198
+ if filter
199
+ group_by(&key).values_at(*series).flatten(1).compact
200
+ else
201
+ sort_by { |v| series.index(v.public_send(key)) || series.size }.compact
202
+ end
198
203
  end
199
204
 
200
205
  # Returns the sole item in the enumerable. If there are no items, or more
@@ -188,6 +188,11 @@ class ERB
188
188
  else
189
189
  raise NotImplementedError, source.matched
190
190
  end
191
+
192
+ unless source.eos? || source.exist?(start_re) || source.exist?(finish_re)
193
+ tokens << [:TEXT, source.rest]
194
+ source.terminate
195
+ end
191
196
  end
192
197
 
193
198
  tokens
@@ -1,18 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Hash
4
- # Returns a hash that includes everything except given keys.
5
- # hash = { a: true, b: false, c: nil }
6
- # hash.except(:c) # => { a: true, b: false }
7
- # hash.except(:a, :b) # => { c: nil }
8
- # hash # => { a: true, b: false, c: nil }
9
- #
10
- # This is useful for limiting a set of parameters to everything but a few known toggles:
11
- # @person.update(params[:person].except(:admin))
12
- def except(*keys)
13
- slice(*self.keys - keys)
14
- end unless method_defined?(:except)
15
-
16
4
  # Removes the given keys from hash and returns it.
17
5
  # hash = { a: true, b: false, c: nil }
18
6
  # hash.except!(:c) # => { a: true, b: false }
@@ -8,13 +8,13 @@ class Hash
8
8
  # hash.stringify_keys
9
9
  # # => {"name"=>"Rob", "age"=>"28"}
10
10
  def stringify_keys
11
- transform_keys(&:to_s)
11
+ transform_keys { |k| Symbol === k ? k.name : k.to_s }
12
12
  end
13
13
 
14
14
  # Destructively converts all keys to strings. Same as
15
15
  # +stringify_keys+, but modifies +self+.
16
16
  def stringify_keys!
17
- transform_keys!(&:to_s)
17
+ transform_keys! { |k| Symbol === k ? k.name : k.to_s }
18
18
  end
19
19
 
20
20
  # Returns a new hash with all keys converted to symbols, as long as
@@ -82,14 +82,14 @@ class Hash
82
82
  # hash.deep_stringify_keys
83
83
  # # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
84
84
  def deep_stringify_keys
85
- deep_transform_keys(&:to_s)
85
+ deep_transform_keys { |k| Symbol === k ? k.name : k.to_s }
86
86
  end
87
87
 
88
88
  # Destructively converts all keys to strings.
89
89
  # This includes the keys from the root hash and from all
90
90
  # nested hashes and arrays.
91
91
  def deep_stringify_keys!
92
- deep_transform_keys!(&:to_s)
92
+ deep_transform_keys! { |k| Symbol === k ? k.name : k.to_s }
93
93
  end
94
94
 
95
95
  # Returns a new hash with all keys converted to symbols, as long as
@@ -19,16 +19,26 @@ class Module
19
19
  end
20
20
  alias_method :attr_internal, :attr_internal_accessor
21
21
 
22
- class << self; attr_accessor :attr_internal_naming_format end
23
- self.attr_internal_naming_format = "@_%s"
22
+ class << self
23
+ attr_reader :attr_internal_naming_format
24
24
 
25
- private
26
- def attr_internal_ivar_name(attr)
27
- Module.attr_internal_naming_format % attr
25
+ def attr_internal_naming_format=(format)
26
+ if format.start_with?("@")
27
+ raise ArgumentError, <<~MESSAGE.squish
28
+ Setting `attr_internal_naming_format` with a `@` prefix is not supported.
29
+
30
+ You can simply replace #{format.inspect} by #{format.delete_prefix("@").inspect}.
31
+ MESSAGE
32
+ end
33
+
34
+ @attr_internal_naming_format = format
28
35
  end
36
+ end
37
+ self.attr_internal_naming_format = "_%s"
29
38
 
39
+ private
30
40
  def attr_internal_define(attr_name, type)
31
- internal_name = attr_internal_ivar_name(attr_name).delete_prefix("@")
41
+ internal_name = Module.attr_internal_naming_format % attr_name
32
42
  # use native attr_* methods as they are faster on some Ruby implementations
33
43
  public_send("attr_#{type}", internal_name)
34
44
  attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
@@ -1,19 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
-
5
3
  class Module
6
- # Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
7
- # option is not used.
8
- class DelegationError < NoMethodError; end
9
-
10
- RUBY_RESERVED_KEYWORDS = %w(__ENCODING__ __LINE__ __FILE__ alias and BEGIN begin break
11
- case class def defined? do else elsif END end ensure false for if in module next nil
12
- not or redo rescue retry return self super then true undef unless until when while yield)
13
- DELEGATION_RESERVED_KEYWORDS = %w(_ arg args block)
14
- DELEGATION_RESERVED_METHOD_NAMES = Set.new(
15
- RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS
16
- ).freeze
4
+ require "active_support/delegation"
5
+ DelegationError = ActiveSupport::DelegationError # :nodoc:
17
6
 
18
7
  # Provides a +delegate+ class method to easily expose contained objects'
19
8
  # public methods as your own.
@@ -21,7 +10,7 @@ class Module
21
10
  # ==== Options
22
11
  # * <tt>:to</tt> - Specifies the target object name as a symbol or string
23
12
  # * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
24
- # * <tt>:allow_nil</tt> - If set to true, prevents a +Module::DelegationError+
13
+ # * <tt>:allow_nil</tt> - If set to true, prevents a +ActiveSupport::DelegationError+
25
14
  # from being raised
26
15
  # * <tt>:private</tt> - If set to true, changes method visibility to private
27
16
  #
@@ -132,7 +121,7 @@ class Module
132
121
  # User.new.age # => 2
133
122
  #
134
123
  # If the target is +nil+ and does not respond to the delegated method a
135
- # +Module::DelegationError+ is raised. If you wish to instead return +nil+,
124
+ # +ActiveSupport::DelegationError+ is raised. If you wish to instead return +nil+,
136
125
  # use the <tt>:allow_nil</tt> option.
137
126
  #
138
127
  # class User < ActiveRecord::Base
@@ -141,7 +130,7 @@ class Module
141
130
  # end
142
131
  #
143
132
  # User.new.age
144
- # # => Module::DelegationError: User#age delegated to profile.age, but profile is nil
133
+ # # => ActiveSupport::DelegationError: User#age delegated to profile.age, but profile is nil
145
134
  #
146
135
  # But if not having a profile yet is fine and should not be an error
147
136
  # condition:
@@ -169,104 +158,15 @@ class Module
169
158
  #
170
159
  # The target method must be public, otherwise it will raise +NoMethodError+.
171
160
  def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)
172
- unless to
173
- raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)."
174
- end
175
-
176
- if prefix == true && /^[^a-z_]/.match?(to)
177
- raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
178
- end
179
-
180
- method_prefix = \
181
- if prefix
182
- "#{prefix == true ? to : prefix}_"
183
- else
184
- ""
185
- end
186
-
187
- location = caller_locations(1, 1).first
188
- file, line = location.path, location.lineno
189
-
190
- receiver = to.to_s
191
- receiver = "self.#{receiver}" if DELEGATION_RESERVED_METHOD_NAMES.include?(receiver)
192
-
193
- method_def = []
194
- method_names = []
195
-
196
- method_def << "self.private" if private
197
-
198
- methods.each do |method|
199
- method_name = prefix ? "#{method_prefix}#{method}" : method
200
- method_names << method_name.to_sym
201
-
202
- # Attribute writer methods only accept one argument. Makes sure []=
203
- # methods still accept two arguments.
204
- definition = \
205
- if /[^\]]=\z/.match?(method)
206
- "arg"
207
- else
208
- method_object =
209
- begin
210
- if to.is_a?(Module)
211
- to.method(method)
212
- elsif receiver == "self.class"
213
- method(method)
214
- end
215
- rescue NameError
216
- # Do nothing. Fall back to `"..."`
217
- end
218
-
219
- if method_object
220
- parameters = method_object.parameters
221
-
222
- if (parameters.map(&:first) & [:opt, :rest, :keyreq, :key, :keyrest]).any?
223
- "..."
224
- else
225
- defn = parameters.filter_map { |type, arg| arg if type == :req }
226
- defn << "&block"
227
- defn.join(", ")
228
- end
229
- else
230
- "..."
231
- end
232
- end
233
-
234
- # The following generated method calls the target exactly once, storing
235
- # the returned value in a dummy variable.
236
- #
237
- # Reason is twofold: On one hand doing less calls is in general better.
238
- # On the other hand it could be that the target has side-effects,
239
- # whereas conceptually, from the user point of view, the delegator should
240
- # be doing one call.
241
- if allow_nil
242
- method = method.to_s
243
-
244
- method_def <<
245
- "def #{method_name}(#{definition})" <<
246
- " _ = #{receiver}" <<
247
- " if !_.nil? || nil.respond_to?(:#{method})" <<
248
- " _.#{method}(#{definition})" <<
249
- " end" <<
250
- "end"
251
- else
252
- method = method.to_s
253
- method_name = method_name.to_s
254
-
255
- method_def <<
256
- "def #{method_name}(#{definition})" <<
257
- " _ = #{receiver}" <<
258
- " _.#{method}(#{definition})" <<
259
- "rescue NoMethodError => e" <<
260
- " if _.nil? && e.name == :#{method}" <<
261
- %( raise DelegationError, "#{self}##{method_name} delegated to #{receiver}.#{method}, but #{receiver} is nil: \#{self.inspect}") <<
262
- " else" <<
263
- " raise" <<
264
- " end" <<
265
- "end"
266
- end
267
- end
268
- module_eval(method_def.join(";"), file, line)
269
- method_names
161
+ ::ActiveSupport::Delegation.generate(
162
+ self,
163
+ methods,
164
+ location: caller_locations(1, 1).first,
165
+ to: to,
166
+ prefix: prefix,
167
+ allow_nil: allow_nil,
168
+ private: private,
169
+ )
270
170
  end
271
171
 
272
172
  # When building decorators, a common pattern may emerge:
@@ -308,7 +208,7 @@ class Module
308
208
  # variables, methods, constants, etc.
309
209
  #
310
210
  # The delegated method must be public on the target, otherwise it will
311
- # raise +DelegationError+. If you wish to instead return +nil+,
211
+ # raise +ActiveSupport::DelegationError+. If you wish to instead return +nil+,
312
212
  # use the <tt>:allow_nil</tt> option.
313
213
  #
314
214
  # The <tt>marshal_dump</tt> and <tt>_dump</tt> methods are exempt from
@@ -316,53 +216,10 @@ class Module
316
216
  # <tt>Marshal.dump(object)</tt>, should the delegation target method
317
217
  # of <tt>object</tt> add or remove instance variables.
318
218
  def delegate_missing_to(target, allow_nil: nil)
319
- target = target.to_s
320
- target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target) || target == "__target"
321
-
322
- if allow_nil
323
- module_eval <<~RUBY, __FILE__, __LINE__ + 1
324
- def respond_to_missing?(name, include_private = false)
325
- # It may look like an oversight, but we deliberately do not pass
326
- # +include_private+, because they do not get delegated.
327
-
328
- return false if name == :marshal_dump || name == :_dump
329
- #{target}.respond_to?(name) || super
330
- end
331
-
332
- def method_missing(method, *args, &block)
333
- __target = #{target}
334
- if __target.nil? && !nil.respond_to?(method)
335
- nil
336
- elsif __target.respond_to?(method)
337
- __target.public_send(method, *args, &block)
338
- else
339
- super
340
- end
341
- end
342
- ruby2_keywords(:method_missing)
343
- RUBY
344
- else
345
- module_eval <<~RUBY, __FILE__, __LINE__ + 1
346
- def respond_to_missing?(name, include_private = false)
347
- # It may look like an oversight, but we deliberately do not pass
348
- # +include_private+, because they do not get delegated.
349
-
350
- return false if name == :marshal_dump || name == :_dump
351
- #{target}.respond_to?(name) || super
352
- end
353
-
354
- def method_missing(method, *args, &block)
355
- __target = #{target}
356
- if __target.nil? && !nil.respond_to?(method)
357
- raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
358
- elsif __target.respond_to?(method)
359
- __target.public_send(method, *args, &block)
360
- else
361
- super
362
- end
363
- end
364
- ruby2_keywords(:method_missing)
365
- RUBY
366
- end
219
+ ::ActiveSupport::Delegation.generate_method_missing(
220
+ self,
221
+ target,
222
+ allow_nil: allow_nil,
223
+ )
367
224
  end
368
225
  end
@@ -14,15 +14,12 @@ class Module
14
14
  # Kernel.warn message
15
15
  # end
16
16
  # end
17
- def deprecate(*method_names, deprecator: nil, **options)
17
+ def deprecate(*method_names, deprecator:, **options)
18
18
  if deprecator.is_a?(ActiveSupport::Deprecation)
19
19
  deprecator.deprecate_methods(self, *method_names, **options)
20
20
  elsif deprecator
21
21
  # we just need any instance to call deprecate_methods, but the deprecation will be emitted by deprecator
22
22
  ActiveSupport.deprecator.deprecate_methods(self, *method_names, **options, deprecator: deprecator)
23
- else
24
- ActiveSupport.deprecator.warn("Module.deprecate without a deprecator is deprecated")
25
- ActiveSupport::Deprecation._instance.deprecate_methods(self, *method_names, **options)
26
23
  end
27
24
  end
28
25
  end