sorbet-runtime 0.5.5841

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 (84) hide show
  1. checksums.yaml +7 -0
  2. data/lib/sorbet-runtime.rb +116 -0
  3. data/lib/types/_types.rb +285 -0
  4. data/lib/types/abstract_utils.rb +50 -0
  5. data/lib/types/boolean.rb +8 -0
  6. data/lib/types/compatibility_patches.rb +95 -0
  7. data/lib/types/configuration.rb +428 -0
  8. data/lib/types/enum.rb +349 -0
  9. data/lib/types/generic.rb +23 -0
  10. data/lib/types/helpers.rb +39 -0
  11. data/lib/types/interface_wrapper.rb +158 -0
  12. data/lib/types/non_forcing_constants.rb +51 -0
  13. data/lib/types/private/abstract/data.rb +36 -0
  14. data/lib/types/private/abstract/declare.rb +48 -0
  15. data/lib/types/private/abstract/hooks.rb +43 -0
  16. data/lib/types/private/abstract/validate.rb +128 -0
  17. data/lib/types/private/casts.rb +22 -0
  18. data/lib/types/private/class_utils.rb +111 -0
  19. data/lib/types/private/decl_state.rb +30 -0
  20. data/lib/types/private/final.rb +51 -0
  21. data/lib/types/private/methods/_methods.rb +460 -0
  22. data/lib/types/private/methods/call_validation.rb +1149 -0
  23. data/lib/types/private/methods/decl_builder.rb +228 -0
  24. data/lib/types/private/methods/modes.rb +16 -0
  25. data/lib/types/private/methods/signature.rb +196 -0
  26. data/lib/types/private/methods/signature_validation.rb +229 -0
  27. data/lib/types/private/mixins/mixins.rb +27 -0
  28. data/lib/types/private/retry.rb +10 -0
  29. data/lib/types/private/runtime_levels.rb +56 -0
  30. data/lib/types/private/sealed.rb +65 -0
  31. data/lib/types/private/types/not_typed.rb +23 -0
  32. data/lib/types/private/types/string_holder.rb +26 -0
  33. data/lib/types/private/types/type_alias.rb +26 -0
  34. data/lib/types/private/types/void.rb +34 -0
  35. data/lib/types/profile.rb +31 -0
  36. data/lib/types/props/_props.rb +161 -0
  37. data/lib/types/props/constructor.rb +40 -0
  38. data/lib/types/props/custom_type.rb +108 -0
  39. data/lib/types/props/decorator.rb +672 -0
  40. data/lib/types/props/errors.rb +8 -0
  41. data/lib/types/props/generated_code_validation.rb +268 -0
  42. data/lib/types/props/has_lazily_specialized_methods.rb +92 -0
  43. data/lib/types/props/optional.rb +81 -0
  44. data/lib/types/props/plugin.rb +37 -0
  45. data/lib/types/props/pretty_printable.rb +107 -0
  46. data/lib/types/props/private/apply_default.rb +170 -0
  47. data/lib/types/props/private/deserializer_generator.rb +165 -0
  48. data/lib/types/props/private/parser.rb +32 -0
  49. data/lib/types/props/private/serde_transform.rb +192 -0
  50. data/lib/types/props/private/serializer_generator.rb +77 -0
  51. data/lib/types/props/private/setter_factory.rb +134 -0
  52. data/lib/types/props/serializable.rb +330 -0
  53. data/lib/types/props/type_validation.rb +111 -0
  54. data/lib/types/props/utils.rb +59 -0
  55. data/lib/types/props/weak_constructor.rb +67 -0
  56. data/lib/types/runtime_profiled.rb +24 -0
  57. data/lib/types/sig.rb +30 -0
  58. data/lib/types/struct.rb +18 -0
  59. data/lib/types/types/attached_class.rb +37 -0
  60. data/lib/types/types/base.rb +151 -0
  61. data/lib/types/types/class_of.rb +38 -0
  62. data/lib/types/types/enum.rb +42 -0
  63. data/lib/types/types/fixed_array.rb +60 -0
  64. data/lib/types/types/fixed_hash.rb +59 -0
  65. data/lib/types/types/intersection.rb +37 -0
  66. data/lib/types/types/noreturn.rb +29 -0
  67. data/lib/types/types/proc.rb +51 -0
  68. data/lib/types/types/self_type.rb +35 -0
  69. data/lib/types/types/simple.rb +33 -0
  70. data/lib/types/types/t_enum.rb +38 -0
  71. data/lib/types/types/type_member.rb +7 -0
  72. data/lib/types/types/type_parameter.rb +23 -0
  73. data/lib/types/types/type_template.rb +7 -0
  74. data/lib/types/types/type_variable.rb +31 -0
  75. data/lib/types/types/typed_array.rb +34 -0
  76. data/lib/types/types/typed_enumerable.rb +161 -0
  77. data/lib/types/types/typed_enumerator.rb +36 -0
  78. data/lib/types/types/typed_hash.rb +43 -0
  79. data/lib/types/types/typed_range.rb +26 -0
  80. data/lib/types/types/typed_set.rb +36 -0
  81. data/lib/types/types/union.rb +56 -0
  82. data/lib/types/types/untyped.rb +29 -0
  83. data/lib/types/utils.rb +217 -0
  84. metadata +223 -0
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ module T::NonForcingConstants
5
+ # NOTE: This method is documented on the RBI in Sorbet's payload, so that it
6
+ # shows up in the hover/completion documentation via LSP.
7
+ T::Sig::WithoutRuntime.sig {params(val: BasicObject, klass: String).returns(T::Boolean)}
8
+ def self.non_forcing_is_a?(val, klass)
9
+ method_name = "T::NonForcingConstants.non_forcing_is_a?"
10
+ if klass.empty?
11
+ raise ArgumentError.new("The string given to `#{method_name}` must not be empty")
12
+ end
13
+
14
+ current_klass = T.let(nil, T.nilable(Module))
15
+ current_prefix = T.let(nil, T.nilable(String))
16
+
17
+ parts = klass.split('::')
18
+ parts.each do |part|
19
+ if current_klass.nil?
20
+ # First iteration
21
+ if part != ""
22
+ raise ArgumentError.new("The string given to `#{method_name}` must be an absolute constant reference that starts with `::`")
23
+ end
24
+
25
+ current_klass = Object
26
+ current_prefix = ''
27
+ else
28
+ if current_klass.autoload?(part)
29
+ # There's an autoload registered for that constant, which means it's not
30
+ # yet loaded. `value` can't be an instance of something not yet loaded.
31
+ return false
32
+ end
33
+
34
+ # Sorbet guarantees that the string is an absolutely resolved name.
35
+ search_inheritance_chain = false
36
+ if !current_klass.const_defined?(part, search_inheritance_chain)
37
+ return false
38
+ end
39
+
40
+ current_klass = current_klass.const_get(part)
41
+ current_prefix = "#{current_prefix}::#{part}"
42
+
43
+ if !Module.===(current_klass)
44
+ raise ArgumentError.new("#{current_prefix} is not a class or module")
45
+ end
46
+ end
47
+ end
48
+
49
+ return current_klass.===(val)
50
+ end
51
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ # We need to associate data with abstract modules. We could add instance methods to them that
5
+ # access ivars, but those methods will unnecessarily pollute the module namespace, and they'd
6
+ # have access to other private state and methods that they don't actually need. We also need to
7
+ # associate data with arbitrary classes/modules that implement abstract mixins, where we don't
8
+ # control the interface at all. So, we access data via these `get` and `set` methods.
9
+ #
10
+ # Using instance_variable_get/set here is gross, but the alternative is to use a hash keyed on
11
+ # `mod`, and we can't trust that arbitrary modules can be added to those, because there are lurky
12
+ # modules that override the `hash` method with something completely broken.
13
+ module T::Private::Abstract::Data
14
+ def self.get(mod, key)
15
+ mod.instance_variable_get("@opus_abstract__#{key}") # rubocop:disable PrisonGuard/NoLurkyInstanceVariableAccess
16
+ end
17
+
18
+ def self.set(mod, key, value)
19
+ mod.instance_variable_set("@opus_abstract__#{key}", value) # rubocop:disable PrisonGuard/NoLurkyInstanceVariableAccess
20
+ end
21
+
22
+ def self.key?(mod, key)
23
+ mod.instance_variable_defined?("@opus_abstract__#{key}")
24
+ end
25
+
26
+ # Works like `setdefault` in Python. If key has already been set, return its value. If not,
27
+ # insert `key` with a value of `default` and return `default`.
28
+ def self.set_default(mod, key, default)
29
+ if self.key?(mod, key)
30
+ self.get(mod, key)
31
+ else
32
+ self.set(mod, key, default)
33
+ default
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module T::Private::Abstract::Declare
5
+ Abstract = T::Private::Abstract
6
+ AbstractUtils = T::AbstractUtils
7
+
8
+ def self.declare_abstract(mod, type:)
9
+ if AbstractUtils.abstract_module?(mod)
10
+ raise "#{mod} is already declared as abstract"
11
+ end
12
+ if T::Private::Final.final_module?(mod)
13
+ raise "#{mod} was already declared as final and cannot be declared as abstract"
14
+ end
15
+
16
+ Abstract::Data.set(mod, :can_have_abstract_methods, true)
17
+ Abstract::Data.set(mod.singleton_class, :can_have_abstract_methods, true)
18
+ Abstract::Data.set(mod, :abstract_type, type)
19
+
20
+ mod.extend(Abstract::Hooks)
21
+ mod.extend(T::InterfaceWrapper::Helpers)
22
+
23
+ if mod.is_a?(Class)
24
+ if type == :interface
25
+ # Since `interface!` is just `abstract!` with some extra validation, we could technically
26
+ # allow this, but it's unclear there are good use cases, and it might be confusing.
27
+ raise "Classes can't be interfaces. Use `abstract!` instead of `interface!`."
28
+ end
29
+
30
+ if mod.instance_method(:initialize).owner == mod
31
+ raise "You must call `abstract!` *before* defining an initialize method"
32
+ end
33
+
34
+ # Don't need to silence warnings via without_ruby_warnings when calling
35
+ # define_method because of the guard above
36
+
37
+ mod.send(:define_method, :initialize) do |*args, &blk|
38
+ if self.class == mod
39
+ raise "#{mod} is declared as abstract; it cannot be instantiated"
40
+ end
41
+ super(*args, &blk)
42
+ end
43
+ if mod.respond_to?(:ruby2_keywords, true)
44
+ mod.send(:ruby2_keywords, :initialize)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module T::Private::Abstract::Hooks
5
+ # This will become the self.extend_object method on a module that extends Abstract::Hooks.
6
+ # It gets called when *that* module gets extended in another class/module (similar to the
7
+ # `extended` hook, but this gets calls before the ancestors of `other` get modified, which
8
+ # is important for our validation).
9
+ private def extend_object(other)
10
+ T::Private::Abstract::Data.set(self, :last_used_by, other)
11
+ super
12
+ end
13
+
14
+ # This will become the self.append_features method on a module that extends Abstract::Hooks.
15
+ # It gets called when *that* module gets included in another class/module (similar to the
16
+ # `included` hook, but this gets calls before the ancestors of `other` get modified, which
17
+ # is important for our validation).
18
+ private def append_features(other)
19
+ T::Private::Abstract::Data.set(self, :last_used_by, other)
20
+ super
21
+ end
22
+
23
+ # This will become the self.inherited method on a class that extends Abstract::Hooks.
24
+ # It gets called when *that* class gets inherited by another class.
25
+ private def inherited(other)
26
+ super
27
+ # `self` may not actually be abstract -- it could be a concrete class that inherited from an
28
+ # abstract class. We only need to check this in `inherited` because, for modules being included
29
+ # or extended, the concrete ones won't have these hooks at all. This is just an optimization.
30
+ return if !T::AbstractUtils.abstract_module?(self)
31
+
32
+ T::Private::Abstract::Data.set(self, :last_used_by, other)
33
+ end
34
+
35
+ # This will become the self.prepended method on a module that extends Abstract::Hooks.
36
+ # It will get called when *that* module gets prepended in another class/module.
37
+ private def prepended(other)
38
+ # Prepending abstract methods is weird. You'd only be able to override them via other prepended
39
+ # modules, or in subclasses. Punt until we have a use case.
40
+ Kernel.raise "Prepending abstract mixins is not currently supported. Please explain your use case " \
41
+ "to #dev-productivity."
42
+ end
43
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module T::Private::Abstract::Validate
5
+ Abstract = T::Private::Abstract
6
+ AbstractUtils = T::AbstractUtils
7
+ Methods = T::Private::Methods
8
+ SignatureValidation = T::Private::Methods::SignatureValidation
9
+
10
+ def self.validate_abstract_module(mod)
11
+ type = Abstract::Data.get(mod, :abstract_type)
12
+ validate_interface(mod) if type == :interface
13
+ end
14
+
15
+ # Validates a class/module with an abstract class/module as an ancestor. This must be called
16
+ # after all methods on `mod` have been defined.
17
+ def self.validate_subclass(mod)
18
+ can_have_abstract_methods = !T::Private::Abstract::Data.get(mod, :can_have_abstract_methods)
19
+ unimplemented_methods = []
20
+
21
+ T::AbstractUtils.declared_abstract_methods_for(mod).each do |abstract_method|
22
+ implementation_method = mod.instance_method(abstract_method.name)
23
+ if AbstractUtils.abstract_method?(implementation_method)
24
+ # Note that when we end up here, implementation_method might not be the same as
25
+ # abstract_method; the latter could've been overridden by another abstract method. In either
26
+ # case, if we have a concrete definition in an ancestor, that will end up as the effective
27
+ # implementation (see CallValidation.wrap_method_if_needed), so that's what we'll validate
28
+ # against.
29
+ implementation_method = T.unsafe(nil)
30
+ mod.ancestors.each do |ancestor|
31
+ if ancestor.instance_methods.include?(abstract_method.name)
32
+ method = ancestor.instance_method(abstract_method.name)
33
+ T::Private::Methods.maybe_run_sig_block_for_method(method)
34
+ if !T::AbstractUtils.abstract_method?(method)
35
+ implementation_method = method
36
+ break
37
+ end
38
+ end
39
+ end
40
+ if !implementation_method
41
+ # There's no implementation
42
+ if can_have_abstract_methods
43
+ unimplemented_methods << describe_method(abstract_method)
44
+ end
45
+ next # Nothing to validate
46
+ end
47
+ end
48
+
49
+ implementation_signature = Methods.signature_for_method(implementation_method)
50
+ # When a signature exists and the method is defined directly on `mod`, we skip the validation
51
+ # here, because it will have already been done when the method was defined (by
52
+ # T::Private::Methods._on_method_added).
53
+ next if implementation_signature&.owner == mod
54
+
55
+ # We validate the remaining cases here: (a) methods defined directly on `mod` without a
56
+ # signature and (b) methods from ancestors (note that these ancestors can come before or
57
+ # after the abstract module in the inheritance chain -- the former coming from
58
+ # walking `mod.ancestors` above).
59
+ abstract_signature = Methods.signature_for_method(abstract_method)
60
+ # We allow implementation methods to be defined without a signature.
61
+ # In that case, get its untyped signature.
62
+ implementation_signature ||= Methods::Signature.new_untyped(
63
+ method: implementation_method,
64
+ mode: Methods::Modes.override
65
+ )
66
+ SignatureValidation.validate_override_shape(implementation_signature, abstract_signature)
67
+ SignatureValidation.validate_override_types(implementation_signature, abstract_signature)
68
+ end
69
+
70
+ method_type = mod.singleton_class? ? "class" : "instance"
71
+ if !unimplemented_methods.empty?
72
+ raise "Missing implementation for abstract #{method_type} method(s) in #{mod}:\n" \
73
+ "#{unimplemented_methods.join("\n")}\n" \
74
+ "If #{mod} is meant to be an abstract class/module, you can call " \
75
+ "`abstract!` or `interface!`. Otherwise, you must implement the method(s)."
76
+ end
77
+ end
78
+
79
+ private_class_method def self.validate_interface_all_abstract(mod, method_names)
80
+ violations = method_names.map do |method_name|
81
+ method = mod.instance_method(method_name)
82
+ if !AbstractUtils.abstract_method?(method)
83
+ describe_method(method, show_owner: false)
84
+ end
85
+ end.compact
86
+
87
+ if !violations.empty?
88
+ raise "`#{mod}` is declared as an interface, but the following methods are not declared " \
89
+ "with `abstract`:\n#{violations.join("\n")}"
90
+ end
91
+ end
92
+
93
+ private_class_method def self.validate_interface(mod)
94
+ interface_methods = T::Utils.methods_excluding_object(mod)
95
+ validate_interface_all_abstract(mod, interface_methods)
96
+ validate_interface_all_public(mod, interface_methods)
97
+ end
98
+
99
+ private_class_method def self.validate_interface_all_public(mod, method_names)
100
+ violations = method_names.map do |method_name|
101
+ if !mod.public_method_defined?(method_name)
102
+ describe_method(mod.instance_method(method_name), show_owner: false)
103
+ end
104
+ end.compact
105
+
106
+ if !violations.empty?
107
+ raise "All methods on an interface must be public. If you intend to have non-public " \
108
+ "methods, declare your class/module using `abstract!` instead of `interface!`. " \
109
+ "The following methods on `#{mod}` are not public: \n#{violations.join("\n")}"
110
+ end
111
+ end
112
+
113
+ private_class_method def self.describe_method(method, show_owner: true)
114
+ if method.source_location
115
+ loc = method.source_location.join(':')
116
+ else
117
+ loc = "<unknown location>"
118
+ end
119
+
120
+ if show_owner
121
+ owner = " declared in #{method.owner}"
122
+ else
123
+ owner = ""
124
+ end
125
+
126
+ " * `#{method.name}`#{owner} at #{loc}"
127
+ end
128
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ # typed: false
3
+
4
+ module T::Private
5
+ module Casts
6
+ def self.cast(value, type, cast_method:)
7
+ begin
8
+ error = T::Utils.coerce(type).error_message_for_obj(value)
9
+ return value unless error
10
+
11
+ caller_loc = T.must(caller_locations(2..2)).first
12
+
13
+ suffix = "Caller: #{T.must(caller_loc).path}:#{T.must(caller_loc).lineno}"
14
+
15
+ raise TypeError.new("#{cast_method}: #{error}\n#{suffix}")
16
+ rescue TypeError => e # raise into rescue to ensure e.backtrace is populated
17
+ T::Configuration.inline_type_error_handler(e)
18
+ value
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+ # typed: false
3
+
4
+ # Cut down version of Chalk::Tools::ClassUtils with only :replace_method functionality.
5
+ # Extracted to a separate namespace so the type system can be used standalone.
6
+ module T::Private::ClassUtils
7
+ class ReplacedMethod
8
+ def initialize(mod, old_method, new_method, overwritten, visibility)
9
+ if old_method.name != new_method.name
10
+ raise "Method names must match. old=#{old_method.name} new=#{new_method.name}"
11
+ end
12
+ @mod = mod
13
+ @old_method = old_method
14
+ @new_method = new_method
15
+ @overwritten = overwritten
16
+ @name = old_method.name
17
+ @visibility = visibility
18
+ @restored = false
19
+ end
20
+
21
+ def restore
22
+ # The check below would also catch this, but this makes the failure mode much clearer
23
+ if @restored
24
+ raise "Method '#{@name}' on '#{@mod}' was already restored"
25
+ end
26
+
27
+ if @mod.instance_method(@name) != @new_method
28
+ raise "Trying to restore #{@mod}##{@name} but the method has changed since the call to replace_method"
29
+ end
30
+
31
+ @restored = true
32
+
33
+ if @overwritten
34
+ # The original method was overwritten. Overwrite again to restore it.
35
+ T::Configuration.without_ruby_warnings do
36
+ @mod.send(:define_method, @old_method.name, @old_method) # rubocop:disable PrisonGuard/UsePublicSend
37
+ end
38
+ else
39
+ # The original method was in an ancestor. Restore it by removing the overriding method.
40
+ @mod.send(:remove_method, @old_method.name) # rubocop:disable PrisonGuard/UsePublicSend
41
+ end
42
+
43
+ # Restore the visibility. Note that we need to do this even when we call remove_method
44
+ # above, because the module may have set custom visibility for a method it inherited.
45
+ @mod.send(@visibility, @old_method.name) # rubocop:disable PrisonGuard/UsePublicSend
46
+
47
+ nil
48
+ end
49
+
50
+ def bind(obj)
51
+ @old_method.bind(obj)
52
+ end
53
+
54
+ def to_s
55
+ @old_method.to_s
56
+ end
57
+ end
58
+
59
+ # `name` must be an instance method (for class methods, pass in mod.singleton_class)
60
+ private_class_method def self.visibility_method_name(mod, name)
61
+ if mod.public_method_defined?(name)
62
+ :public
63
+ elsif mod.protected_method_defined?(name)
64
+ :protected
65
+ elsif mod.private_method_defined?(name)
66
+ :private
67
+ else
68
+ raise NameError.new("undefined method `#{name}` for `#{mod}`")
69
+ end
70
+ end
71
+
72
+ # Replaces a method, either by overwriting it (if it is defined directly on `mod`) or by
73
+ # overriding it (if it is defined by one of mod's ancestors). Returns a ReplacedMethod instance
74
+ # on which you can call `bind(...).call(...)` to call the original method, or `restore` to
75
+ # restore the original method (by overwriting or removing the override).
76
+ def self.replace_method(mod, name, &blk)
77
+ original_method = mod.instance_method(name)
78
+ original_visibility = visibility_method_name(mod, name)
79
+ original_owner = original_method.owner
80
+
81
+ mod.ancestors.each do |ancestor|
82
+ break if ancestor == mod
83
+ if ancestor == original_owner
84
+ # If we get here, that means the method we're trying to replace exists on a *prepended*
85
+ # mixin, which means in order to supersede it, we'd need to create a method on a new
86
+ # module that we'd prepend before `ancestor`. The problem with that approach is there'd
87
+ # be no way to remove that new module after prepending it, so we'd be left with these
88
+ # empty anonymous modules in the ancestor chain after calling `restore`.
89
+ #
90
+ # That's not necessarily a deal breaker, but for now, we're keeping it as unsupported.
91
+ raise "You're trying to replace `#{name}` on `#{mod}`, but that method exists in a " \
92
+ "prepended module (#{ancestor}), which we don't currently support. Talk to " \
93
+ "#dev-productivity for help."
94
+ end
95
+ end
96
+
97
+ overwritten = original_owner == mod
98
+ T::Configuration.without_ruby_warnings do
99
+ T::Private::DeclState.current.without_on_method_added do
100
+ mod.send(:define_method, name, &blk) # rubocop:disable PrisonGuard/UsePublicSend
101
+ if blk.arity < 0 && mod.respond_to?(:ruby2_keywords, true)
102
+ mod.send(:ruby2_keywords, name)
103
+ end
104
+ end
105
+ end
106
+ mod.send(original_visibility, name) # rubocop:disable PrisonGuard/UsePublicSend
107
+ new_method = mod.instance_method(name)
108
+
109
+ ReplacedMethod.new(mod, original_method, new_method, overwritten, original_visibility)
110
+ end
111
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ class T::Private::DeclState
5
+ def self.current
6
+ Thread.current[:opus_types__decl_state] ||= self.new
7
+ end
8
+
9
+ def self.current=(other)
10
+ Thread.current[:opus_types__decl_state] = other
11
+ end
12
+
13
+ attr_accessor :active_declaration
14
+ attr_accessor :skip_on_method_added
15
+
16
+ def reset!
17
+ self.active_declaration = nil
18
+ end
19
+
20
+ def without_on_method_added
21
+ begin
22
+ # explicit 'self' is needed here
23
+ old_value = self.skip_on_method_added
24
+ self.skip_on_method_added = true
25
+ yield
26
+ ensure
27
+ self.skip_on_method_added = old_value
28
+ end
29
+ end
30
+ end