rbs 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -125,6 +125,49 @@ module RBS
125
125
  end
126
126
  end
127
127
 
128
+ class NoSuperclassFoundError < StandardError
129
+ attr_reader :type_name
130
+ attr_reader :location
131
+
132
+ def initialize(type_name:, location:)
133
+ @type_name = type_name
134
+ @location = location
135
+
136
+ super "#{Location.to_string location}: Could not find super class: #{type_name}"
137
+ end
138
+
139
+ def self.check!(type_name, env:, location:)
140
+ env.class_decls.key?(type_name) or raise new(type_name: type_name, location: location)
141
+ end
142
+ end
143
+
144
+ class NoMixinFoundError < StandardError
145
+ attr_reader :type_name
146
+ attr_reader :member
147
+
148
+ def initialize(type_name:, member:)
149
+ @type_name = type_name
150
+ @member = member
151
+
152
+ super "#{Location.to_string location}: Could not find mixin: #{type_name}"
153
+ end
154
+
155
+ def location
156
+ member.location
157
+ end
158
+
159
+ def self.check!(type_name, env:, member:)
160
+ dic = case
161
+ when type_name.class?
162
+ env.class_decls
163
+ when type_name.interface?
164
+ env.interface_decls
165
+ end
166
+
167
+ dic.key?(type_name) or raise new(type_name: type_name, member: member)
168
+ end
169
+ end
170
+
128
171
  class DuplicatedMethodDefinitionError < StandardError
129
172
  attr_reader :decl
130
173
  attr_reader :location
@@ -187,17 +230,6 @@ module RBS
187
230
  end
188
231
  end
189
232
 
190
- class MixedClassModuleDeclarationError < StandardError
191
- attr_reader :name
192
- attr_reader :decl
193
-
194
- def initialize(name:, decl:)
195
- @name = name
196
- @decl = decl
197
- super "#{Location.to_string decl.location}: Both class and module declarations: #{name}"
198
- end
199
- end
200
-
201
233
  class SuperclassMismatchError < StandardError
202
234
  attr_reader :name
203
235
  attr_reader :entry
@@ -209,20 +241,6 @@ module RBS
209
241
  end
210
242
  end
211
243
 
212
- class ModuleSelfTypeMismatchError < StandardError
213
- attr_reader :name
214
- attr_reader :entry
215
- attr_reader :location
216
-
217
- def initialize(name:, entry:, location:)
218
- @name = name
219
- @entry = entry
220
- @location = location
221
-
222
- super "#{Location.to_string location}: Module self type mismatch: #{name}"
223
- end
224
- end
225
-
226
244
  class InconsistentMethodVisibilityError < StandardError
227
245
  attr_reader :type_name
228
246
  attr_reader :method_name
@@ -0,0 +1,14 @@
1
+ module RBS
2
+ class Factory
3
+ def type_name(string)
4
+ absolute = string.start_with?("::")
5
+
6
+ *path, name = string.delete_prefix("::").split("::").map(&:to_sym)
7
+
8
+ TypeName.new(
9
+ name: name,
10
+ namespace: Namespace.new(path: path, absolute: absolute)
11
+ )
12
+ end
13
+ end
14
+ end
@@ -106,28 +106,28 @@ rule
106
106
  }
107
107
 
108
108
  module_decl:
