object_shadow 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+