object_shadow 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ObjectShadow
4
+ module InstanceVariables
5
+ # Returns the instance variable given
6
+ # Please note: Does not expect @ prefix
7
+ def [](ivar_name)
8
+ object.instance_variable_get(:"@#{ivar_name}")
9
+ end
10
+
11
+ # Sets the instance variable given
12
+ # Please note: Does not expect @ prefix
13
+ def []=(ivar_name, value)
14
+ object.instance_variable_set(:"@#{ivar_name}", value)
15
+ end
16
+
17
+ def remove(ivar_name)
18
+ object.remove_instance_variable(:"@#{ivar_name}")
19
+ end
20
+
21
+ def variable?(ivar_name)
22
+ object.instance_variable_defined?(:"@#{ivar_name}")
23
+ end
24
+
25
+ def variables
26
+ object.instance_variables.map{ |ivar| ivar[1..-1].to_sym }
27
+ end
28
+
29
+ def to_h
30
+ variables.map{ |ivar| [ivar, self[ivar]] }.to_h
31
+ end
32
+
33
+ def to_a
34
+ variables.map{ |ivar| self[ivar] }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,258 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ObjectShadow
4
+ module MethodIntrospection
5
+ # #shadow#methods returns a sorted list of methods related to the object
6
+ # in question. It lets you specify the kind of methods you want to retrieve
7
+ # by the following keyword parameters:
8
+ #
9
+ # target: (default :self)
10
+ #
11
+ # - :self - This returns the list of methods available to call
12
+ # on the current object, including singleton methods
13
+ # If the object is a class/module, this means that it
14
+ # will return class methods
15
+ #
16
+ # - :class - This will refer to the object's class (via the `class`) method
17
+ # and return its methods, usually to be called from an instance
18
+ # If called for a class or module, it will return `Class`' methods
19
+ #
20
+ # - :instances - This will list all methods which instances of the class (or the class
21
+ # that includes the module, in case of a module) in question will have
22
+ # Raises an ArgumentError when called on with a non-`Module`
23
+ #
24
+ #
25
+ # scope: (default :public)
26
+ #
27
+ # - :public - Restrict to to methods of public visibility
28
+ #
29
+ # - :protected - Restrict to to methods of protected visibility
30
+ #
31
+ # - :private - Restrict to to methods of private visibility
32
+ #
33
+ # - :all - Restrict to to methods of public visibility
34
+ #
35
+ #
36
+ # inherit: (default :exclude_object)
37
+ #
38
+ # - :singleton - Show only methods directly defined in the object's singleton class
39
+ #
40
+ # - :self - Show singleton methods and methods directly defined in the object's class,
41
+ # but do not traverse the inheritance chain
42
+ #
43
+ # - :exclude_class - Stop inheritance chain just before Class or Module. For
44
+ # non-modules it fallbacks to :exclude_object
45
+ #
46
+ # - :exclude_object - Stop inheritance chain just before Object
47
+ #
48
+ # - :all - Show methods from the whole inheritance chain
49
+ #
50
+ def methods(target: :self, scope: :public, inherit: :exclude_class)
51
+ MethodIntrospection.lookup_chain_for(object, target, inherit, true).flat_map { |lookup_class|
52
+ full_inheritance_lookup = inherit == :all || inherit == true
53
+
54
+ case scope
55
+ when :public
56
+ lookup_class.public_instance_methods(full_inheritance_lookup)
57
+ when :protected
58
+ lookup_class.protected_instance_methods(full_inheritance_lookup)
59
+ when :private
60
+ lookup_class.private_instance_methods(full_inheritance_lookup)
61
+ when :all
62
+ lookup_class.instance_methods(full_inheritance_lookup) +
63
+ lookup_class.private_instance_methods(full_inheritance_lookup)
64
+ else
65
+ Kernel.raise ArgumentError, \
66
+ "(ObjectShadow) Method scope: must be one of [:public, :protected, :private, :all]"
67
+ end
68
+ }.uniq.sort
69
+ end
70
+
71
+ # Returns true if a method of this name is defined
72
+ # First parameter is the name of the method in question
73
+ #
74
+ # - target: must be one of [:self, :class, :instances]
75
+ #
76
+ def method?(method_name, target: :self)
77
+ MethodIntrospection.simple_lookup_chain_for(object, target).any?{ |lookup_class|
78
+ if RUBY_VERSION >= "2.6.0"
79
+ lookup_class.method_defined?(method_name, true) ||
80
+ lookup_class.private_method_defined?(method_name, true)
81
+ else
82
+ lookup_class.method_defined?(method_name) ||
83
+ lookup_class.private_method_defined?(method_name)
84
+ end
85
+ }
86
+ end
87
+
88
+ # Returns the scope of method name given
89
+ #
90
+ # - target: must be one of [:self, :class, :instances]
91
+ #
92
+ # Possible return values: [:public, :protected, :private, nil]
93
+ #
94
+ def method_scope(method_name, target: :self)
95
+ MethodIntrospection.simple_lookup_chain_for(object, target).map{ |lookup_class|
96
+ if RUBY_VERSION >= "2.6.0"
97
+ case
98
+ when lookup_class.public_method_defined?(method_name, true)
99
+ :public
100
+ when lookup_class.protected_method_defined?(method_name, true)
101
+ :protected
102
+ when lookup_class.private_method_defined?(method_name, true)
103
+ :private
104
+ else
105
+ nil
106
+ end
107
+ else
108
+ case
109
+ when lookup_class.public_method_defined?(method_name)
110
+ :public
111
+ when lookup_class.protected_method_defined?(method_name)
112
+ :protected
113
+ when lookup_class.private_method_defined?(method_name)
114
+ :private
115
+ else
116
+ nil
117
+ end
118
+ end
119
+ }.compact.first
120
+ end
121
+
122
+ # Returns the objectified reference to a method
123
+ #
124
+ # - target: must be one of [:self, :class, :instances]
125
+ #
126
+ # Pass unbind: true if you always want UnboundMethod objects
127
+ #
128
+ # Pass all: true to not only get the method that would be called,
129
+ # but an Array with every occurrence of this method along the inheritance chain
130
+ def method(method_name, target: :self, unbind: false, all: false)
131
+ if all
132
+ MethodIntrospection.lookup_chain_for(object, target, :all).map{ |lookup_class|
133
+ if lookup_class.instance_methods(false).include?(method_name) ||
134
+ lookup_class.private_instance_methods(false).include?(method_name)
135
+ lookup_class.instance_method(method_name)
136
+ end
137
+ }.compact
138
+ else
139
+ MethodIntrospection.get_method(object, target, method_name, unbind)
140
+ end
141
+ end
142
+
143
+ # Returns the lookup/ancestor chain for an object
144
+ #
145
+ # - target: must be one of [:self, :class, :instances]
146
+ #
147
+ # Takes the same inherit options like #methods
148
+ def method_lookup_chain(target: :self, inherit: :exclude_class)
149
+ MethodIntrospection.lookup_chain_for(object, target, inherit)
150
+ end
151
+
152
+ class << self
153
+ def lookup_chain_for(object, target, inherit, optimize = false)
154
+ validate_arguments! object, target, inherit
155
+ singleton, klass, chain = get_singleton_klass_and_chain(object, target)
156
+
157
+ case inherit
158
+ when :singleton
159
+ singleton
160
+ when :self, false
161
+ singleton + klass
162
+ when :exclude_class
163
+ singleton + chain[0...(chain.index(Class) || chain.index(Module) || chain.index(Object))]
164
+ when :exclude_object
165
+ singleton + chain[0...chain.index(Object)]
166
+ when Module
167
+ singleton + chain[0..chain.index(inherit)]
168
+ when :all, true
169
+ if optimize # full chain build by Ruby using (all=true) param in list
170
+ singleton + klass
171
+ else
172
+ singleton + chain
173
+ end
174
+ end
175
+ end
176
+
177
+ def simple_lookup_chain_for(object, target)
178
+ validate_arguments! object, target
179
+ singleton, klass, = get_singleton_klass_and_chain(object, target)
180
+
181
+ singleton + klass
182
+ end
183
+
184
+ def get_method(object, target, method_name, unbind)
185
+ case target
186
+ when :self
187
+ if unbind
188
+ object.method(method_name).unbind()
189
+ else
190
+ object.method(method_name)
191
+ end
192
+ when :class
193
+ if unbind
194
+ object.class.method(method_name).unbind()
195
+ else
196
+ object.class.method(method_name)
197
+ end
198
+ when :instances
199
+ object.instance_method(method_name)
200
+ else
201
+ Kernel.raise ArgumentError, "(ObjectShadow) target: must be one of [:self, :class, :instances]"
202
+ end
203
+ rescue NameError
204
+ nil
205
+ end
206
+
207
+ private
208
+
209
+ # Explanation:
210
+ # - singleton: [singleton_class] or [] if not retrievable
211
+ # - klass: [first entry of inheritance chain] or [] for modules
212
+ # - chain: inheritance chain without singleton
213
+ def get_singleton_klass_and_chain(object, target)
214
+ target_object = target == :class ? object.class : object
215
+
216
+ begin
217
+ if target == :instances
218
+ singleton = []
219
+ klass = target_object.ancestors[0, 1]
220
+ chain = target_object.ancestors
221
+ else
222
+ singleton = [target_object.singleton_class]
223
+
224
+ if target_object.is_a? Module
225
+ klass = []
226
+ chain = target_object.singleton_class.ancestors[1..-1]
227
+ else
228
+ klass = target_object.singleton_class.ancestors[1, 1]
229
+ chain = target_object.singleton_class.ancestors[1..-1]
230
+ end
231
+ end
232
+ rescue TypeError # e.g. Integer
233
+ singleton = []
234
+ klass = target_object.class.ancestors[0, 1]
235
+ chain = target_object.class.ancestors
236
+ end
237
+
238
+ [singleton, klass, chain]
239
+ end
240
+
241
+ def validate_arguments!(object, target, inherit = nil)
242
+ unless [:self, :class, :instances].include?(target)
243
+ Kernel.raise ArgumentError, "(ObjectShadow) target: must be one of [:self, :class, :instances]"
244
+ end
245
+
246
+ if target == :instances
247
+ if !object.is_a?(Module)
248
+ Kernel.raise ArgumentError, "(ObjectShadow) target: can only be set to :instances for classes and modules"
249
+ end
250
+
251
+ if inherit == :singleton
252
+ Kernel.raise ArgumentError, "(ObjectShadow) cannot request :singleton inheritance when target is :instances"
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ObjectShadow
4
+ module ObjectMethod
5
+ def shadow
6
+ ObjectShadow.new(self)
7
+ end
8
+ end
9
+ end
10
+
11
+ Object.include(ObjectShadow::ObjectMethod)
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "basic_object"
4
+
5
+ class ObjectShadow
6
+ VERSION = "1.0.0"
7
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ObjectShadow
4
+ module Wrap
5
+ attr_reader :object
6
+
7
+ def initialize(object)
8
+ @object = object
9
+ end
10
+
11
+ # Since shadows are not supposed to be passed around, to_s is left neutral
12
+ def to_s
13
+ "#<ObjectShadow of #{object.inspect}>"
14
+ end
15
+
16
+ # The base inspect is boring, too, but it will be improved by InfoInspect
17
+ alias inspect to_s
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.dirname(__FILE__) + "/lib/object_shadow/version"
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "object_shadow"
7
+ gem.version = ObjectShadow::VERSION
8
+ gem.summary = "Metaprogramming Level 2"
9
+ gem.description = "provides a simple convenient API for accessing an object's state."
10
+ gem.authors = ["Jan Lelis"]
11
+ gem.email = ["mail@janlelis.de"]
12
+ gem.homepage = "https://github.com/janlelis/object_shadow"
13
+ gem.license = "MIT"
14
+
15
+ gem.files = Dir["{**/}{.*,*}"].select{ |path| File.file?(path) && path !~ /^pkg/ && path !~ /png\z/ }
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.required_ruby_version = "~> 2.0"
21
+ end
@@ -0,0 +1,71 @@
1
+ require_relative "../lib/object_shadow"
2
+ require "minitest/autorun"
3
+
4
+ describe "Object#shadow" do
5
+ let :c do
6
+ Class.new do
7
+ def initialize
8
+ @ivar = 42
9
+ @another_variable = 43
10
+ end
11
+ end
12
+ end
13
+
14
+ let :o do
15
+ c.new
16
+ end
17
+
18
+ describe "#variables" do
19
+ it "shows list of instance variable names" do
20
+ assert_equal \
21
+ [:ivar, :another_variable],
22
+ o.shadow.variables
23
+ end
24
+ end
25
+
26
+ describe "#variable?" do
27
+ it "returns true if variable is defined" do
28
+ assert \
29
+ o.shadow.variable?(:ivar)
30
+ end
31
+
32
+ it "returns false if variable is not defined" do
33
+ refute \
34
+ o.shadow.variable?(:ovar)
35
+ end
36
+ end
37
+
38
+ describe "#[]" do
39
+ it "returns value of given instance variable name" do
40
+ assert_equal \
41
+ 42,
42
+ o.shadow[:ivar]
43
+ end
44
+ end
45
+
46
+ describe "#[]=" do
47
+ it "sets value of given instance variable name" do
48
+ o.shadow[:ivar] = 1
49
+ assert_equal \
50
+ 1,
51
+ o.instance_variable_get(:@ivar)
52
+ end
53
+ end
54
+
55
+ describe "to_h" do
56
+ it "returns hash of all instance variables" do
57
+ assert_equal(
58
+ { ivar: 42, another_variable: 43 },
59
+ o.shadow.to_h
60
+ )
61
+ end
62
+ end
63
+
64
+ describe "to_a" do
65
+ it "returns array of all instance variable values" do
66
+ assert_equal \
67
+ [42, 43],
68
+ o.shadow.to_a
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,914 @@
1
+ require_relative "../lib/object_shadow"
2
+ require "minitest/autorun"
3
+
4
+ describe "Object#shadow" do
5
+ let :astronomical_body do
6
+ Class.new do
7
+ def fly
8
+ end
9
+
10
+ protected
11
+
12
+ def crash
13
+ end
14
+
15
+ private
16
+
17
+ def explode
18
+ end
19
+
20
+
21
+ class << self
22
+ def make
23
+ end
24
+
25
+ protected
26
+
27
+ def stir
28
+ end
29
+
30
+ private
31
+
32
+ def idea
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ let :planet do
39
+ Class.new(astronomical_body) do
40
+ def rotate
41
+ end
42
+
43
+ protected
44
+
45
+ def bump
46
+ end
47
+
48
+ private
49
+
50
+ def implode
51
+ end
52
+
53
+ class << self
54
+ def construct
55
+ end
56
+
57
+ protected
58
+
59
+ def prepare
60
+ end
61
+
62
+ private
63
+
64
+ def think
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ let :earth do
71
+ earth = planet.new
72
+
73
+ class << earth
74
+ def develop
75
+ end
76
+
77
+ protected
78
+
79
+ def magnetize
80
+ end
81
+
82
+ private
83
+
84
+ def repair
85
+ end
86
+ end
87
+
88
+ earth
89
+ end
90
+
91
+
92
+ describe "#methods" do
93
+ describe "[target: :self]" do
94
+ describe "[scope: :public]" do
95
+ describe "[inherit: :singleton]" do
96
+ it "✓" do
97
+ assert_equal \
98
+ [:develop],
99
+ earth.shadow.methods(target: :self, scope: :public, inherit: :singleton)
100
+ end
101
+ end
102
+
103
+ describe "[inherit: :self, inherit: false]" do
104
+ it "✓" do
105
+ assert_equal \
106
+ [:develop, :rotate],
107
+ earth.shadow.methods(target: :self, scope: :public, inherit: :self)
108
+ end
109
+ end
110
+
111
+ describe "[inherit: :exclude_object]" do
112
+ it "✓" do
113
+ assert_equal \
114
+ [:develop, :fly, :rotate],
115
+ earth.shadow.methods(target: :self, scope: :public, inherit: :exclude_object)
116
+ end
117
+ end
118
+
119
+ describe "[inherit: Module]" do
120
+ it "✓" do
121
+ assert_equal \
122
+ [:develop, :fly, :rotate],
123
+ earth.shadow.methods(target: :self, scope: :public, inherit: astronomical_body)
124
+ end
125
+ end
126
+
127
+ describe "[inherit: :all, inherit: true]" do
128
+ it "✓" do
129
+ assert_equal \
130
+ (
131
+ [:develop, :fly, :rotate] +
132
+ Object.public_instance_methods
133
+ ).sort,
134
+ earth.shadow.methods(target: :self, scope: :public, inherit: :all)
135
+ end
136
+ end
137
+ end
138
+
139
+ describe "[scope: :protected]" do
140
+ describe "[inherit: :singleton]" do
141
+ it "✓" do
142
+ assert_equal \
143
+ [:magnetize],
144
+ earth.shadow.methods(target: :self, scope: :protected, inherit: :singleton)
145
+ end
146
+ end
147
+
148
+ describe "[inherit: :self, inherit: false]" do
149
+ it "✓" do
150
+ assert_equal \
151
+ [:bump, :magnetize],
152
+ earth.shadow.methods(target: :self, scope: :protected, inherit: :self)
153
+ end
154
+ end
155
+
156
+ describe "[inherit: :exclude_class]" do
157
+ it "✓" do
158
+ assert_equal \
159
+ [:bump, :crash, :magnetize],
160
+ earth.shadow.methods(target: :self, scope: :protected, inherit: :exclude_class)
161
+ end
162
+ end
163
+
164
+ describe "[inherit: :exclude_object]" do
165
+ it "✓" do
166
+ assert_equal \
167
+ [:bump, :crash, :magnetize],
168
+ earth.shadow.methods(target: :self, scope: :protected, inherit: :exclude_object)
169
+ end
170
+ end
171
+
172
+ describe "[inherit: Module]" do
173
+ it "✓" do
174
+ assert_equal \
175
+ [:bump, :crash, :magnetize],
176
+ earth.shadow.methods(target: :self, scope: :protected, inherit: astronomical_body)
177
+ end
178
+ end
179
+
180
+ describe "[inherit: :all, inherit: true]" do
181
+ it "✓" do
182
+ assert_equal \
183
+ (
184
+ [:bump, :crash, :magnetize] +
185
+ Object.protected_instance_methods
186
+ ).sort,
187
+ earth.shadow.methods(target: :self, scope: :protected, inherit: :all)
188
+ end
189
+ end
190
+ end
191
+
192
+ describe "[scope: :private]" do
193
+ describe "[inherit: :singleton]" do
194
+ it "✓" do
195
+ assert_equal \
196
+ [:repair],
197
+ earth.shadow.methods(target: :self, scope: :private, inherit: :singleton)
198
+ end
199
+ end
200
+
201
+ describe "[inherit: :self, inherit: false]" do
202
+ it "✓" do
203
+ assert_equal \
204
+ [:implode, :repair],
205
+ earth.shadow.methods(target: :self, scope: :private, inherit: :self)
206
+ end
207
+ end
208
+
209
+ describe "[inherit: :exclude_class]" do
210
+ it "✓" do
211
+ assert_equal \
212
+ [:explode, :implode, :repair],
213
+ earth.shadow.methods(target: :self, scope: :private, inherit: :exclude_class)
214
+ end
215
+ end
216
+
217
+ describe "[inherit: :exclude_object]" do
218
+ it "✓" do
219
+ assert_equal \
220
+ [:explode, :implode, :repair],
221
+ earth.shadow.methods(target: :self, scope: :private, inherit: :exclude_object)
222
+ end
223
+ end
224
+
225
+ describe "[inherit: Module]" do
226
+ it "✓" do
227
+ assert_equal \
228
+ [:explode, :implode, :repair],
229
+ earth.shadow.methods(target: :self, scope: :private, inherit: astronomical_body)
230
+ end
231
+ end
232
+
233
+ describe "[inherit: :all, inherit: true]" do
234
+ it "✓" do
235
+ assert_equal \
236
+ (
237
+ [:explode, :implode, :repair] +
238
+ Object.private_instance_methods
239
+ ).sort,
240
+ earth.shadow.methods(target: :self, scope: :private, inherit: :all)
241
+ end
242
+ end
243
+ end
244
+
245
+ describe "[scope: :all]" do
246
+ describe "[inherit: :singleton]" do
247
+ it "✓" do
248
+ assert_equal \
249
+ [:develop, :magnetize, :repair],
250
+ earth.shadow.methods(target: :self, scope: :all, inherit: :singleton)
251
+ end
252
+ end
253
+
254
+ describe "[inherit: :self, inherit: false]" do
255
+ it "✓" do
256
+ assert_equal \
257
+ [:bump, :develop, :implode, :magnetize, :repair, :rotate],
258
+ earth.shadow.methods(target: :self, scope: :all, inherit: :self)
259
+ end
260
+ end
261
+
262
+ describe "[inherit: :exclude_class]" do
263
+ it "✓" do
264
+ assert_equal \
265
+ [:bump, :crash, :develop, :explode, :fly, :implode, :magnetize, :repair, :rotate],
266
+ earth.shadow.methods(target: :self, scope: :all, inherit: :exclude_object)
267
+ end
268
+ end
269
+
270
+ describe "[inherit: :exclude_object]" do
271
+ it "✓" do
272
+ assert_equal \
273
+ [:bump, :crash, :develop, :explode, :fly, :implode, :magnetize, :repair, :rotate],
274
+ earth.shadow.methods(target: :self, scope: :all, inherit: :exclude_object)
275
+ end
276
+ end
277
+
278
+ describe "[inherit: Module]" do
279
+ it "✓" do
280
+ assert_equal \
281
+ [:bump, :crash, :develop, :explode, :fly, :implode, :magnetize, :repair, :rotate],
282
+ earth.shadow.methods(target: :self, scope: :all, inherit: astronomical_body)
283
+ end
284
+ end
285
+
286
+ describe "[inherit: :all, inherit: true]" do
287
+ it "✓" do
288
+ assert_equal \
289
+ (
290
+ [:bump, :crash, :develop, :explode, :fly, :implode, :magnetize, :repair, :rotate] +
291
+ Object.instance_methods +
292
+ Object.private_instance_methods
293
+ ).sort,
294
+ earth.shadow.methods(target: :self, scope: :all, inherit: :all)
295
+ end
296
+ end
297
+ end
298
+ end
299
+
300
+
301
+ describe "[target: :class]" do
302
+ describe "[scope: :public]" do
303
+ describe "[inherit: :singleton]" do
304
+ it "✓" do
305
+ assert_equal \
306
+ [:construct],
307
+ earth.shadow.methods(target: :class, scope: :public, inherit: :singleton)
308
+ end
309
+ end
310
+
311
+ describe "[inherit: :self, inherit: false]" do
312
+ it "✓" do
313
+ assert_equal \
314
+ [:construct],
315
+ earth.shadow.methods(target: :class, scope: :public, inherit: :self)
316
+ end
317
+ end
318
+
319
+ describe "[inherit: :exclude_class]" do
320
+ it "✓" do
321
+ assert_equal \
322
+ (
323
+ [:construct, :make] +
324
+ Object.singleton_class.public_instance_methods(false) +
325
+ BasicObject.singleton_class.public_instance_methods(false)
326
+ ).sort,
327
+ earth.shadow.methods(target: :class, scope: :public, inherit: :exclude_class)
328
+ end
329
+ end
330
+
331
+ describe "[inherit: :exclude_object]" do
332
+ it "✓" do
333
+ assert_equal \
334
+ (
335
+ [:construct, :make] +
336
+ Object.singleton_class.public_instance_methods(false) +
337
+ BasicObject.singleton_class.public_instance_methods(false) +
338
+ Class.public_instance_methods(false) +
339
+ Module.public_instance_methods(false)
340
+ ).sort,
341
+ earth.shadow.methods(target: :class, scope: :public, inherit: :exclude_object)
342
+ end
343
+ end
344
+
345
+ describe "[inherit: Module]" do
346
+ it "✓" do
347
+ assert_equal \
348
+ (
349
+ [:construct, :make] +
350
+ Object.singleton_class.public_instance_methods(false)
351
+ ).sort,
352
+ earth.shadow.methods(target: :class, scope: :public, inherit: Object.singleton_class)
353
+ end
354
+ end
355
+
356
+ describe "[inherit: :all, inherit: true]" do
357
+ it "✓" do
358
+ assert_equal \
359
+ (
360
+ [:construct, :make] +
361
+ Object.singleton_class.public_instance_methods(true)
362
+ ).sort,
363
+ earth.shadow.methods(target: :class, scope: :public, inherit: :all)
364
+ end
365
+ end
366
+ end
367
+
368
+ describe "[scope: :protected]" do
369
+ describe "[inherit: :singleton]" do
370
+ it "✓" do
371
+ assert_equal \
372
+ [:prepare],
373
+ earth.shadow.methods(target: :class, scope: :protected, inherit: :singleton)
374
+ end
375
+ end
376
+
377
+ describe "[inherit: :self, inherit: false]" do
378
+ it "✓" do
379
+ assert_equal \
380
+ [:prepare],
381
+ earth.shadow.methods(target: :class, scope: :protected, inherit: :self)
382
+ end
383
+ end
384
+
385
+ describe "[inherit: :exclude_class]" do
386
+ it "✓" do
387
+ assert_equal \
388
+ (
389
+ [:prepare, :stir] +
390
+ Object.singleton_class.protected_instance_methods(false) +
391
+ BasicObject.singleton_class.protected_instance_methods(false)
392
+ ).sort,
393
+ earth.shadow.methods(target: :class, scope: :protected, inherit: :exclude_class)
394
+ end
395
+ end
396
+
397
+ describe "[inherit: :exclude_object]" do
398
+ it "✓" do
399
+ assert_equal \
400
+ (
401
+ [:prepare, :stir] +
402
+ Object.singleton_class.protected_instance_methods(false) +
403
+ BasicObject.singleton_class.protected_instance_methods(false) +
404
+ Class.protected_instance_methods(false) +
405
+ Module.protected_instance_methods(false)
406
+ ).sort,
407
+ earth.shadow.methods(target: :class, scope: :protected, inherit: :exclude_object)
408
+ end
409
+ end
410
+
411
+ describe "[inherit: Module]" do
412
+ it "✓" do
413
+ assert_equal \
414
+ (
415
+ [:prepare, :stir] +
416
+ Object.singleton_class.protected_instance_methods(false)
417
+ ).sort,
418
+ earth.shadow.methods(target: :class, scope: :protected, inherit: Object.singleton_class)
419
+ end
420
+ end
421
+
422
+ describe "[inherit: :all, inherit: true]" do
423
+ it "✓" do
424
+ assert_equal \
425
+ (
426
+ [:prepare, :stir] +
427
+ Object.singleton_class.protected_instance_methods(true)
428
+ ).sort,
429
+ earth.shadow.methods(target: :class, scope: :protected, inherit: :all)
430
+ end
431
+ end
432
+ end
433
+
434
+ describe "[scope: :private]" do
435
+ describe "[inherit: :singleton]" do
436
+ it "✓" do
437
+ assert_equal \
438
+ [:think],
439
+ earth.shadow.methods(target: :class, scope: :private, inherit: :singleton)
440
+ end
441
+ end
442
+
443
+ describe "[inherit: :self, inherit: false]" do
444
+ it "✓" do
445
+ assert_equal \
446
+ [:think],
447
+ earth.shadow.methods(target: :class, scope: :private, inherit: :self)
448
+ end
449
+ end
450
+
451
+ describe "[inherit: :exclude_class]" do
452
+ it "✓" do
453
+ assert_equal \
454
+ (
455
+ [:think, :idea] +
456
+ Object.singleton_class.private_instance_methods(false) +
457
+ BasicObject.singleton_class.private_instance_methods(false)
458
+ ).sort,
459
+ earth.shadow.methods(target: :class, scope: :private, inherit: :exclude_class)
460
+ end
461
+ end
462
+
463
+ describe "[inherit: :exclude_object]" do
464
+ it "✓" do
465
+ assert_equal \
466
+ (
467
+ [:think, :idea] +
468
+ Object.singleton_class.private_instance_methods(false) +
469
+ BasicObject.singleton_class.private_instance_methods(false) +
470
+ Class.private_instance_methods(false) +
471
+ Module.private_instance_methods(false)
472
+ ).uniq.sort,
473
+ earth.shadow.methods(target: :class, scope: :private, inherit: :exclude_object)
474
+ end
475
+ end
476
+
477
+ describe "[inherit: Module]" do
478
+ it "✓" do
479
+ assert_equal \
480
+ (
481
+ [:think, :idea] +
482
+ Object.singleton_class.private_instance_methods(false)
483
+ ).sort,
484
+ earth.shadow.methods(target: :class, scope: :private, inherit: Object.singleton_class)
485
+ end
486
+ end
487
+
488
+ describe "[inherit: :all, inherit: true]" do
489
+ it "✓" do
490
+ assert_equal \
491
+ (
492
+ [:think, :idea] +
493
+ Object.singleton_class.private_instance_methods(true)
494
+ ).sort,
495
+ earth.shadow.methods(target: :class, scope: :private, inherit: :all)
496
+ end
497
+ end
498
+ end
499
+
500
+ describe "[scope: :all]" do
501
+ describe "[inherit: :singleton]" do
502
+ it "✓" do
503
+ assert_equal \
504
+ [:construct, :prepare, :think],
505
+ earth.shadow.methods(target: :class, scope: :all, inherit: :singleton)
506
+ end
507
+ end
508
+
509
+ describe "[inherit: :self, inherit: false]" do
510
+ it "✓" do
511
+ assert_equal \
512
+ [:construct, :prepare, :think],
513
+ earth.shadow.methods(target: :class, scope: :all, inherit: :self)
514
+ end
515
+ end
516
+
517
+ describe "[inherit: :exclude_class]" do
518
+ it "✓" do
519
+ assert_equal \
520
+ (
521
+ [:construct, :idea, :make, :prepare, :stir, :think] +
522
+ Object.singleton_class.instance_methods(false) +
523
+ Object.singleton_class.private_instance_methods(false) +
524
+ BasicObject.singleton_class.instance_methods(false) +
525
+ BasicObject.singleton_class.private_instance_methods(false)
526
+ ).sort,
527
+ earth.shadow.methods(target: :class, scope: :all, inherit: :exclude_class)
528
+ end
529
+ end
530
+
531
+ describe "[inherit: :exclude_object]" do
532
+ it "✓" do
533
+ assert_equal \
534
+ (
535
+ [:construct, :idea, :make, :prepare, :stir, :think] +
536
+ Object.singleton_class.instance_methods(false) +
537
+ Object.singleton_class.private_instance_methods(false) +
538
+ BasicObject.singleton_class.instance_methods(false) +
539
+ BasicObject.singleton_class.private_instance_methods(false) +
540
+ Class.instance_methods(false) +
541
+ Class.private_instance_methods(false) +
542
+ Module.instance_methods(false) +
543
+ Module.private_instance_methods(false)
544
+ ).uniq.sort,
545
+ earth.shadow.methods(target: :class, scope: :all, inherit: :exclude_object)
546
+ end
547
+ end
548
+
549
+ describe "[inherit: Module]" do
550
+ it "✓" do
551
+ assert_equal \
552
+ (
553
+ [:construct, :idea, :make, :prepare, :stir, :think] +
554
+ Object.singleton_class.instance_methods(false) +
555
+ Object.singleton_class.private_instance_methods(false)
556
+ ).sort,
557
+ earth.shadow.methods(target: :class, scope: :all, inherit: Object.singleton_class)
558
+ end
559
+ end
560
+
561
+ describe "[inherit: :all, inherit: true]" do
562
+ it "✓" do
563
+ assert_equal \
564
+ (
565
+ [:construct, :idea, :make, :prepare, :stir, :think] +
566
+ Object.singleton_class.instance_methods(true) +
567
+ Object.singleton_class.private_instance_methods(true)
568
+ ).sort,
569
+ earth.shadow.methods(target: :class, scope: :all, inherit: :all)
570
+ end
571
+ end
572
+ end
573
+ end
574
+
575
+
576
+ describe "[target: :instances]" do
577
+ describe "context: called for a non-module" do
578
+ it "✗ will raise an ArgumentError" do
579
+ assert_raises(ArgumentError) {
580
+ earth.shadow.methods(target: :instances)
581
+ }
582
+ end
583
+ end
584
+
585
+ describe "[inherit: :singleton]" do
586
+ it "✗ will raise an ArgumentError" do
587
+ assert_raises(ArgumentError) {
588
+ planet.shadow.methods(target: :instances, inherit: :singleton)
589
+ }
590
+ end
591
+ end
592
+
593
+ describe "[scope: :public]" do
594
+ describe "[inherit: :self, inherit: false]" do
595
+ it "✓" do
596
+ assert_equal \
597
+ [:rotate],
598
+ planet.shadow.methods(target: :instances, scope: :public, inherit: :self)
599
+ end
600
+ end
601
+
602
+ describe "[inherit: :exclude_object]" do
603
+ it "✓" do
604
+ assert_equal \
605
+ [:fly, :rotate],
606
+ planet.shadow.methods(target: :instances, scope: :public, inherit: :exclude_object)
607
+ end
608
+ end
609
+
610
+ describe "[inherit: Module]" do
611
+ it "✓" do
612
+ assert_equal \
613
+ [:fly, :rotate],
614
+ planet.shadow.methods(target: :instances, scope: :public, inherit: astronomical_body)
615
+ end
616
+ end
617
+
618
+ describe "[inherit: :all, inherit: true]" do
619
+ it "✓" do
620
+ assert_equal \
621
+ (
622
+ [:fly, :rotate] +
623
+ Object.public_instance_methods
624
+ ).sort,
625
+ planet.shadow.methods(target: :instances, scope: :public, inherit: :all)
626
+ end
627
+ end
628
+ end
629
+
630
+ describe "[scope: :protected]" do
631
+ describe "[inherit: :self, inherit: false]" do
632
+ it "✓" do
633
+ assert_equal \
634
+ [:bump],
635
+ planet.shadow.methods(target: :instances, scope: :protected, inherit: :self)
636
+ end
637
+ end
638
+
639
+ describe "[inherit: :exclude_class]" do
640
+ it "✓" do
641
+ assert_equal \
642
+ [:bump, :crash],
643
+ planet.shadow.methods(target: :instances, scope: :protected, inherit: :exclude_class)
644
+ end
645
+ end
646
+
647
+ describe "[inherit: :exclude_object]" do
648
+ it "✓" do
649
+ assert_equal \
650
+ [:bump, :crash],
651
+ planet.shadow.methods(target: :instances, scope: :protected, inherit: :exclude_object)
652
+ end
653
+ end
654
+
655
+ describe "[inherit: Module]" do
656
+ it "✓" do
657
+ assert_equal \
658
+ [:bump, :crash],
659
+ planet.shadow.methods(target: :instances, scope: :protected, inherit: astronomical_body)
660
+ end
661
+ end
662
+
663
+ describe "[inherit: :all, inherit: true]" do
664
+ it "✓" do
665
+ assert_equal \
666
+ (
667
+ [:bump, :crash] +
668
+ Object.protected_instance_methods
669
+ ).sort,
670
+ planet.shadow.methods(target: :instances, scope: :protected, inherit: :all)
671
+ end
672
+ end
673
+ end
674
+
675
+ describe "[scope: :private]" do
676
+ describe "[inherit: :self, inherit: false]" do
677
+ it "✓" do
678
+ assert_equal \
679
+ [:implode],
680
+ planet.shadow.methods(target: :instances, scope: :private, inherit: :self)
681
+ end
682
+ end
683
+
684
+ describe "[inherit: :exclude_class]" do
685
+ it "✓" do
686
+ assert_equal \
687
+ [:explode, :implode],
688
+ planet.shadow.methods(target: :instances, scope: :private, inherit: :exclude_class)
689
+ end
690
+ end
691
+
692
+ describe "[inherit: :exclude_object]" do
693
+ it "✓" do
694
+ assert_equal \
695
+ [:explode, :implode],
696
+ planet.shadow.methods(target: :instances, scope: :private, inherit: :exclude_object)
697
+ end
698
+ end
699
+
700
+ describe "[inherit: Module]" do
701
+ it "✓" do
702
+ assert_equal \
703
+ [:explode, :implode],
704
+ planet.shadow.methods(target: :instances, scope: :private, inherit: astronomical_body)
705
+ end
706
+ end
707
+
708
+ describe "[inherit: :all, inherit: true]" do
709
+ it "✓" do
710
+ assert_equal \
711
+ (
712
+ [:explode, :implode] +
713
+ Object.private_instance_methods
714
+ ).sort,
715
+ planet.shadow.methods(target: :instances, scope: :private, inherit: :all)
716
+ end
717
+ end
718
+ end
719
+
720
+ describe "[scope: :all]" do
721
+ describe "[inherit: :self, inherit: false]" do
722
+ it "✓" do
723
+ assert_equal \
724
+ [:bump, :implode, :rotate],
725
+ planet.shadow.methods(target: :instances, scope: :all, inherit: :self)
726
+ end
727
+ end
728
+
729
+ describe "[inherit: :exclude_class]" do
730
+ it "✓" do
731
+ assert_equal \
732
+ [:bump, :crash, :explode, :fly, :implode, :rotate],
733
+ planet.shadow.methods(target: :instances, scope: :all, inherit: :exclude_object)
734
+ end
735
+ end
736
+
737
+ describe "[inherit: :exclude_object]" do
738
+ it "✓" do
739
+ assert_equal \
740
+ [:bump, :crash, :explode, :fly, :implode, :rotate],
741
+ planet.shadow.methods(target: :instances, scope: :all, inherit: :exclude_object)
742
+ end
743
+ end
744
+
745
+ describe "[inherit: Module]" do
746
+ it "✓" do
747
+ assert_equal \
748
+ [:bump, :crash, :explode, :fly, :implode, :rotate],
749
+ planet.shadow.methods(target: :instances, scope: :all, inherit: astronomical_body)
750
+ end
751
+ end
752
+
753
+ describe "[inherit: :all, inherit: true]" do
754
+ it "✓" do
755
+ assert_equal \
756
+ (
757
+ [:bump, :crash, :explode, :fly, :implode, :rotate] +
758
+ Object.instance_methods +
759
+ Object.private_instance_methods
760
+ ).sort,
761
+ planet.shadow.methods(target: :instances, scope: :all, inherit: :all)
762
+ end
763
+ end
764
+ end
765
+ end
766
+ end
767
+
768
+ describe "#method?" do
769
+ it "will return true if method exists" do
770
+ assert earth.shadow.method?(:bump)
771
+ assert earth.shadow.method?(:magnetize)
772
+ assert earth.shadow.method?(:stir, target: :class)
773
+
774
+ assert planet.shadow.method?(:idea)
775
+ assert planet.shadow.method?(:implode, target: :instances)
776
+ end
777
+
778
+ it "will return false if method does not exist" do
779
+ refute earth.shadow.method?(:idea)
780
+ refute earth.shadow.method?(:idiosyncratic)
781
+ refute earth.shadow.method?(:magnetize, target: :class)
782
+
783
+ refute planet.shadow.method?(:rotate)
784
+ refute planet.shadow.method?(:stir, target: :instances)
785
+ end
786
+ end
787
+
788
+ describe "#method_scope" do
789
+ it "will return :public for public methods" do
790
+ assert_equal :public, earth.shadow.method_scope(:rotate)
791
+ assert_equal :public, earth.shadow.method_scope(:develop)
792
+ assert_equal :public, earth.shadow.method_scope(:make, target: :class)
793
+
794
+ assert_equal :public, planet.shadow.method_scope(:construct)
795
+ assert_equal :public, planet.shadow.method_scope(:rotate, target: :instances)
796
+ end
797
+
798
+ it "will return :protected for protected methods" do
799
+ assert_equal :protected, earth.shadow.method_scope(:bump)
800
+ assert_equal :protected, earth.shadow.method_scope(:magnetize)
801
+ assert_equal :protected, earth.shadow.method_scope(:stir, target: :class)
802
+
803
+ assert_equal :protected, planet.shadow.method_scope(:prepare)
804
+ assert_equal :protected, planet.shadow.method_scope(:bump, target: :instances)
805
+ end
806
+
807
+ it "will return :private for private methods" do
808
+ assert_equal :private, earth.shadow.method_scope(:implode)
809
+ assert_equal :private, earth.shadow.method_scope(:repair)
810
+ assert_equal :private, earth.shadow.method_scope(:think, target: :class)
811
+
812
+ assert_equal :private, planet.shadow.method_scope(:idea)
813
+ assert_equal :private, planet.shadow.method_scope(:implode, target: :instances)
814
+ end
815
+
816
+ it "will return nil if method does not exist" do
817
+ refute earth.shadow.method?(:idea)
818
+ refute earth.shadow.method?(:magnetize, target: :class)
819
+
820
+ refute planet.shadow.method?(:rotate)
821
+ refute planet.shadow.method?(:stir, target: :instances)
822
+ end
823
+ end
824
+
825
+ describe "#method" do
826
+ it "will return Method object" do
827
+ assert_instance_of Method, earth.shadow.method(:bump)
828
+ assert_instance_of Method, earth.shadow.method(:magnetize)
829
+ assert_instance_of Method, earth.shadow.method(:stir, target: :class)
830
+
831
+ assert_instance_of Method, planet.shadow.method(:idea)
832
+ end
833
+
834
+ it "will return UnboundMethod object for target: instances "do
835
+ assert_instance_of UnboundMethod, planet.shadow.method(:implode, target: :instances)
836
+ end
837
+
838
+ it "will return UnboundMethod object when unbind: true is passed" do
839
+ assert_instance_of UnboundMethod, earth.shadow.method(:bump, unbind: true)
840
+ end
841
+
842
+ it "will return nil if method does not exist" do
843
+ assert_nil earth.shadow.method(:fun)
844
+ end
845
+
846
+
847
+ it "returns an array of all (unbound) methods in the lookup chain if all: true is passed" do
848
+ def earth.bump() end
849
+
850
+ res = earth.shadow.method(:bump, all: true)
851
+ assert_instance_of Array, res
852
+ assert_instance_of UnboundMethod, res[0]
853
+ assert_instance_of UnboundMethod, res[1]
854
+ end
855
+ end
856
+
857
+ describe "#method_lookup_chain" do
858
+ describe "[inherit: :exclude_class]" do
859
+ it "shows the lookup chain (including singleton class) for non-classes, stops lookup chain before Class" do
860
+ assert_equal \
861
+ [earth.singleton_class, planet, astronomical_body],
862
+ earth.shadow.method_lookup_chain
863
+ end
864
+
865
+ it "shows the lookup chain for classes, stops lookup chain before Class" do
866
+ assert_equal \
867
+ [
868
+ planet,
869
+ astronomical_body,
870
+ Object,
871
+ BasicObject,
872
+ ].map(&:singleton_class),
873
+ planet.shadow.method_lookup_chain
874
+ end
875
+ end
876
+
877
+ describe "[inherit: :all]" do
878
+ it "shows the lookup chain (including singleton class) for non-classes" do
879
+ assert_equal \
880
+ [
881
+ earth.singleton_class,
882
+ planet,
883
+ astronomical_body,
884
+ Object,
885
+ Minitest::Expectations,
886
+ ObjectShadow::ObjectMethod,
887
+ Kernel,
888
+ BasicObject,
889
+ ],
890
+ earth.shadow.method_lookup_chain(inherit: :all)
891
+ end
892
+
893
+ it "shows the lookup chain for classes" do
894
+ assert_equal \
895
+ [
896
+ planet,
897
+ astronomical_body,
898
+ Object,
899
+ BasicObject,
900
+ ].map(&:singleton_class) + [
901
+ Class,
902
+ Module,
903
+ Object,
904
+ Minitest::Expectations,
905
+ ObjectShadow::ObjectMethod,
906
+ Kernel,
907
+ BasicObject,
908
+ ],
909
+ planet.shadow.method_lookup_chain(inherit: :all)
910
+ end
911
+ end
912
+ end
913
+ end
914
+