attic 0.9.0.pre.rc2 → 1.0.0.pre.RC3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|