activesupport 6.0.0.beta2 → 6.0.2.rc1

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +287 -3
  3. data/README.rdoc +2 -1
  4. data/lib/active_support.rb +1 -0
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/backtrace_cleaner.rb +5 -1
  7. data/lib/active_support/cache.rb +5 -5
  8. data/lib/active_support/cache/memory_store.rb +2 -0
  9. data/lib/active_support/cache/redis_cache_store.rb +9 -6
  10. data/lib/active_support/concern.rb +24 -1
  11. data/lib/active_support/configurable.rb +3 -3
  12. data/lib/active_support/core_ext/array/access.rb +18 -6
  13. data/lib/active_support/core_ext/class/attribute.rb +10 -15
  14. data/lib/active_support/core_ext/date_and_time/calculations.rb +0 -30
  15. data/lib/active_support/core_ext/digest.rb +3 -0
  16. data/lib/active_support/core_ext/enumerable.rb +24 -4
  17. data/lib/active_support/core_ext/hash/deep_transform_values.rb +2 -2
  18. data/lib/active_support/core_ext/hash/except.rb +1 -1
  19. data/lib/active_support/core_ext/module/attribute_accessors.rb +5 -5
  20. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +5 -5
  21. data/lib/active_support/core_ext/module/delegation.rb +6 -0
  22. data/lib/active_support/core_ext/object/duplicable.rb +7 -117
  23. data/lib/active_support/core_ext/range/compare_range.rb +27 -12
  24. data/lib/active_support/core_ext/range/include_time_with_zone.rb +2 -2
  25. data/lib/active_support/core_ext/string/filters.rb +1 -1
  26. data/lib/active_support/core_ext/string/inflections.rb +7 -2
  27. data/lib/active_support/core_ext/string/output_safety.rb +51 -4
  28. data/lib/active_support/core_ext/time/calculations.rb +31 -2
  29. data/lib/active_support/dependencies.rb +41 -5
  30. data/lib/active_support/dependencies/zeitwerk_integration.rb +44 -21
  31. data/lib/active_support/deprecation/method_wrappers.rb +7 -18
  32. data/lib/active_support/deprecation/proxy_wrappers.rb +24 -3
  33. data/lib/active_support/descendants_tracker.rb +52 -6
  34. data/lib/active_support/duration.rb +2 -3
  35. data/lib/active_support/evented_file_update_checker.rb +14 -2
  36. data/lib/active_support/gem_version.rb +2 -2
  37. data/lib/active_support/hash_with_indifferent_access.rb +6 -3
  38. data/lib/active_support/i18n_railtie.rb +6 -1
  39. data/lib/active_support/inflector/transliterate.rb +43 -14
  40. data/lib/active_support/logger_thread_safe_level.rb +2 -1
  41. data/lib/active_support/notifications/fanout.rb +2 -2
  42. data/lib/active_support/notifications/instrumenter.rb +12 -11
  43. data/lib/active_support/ordered_hash.rb +1 -1
  44. data/lib/active_support/ordered_options.rb +1 -1
  45. data/lib/active_support/parameter_filter.rb +6 -1
  46. data/lib/active_support/security_utils.rb +1 -1
  47. data/lib/active_support/subscriber.rb +55 -6
  48. data/lib/active_support/testing/parallelization.rb +21 -2
  49. metadata +13 -14
@@ -125,6 +125,8 @@ module ActiveSupport
125
125
  entry = @data[key]
126
126
  synchronize do
127
127
  if entry
128
+ entry = entry.dup
129
+ entry.dup_value!
128
130
  @key_access[key] = Time.now.to_f
129
131
  else
130
132
  @key_access.delete(key)
@@ -152,12 +152,14 @@ module ActiveSupport
152
152
 
153
153
  # Creates a new Redis cache store.
154
154
  #
155
- # Handles three options: block provided to instantiate, single URL
156
- # provided, and multiple URLs provided.
155
+ # Handles four options: :redis block, :redis instance, single :url
156
+ # string, and multiple :url strings.
157
157
  #