109
- annotations kMODULE start_new_scope class_name module_type_params module_self_type class_members kEND {
109
+ annotations kMODULE start_new_scope class_name module_type_params colon_module_self_types class_members kEND {
110
110
  reset_variable_scope
111
111
 
112
112
  location = val[1].location + val[7].location
113
113
  result = Declarations::Module.new(
114
114
  name: val[3].value,
115
115
  type_params: val[4]&.value || Declarations::ModuleTypeParams.empty,
116
- self_type: val[5],
116
+ self_types: val[5],
117
117
  members: val[6],
118
118
  annotations: val[0],
119
119
  location: location,
120
120
  comment: leading_comment(val[0].first&.location || location)
121
121
  )
122
122
  }
123
- | annotations kMODULE start_new_scope tUKEYWORD type class_members kEND {
123
+ | annotations kMODULE start_new_scope tUKEYWORD module_self_types class_members kEND {
124
124
  reset_variable_scope
125
125
 
126
126
  location = val[1].location + val[6].location
127
127
  result = Declarations::Module.new(
128
128
  name: val[3].value,
129
129
  type_params: Declarations::ModuleTypeParams.empty,
130
- self_type: val[4],
130
+ self_types: val[4],
131
131
  members: val[5],
132
132
  annotations: val[0],
133
133
  location: location,
@@ -135,12 +135,50 @@ rule
135
135
  )
136
136
  }
137
137
 
138
- module_self_type:
139
- { result = nil }
140
- | kCOLON type {
138
+ colon_module_self_types:
139
+ { result = [] }
140
+ | kCOLON module_self_types {
141
141
  result = val[1]
142
142
  }
143
143
 
144
+ module_self_types:
145
+ module_self_type {
146
+ result = [val[0]]
147
+ }
148
+ | module_self_types kCOMMA module_self_type {
149
+ result = val[0].push(val[2])
150
+ }
151
+
152
+ module_self_type:
153
+ qualified_name kLBRACKET type_list kRBRACKET {
154
+ name = val[0].value
155
+ args = val[2]
156
+ location = val[0].location + val[3].location
157
+
158
+ case
159
+ when name.class?
160
+ result = Declarations::Module::Self.new(name: name, args: args, location: location)
161
+ when name.interface?
162
+ result = Declarations::Module::Self.new(name: name, args: args, location: location)
163
+ else
164
+ raise SemanticsError.new("Module self type should be instance or interface", subject: val[0], location: val[0].location)
165
+ end
166
+ }
167
+ | qualified_name {
168
+ name = val[0].value
169
+ args = []
170
+ location = val[0].location
171
+
172
+ case
173
+ when name.class?
174
+ result = Declarations::Module::Self.new(name: name, args: args, location: location)
175
+ when name.interface?
176
+ result = Declarations::Module::Self.new(name: name, args: args, location: location)
177
+ else
178
+ raise SemanticsError.new("Module self type should be instance or interface", subject: val[0], location: val[0].location)
179
+ end
180
+ }
181
+
144
182
  class_members:
145
183
  { result = [] }
146
184
  | class_members class_member {
@@ -1030,6 +1068,7 @@ def initialize(type, buffer:, eof_re:)
1030
1068
  @eof = false
1031
1069
  @bound_variables_stack = []
1032
1070
  @comments = {}
1071
+ @ascii_only = buffer.content.ascii_only?
1033
1072
  end
1034
1073
 
1035
1074
  def start_merged_variables_scope
@@ -1123,8 +1162,9 @@ def push_comment(string, location)
1123
1162
  end
1124
1163
 
1125
1164
  def new_token(type, value = input.matched)
1126
- start_index = input.charpos - input.matched.size
1127
- end_index = input.charpos
1165
+ charpos = charpos(input)
1166
+ start_index = charpos - input.matched.size
1167
+ end_index = charpos
1128
1168
 
1129
1169
  location = RBS::Location.new(buffer: buffer,
1130
1170
  start_pos: start_index,
@@ -1133,6 +1173,14 @@ def new_token(type, value = input.matched)
1133
1173
  [type, LocatedValue.new(location: location, value: value)]
1134
1174
  end
1135
1175
 
1176
+ def charpos(scanner)
1177
+ if @ascii_only
1178
+ scanner.pos
1179
+ else
1180
+ scanner.charpos
1181
+ end
1182
+ end
1183
+
1136
1184
  def empty_params_result
1137
1185
  [
1138
1186
  [],
@@ -1250,8 +1298,9 @@ def next_token
1250
1298
  when input.scan(/\s+/)
1251
1299
  # skip
1252
1300
  when input.scan(/#(( *)|( ?(?<string>.*)))\n/)
1253
- start_index = input.charpos - input.matched.size
1254
- end_index = input.charpos-1
1301
+ charpos = charpos(input)
1302
+ start_index = charpos - input.matched.size
1303
+ end_index = charpos-1
1255
1304
 
1256
1305
  location = RBS::Location.new(buffer: buffer,
1257
1306
  start_pos: start_index,
@@ -81,7 +81,7 @@ module RBS
81
81
  mod = AST::Declarations::Module.new(
82
82
  name: const_to_name(module_name),
83
83
  type_params: AST::Declarations::ModuleTypeParams.empty,
84
- self_type: nil,
84
+ self_types: [],
85
85
  members: [],
86
86
  annotations: [],
87
87
  location: nil,
@@ -69,7 +69,7 @@ module RBS
69
69
  members: [],
70
70
  annotations: [],
71
71
  location: nil,
72
- self_type: nil,
72
+ self_types: [],
73
73
  comment: comment
74
74
  )
75
75
 
@@ -370,7 +370,7 @@ module RBS
370
370
  decl = AST::Declarations::Module.new(
371
371
  name: type_name,
372
372
  type_params: AST::Declarations::ModuleTypeParams.empty,
373
- self_type: nil,
373
+ self_types: [],
374
374
  members: [],
375
375
  annotations: [],
376
376
  location: nil,
@@ -1,6 +1,9 @@
1
+ require "securerandom"
2
+ require "rbs/test/observer"
1
3
  require "rbs/test/spy"
2
4
  require "rbs/test/errors"
3
5
  require "rbs/test/type_check"
6
+ require "rbs/test/tester"
4
7
  require "rbs/test/hook"
5
8
 
6
9
  module RBS
@@ -16,11 +19,86 @@ module RBS
16
19
  INSPECT = Kernel.instance_method(:inspect)
17
20
  METHODS = Kernel.instance_method(:methods)
18
21
 
19
- ArgumentsReturn = Struct.new(:arguments, :return_value, :exception, keyword_init: true)
22
+ class ArgumentsReturn
23
+ attr_reader :arguments
24
+ attr_reader :exit_value
25
+ attr_reader :exit_type
26
+
27
+ def initialize(arguments:, exit_value:, exit_type:)
28
+ @arguments = arguments
29
+ @exit_value = exit_value
30
+ @exit_type = exit_type
31
+ end
32
+
33
+ def self.return(arguments:, value:)
34
+ new(arguments: arguments, exit_value: value, exit_type: :return)
35
+ end
36
+
37
+ def self.exception(arguments:, exception:)
38
+ new(arguments: arguments, exit_value: exception, exit_type: :exception)
39
+ end
40
+
41
+ def self.break(arguments:)
42
+ new(arguments: arguments, exit_value: nil, exit_type: :break)
43
+ end
44
+
45
+ def return_value
46
+ raise unless exit_type == :return
47
+ exit_value
48
+ end
49
+
50
+ def exception
51
+ raise unless exit_type == :exception
52
+ exit_value
53
+ end
54
+
55
+ def return?
56
+ exit_type == :return
57
+ end
58
+
59
+ def exception?
60
+ exit_type == :exception
61
+ end
62
+
63
+ def break?
64
+ exit_type == :break
65
+ end
66
+ end
67
+
20
68
  CallTrace = Struct.new(:method_name, :method_call, :block_calls, :block_given, keyword_init: true)
21
69
 
22
- def self.call(receiver, method, *args, **kwargs, &block)
23
- method.bind_call(receiver, *args, **kwargs, &block)
70
+ class <<self
71
+ attr_accessor :suffix
72
+
73
+ def reset_suffix
74
+ self.suffix = "RBS_TEST_#{SecureRandom.hex(3)}"
75
+ end
76
+ end
77
+
78
+ reset_suffix
79
+
80
+ if ::UnboundMethod.instance_methods.include?(:bind_call)
81
+ def self.call(receiver, method, *args, &block)
82
+ method.bind_call(receiver, *args, &block)
83
+ end
84
+ else
85
+ def self.call(receiver, method, *args, &block)
86
+ method.bind(receiver).call(*args, &block)
87
+ end
24
88
  end
25
89
  end
26
90
  end
91
+
92
+ unless ::Module.private_instance_methods.include?(:ruby2_keywords)
93
+ class Module
94
+ private
95
+ def ruby2_keywords(*)
96
+ end
97
+ end
98
+ end
99
+
100
+ unless ::Proc.instance_methods.include?(:ruby2_keywords)
101
+ class Proc
102
+ def ruby2_keywords; end
103
+ end
104
+ end
@@ -28,7 +28,7 @@ module RBS
28
28
  end
29
29
 
30
30
  def self.inspect_(obj)
31
- Hook.inspect_(obj)
31
+ Test::INSPECT.bind(obj).call
32
32
  end
33
33
 
34
34
  def self.to_string(error)
@@ -3,291 +3,165 @@ require "pp"
3
3
 
4
4
  module RBS
5
5
  module Test
6
- class Hook
7
- class Error < Exception
8
- attr_reader :errors
9
-
10
- def initialize(errors)
11
- @errors = errors
12
- super "Type error detected: [#{errors.map {|e| Errors.to_string(e) }.join(", ")}]"
13
- end
14
- end
15
-
16
- attr_reader :env
17
- attr_reader :logger
18
-
19
- attr_reader :instance_module
20
- attr_reader :instance_methods
21
- attr_reader :singleton_module
22
- attr_reader :singleton_methods
23
-
24
- attr_reader :klass
25
- attr_reader :errors
26
-
27
- def builder
28
- @builder ||= DefinitionBuilder.new(env: env)
29
- end
30
-
31
- def typecheck
32
- @typecheck ||= TypeCheck.new(self_class: klass, builder: builder)
33
- end
34
-
35
- def initialize(env, klass, logger:, raise_on_error: false)
36
- @env = env
37
- @logger = logger
38
- @klass = klass
39
-
40
- @instance_module = Module.new
41
- @instance_methods = []
42
-
43
- @singleton_module = Module.new
44
- @singleton_methods = []
45
-
46
- @errors = []
47
-
48
- @raise_on_error = raise_on_error
49
- end
50
-
51
- def raise_on_error!(error = true)
52
- @raise_on_error = error
53
- self
54
- end
55
-
56
- def raise_on_error?
57
- @raise_on_error
58
- end
59
-
60
- def prepend!
61
- klass.prepend @instance_module
62
- klass.singleton_class.prepend @singleton_module
6
+ module Hook
7
+ OPERATORS = {
8
+ :== => "eqeq",
9
+ :=== => "eqeqeq",
10
+ :+ => "plus",
11
+ :- => "minus",
12
+ :* => "star",
13
+ :/ => "slash",
14
+ :> => "gt",
15
+ :>= => "gteq",
16
+ :< => "lt",
17
+ :<= => "lteq",
18
+ :<=> => "ufo",
19
+ :& => "amp",
20
+ :| => "vbar",
21
+ :^ => "hat",
22
+ :! => "not",
23
+ :<< => "lshift",
24
+ :>> => "rshift",
25
+ :~ => "tilda"
26
+ }
27
+ def self.alias_names(target)
28
+ case target
29
+ when *OPERATORS.keys
30
+ name = OPERATORS[target]
31
+ [
32
+ "#{name}____with__#{Test.suffix}",
33
+ "#{name}____without__#{Test.suffix}"
34
+ ]
35
+ else
36
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
63
37
 
64
- if block_given?
65
- yield
66
- disable
38
+ [
39
+ "#{aliased_target}__with__#{Test.suffix}#{punctuation}",
40
+ "#{aliased_target}__without__#{Test.suffix}#{punctuation}"
41
+ ]
67
42
  end
68
-
69
- self
70
43
  end
71
44
 
72
- def self.install(env, klass, logger:)
73
- new(env, klass, logger: logger).prepend!
74
- end
75
-
76
- def refinement
77
- klass = self.klass
78
- instance_module = self.instance_module
79
- singleton_module = self.singleton_module
80
-
81
- Module.new do
82
- refine klass do
83
- prepend instance_module
84
- end
85
-
86
- refine klass.singleton_class do
87
- prepend singleton_module
88
- end
89
- end
90
- end
45
+ def self.setup_alias_method_chain(klass, target)
46
+ with_method, without_method = alias_names(target)
91
47
 
92
- def verify_all
93
- type_name = Namespace.parse(klass.name).to_type_name.absolute!
48
+ RBS.logger.debug "alias name: #{target}, #{with_method}, #{without_method}"
94
49
 
95
- builder.build_instance(type_name).tap do |definition|
96
- definition.methods.each do |name, method|
97
- if method.defined_in == type_name
98
- unless method.annotations.any? {|a| a.string == "rbs:test:skip" }
99
- logger.info "Installing a hook on #{type_name}##{name}: #{method.method_types.join(" | ")}"
100
- verify instance_method: name, types: method.method_types
101
- else
102
- logger.info "Skipping test of #{type_name}##{name}"
103
- end
104
- end
105
- end
106
- end
50
+ klass.instance_eval do
51
+ alias_method without_method, target
52
+ alias_method target, with_method
107
53
 
108
- builder.build_singleton(type_name).tap do |definition|
109
- definition.methods.each do |name, method|
110
- if method.defined_in == type_name || name == :new
111
- unless method.annotations.any? {|a| a.string == "rbs:test:skip" }
112
- logger.info "Installing a hook on #{type_name}.#{name}: #{method.method_types.join(" | ")}"
113
- verify singleton_method: name, types: method.method_types
114
- else
115
- logger.info "Skipping test of #{type_name}.#{name}"
116
- end
117
- end
54
+ case
55
+ when public_method_defined?(without_method)
56
+ public target
57
+ when protected_method_defined?(without_method)
58
+ protected target
59
+ when private_method_defined?(without_method)
60
+ private target
118
61
  end
119
62
  end
120
-
121
- self
122
- end
123
-
124
- def delegation(name, method_types, method_name)
125
- hook = self
126
-
127
- -> (*args, &block) do
128
- hook.logger.debug { "#{method_name} receives arguments: #{hook.inspect_(args)}" }
129
-
130
- block_calls = []
131
-
132
- if block
133
- original_block = block
134
-
135
- block = hook.call(Object.new, INSTANCE_EVAL) do |fresh_obj|
136
- ->(*as) do
137
- hook.logger.debug { "#{method_name} receives block arguments: #{hook.inspect_(as)}" }
138
-
139
- ret = if self.equal?(fresh_obj)
140
- original_block[*as]
141
- else
142
- hook.call(self, INSTANCE_EXEC, *as, &original_block)
143
- end
144
-
145
- block_calls << ArgumentsReturn.new(
146
- arguments: as,
147
- return_value: ret,
148
- exception: nil
149
- )
150
-
151
- hook.logger.debug { "#{method_name} returns from block: #{hook.inspect_(ret)}" }
152
-
153
- ret
154
- end.ruby2_keywords
155
- end
156
- end
157
-
158
- method = hook.call(self, METHOD, name)
159
- klass = hook.call(self, CLASS)
160
- singleton_klass = begin
161
- hook.call(self, SINGLETON_CLASS)
162
- rescue TypeError
163
- nil
164
- end
165
- prepended = klass.ancestors.include?(hook.instance_module) || singleton_klass&.ancestors&.include?(hook.singleton_module)
166
- exception = nil
167
- result = begin
168
- if prepended
169
- method.super_method.call(*args, &block)
170
- else
171
- # Using refinement
172
- method.call(*args, &block)
173
- end
174
- rescue Exception => e
175
- exception = e
176
- nil
177
- end
178
-
179
- hook.logger.debug { "#{method_name} returns: #{hook.inspect_(result)}" }
180
-
181
- call = CallTrace.new(method_call: ArgumentsReturn.new(arguments: args, return_value: result, exception: exception),
182
- block_calls: block_calls,
183
- block_given: block != nil)
184
-
185
- method_type_errors = method_types.map do |method_type|
186
- hook.typecheck.method_call(method_name, method_type, call, errors: [])
187
- end
188
-
189
- new_errors = []
190
-
191
- if method_type_errors.none?(&:empty?)
192
- if (best_errors = hook.find_best_errors(method_type_errors))
193
- new_errors.push(*best_errors)
194
- else
195
- new_errors << Errors::UnresolvedOverloadingError.new(
196
- klass: hook.klass,
197
- method_name: method_name,
198
- method_types: method_types
199
- )
200
- end
201
- end
202
-
203
- unless new_errors.empty?
204
- new_errors.each do |error|
205
- hook.logger.error Errors.to_string(error)
206
- end
207
-
208
- hook.errors.push(*new_errors)
209
-
210
- if hook.raise_on_error?
211
- raise Error.new(new_errors)
212
- end
213
- end
214
-
215
- result
216
- end.ruby2_keywords
217
63
  end
218
64
 
219
- def verify(instance_method: nil, singleton_method: nil, types:)
220
- method_types = types.map do |type|
221
- case type
222
- when String
223
- Parser.parse_method_type(type)
65
+ def self.hook_method_source(prefix, method_name, key)
66
+ with_name, without_name = alias_names(method_name)
67
+ full_method_name = "#{prefix}#{method_name}"
68
+
69
+ [__LINE__ + 1, <<RUBY]
70
+ def #{with_name}(*args)
71
+ ::RBS.logger.debug { "#{full_method_name} with arguments: [" + args.map(&:inspect).join(", ") + "]" }
72
+
73
+ begin
74
+ return_from_call = false
75
+ block_calls = []
76
+
77
+ if block_given?
78
+ result = __send__(:"#{without_name}", *args) do |*block_args|
79
+ return_from_block = false
80
+
81
+ begin
82
+ block_result = yield(*block_args)
83
+ return_from_block = true
84
+ ensure
85
+ exn = $!
86
+
87
+ case
88
+ when return_from_block
89
+ # Returned from yield
90
+ block_calls << ::RBS::Test::ArgumentsReturn.return(
91
+ arguments: block_args,
92
+ value: block_result
93
+ )
94
+ when exn
95
+ # Exception
96
+ block_calls << ::RBS::Test::ArgumentsReturn.exception(
97
+ arguments: block_args,
98
+ exception: exn
99
+ )
224
100
  else
225
- type
101
+ # break?
102
+ block_calls << ::RBS::Test::ArgumentsReturn.break(
103
+ arguments: block_args
104
+ )
226
105
  end
227
106
  end
228
107
 
229
- case
230
- when instance_method
231
- instance_methods << instance_method
232
- call(self.instance_module, DEFINE_METHOD, instance_method, &delegation(instance_method, method_types, "##{instance_method}"))
233
- when singleton_method
234
- call(self.singleton_module, DEFINE_METHOD, singleton_method, &delegation(singleton_method, method_types, ".#{singleton_method}"))
235
- end
236
-
237
- self
108
+ block_result
238
109
  end
110
+ else
111
+ result = __send__(:"#{without_name}", *args)
112
+ end
113
+ return_from_call = true
114
+ result
115
+ ensure
116
+ exn = $!
117
+
118
+ case
119
+ when return_from_call
120
+ ::RBS.logger.debug { "#{full_method_name} return with value: " + result.inspect }
121
+ method_call = ::RBS::Test::ArgumentsReturn.return(
122
+ arguments: args,
123
+ value: result
124
+ )
125
+ when exn
126
+ ::RBS.logger.debug { "#{full_method_name} exit with exception: " + exn.inspect }
127
+ method_call = ::RBS::Test::ArgumentsReturn.exception(
128
+ arguments: args,
129
+ exception: exn
130
+ )
131
+ else
132
+ ::RBS.logger.debug { "#{full_method_name} exit with jump" }
133
+ method_call = ::RBS::Test::ArgumentsReturn.break(arguments: args)
134
+ end
239
135
 
240
- def find_best_errors(errorss)
241
- if errorss.size == 1
242
- errorss[0]
243
- else
244
- no_arity_errors = errorss.select do |errors|
245
- errors.none? do |error|
246
- error.is_a?(Errors::ArgumentError) ||
247
- error.is_a?(Errors::BlockArgumentError) ||
248
- error.is_a?(Errors::MissingBlockError) ||
249
- error.is_a?(Errors::UnexpectedBlockError)
250
- end
251
- end
136
+ trace = ::RBS::Test::CallTrace.new(
137
+ method_name: #{method_name.inspect},
138
+ method_call: method_call,
139
+ block_calls: block_calls,
140
+ block_given: block_given?,
141
+ )
252
142
 
253
- unless no_arity_errors.empty?
254
- # Choose a error set which doesn't include arity error
255
- return no_arity_errors[0] if no_arity_errors.size == 1
256
- end
257
- end
258
- end
143
+ ::RBS::Test::Observer.notify(#{key.inspect}, self, trace)
144
+ end
259
145
 
260
- def self.backtrace(skip: 2)
261
- raise
262
- rescue => exn
263
- exn.backtrace.drop(skip)
264
- end
146
+ result
147
+ end
265
148
 
266
- def run
267
- yield
268
- self
269
- ensure
270
- disable
149
+ ruby2_keywords :#{with_name}
150
+ RUBY
271
151
  end
272
152
 
273
- def call(receiver, method, *args, &block)
274
- method.bind(receiver).call(*args, &block)
275
- end
153
+ def self.hook_instance_method(klass, method, key:)
154
+ line, source = hook_method_source("#{klass}#", method, key)
276
155
 
277
- def inspect_(obj)
278
- Hook.inspect_(obj)
156
+ klass.module_eval(source, __FILE__, line)
157
+ setup_alias_method_chain klass, method
279
158
  end
280
159
 
281
- def self.inspect_(obj)
282
- obj.inspect
283
- rescue
284
- INSPECT.bind(obj).call()
285
- end
160
+ def self.hook_singleton_method(klass, method, key:)
161
+ line, source = hook_method_source("#{klass}.",method, key)
286
162
 
287
- def disable
288
- self.instance_module.remove_method(*instance_methods)
289
- self.singleton_module.remove_method(*singleton_methods)
290
- self
163
+ klass.singleton_class.module_eval(source, __FILE__, line)
164
+ setup_alias_method_chain klass.singleton_class, method
291
165
  end
292
166
  end
293
167
  end