29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
# File 'lib/fattr.rb', line 29
def fattrs(*args, &block)
unless args.empty?
returned = Hash.new
args.flatten!
args.compact!
all_hashes = args.all?{|arg| Hash===arg}
names_and_configs = {}
if all_hashes
args.each do |hash|
hash.each do |key, val|
name = key.to_s
config = Hash===val ? val : {:default => val}
names_and_configs[name] = config
end
end
else
config = Hash===args.last ? args.pop : {}
names = args.select{|arg| Symbol===arg or String===arg}.map{|arg| arg.to_s}
names.each do |name|
names_and_configs[name] = config
end
end
initializers = __fattrs__.initializers
names_and_configs.each do |name, config|
raise(NameError, "bad instance variable name '@#{ name }'") if("@#{ name }" =~ %r/[!?=]$/o)
name = name.to_s
default = nil
default = config[:default] if config.has_key?(:default)
default = config['default'] if config.has_key?('default')
inheritable = false
if Class===self
inheritable = config[:inheritable] if config.has_key?(:inheritable)
inheritable = config['inheritable'] if config.has_key?('inheritable')
end
initialize = (
block || (
unless inheritable
lambda{ default }
else
lambda do
if ancestors[1..-1].any?{|ancestor| ancestor.Fattrs.include?(name)}
ancestors[1].send(name)
else
default
end
end
end
)
)
initializer = lambda do |this|
Object.instance_method('instance_eval').bind(this).call(&initialize)
end
initializer_id = initializer.object_id
__fattrs__.initializers[name] = initializer
compile = lambda do |code|
begin
module_eval(code)
rescue SyntaxError
raise(SyntaxError, "\n#{ code }\n")
end
end
code = " def \#{ name }=(*value, &block)\n value.unshift block if block\n @\#{ name } = value.first\n end\n code\n compile[code]\n\n # getter, providing a value or block causes it to acts as setter\n code = <<-code\n def \#{ name }(*value, &block)\n value.unshift block if block\n return self.send('\#{ name }=', value.first) unless value.empty?\n \#{ name }! unless defined? @\#{ name }\n @\#{ name }\n end\n code\n compile[code]\n\n # bang method re-calls any initializer given at declaration time\n code = <<-code\n def \#{ name }!\n initializer = ObjectSpace._id2ref \#{ initializer_id }\n self.\#{ name } = initializer.call(self)\n @\#{ name }\n end\n code\n compile[code]\n\n # query simply defers to getter - cast to bool\n code = <<-code\n def \#{ name }?\n self.\#{ name }\n end\n code\n compile[code]\n\n fattrs << name\n returned[name] = initializer \n end\n\n returned\n else\n begin\n __fattr_list__\n rescue NameError\n singleton_class =\n class << self\n self\n end\n klass = self\n singleton_class.module_eval do\n fattr_list = List.new \n define_method('fattr_list'){ klass == self ? fattr_list : raise(NameError) }\n alias_method '__fattr_list__', 'fattr_list'\n end\n __fattr_list__\n end\n end\nend\n"
|