158
- # :redis Proc -> options[:redis].call
159
- # :url String -> Redis.new(url: …)
160
- # :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: … }, …])
161
163
  #
162
164
  # No namespace is set by default. Provide one if the Redis cache
163
165
  # server is shared with other apps: <tt>namespace: 'myapp-cache'</tt>.
@@ -361,6 +363,7 @@ module ActiveSupport
361
363
  def read_multi_mget(*names)
362
364
  options = names.extract_options!
363
365
  options = merged_options(options)
366
+ return {} if names == []
364
367
 
365
368
  keys = names.map { |name| normalize_key(name, options) }
366
369
 
@@ -468,7 +471,7 @@ module ActiveSupport
468
471
 
469
472
  def failsafe(method, returning: nil)
470
473
  yield
471
- rescue ::Redis::BaseConnectionError => e
474
+ rescue ::Redis::BaseError => e
472
475
  handle_exception exception: e, method: method, returning: returning
473
476
  returning
474
477
  end
@@ -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) :
@@ -67,8 +67,8 @@ module ActiveSupport
67
67
  # end
68
68
  # # => NameError: invalid config attribute name
69
69
  #
70
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
71
- # 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>.
72
72
  #
73
73
  # class User
74
74
  # include ActiveSupport::Configurable
@@ -81,7 +81,7 @@ module ActiveSupport
81
81
  # User.new.allowed_access = true # => NoMethodError
82
82
  # User.new.allowed_access # => NoMethodError
83
83
  #
84
- # 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.
85
85
  #
86
86
  # class User
87
87
  # include ActiveSupport::Configurable
@@ -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>.
@@ -84,16 +84,17 @@ 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
@@ -102,9 +103,7 @@ class Class
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
@@ -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
@@ -198,21 +188,11 @@ module DateAndTime
198
188
  end
199
189
  end
200
190
 
201
- # Returns a new date/time the specified number of months in the future.
202
- def next_month(months = 1)
203
- advance(months: months)
204
- end
205
-
206
191
  # Short-hand for months_since(3)
207
192
  def next_quarter
208
193
  months_since(3)
209
194
  end
210
195
 
211
- # Returns a new date/time the specified number of years in the future.
212
- def next_year(years = 1)
213
- advance(years: years)
214
- end
215
-
216
196
  # Returns a new date/time representing the given day in the previous week.
217
197
  # Week is assumed to start on +start_day+, default is
218
198
  # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
@@ -233,11 +213,6 @@ module DateAndTime
233
213
  end
234
214
  alias_method :last_weekday, :prev_weekday
235
215
 
236
- # Returns a new date/time the specified number of months ago.
237
- def prev_month(months = 1)
238
- advance(months: -months)
239
- end
240
-
241
216
  # Short-hand for months_ago(1).
242
217
  def last_month
243
218
  months_ago(1)
@@ -249,11 +224,6 @@ module DateAndTime
249
224
  end
250
225
  alias_method :last_quarter, :prev_quarter
251
226
 
252
- # Returns a new date/time the specified number of years ago.
253
- def prev_year(years = 1)
254
- advance(years: -years)
255
- end
256
-
257
227
  # Short-hand for years_ago(1).
258
228
  def last_year
259
229
  years_ago(1)
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/digest/uuid"
@@ -97,23 +97,43 @@ module Enumerable
97
97
  end
98
98
  end
99
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
+
100
111
  # The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
101
112
  # collection does not include the object.
102
113
  def exclude?(object)
103
114
  !include?(object)
104
115
  end
105
116
 
106
- # 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"]
107
121
  #
108
- # ["David", "Rafael", "Aaron", "Todd"].without "Aaron", "Todd"
122
+ # ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
109
123
  # # => ["David", "Rafael"]
110
124
  #
111
- # {foo: 1, bar: 2, baz: 3}.without :bar
125
+ # {foo: 1, bar: 2, baz: 3}.excluding :bar
112
126
  # # => {foo: 1, baz: 3}
113
- def without(*elements)
127
+ def excluding(*elements)
128
+ elements.flatten!(1)
114
129
  reject { |element| elements.include?(element) }
