attic 0.9.0.pre.rc2 → 1.0.0.pre.RC3
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -5
- data/README.md +3 -6
- data/attic.gemspec +1 -1
- data/lib/attic/class_methods.rb +4 -2
- data/lib/attic/instance_methods.rb +29 -2
- data/lib/attic.rb +32 -21
- data/try/01_mixins_tryouts.rb +62 -14
- data/try/10_attic_tryouts.rb +38 -28
- data/try/20_accessing_tryouts.rb +9 -8
- data/try/25_string_tryouts.rb +6 -6
- data/try/30_nometaclass_tryouts.rb +8 -0
- metadata +3 -7
- data/lib/attic/core_ext.rb +0 -70
- data/try/X1_metaclasses.rb +0 -112
- data/try/X2_extending.rb +0 -27
- data/try/X3_nosingleton.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd963a291df8b9b731adac87c5886f9f25b83606c7a171347d0e6e8396c98533
|
4
|
+
data.tar.gz: '0113974e4554b99b6061005f7d0218398592384d7bcd85f2fe4899b0d51232e9'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92fcb090695086fcfa85472d69f8f2631054c035c99f3024f7cebc67b730e418c6ed650f2c8042f4c6882e6782c1febea6fc73768c43258e6e84d28d6219c149
|
7
|
+
data.tar.gz: a22f7131a2aca63c30bde0546c3ad0fdf77ef095b3d2cd5ed23853da02b314535c26771a900df059de056a76e32d50ad5e4802eab943736e9b423719ad036731
|
data/Gemfile.lock
CHANGED
@@ -5,7 +5,7 @@ GEM
|
|
5
5
|
byebug (11.1.3)
|
6
6
|
coderay (1.1.3)
|
7
7
|
drydock (0.6.9)
|
8
|
-
json (2.7.
|
8
|
+
json (2.7.2)
|
9
9
|
language_server-protocol (3.17.0.3)
|
10
10
|
method_source (1.0.0)
|
11
11
|
parallel (1.24.0)
|
@@ -46,8 +46,7 @@ GEM
|
|
46
46
|
yard (0.9.36)
|
47
47
|
|
48
48
|
PLATFORMS
|
49
|
-
arm64-darwin-
|
50
|
-
ruby
|
49
|
+
arm64-darwin-22
|
51
50
|
|
52
51
|
DEPENDENCIES
|
53
52
|
byebug
|
@@ -57,7 +56,7 @@ DEPENDENCIES
|
|
57
56
|
tryouts (= 2.2.0.pre.RC1)
|
58
57
|
|
59
58
|
RUBY VERSION
|
60
|
-
ruby 3.
|
59
|
+
ruby 3.2.0p0
|
61
60
|
|
62
61
|
BUNDLED WITH
|
63
|
-
2.
|
62
|
+
2.4.12
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Attic - v1.0-RC1 (
|
1
|
+
# Attic - v1.0-RC1 (2024-04-01)
|
2
2
|
|
3
3
|
A place to hide private instance variables in your Ruby objects: in the attic.
|
4
4
|
|
@@ -11,6 +11,7 @@ Like, _why though_? Well sometimes you want to hide thing from the public interf
|
|
11
11
|
|
12
12
|
### Example
|
13
13
|
|
14
|
+
|
14
15
|
```ruby
|
15
16
|
require 'attic'
|
16
17
|
|
@@ -49,7 +50,7 @@ Like, _why though_? Well sometimes you want to hide thing from the public interf
|
|
49
50
|
|
50
51
|
### **Objects without singleton classes
|
51
52
|
|
52
|
-
Symbol, Integer, Float, TrueClass, FalseClass, NilClass, and
|
53
|
+
Symbol, Integer, Float, TrueClass, FalseClass, NilClass, and Integer are all objects that do not have singleton classes. TrueClass, FalseClass, and NilClass are all singletons themselves. Integer is a singleton of Integer.
|
53
54
|
|
54
55
|
These objects do not have metaclasses so the attic is hidden in the object itself.
|
55
56
|
|
@@ -139,10 +140,6 @@ which produced the same results for both.
|
|
139
140
|
]
|
140
141
|
```
|
141
142
|
|
142
|
-
## Credits
|
143
|
-
|
144
|
-
* (@delano) Delano Mandelbaum
|
145
|
-
|
146
143
|
|
147
144
|
## License
|
148
145
|
|
data/attic.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "attic"
|
3
|
-
s.version = "0.
|
3
|
+
s.version = "1.0.0-RC3"
|
4
4
|
s.summary = "When in doubt, store it in the attic"
|
5
5
|
s.description = "Attic: a place to hide metadata about the class or variable itself (e.g. SHA hash summaries)."
|
6
6
|
s.authors = ["Delano Mandelbaum"]
|
data/lib/attic/class_methods.rb
CHANGED
@@ -21,8 +21,8 @@ module Attic
|
|
21
21
|
# for the object. In either case, objects that cannot have
|
22
22
|
# cannot have a dedicated singleton class (e.g. nil, true,
|
23
23
|
# false) will raise a TypeError. We rescue this and add the
|
24
|
-
# object to the NoSingletonError list so we don't
|
25
|
-
#
|
24
|
+
# object to the NoSingletonError list so we don't keep
|
25
|
+
# trying to access its singleton class over and over.
|
26
26
|
!singleton_class.nil?
|
27
27
|
|
28
28
|
rescue TypeError
|
@@ -40,6 +40,8 @@ module Attic
|
|
40
40
|
|
41
41
|
safe_name = "@_attic_#{name}"
|
42
42
|
instance_variable_set(safe_name, name)
|
43
|
+
rescue TypeError => e
|
44
|
+
raise NoSingletonError, name, caller
|
43
45
|
end
|
44
46
|
|
45
47
|
def attic_variable?(name)
|
@@ -8,12 +8,39 @@ module Attic
|
|
8
8
|
module InstanceMethods
|
9
9
|
|
10
10
|
def attic
|
11
|
-
raise
|
11
|
+
raise NoSingletonError, self, caller unless attic?
|
12
12
|
|
13
13
|
singleton_class
|
14
14
|
|
15
15
|
rescue TypeError
|
16
|
-
|
16
|
+
NoSingletonError.add_member self
|
17
|
+
end
|
18
|
+
|
19
|
+
# A quick way to check if this object instance already has a
|
20
|
+
# dedicated singleton class. We like to know this upfront
|
21
|
+
# because this is where our attic variables are to be stored.
|
22
|
+
def attic?
|
23
|
+
return false if NoSingletonError.member? self
|
24
|
+
|
25
|
+
# NOTE: Calling this on an object for the first time lazily
|
26
|
+
# creates a singleton class for itself. Another way of doing
|
27
|
+
# the same thing is to attempt defining a singleton method
|
28
|
+
# for the object. In either case, objects that cannot have
|
29
|
+
# cannot have a dedicated singleton class (e.g. nil, true,
|
30
|
+
# false) will raise a TypeError. We rescue this and add the
|
31
|
+
# object to the NoSingletonError list so we don't keep
|
32
|
+
# trying to access its singleton class over and over.
|
33
|
+
#
|
34
|
+
# NOTE 2: Module#singleton_class? is only available for
|
35
|
+
# modules and classes (which are also modules); it is not
|
36
|
+
# available for instances of classes.
|
37
|
+
#
|
38
|
+
!singleton_class.nil?
|
39
|
+
|
40
|
+
rescue TypeError
|
41
|
+
# Remember for next time.
|
42
|
+
NoSingletonError.add_member self
|
43
|
+
false
|
17
44
|
end
|
18
45
|
|
19
46
|
def attic_variables
|
data/lib/attic.rb
CHANGED
@@ -100,8 +100,7 @@ require_relative "attic/instance_methods"
|
|
100
100
|
# nil.attic.object_id #=> 800
|
101
101
|
#
|
102
102
|
module Attic
|
103
|
-
VERSION = '0.
|
104
|
-
attr_reader :all_attic_variables
|
103
|
+
VERSION = '1.0.0-RC3'.freeze unless defined?(VERSION)
|
105
104
|
|
106
105
|
# A convenince method at the class level for including
|
107
106
|
# ConstructMethods in the given object specifically.
|
@@ -121,8 +120,10 @@ module Attic
|
|
121
120
|
obj.include Attic::ClassMethods
|
122
121
|
end
|
123
122
|
|
123
|
+
# Friendly exception to say we're not to be included
|
124
|
+
#
|
124
125
|
def self.included(obj)
|
125
|
-
raise
|
126
|
+
raise RuntimeError, "Did you mean to `extend Attic`` in #{obj}"
|
126
127
|
end
|
127
128
|
|
128
129
|
def self.extended(obj)
|
@@ -145,28 +146,29 @@ module Attic
|
|
145
146
|
attic_vars = self.attic_variables.clone
|
146
147
|
klass.attic.instance_variable_set("@attic_variables", attic_vars)
|
147
148
|
end
|
148
|
-
if method_defined? :instance_variables
|
149
|
-
instance_variables_orig = instance_method(:instance_variables)
|
150
|
-
define_method :instance_variables do
|
151
|
-
ret =
|
149
|
+
if obj.method_defined? :instance_variables
|
150
|
+
instance_variables_orig = obj.instance_method(:instance_variables)
|
151
|
+
obj.define_method :instance_variables do
|
152
|
+
ret = instance_variables_orig.bind(self).call.clone
|
152
153
|
ret.reject! { |v| v.to_s =~ /^@___?attic/ } # match 2 or 3 underscores
|
153
154
|
ret
|
154
155
|
end
|
155
|
-
define_method :all_instance_variables do
|
156
|
-
|
156
|
+
obj.define_method :all_instance_variables do
|
157
|
+
instance_variables_orig.bind(self).call
|
157
158
|
end
|
158
159
|
end
|
159
160
|
|
160
161
|
rescue TypeError => e
|
161
|
-
raise
|
162
|
+
raise NoSingletonError, obj, caller
|
162
163
|
end
|
163
164
|
|
164
165
|
# A class method for defining variables to store in the attic.
|
165
166
|
# * +names+ is a list of variables names. Accessor methods are
|
166
167
|
# created for each variable name in the list.
|
167
168
|
#
|
168
|
-
# Returns
|
169
|
-
# given
|
169
|
+
# Returns an Array of all attic variables for the current
|
170
|
+
# class unless no arguments are given in which case it
|
171
|
+
# returns its singleton.
|
170
172
|
#
|
171
173
|
# e.g.
|
172
174
|
#
|
@@ -178,12 +180,12 @@ module Attic
|
|
178
180
|
# * <tt>String#timestamp</tt> for setting the value
|
179
181
|
#
|
180
182
|
def attic(*names)
|
181
|
-
return
|
183
|
+
return singleton_class if names.empty?
|
182
184
|
|
183
185
|
names.each do |name|
|
184
186
|
next if attic_variable? name
|
185
187
|
|
186
|
-
self.
|
188
|
+
self.attic_variables << name
|
187
189
|
|
188
190
|
unless method_defined?(name)
|
189
191
|
define_method(name) do
|
@@ -198,7 +200,17 @@ module Attic
|
|
198
200
|
end
|
199
201
|
end
|
200
202
|
|
201
|
-
|
203
|
+
attic_variables # only after defining new attic vars
|
204
|
+
end
|
205
|
+
|
206
|
+
def attic?
|
207
|
+
return false if NoSingletonError.member? self
|
208
|
+
|
209
|
+
singleton_class?
|
210
|
+
|
211
|
+
rescue TypeError
|
212
|
+
NoSingletonError.add_member self
|
213
|
+
false
|
202
214
|
end
|
203
215
|
|
204
216
|
# Returns an Array of attic variables for the current class.
|
@@ -208,12 +220,11 @@ module Attic
|
|
208
220
|
# String.attic :timestamp
|
209
221
|
# String.attic_variables # => [:timestamp]
|
210
222
|
#
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
# alias attic_vars attic_variables
|
223
|
+
def attic_variables
|
224
|
+
a = attic.instance_variable_get('@attic_variables')
|
225
|
+
a ||= attic.instance_variable_set('@attic_variables', [])
|
226
|
+
a
|
227
|
+
end
|
217
228
|
|
218
229
|
def attic_variable?(name)
|
219
230
|
attic_variables.member? name
|
data/try/01_mixins_tryouts.rb
CHANGED
@@ -1,17 +1,65 @@
|
|
1
|
-
|
1
|
+
require_relative '../lib/attic'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
#=>
|
3
|
+
#
|
4
|
+
# Tests for the Object mixins that Attic relies on.
|
5
|
+
#
|
6
|
+
|
7
|
+
## Has a valid NoMetaClass exception class
|
8
|
+
NoMetaClass < RuntimeError
|
9
|
+
#=> true
|
10
10
|
|
11
|
-
##
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
## Has a pre-populated array of built-ins without a metaclass
|
12
|
+
begin
|
13
|
+
Object::NOMETACLASS
|
14
|
+
rescue NameError => e
|
15
|
+
e.class
|
16
16
|
end
|
17
|
-
#=>
|
17
|
+
#=> NameError
|
18
|
+
|
19
|
+
## Has Object#metaclass method
|
20
|
+
Object.new.respond_to? :metaclass
|
21
|
+
#=> false
|
22
|
+
|
23
|
+
## Has Object#singleton_class method
|
24
|
+
Object.new.respond_to? :singleton_class
|
25
|
+
#=> true
|
26
|
+
|
27
|
+
## Object#singleton_class is a class
|
28
|
+
Object.new.singleton_class.class
|
29
|
+
#=> Class
|
30
|
+
|
31
|
+
## Object#singleton_class is a class
|
32
|
+
Object.new.singleton_class.object_id.class
|
33
|
+
#=> Integer
|
34
|
+
|
35
|
+
## Object#singleton_class is a class
|
36
|
+
a = Object.new
|
37
|
+
b = Object.new
|
38
|
+
a.singleton_class.object_id == b.singleton_class.object_id
|
39
|
+
#=> false
|
40
|
+
|
41
|
+
## Object#singleton_class is an Object class
|
42
|
+
Object.new.singleton_class.superclass
|
43
|
+
#=> Object
|
44
|
+
|
45
|
+
## Object#singleton_class is equivalent to `class << self; self; end;`
|
46
|
+
a = Object.new
|
47
|
+
a.singleton_class == (class << a; self; end)
|
48
|
+
#=> true
|
49
|
+
|
50
|
+
## Integer doesn't have a singleton_class
|
51
|
+
Integer.singleton_class?
|
52
|
+
#=> false
|
53
|
+
|
54
|
+
## Symbol doesn't have a singleton_class
|
55
|
+
Symbol.singleton_class?
|
56
|
+
#=> false
|
57
|
+
|
58
|
+
## Object has a singleton_class
|
59
|
+
Object.singleton_class?
|
60
|
+
#=> false
|
61
|
+
|
62
|
+
## Object#singleton_class is equivalent to Object#singleton_class
|
63
|
+
a = Object.new
|
64
|
+
a.singleton_class == a.singleton_class
|
65
|
+
#=> true
|
data/try/10_attic_tryouts.rb
CHANGED
@@ -1,41 +1,51 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
require_relative '../lib/attic'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Tests for the Attic module
|
5
|
+
#
|
6
|
+
|
7
|
+
## A class we define can extend Attic
|
8
|
+
class ::ExampleClass
|
5
9
|
extend Attic
|
6
|
-
def kind() :
|
10
|
+
def kind() :unlikely_value end
|
7
11
|
end
|
8
|
-
|
9
|
-
Worker.methods.member?(:attic) || Worker.methods.member?('attic')
|
12
|
+
ExampleClass.methods.member?(:attic)
|
10
13
|
#=> true
|
11
|
-
|
12
|
-
##
|
14
|
+
|
15
|
+
## Trying to include Attic raises an exception
|
13
16
|
begin
|
14
|
-
class ::
|
17
|
+
class ::ExampleClass
|
15
18
|
include Attic
|
16
19
|
end
|
17
|
-
rescue =>
|
18
|
-
|
20
|
+
rescue => e
|
21
|
+
e.class
|
19
22
|
end
|
20
|
-
#=>
|
23
|
+
#=> RuntimeError
|
21
24
|
|
22
|
-
##
|
23
|
-
|
24
|
-
w =
|
25
|
-
#w.attic :size
|
26
|
-
p Worker.instance_methods(false)
|
27
|
-
p Worker.methods.sort
|
25
|
+
## Can define attic variables at class level
|
26
|
+
ExampleClass.attic :size
|
27
|
+
w = ExampleClass.new
|
28
28
|
w.respond_to? :size
|
29
|
-
#=> true
|
30
|
-
|
31
|
-
##
|
32
|
-
|
29
|
+
#=> true
|
30
|
+
|
31
|
+
## Accessing attic vars at the instance level fails
|
32
|
+
begin
|
33
|
+
w = ExampleClass.new
|
34
|
+
w.attic :size, 2
|
35
|
+
rescue => e
|
36
|
+
e.class
|
37
|
+
end
|
38
|
+
#=> NoMethodError
|
39
|
+
|
40
|
+
## Can access attic vars the long way though
|
41
|
+
w = ExampleClass.new
|
33
42
|
w.attic_variable_set :size, 2
|
34
43
|
w.attic_variable_get :size
|
35
44
|
#=> 2
|
36
|
-
|
37
|
-
##
|
38
|
-
|
39
|
-
|
45
|
+
|
46
|
+
## Won't clobber an existing method with the same name
|
47
|
+
## NOTE: But also won't tell you it didn't define the method
|
48
|
+
ExampleClass.attic :kind
|
49
|
+
a = ExampleClass.new
|
40
50
|
a.kind
|
41
|
-
#=> :
|
51
|
+
#=> :unlikely_value
|
data/try/20_accessing_tryouts.rb
CHANGED
@@ -1,29 +1,30 @@
|
|
1
|
-
|
1
|
+
require_relative '../lib/attic'
|
2
|
+
|
2
3
|
class ::Worker
|
3
4
|
extend Attic
|
4
5
|
attic :size
|
5
6
|
end
|
6
7
|
|
7
|
-
|
8
|
+
|
8
9
|
## save an instance variable the long way
|
9
10
|
w = Worker.new
|
10
|
-
w.
|
11
|
-
w.
|
11
|
+
w.attic.instance_variable_set '@mattress', 'S&F'
|
12
|
+
w.attic.instance_variable_get '@mattress'
|
12
13
|
#=> 'S&F'
|
13
|
-
|
14
|
+
|
14
15
|
## save an instance variable the short way
|
15
16
|
w = Worker.new
|
16
17
|
w.size = :california_king
|
17
18
|
w.size
|
18
19
|
#=> :california_king
|
19
|
-
|
20
|
+
|
20
21
|
## new instances don't cross streams
|
21
22
|
w = Worker.new
|
22
23
|
w.size
|
23
24
|
#=> nil
|
24
|
-
|
25
|
+
|
25
26
|
## instance variables are hidden
|
26
27
|
w = Worker.new
|
27
|
-
w.
|
28
|
+
w.attic.instance_variable_set '@mattress', 'S&F'
|
28
29
|
w.instance_variables
|
29
30
|
## []
|
data/try/25_string_tryouts.rb
CHANGED
@@ -4,25 +4,25 @@ require 'attic'
|
|
4
4
|
String.extend Attic
|
5
5
|
String.respond_to? :attic
|
6
6
|
#=> true
|
7
|
-
|
7
|
+
|
8
8
|
## save an instance variable the long way
|
9
9
|
s = ""
|
10
|
-
s.
|
11
|
-
s.
|
10
|
+
s.attic.instance_variable_set '@mattress', 'S&F'
|
11
|
+
s.attic.instance_variable_get '@mattress'
|
12
12
|
#=> 'S&F'
|
13
13
|
|
14
14
|
## can create attributes
|
15
15
|
String.attic :goodies
|
16
16
|
#=> [:goodies]
|
17
|
-
|
17
|
+
|
18
18
|
## save an instance variable the short way
|
19
19
|
s = ""
|
20
20
|
s.goodies = :california_king
|
21
21
|
p s.instance_variables
|
22
|
-
p s.
|
22
|
+
p s.attic_variables
|
23
23
|
s.goodies
|
24
24
|
#=> :california_king
|
25
|
-
|
25
|
+
|
26
26
|
## String instances don't cross streams
|
27
27
|
String.extend Attic
|
28
28
|
String.attic :name
|
@@ -39,6 +39,14 @@ Symbol.extend Attic
|
|
39
39
|
:any.attic?
|
40
40
|
#=> false
|
41
41
|
|
42
|
+
## A String's attic vars appear in `all_instance_variables` do
|
43
|
+
String.extend Attic
|
44
|
+
String.attic :_name
|
45
|
+
a, b = 'String1', 'String2'
|
46
|
+
a._name = :roger
|
47
|
+
a.all_instance_variables
|
48
|
+
#=> [:@___attic_name]
|
49
|
+
|
42
50
|
## A Symbol's attic vars appear in `all_instance_variables` do
|
43
51
|
Symbol.extend Attic
|
44
52
|
Symbol.attic :_name
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.pre.RC3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-04-
|
11
|
+
date: 2024-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -69,7 +69,6 @@ files:
|
|
69
69
|
- attic.gemspec
|
70
70
|
- lib/attic.rb
|
71
71
|
- lib/attic/class_methods.rb
|
72
|
-
- lib/attic/core_ext.rb
|
73
72
|
- lib/attic/errors.rb
|
74
73
|
- lib/attic/instance_methods.rb
|
75
74
|
- try/01_mixins_tryouts.rb
|
@@ -78,9 +77,6 @@ files:
|
|
78
77
|
- try/25_string_tryouts.rb
|
79
78
|
- try/30_nometaclass_tryouts.rb
|
80
79
|
- try/40_explicit_accessor_tryouts.rb
|
81
|
-
- try/X1_metaclasses.rb
|
82
|
-
- try/X2_extending.rb
|
83
|
-
- try/X3_nosingleton.rb
|
84
80
|
homepage: https://github.com/delano/attic
|
85
81
|
licenses:
|
86
82
|
- MIT
|
@@ -100,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
96
|
- !ruby/object:Gem::Version
|
101
97
|
version: 1.3.1
|
102
98
|
requirements: []
|
103
|
-
rubygems_version: 3.
|
99
|
+
rubygems_version: 3.4.12
|
104
100
|
signing_key:
|
105
101
|
specification_version: 4
|
106
102
|
summary: When in doubt, store it in the attic
|
data/lib/attic/core_ext.rb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
|
2
|
-
class NoMetaClass < RuntimeError
|
3
|
-
end
|
4
|
-
|
5
|
-
# = Object
|
6
|
-
#
|
7
|
-
# These methods are copied directly from _why's metaid.rb.
|
8
|
-
# See: http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
|
9
|
-
class Object
|
10
|
-
|
11
|
-
unless defined?(::Object::NOMETACLASS)
|
12
|
-
# An Array of classes which do not have metaclasses.
|
13
|
-
NOMETACLASS = [Symbol, Fixnum].freeze
|
14
|
-
end
|
15
|
-
|
16
|
-
def nometaclass?
|
17
|
-
NOMETACLASS.member?(self)
|
18
|
-
end
|
19
|
-
|
20
|
-
def metaclass?
|
21
|
-
!NOMETACLASS.member?(self.class)
|
22
|
-
end
|
23
|
-
|
24
|
-
# A convenient method for getting the metaclass of the current object.
|
25
|
-
# i.e.
|
26
|
-
#
|
27
|
-
# class << self; self; end;
|
28
|
-
#
|
29
|
-
# NOTE: Some Ruby class do not have meta classes (see: NOMETACLASS).
|
30
|
-
# For these classes, this method returns the class itself. That means
|
31
|
-
# the instance variables will stored in the class itself.
|
32
|
-
def metaclass
|
33
|
-
if !self.metaclass?
|
34
|
-
raise NoMetaClass, self
|
35
|
-
else
|
36
|
-
class << self; self; end;
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# Execute a block +&blk+ within the metaclass of the current object.
|
41
|
-
def meta_eval &blk; metaclass.instance_eval &blk; end
|
42
|
-
|
43
|
-
# Add an instance method called +name+ to metaclass for the current object.
|
44
|
-
# This is useful because it will be available as a singleton method
|
45
|
-
# to all subclasses too.
|
46
|
-
def meta_def name, &blk
|
47
|
-
meta_eval { define_method name, &blk }
|
48
|
-
end
|
49
|
-
|
50
|
-
# Add a class method called +name+ for the current object's class. This
|
51
|
-
# isn't so special but it maintains consistency with meta_def.
|
52
|
-
def class_def name, &blk
|
53
|
-
class_eval { define_method name, &blk }
|
54
|
-
end
|
55
|
-
|
56
|
-
|
57
|
-
# A convenient method for getting the metaclass of the metaclass
|
58
|
-
# i.e.
|
59
|
-
#
|
60
|
-
# self.metaclass.metaclass
|
61
|
-
#
|
62
|
-
def metametaclass; self.metaclass.metaclass; end
|
63
|
-
|
64
|
-
def metameta_eval &blk; metametaclass.instance_eval &blk; end
|
65
|
-
|
66
|
-
def metameta_def name, &blk
|
67
|
-
metameta_eval { define_method name, &blk }
|
68
|
-
end
|
69
|
-
|
70
|
-
end
|
data/try/X1_metaclasses.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
# $ ruby tryouts/metaclasses.rb
|
2
|
-
|
3
|
-
class Object
|
4
|
-
|
5
|
-
# A convenient method for getting the metaclass of the current object.
|
6
|
-
# i.e.
|
7
|
-
#
|
8
|
-
# class << self; self; end;
|
9
|
-
#
|
10
|
-
def metaclass; class << self; self; end; end
|
11
|
-
|
12
|
-
# Execute a block +&blk+ within the metaclass of the current object.
|
13
|
-
def meta_eval &blk; metaclass.instance_eval &blk; end
|
14
|
-
|
15
|
-
# Add an instance method called +name+ to metaclass for the current object.
|
16
|
-
# This is useful because it will be available as a singleton method
|
17
|
-
# to all subclasses too.
|
18
|
-
def meta_def name, &blk
|
19
|
-
meta_eval { define_method name, &blk }
|
20
|
-
end
|
21
|
-
|
22
|
-
# Add a class method called +name+ for the current object's class. This
|
23
|
-
# isn't so special but it maintains consistency with meta_def.
|
24
|
-
def class_def name, &blk
|
25
|
-
class_eval { define_method name, &blk }
|
26
|
-
end
|
27
|
-
|
28
|
-
|
29
|
-
# A convenient method for getting the metaclass of the metaclass
|
30
|
-
# i.e.
|
31
|
-
#
|
32
|
-
# self.metaclass.metaclass
|
33
|
-
#
|
34
|
-
def metametaclass; self.metaclass.metaclass; end
|
35
|
-
|
36
|
-
def metameta_eval &blk; metametaclass.instance_eval &blk; end
|
37
|
-
|
38
|
-
def metameta_def name, &blk
|
39
|
-
metameta_eval { define_method name, &blk }
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
# Create an instance method
|
45
|
-
class NamedArray1
|
46
|
-
class_eval do
|
47
|
-
define_method(:name) do
|
48
|
-
:roger
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
p [1, NamedArray1.new.name]
|
53
|
-
|
54
|
-
# Create getter and setter instance methods
|
55
|
-
class NamedArray2
|
56
|
-
class_eval do
|
57
|
-
define_method(:name) do
|
58
|
-
instance_variable_get("@name")
|
59
|
-
end
|
60
|
-
define_method(:name=) do |val|
|
61
|
-
instance_variable_set("@name", val)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
a = NamedArray2.new
|
66
|
-
a.name = :roger
|
67
|
-
p [2, a.name, a.instance_variables]
|
68
|
-
|
69
|
-
# Create getter and setter instance methods,
|
70
|
-
# store instance variable in metaclass
|
71
|
-
class NamedArray3
|
72
|
-
class_eval do
|
73
|
-
define_method(:name) do
|
74
|
-
metaclass.instance_variable_get("@name")
|
75
|
-
end
|
76
|
-
define_method(:name=) do |val|
|
77
|
-
metaclass.instance_variable_set("@name", val)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
a = NamedArray3.new
|
82
|
-
a.name = :roger
|
83
|
-
p [3, a.name, a.instance_variables, a.metaclass.instance_variables]
|
84
|
-
|
85
|
-
# Create a module with the which puts the functionality
|
86
|
-
# in NamedArray3 into a class method.
|
87
|
-
module StorageArea
|
88
|
-
def store *junk
|
89
|
-
junk.each do |name|
|
90
|
-
class_eval do
|
91
|
-
define_method(name) do
|
92
|
-
metaclass.instance_variable_get("@#{name}")
|
93
|
-
end
|
94
|
-
define_method("#{name}=") do |val|
|
95
|
-
metaclass.instance_variable_set("@#{name}", val)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
class NamedArray4
|
102
|
-
extend StorageArea
|
103
|
-
store :name
|
104
|
-
end
|
105
|
-
a = NamedArray4.new
|
106
|
-
a.name = :roger
|
107
|
-
p [4, a.name, a.instance_variables, a.metaclass.instance_variables]
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
data/try/X2_extending.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
$:.unshift './lib'
|
2
|
-
require 'attic'
|
3
|
-
|
4
|
-
class A
|
5
|
-
extend Attic
|
6
|
-
attic :andy
|
7
|
-
end
|
8
|
-
|
9
|
-
class B < A
|
10
|
-
attic :size
|
11
|
-
end
|
12
|
-
|
13
|
-
class C
|
14
|
-
extend Attic
|
15
|
-
attic :third
|
16
|
-
end
|
17
|
-
|
18
|
-
a, b, c = A.new, B.new, C.new
|
19
|
-
|
20
|
-
a.andy, b.andy = 1, 2
|
21
|
-
|
22
|
-
p [a.respond_to?(:andy), b.respond_to?(:andy)] # true, true
|
23
|
-
p [a.andy, b.andy] # 1, 2
|
24
|
-
|
25
|
-
p [a.class.attic_vars, b.class.attic_vars, c.class.attic_vars]
|
26
|
-
|
27
|
-
|
data/try/X3_nosingleton.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# "".has_singleton_class? # => true
|
4
|
-
# :"".has_singleton_class? # => false
|
5
|
-
# 1.has_singleton_class? # =>
|
6
|
-
# nil.has_singleton_class? # => false
|
7
|
-
# NilClass.has_singleton_class? # => true
|
8
|
-
|
9
|
-
# members = ["", :food, 1, 1.00000001, nil, NilClass, true, TrueClass]
|
10
|
-
# members2 = members.clone
|
11
|
-
|
12
|
-
# members.each_with_index do |member, idx|
|
13
|
-
# puts "member: #{member.inspect}"
|
14
|
-
# member2 = members2[idx]
|
15
|
-
|
16
|
-
# member.has_a_dedicated_singleton_class? # => false
|
17
|
-
# member2.has_a_dedicated_singleton_class? # => false
|
18
|
-
# member.bestow_a_singleton_class!
|
19
|
-
# member.has_a_dedicated_singleton_class? # => true
|
20
|
-
# member2.has_a_dedicated_singleton_class? # => false
|
21
|
-
# member2.bestow_a_singleton_class!
|
22
|
-
# member2.has_a_dedicated_singleton_class? # => true
|
23
|
-
|
24
|
-
# member.singleton_class.object_id # => 600
|
25
|
-
# member2.singleton_class.object_id # => 700
|
26
|
-
|
27
|
-
# member.has_method?(:foo) # => false
|
28
|
-
# member.foo # => NoMethodError
|
29
|
-
# member.add_to_singleton_class(:foo)
|
30
|
-
# member.has_method?(:foo) # => true
|
31
|
-
# member.foo = :bar
|
32
|
-
# member.foo # => :bar
|
33
|
-
# member.foo.object_id # => 601
|
34
|
-
|
35
|
-
# member2.has_method?(:foo) # => false
|
36
|
-
# member2.foo # => NoMethodError
|
37
|
-
# member2.add_to_singleton_class(:foo)
|
38
|
-
# member2.has_method?(:foo) # => true
|
39
|
-
# member2.foo = :bar
|
40
|
-
# member2.foo # => :bar
|
41
|
-
# member2.foo.object_id # => 701
|
42
|
-
|
43
|
-
# member2.foo.object_id == member.foo.object_id # => false
|
44
|
-
# end
|