attic 0.3 → 0.3.1
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.
- data/CHANGES.txt +4 -0
- data/attic.gemspec +10 -1
- data/lib/attic.rb +83 -0
- data/lib/attic/mixins.rb +3 -0
- data/lib/attic/mixins/object.rb +57 -0
- data/try/01_mixins_tryouts.rb +12 -0
- data/try/10_attic_tryouts.rb +28 -0
- data/try/20_accessing_tryouts.rb +34 -0
- data/try/25_string_tryouts.rb +38 -0
- data/try/30_nometaclass_tryouts.rb +24 -0
- data/try/metaclasses.rb +112 -0
- metadata +11 -2
data/CHANGES.txt
CHANGED
data/attic.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
@spec = Gem::Specification.new do |s|
|
2
2
|
s.name = "attic"
|
3
3
|
s.rubyforge_project = "attic"
|
4
|
-
s.version = "0.3"
|
4
|
+
s.version = "0.3.1"
|
5
5
|
s.summary = "A place for Ruby objects to hide instance variables."
|
6
6
|
s.description = s.summary
|
7
7
|
s.author = "Delano Mandelbaum"
|
@@ -39,6 +39,15 @@
|
|
39
39
|
README.rdoc
|
40
40
|
Rakefile
|
41
41
|
attic.gemspec
|
42
|
+
lib/attic.rb
|
43
|
+
lib/attic/mixins.rb
|
44
|
+
lib/attic/mixins/object.rb
|
45
|
+
try/01_mixins_tryouts.rb
|
46
|
+
try/10_attic_tryouts.rb
|
47
|
+
try/20_accessing_tryouts.rb
|
48
|
+
try/25_string_tryouts.rb
|
49
|
+
try/30_nometaclass_tryouts.rb
|
50
|
+
try/metaclasses.rb
|
42
51
|
)
|
43
52
|
|
44
53
|
s.has_rdoc = true
|
data/lib/attic.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
|
2
|
+
require 'attic/mixins'
|
3
|
+
|
4
|
+
# = Attic
|
5
|
+
#
|
6
|
+
# A place to store instance variables.
|
7
|
+
#
|
8
|
+
module Attic
|
9
|
+
VERSION = '0.3.1'
|
10
|
+
|
11
|
+
def self.included(o)
|
12
|
+
raise "You probably meant to 'extend Attic' in #{o}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.extended(o)
|
16
|
+
## NOTE: This is just a reminder for a more descerning way to
|
17
|
+
## include the meta methods, instead of using a global mixin.
|
18
|
+
##o.class_eval do
|
19
|
+
## include ObjectHelpers
|
20
|
+
##end
|
21
|
+
|
22
|
+
# Create an instance method that returns the attic variables.
|
23
|
+
o.class_eval do
|
24
|
+
define_method :attic_vars do
|
25
|
+
self.class.attic_vars
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# A class method for defining variables to store in the attic.
|
31
|
+
# * +junk+ is a list of variables names. Accessor methods are
|
32
|
+
# created for each variable name in the list.
|
33
|
+
#
|
34
|
+
# Returns the list of attic variable names or if not junk was
|
35
|
+
# given, returns the metaclass.
|
36
|
+
#
|
37
|
+
# e.g.
|
38
|
+
#
|
39
|
+
# String.extend Attic
|
40
|
+
# String.attic :timestamp
|
41
|
+
#
|
42
|
+
# In this example, attic created two instance methods:
|
43
|
+
# * +String#timestamp+ for getting the value
|
44
|
+
# * +String#timestamp+ for setting the value
|
45
|
+
#
|
46
|
+
def attic *junk
|
47
|
+
return metaclass if junk.empty?
|
48
|
+
|
49
|
+
# Add the attic variables named to the list. Notice that we
|
50
|
+
# cheakily store this in the metameta class so as to not
|
51
|
+
# disturb the metaclass instance variables.
|
52
|
+
metametaclass.instance_variable_set("@attic", [attic_vars, *junk].flatten)
|
53
|
+
|
54
|
+
junk.each do |name|
|
55
|
+
class_eval do
|
56
|
+
define_method(name) do
|
57
|
+
metaclass.instance_variable_get("@#{name}")
|
58
|
+
end
|
59
|
+
define_method("#{name}=") do |val|
|
60
|
+
metaclass.instance_variable_set("@#{name}", val)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
attic_vars
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns an Array of attic variables for the current class.
|
69
|
+
# e.g.
|
70
|
+
#
|
71
|
+
# String.extend Attic
|
72
|
+
# String.attic :timestamp
|
73
|
+
# String.attic_vars # => [:timestamp]
|
74
|
+
#
|
75
|
+
def attic_vars
|
76
|
+
metametaclass.instance_variable_get("@attic") || []
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
# - Module#instance_method returns an UnboundMethod
|
83
|
+
# - http://ruby-doc.org/core/classes/Module.html#M001659
|
data/lib/attic/mixins.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
|
2
|
+
# = Object
|
3
|
+
#
|
4
|
+
# These methods are copied directly from _why's metaid.rb.
|
5
|
+
# See: http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
|
6
|
+
class Object
|
7
|
+
|
8
|
+
# An Array of classes which do not have metaclasses.
|
9
|
+
NOMETACLASS = [Symbol, Fixnum].freeze
|
10
|
+
|
11
|
+
# A convenient method for getting the metaclass of the current object.
|
12
|
+
# i.e.
|
13
|
+
#
|
14
|
+
# class << self; self; end;
|
15
|
+
#
|
16
|
+
# NOTE: Some Ruby class do not have meta classes (see: NOMETACLASS).
|
17
|
+
# For these classes, this method returns the class itself. That means
|
18
|
+
# the instance variables will stored in the class itself.
|
19
|
+
def metaclass;
|
20
|
+
if NOMETACLASS.member? self.class
|
21
|
+
self.class
|
22
|
+
else
|
23
|
+
class << self; self; end;
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Execute a block +&blk+ within the metaclass of the current object.
|
28
|
+
def meta_eval &blk; metaclass.instance_eval &blk; end
|
29
|
+
|
30
|
+
# Add an instance method called +name+ to metaclass for the current object.
|
31
|
+
# This is useful because it will be available as a singleton method
|
32
|
+
# to all subclasses too.
|
33
|
+
def meta_def name, &blk
|
34
|
+
meta_eval { define_method name, &blk }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add a class method called +name+ for the current object's class. This
|
38
|
+
# isn't so special but it maintains consistency with meta_def.
|
39
|
+
def class_def name, &blk
|
40
|
+
class_eval { define_method name, &blk }
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# A convenient method for getting the metaclass of the metaclass
|
45
|
+
# i.e.
|
46
|
+
#
|
47
|
+
# self.metaclass.metaclass
|
48
|
+
#
|
49
|
+
def metametaclass; self.metaclass.metaclass; end
|
50
|
+
|
51
|
+
def metameta_eval &blk; metametaclass.instance_eval &blk; end
|
52
|
+
|
53
|
+
def metameta_def name, &blk
|
54
|
+
metameta_eval { define_method name, &blk }
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
group "Attic"
|
2
|
+
library :attic, "lib"
|
3
|
+
tryouts "Basics" do
|
4
|
+
|
5
|
+
drill "can extend Attic", true do
|
6
|
+
class ::Worker
|
7
|
+
extend Attic
|
8
|
+
end
|
9
|
+
Worker.methods.member? :attic
|
10
|
+
end
|
11
|
+
|
12
|
+
drill "can't include Attic raises exception", :exception, RuntimeError do
|
13
|
+
class ::Worker
|
14
|
+
include Attic
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
drill "can define attic attribute", true do
|
19
|
+
Worker.attic :size
|
20
|
+
w = Worker.new
|
21
|
+
#w.attic :size
|
22
|
+
stash :methods, w.methods.sort
|
23
|
+
stash :metamethods, Worker.methods.sort
|
24
|
+
w.respond_to? :size
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
group "Attic"
|
2
|
+
library :attic, "lib"
|
3
|
+
tryouts "Setting and Getting" do
|
4
|
+
|
5
|
+
setup do
|
6
|
+
class ::Worker
|
7
|
+
extend Attic
|
8
|
+
attic :size
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
drill "save an instance variable the long way", 'S&F' do
|
13
|
+
w = Worker.new
|
14
|
+
w.metametaclass.instance_variable_set '@mattress', 'S&F'
|
15
|
+
w.metametaclass.instance_variable_get '@mattress'
|
16
|
+
end
|
17
|
+
|
18
|
+
drill "save an instance variable the short way", :california_king do
|
19
|
+
w = Worker.new
|
20
|
+
w.size = :california_king
|
21
|
+
w.size
|
22
|
+
end
|
23
|
+
|
24
|
+
drill "new instances don't cross streams", nil do
|
25
|
+
w = Worker.new
|
26
|
+
w.size
|
27
|
+
end
|
28
|
+
|
29
|
+
drill "instance variables are hidden", [] do
|
30
|
+
w = Worker.new
|
31
|
+
w.metametaclass.instance_variable_set '@mattress', 'S&F'
|
32
|
+
w.instance_variables
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
group "Attic"
|
2
|
+
library :attic, "lib"
|
3
|
+
tryouts "String Setting and Getting" do
|
4
|
+
|
5
|
+
drill "String can extend Attic", true do
|
6
|
+
String.extend Attic
|
7
|
+
String.respond_to? :attic
|
8
|
+
end
|
9
|
+
|
10
|
+
drill "save an instance variable the long way", 'S&F' do
|
11
|
+
s = ""
|
12
|
+
s.metametaclass.instance_variable_set '@mattress', 'S&F'
|
13
|
+
s.metametaclass.instance_variable_get '@mattress'
|
14
|
+
end
|
15
|
+
|
16
|
+
drill "can create attributes", [:goodies] do
|
17
|
+
String.attic :goodies
|
18
|
+
end
|
19
|
+
|
20
|
+
drill "save an instance variable the short way", :california_king do
|
21
|
+
s = ""
|
22
|
+
s.goodies = :california_king
|
23
|
+
stash :ivars, s.instance_variables
|
24
|
+
stash :avars, s.attic_vars
|
25
|
+
s.goodies
|
26
|
+
end
|
27
|
+
|
28
|
+
drill "String instances don't cross streams", false do
|
29
|
+
String.extend Attic
|
30
|
+
String.attic :name
|
31
|
+
a = "any"
|
32
|
+
a.name = :roger
|
33
|
+
a.name == "".name
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
group "No Meta Class"
|
2
|
+
library :attic, 'lib'
|
3
|
+
tryouts "Basics" do
|
4
|
+
|
5
|
+
dream :class, Array
|
6
|
+
dream [Symbol, Fixnum]
|
7
|
+
drill "has list of no metaclass classes" do
|
8
|
+
Object::NOMETACLASS
|
9
|
+
end
|
10
|
+
|
11
|
+
drill "Symbol metaclass returns Symbol", Symbol do
|
12
|
+
:any.metaclass
|
13
|
+
end
|
14
|
+
|
15
|
+
## NOTE: fails
|
16
|
+
drill "Symbol instances don't cross streams", true do
|
17
|
+
Symbol.extend Attic
|
18
|
+
Symbol.attic :name
|
19
|
+
a = :any
|
20
|
+
a.name = :roger
|
21
|
+
[a.name, :another.name]
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/try/metaclasses.rb
ADDED
@@ -0,0 +1,112 @@
|
|
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
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-07-
|
12
|
+
date: 2009-07-13 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -29,6 +29,15 @@ files:
|
|
29
29
|
- README.rdoc
|
30
30
|
- Rakefile
|
31
31
|
- attic.gemspec
|
32
|
+
- lib/attic.rb
|
33
|
+
- lib/attic/mixins.rb
|
34
|
+
- lib/attic/mixins/object.rb
|
35
|
+
- try/01_mixins_tryouts.rb
|
36
|
+
- try/10_attic_tryouts.rb
|
37
|
+
- try/20_accessing_tryouts.rb
|
38
|
+
- try/25_string_tryouts.rb
|
39
|
+
- try/30_nometaclass_tryouts.rb
|
40
|
+
- try/metaclasses.rb
|
32
41
|
has_rdoc: true
|
33
42
|
homepage: http://github.com/delano/attic
|
34
43
|
licenses: []
|