115
130
  end
116
131
 
132
+ # Alias for #excluding.
133
+ def without(*elements)
134
+ excluding(*elements)
135
+ end
136
+
117
137
  # Convert an enumerable to an array based on the given key.
118
138
  #
119
139
  # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Hash
4
- # Returns a new hash with all keys converted by the block operation.
5
- # This includes the keys from the root hash and from all
4
+ # Returns a new hash with all values converted by the block operation.
5
+ # This includes the values from the root hash and from all
6
6
  # nested hashes and arrays.
7
7
  #
8
8
  # hash = { person: { name: 'Rob', age: '28' } }
@@ -10,7 +10,7 @@ class Hash
10
10
  # This is useful for limiting a set of parameters to everything but a few known toggles:
11
11
  # @person.update(params[:person].except(:admin))
12
12
  def except(*keys)
13
- dup.except!(*keys)
13
+ slice(*self.keys - keys)
14
14
  end
15
15
 
16
16
  # Removes the given keys from hash and returns it.
@@ -24,7 +24,7 @@ class Module
24
24
  # end
25
25
  # # => NameError: invalid attribute name: 1_Badname
26
26
  #
27
- # If you want to opt out the creation on the instance reader method, pass
27
+ # To omit the instance reader method, pass
28
28
  # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
29
29
  #
30
30
  # module HairColors
@@ -91,7 +91,7 @@ class Module
91
91
  # Person.new.hair_colors = [:blonde, :red]
92
92
  # HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
93
93
  #
94
- # If you want to opt out the instance writer method, pass
94
+ # To omit the instance writer method, pass
95
95
  # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
96
96
  #
97
97
  # module HairColors
@@ -166,8 +166,8 @@ class Module
166
166
  # Citizen.new.hair_colors << :blue
167
167
  # Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
168
168
  #
169
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
170
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
169
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
170
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
171
171
  #
172
172
  # module HairColors
173
173
  # mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
@@ -180,7 +180,7 @@ class Module
180
180
  # Person.new.hair_colors = [:brown] # => NoMethodError
181
181
  # Person.new.hair_colors # => NoMethodError
182
182
  #
183
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
183
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
184
184
  #
185
185
  # module HairColors
186
186
  # mattr_accessor :hair_colors, instance_accessor: false
@@ -25,7 +25,7 @@ class Module
25
25
  # end
26
26
  # # => NameError: invalid attribute name: 1_Badname
27
27
  #
28
- # If you want to opt out of the creation of the instance reader method, pass
28
+ # To omit the instance reader method, pass
29
29
  # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
30
30
  #
31
31
  # class Current
@@ -66,7 +66,7 @@ class Module
66
66
  # Current.user = "DHH"
67
67
  # Thread.current[:attr_Current_user] # => "DHH"
68
68
  #
69
- # If you want to opt out of the creation of the instance writer method, pass
69
+ # To omit the instance writer method, pass
70
70
  # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
71
71
  #
72
72
  # class Current
@@ -118,8 +118,8 @@ class Module
118
118
  # Customer.user # => "Rafael"
119
119
  # Account.user # => "DHH"
120
120
  #
121
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
122
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
121
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
122
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
123
123
  #
124
124
  # class Current
125
125
  # thread_mattr_accessor :user, instance_writer: false, instance_reader: false
@@ -128,7 +128,7 @@ class Module
128
128
  # Current.new.user = "DHH" # => NoMethodError
129
129
  # Current.new.user # => NoMethodError
130
130
  #
131
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
131
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
132
132
  #
133
133
  # class Current
134
134
  # thread_mattr_accessor :user, instance_accessor: false
@@ -275,6 +275,11 @@ class Module
275
275
  #
276
276
  # The delegated method must be public on the target, otherwise it will
277
277
  # raise +NoMethodError+.
278
+ #
279
+ # The <tt>marshal_dump</tt> and <tt>_dump</tt> methods are exempt from
280
+ # delegation due to possible interference when calling
281
+ # <tt>Marshal.dump(object)</tt>, should the delegation target method
282
+ # of <tt>object</tt> add or remove instance variables.
278
283
  def delegate_missing_to(target)
279
284
  target = target.to_s
280
285
  target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
@@ -284,6 +289,7 @@ class Module
284
289
  # It may look like an oversight, but we deliberately do not pass
285
290
  # +include_private+, because they do not get delegated.
286
291
 
292
+ return false if name == :marshal_dump || name == :_dump
287
293
  #{target}.respond_to?(name) || super
288
294
  end
289
295
 
@@ -28,96 +28,6 @@ class Object
28
28
  end
29
29
  end
30
30
 
31
- class NilClass
32
- begin
33
- nil.dup
34
- rescue TypeError
35
-
36
- # +nil+ is not duplicable:
37
- #
38
- # nil.duplicable? # => false
39
- # nil.dup # => TypeError: can't dup NilClass
40
- def duplicable?
41
- false
42
- end
43
- end
44
- end
45
-
46
- class FalseClass
47
- begin
48
- false.dup
49
- rescue TypeError
50
-
51
- # +false+ is not duplicable:
52
- #
53
- # false.duplicable? # => false
54
- # false.dup # => TypeError: can't dup FalseClass
55
- def duplicable?
56
- false
57
- end
58
- end
59
- end
60
-
61
- class TrueClass
62
- begin
63
- true.dup
64
- rescue TypeError
65
-
66
- # +true+ is not duplicable:
67
- #
68
- # true.duplicable? # => false
69
- # true.dup # => TypeError: can't dup TrueClass
70
- def duplicable?
71
- false
72
- end
73
- end
74
- end
75
-
76
- class Symbol
77
- begin
78
- :symbol.dup
79
-
80
- # Some symbols couldn't be duped in Ruby 2.4.0 only, due to a bug.
81
- # This feature check catches any regression.
82
- "symbol_from_string".to_sym.dup
83
- rescue TypeError
84
-
85
- # Symbols are not duplicable:
86
- #
87
- # :my_symbol.duplicable? # => false
88
- # :my_symbol.dup # => TypeError: can't dup Symbol
89
- def duplicable?
90
- false
91
- end
92
- end
93
- end
94
-
95
- class Numeric
96
- begin
97
- 1.dup
98
- rescue TypeError
99
-
100
- # Numbers are not duplicable:
101
- #
102
- # 3.duplicable? # => false
103
- # 3.dup # => TypeError: can't dup Integer
104
- def duplicable?
105
- false
106
- end
107
- end
108
- end
109
-
110
- require "bigdecimal"
111
- class BigDecimal
112
- # BigDecimals are duplicable:
113
- #
114
- # BigDecimal("1.2").duplicable? # => true
115
- # BigDecimal("1.2").dup # => #<BigDecimal:...,'0.12E1',18(18)>
116
- def duplicable?
117
- true
118
- end
119
- end
120
-
121
31
  class Method
122
32
  # Methods are not duplicable:
123
33
  #
@@ -128,32 +38,12 @@ class Method
128
38
  end
129
39
  end
130
40
 
131
- class Complex
132
- begin
133
- Complex(1).dup
134
- rescue TypeError
135
-
136
- # Complexes are not duplicable:
137
- #
138
- # Complex(1).duplicable? # => false
139
- # Complex(1).dup # => TypeError: can't copy Complex
140
- def duplicable?
141
- false
142
- end
143
- end
144
- end
145
-
146
- class Rational
147
- begin
148
- Rational(1).dup
149
- rescue TypeError
150
-
151
- # Rationals are not duplicable:
152
- #
153
- # Rational(1).duplicable? # => false
154
- # Rational(1).dup # => TypeError: can't copy Rational
155
- def duplicable?
156
- false
157
- end
41
+ class UnboundMethod
42
+ # Unbound methods are not duplicable:
43
+ #
44
+ # method(:puts).unbind.duplicable? # => false
45
+ # method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
46
+ def duplicable?
47
+ false
158
48
  end
159
49
  end