18
18
# Boston, MA 02110-1301, USA.
29
predicate? or imperative? or writer? end
30
def without_punctuation
31
to_s.delete("?!=").to_sym end
34
without_punctuation.to_s + "?" end
36
without_punctuation.to_s + "!" end
38
without_punctuation.to_s + "=" end
29
predicate? or imperative? or writer? end
30
def without_punctuation
31
to_s.delete("?!=").to_sym end
34
without_punctuation.to_s + "?" end
36
without_punctuation.to_s + "!" end
38
without_punctuation.to_s + "=" end
43
replace Hash[*collect(&block).flatten]
43
replace Hash[*collect(&block).flatten]
58
def define_hard_aliases (name_pairs)
59
for new_aliases, existing_name in name_pairs do
60
new_aliases.kind_of? Array or new_aliases = [new_aliases]
61
for new_alias in new_aliases do
62
alias_method(new_alias, existing_name)
67
def define_soft_aliases (name_pairs)
68
for new_aliases, existing_name in name_pairs do
69
new_aliases.kind_of? Array or new_aliases = [new_aliases]
70
for new_alias in new_aliases do
71
class_eval %{def #{new_alias}(*args, &block)
72
#{existing_name}(*args, &block) end}
78
:define_hard_alias => :define_hard_aliases,
79
:define_soft_alias => :define_soft_aliases
81
# This method lets you define predicates like :foo?,
82
# which will be defined to return the value of @foo.
83
def define_readers (*names)
84
for name in names.map { |x| x.to_sym } do
86
# There's no way to define an efficient reader whose
87
# name is different from the instance variable.
88
class_eval %{def #{name} ; @#{name.without_punctuation} end}
90
# Use `attr_reader' to define an efficient method.
96
def writer_defined? (name)
97
method_defined? name.to_sym.writer
100
# If you pass a predicate symbol :foo? to this method, it'll first
101
# define a regular writer method :foo, without a question mark.
102
# Then it'll define an imperative writer method :foo! as a shorthand
103
# for setting the property to true.
104
def define_writers (*names, &body)
105
for name in names.map { |x| x.to_sym } do
107
define_method(name.writer, &body)
109
attr_writer(name.without_punctuation)
112
class_eval %{def #{name.imperative}
113
self.#{name.writer} true end}
118
define_soft_aliases \
119
:define_reader => :define_readers,
120
:define_writer => :define_writers
122
# We don't need a singular alias for `define_accessors',
123
# because it always defines at least two methods.
125
def define_accessors (*names)
126
define_readers(*names)
127
define_writers(*names)
130
def define_opposite_readers (name_pairs)
131
name_pairs.collect! { |k, v| [k.to_sym, v.to_sym] }
132
for opposite_name, name in name_pairs do
133
define_reader(name) unless method_defined? name
134
class_eval %{def #{opposite_name} ; not #{name} end}
138
def define_opposite_writers (name_pairs)
139
name_pairs.collect! { |k, v| [k.to_sym, v.to_sym] }
140
for opposite_name, name in name_pairs do
141
define_writer(name) unless writer_defined? name
142
class_eval %{def #{opposite_name.writer} x
143
self.#{name.writer} !x end}
144
class_eval %{def #{opposite_name.imperative}
145
self.#{name.writer} false end}
149
define_soft_aliases \
150
:define_opposite_reader => :define_opposite_readers,
151
:define_opposite_writer => :define_opposite_writers
153
def define_opposite_accessors (name_pairs)
154
define_opposite_readers name_pairs
155
define_opposite_writers name_pairs
158
def define_reader_with_opposite (name_pair, &body)
159
name, opposite_name = name_pair.flatten.collect { |x| x.to_sym }
160
define_method(name, &body)
161
define_opposite_reader(opposite_name => name)
164
def define_writer_with_opposite (name_pair, &body)
165
name, opposite_name = name_pair.flatten.collect { |x| x.to_sym }
166
define_writer(name, &body)
167
define_opposite_writer(opposite_name => name)
58
def define_hard_aliases (name_pairs)
59
for new_aliases, existing_name in name_pairs do
60
new_aliases.kind_of? Array or new_aliases = [new_aliases]
61
for new_alias in new_aliases do
62
alias_method(new_alias, existing_name)
67
def define_soft_aliases (name_pairs)
68
for new_aliases, existing_name in name_pairs do
69
new_aliases.kind_of? Array or new_aliases = [new_aliases]
70
for new_alias in new_aliases do
71
class_eval %{def #{new_alias}(*args, &block)
72
#{existing_name}(*args, &block) end}
78
:define_hard_alias => :define_hard_aliases,
79
:define_soft_alias => :define_soft_aliases
81
# This method lets you define predicates like :foo?,
82
# which will be defined to return the value of @foo.
83
def define_readers (*names)
84
for name in names.map { |x| x.to_sym } do
86
# There's no way to define an efficient reader whose
87
# name is different from the instance variable.
88
class_eval %{def #{name} ; @#{name.without_punctuation} end}
90
# Use `attr_reader' to define an efficient method.
96
def writer_defined? (name)
97
method_defined? name.to_sym.writer
100
# If you pass a predicate symbol :foo? to this method, it'll first
101
# define a regular writer method :foo, without a question mark.
102
# Then it'll define an imperative writer method :foo! as a shorthand
103
# for setting the property to true.
104
def define_writers (*names, &body)
105
for name in names.map { |x| x.to_sym } do
107
define_method(name.writer, &body)
109
attr_writer(name.without_punctuation)
112
class_eval %{def #{name.imperative}
113
self.#{name.writer} true end}
118
define_soft_aliases \
119
:define_reader => :define_readers,
120
:define_writer => :define_writers
122
# We don't need a singular alias for `define_accessors',
123
# because it always defines at least two methods.
125
def define_accessors (*names)
126
define_readers(*names)
127
define_writers(*names)
130
def define_opposite_readers (name_pairs)
131
name_pairs.collect! { |k, v| [k.to_sym, v.to_sym] }
132
for opposite_name, name in name_pairs do
133
define_reader(name) unless method_defined? name
134
class_eval %{def #{opposite_name} ; not #{name} end}
138
def define_opposite_writers (name_pairs)
139
name_pairs.collect! { |k, v| [k.to_sym, v.to_sym] }
140
for opposite_name, name in name_pairs do
141
define_writer(name) unless writer_defined? name
142
class_eval %{def #{opposite_name.writer} x
143
self.#{name.writer} !x end}
144
class_eval %{def #{opposite_name.imperative}
145
self.#{name.writer} false end}
149
define_soft_aliases \
150
:define_opposite_reader => :define_opposite_readers,
151
:define_opposite_writer => :define_opposite_writers
153
def define_opposite_accessors (name_pairs)
154
define_opposite_readers name_pairs
155
define_opposite_writers name_pairs
158
def define_reader_with_opposite (name_pair, &body)
159
name, opposite_name = name_pair.flatten.collect { |x| x.to_sym }
160
define_method(name, &body)
161
define_opposite_reader(opposite_name => name)
164
def define_writer_with_opposite (name_pair, &body)
165
name, opposite_name = name_pair.flatten.collect { |x| x.to_sym }
166
define_writer(name, &body)
167
define_opposite_writer(opposite_name => name)
170
170
public :define_method
172
def define_methods (*names, &body)
173
names.each { |name| define_method(name, &body) }
176
def define_private_methods (*names, &body)
177
define_methods(*names, &body)
178
names.each { |name| private name }
181
def define_protected_methods (*names, &body)
182
define_methods(*names, &body)
183
names.each { |name| protected name }
186
def define_private_method (name, &body)
187
define_method(name, &body)
191
def define_protected_method (name, &body)
192
define_method(name, &body)
172
def define_methods (*names, &body)
173
names.each { |name| define_method(name, &body) }
176
def define_private_methods (*names, &body)
177
define_methods(*names, &body)
178
names.each { |name| private name }
181
def define_protected_methods (*names, &body)
182
define_methods(*names, &body)
183
names.each { |name| protected name }
186
def define_private_method (name, &body)
187
define_method(name, &body)
191
def define_protected_method (name, &body)
192
define_method(name, &body)
197
197
class ImmutableAttributeError < StandardError
198
def initialize (attribute=nil, message=nil)
200
@attribute = attribute
203
define_accessors :attribute
206
if @attribute and @message
207
"cannot change the value of `#@attribute': #@message"
209
"cannot change the value of `#@attribute'"
211
"cannot change the value of attribute: #@message"
213
"cannot change the value of attribute"
198
def initialize (attribute=nil, message=nil)
200
@attribute = attribute
219
# Guard each of the specified attributes by replacing the writer
220
# method with a proxy that asks the supplied block before proceeding
223
# If it's okay to change the attribute, the block should return
224
# either nil or the symbol :mutable. If it isn't okay, the block
225
# should return a string saying why the attribute can't be changed.
226
# If you don't want to provide a reason, you can have the block
227
# return just the symbol :immutable.
228
def guard_writers(*names, &predicate)
229
for name in names.map { |x| x.to_sym } do
230
define_hard_alias("__unguarded_#{name.writer}" => name.writer)
231
define_method(name.writer) do |new_value|
232
case result = predicate.call
234
__send__("__unguarded_#{name.writer}", new_value)
236
raise ImmutableAttributeError.new(name)
203
define_accessors :attribute
206
if @attribute and @message
207
"cannot change the value of `#@attribute': #@message"
209
"cannot change the value of `#@attribute'"
211
"cannot change the value of attribute: #@message"
238
raise ImmutableAttributeError.new(name, result)
244
def define_guarded_writers (*names, &block)
245
define_writers(*names)
246
guard_writers(*names, &block)
249
define_soft_alias :guard_writer => :guard_writers
250
define_soft_alias :define_guarded_writer => :define_guarded_writers
213
"cannot change the value of attribute"
219
# Guard each of the specified attributes by replacing the writer
220
# method with a proxy that asks the supplied block before proceeding
223
# If it's okay to change the attribute, the block should return
224
# either nil or the symbol :mutable. If it isn't okay, the block
225
# should return a string saying why the attribute can't be changed.
226
# If you don't want to provide a reason, you can have the block
227
# return just the symbol :immutable.
228
def guard_writers(*names, &predicate)
229
for name in names.map { |x| x.to_sym } do
230
define_hard_alias("__unguarded_#{name.writer}" => name.writer)
231
define_method(name.writer) do |new_value|
232
case result = predicate.call
234
__send__("__unguarded_#{name.writer}", new_value)
236
raise ImmutableAttributeError.new(name)
238
raise ImmutableAttributeError.new(name, result)
244
def define_guarded_writers (*names, &block)
245
define_writers(*names)
246
guard_writers(*names, &block)
249
define_soft_alias :guard_writer => :guard_writers
250
define_soft_alias :define_guarded_writer => :define_guarded_writers
253
253
if __FILE__ == $0
256
class DefineAccessorsTest < Test::Unit::TestCase
264
def test_define_hard_aliases
265
@X.define_method(:foo) { 123 }
266
@X.define_method(:baz) { 321 }
267
@X.define_hard_aliases :bar => :foo, :quux => :baz
268
assert_equal @x.foo, 123
269
assert_equal @x.bar, 123
270
assert_equal @y.foo, 123
271
assert_equal @y.bar, 123
272
assert_equal @x.baz, 321
273
assert_equal @x.quux, 321
274
assert_equal @y.baz, 321
275
assert_equal @y.quux, 321
276
@Y.define_method(:foo) { 456 }
277
assert_equal @y.foo, 456
278
assert_equal @y.bar, 123
279
@Y.define_method(:quux) { 654 }
280
assert_equal @y.baz, 321
281
assert_equal @y.quux, 654
284
def test_define_soft_aliases
285
@X.define_method(:foo) { 123 }
286
@X.define_method(:baz) { 321 }
287
@X.define_soft_aliases :bar => :foo, :quux => :baz
288
assert_equal @x.foo, 123
289
assert_equal @x.bar, 123
290
assert_equal @y.foo, 123
291
assert_equal @y.bar, 123
292
assert_equal @x.baz, 321
293
assert_equal @x.quux, 321
294
assert_equal @y.baz, 321
295
assert_equal @y.quux, 321
296
@Y.define_method(:foo) { 456 }
297
assert_equal @y.foo, @y.bar, 456
298
@Y.define_method(:quux) { 654 }
299
assert_equal @y.baz, 321
300
assert_equal @y.quux, 654
303
def test_define_readers
304
@X.define_readers :foo, :bar
305
assert !@x.respond_to?(:foo=)
306
assert !@x.respond_to?(:bar=)
307
@x.instance_eval { @foo = 123 ; @bar = 456 }
308
assert_equal @x.foo, 123
309
assert_equal @x.bar, 456
310
@X.define_readers :baz?, :quux?
311
assert !@x.respond_to?(:baz=)
312
assert !@x.respond_to?(:quux=)
313
@x.instance_eval { @baz = false ; @quux = true }
318
def test_define_writers
319
assert !@X.writer_defined?(:foo)
320
assert !@X.writer_defined?(:bar)
321
@X.define_writers :foo, :bar
322
assert @X.writer_defined?(:foo)
323
assert @X.writer_defined?(:bar)
324
assert @X.writer_defined?(:foo=)
325
assert @X.writer_defined?(:bar=)
326
assert @X.writer_defined?(:foo?)
327
assert @X.writer_defined?(:bar?)
328
assert !@x.respond_to?(:foo)
329
assert !@x.respond_to?(:bar)
332
assert_equal @x.instance_eval { @foo }, 123
333
assert_equal @x.instance_eval { @bar }, 456
334
@X.define_writers :baz?, :quux?
335
assert !@x.respond_to?(:baz?)
336
assert !@x.respond_to?(:quux?)
339
assert_equal @x.instance_eval { @baz }, true
340
assert_equal @x.instance_eval { @quux }, false
343
def test_define_accessors
344
@X.define_accessors :foo, :bar
345
@x.foo = 123 ; @x.bar = 456
346
assert_equal @x.foo, 123
347
assert_equal @x.bar, 456
350
def test_define_opposite_readers
351
@X.define_opposite_readers :foo? => :bar?, :baz? => :quux?
352
assert !@x.respond_to?(:foo=)
353
assert !@x.respond_to?(:bar=)
354
assert !@x.respond_to?(:baz=)
355
assert !@x.respond_to?(:quux=)
356
@x.instance_eval { @bar = true ; @quux = false }
363
def test_define_opposite_writers
364
@X.define_opposite_writers :foo? => :bar?, :baz => :quux
256
class DefineAccessorsTest < Test::Unit::TestCase
264
def test_define_hard_aliases
265
@X.define_method(:foo) { 123 }
266
@X.define_method(:baz) { 321 }
267
@X.define_hard_aliases :bar => :foo, :quux => :baz
268
assert_equal @x.foo, 123
269
assert_equal @x.bar, 123
270
assert_equal @y.foo, 123
271
assert_equal @y.bar, 123
272
assert_equal @x.baz, 321
273
assert_equal @x.quux, 321
274
assert_equal @y.baz, 321
275
assert_equal @y.quux, 321
276
@Y.define_method(:foo) { 456 }
277
assert_equal @y.foo, 456
278
assert_equal @y.bar, 123
279
@Y.define_method(:quux) { 654 }
280
assert_equal @y.baz, 321
281
assert_equal @y.quux, 654
284
def test_define_soft_aliases
285
@X.define_method(:foo) { 123 }
286
@X.define_method(:baz) { 321 }
287
@X.define_soft_aliases :bar => :foo, :quux => :baz
288
assert_equal @x.foo, 123
289
assert_equal @x.bar, 123
290
assert_equal @y.foo, 123
291
assert_equal @y.bar, 123
292
assert_equal @x.baz, 321
293
assert_equal @x.quux, 321
294
assert_equal @y.baz, 321
295
assert_equal @y.quux, 321
296
@Y.define_method(:foo) { 456 }
297
assert_equal @y.foo, @y.bar, 456
298
@Y.define_method(:quux) { 654 }
299
assert_equal @y.baz, 321
300
assert_equal @y.quux, 654
303
def test_define_readers
304
@X.define_readers :foo, :bar
305
assert !@x.respond_to?(:foo=)
306
assert !@x.respond_to?(:bar=)
307
@x.instance_eval { @foo = 123 ; @bar = 456 }
308
assert_equal @x.foo, 123
309
assert_equal @x.bar, 456
310
@X.define_readers :baz?, :quux?
311
assert !@x.respond_to?(:baz=)
312
assert !@x.respond_to?(:quux=)
313
@x.instance_eval { @baz = false ; @quux = true }
318
def test_define_writers
319
assert !@X.writer_defined?(:foo)
320
assert !@X.writer_defined?(:bar)
321
@X.define_writers :foo, :bar
322
assert @X.writer_defined?(:foo)
323
assert @X.writer_defined?(:bar)
324
assert @X.writer_defined?(:foo=)
325
assert @X.writer_defined?(:bar=)
326
assert @X.writer_defined?(:foo?)
327
assert @X.writer_defined?(:bar?)
328
assert !@x.respond_to?(:foo)
329
assert !@x.respond_to?(:bar)
332
assert_equal @x.instance_eval { @foo }, 123
333
assert_equal @x.instance_eval { @bar }, 456
334
@X.define_writers :baz?, :quux?
335
assert !@x.respond_to?(:baz?)
336
assert !@x.respond_to?(:quux?)
339
assert_equal @x.instance_eval { @baz }, true
340
assert_equal @x.instance_eval { @quux }, false
343
def test_define_accessors
344
@X.define_accessors :foo, :bar
345
@x.foo = 123 ; @x.bar = 456
346
assert_equal @x.foo, 123
347
assert_equal @x.bar, 456
350
def test_define_opposite_readers
351
@X.define_opposite_readers :foo? => :bar?, :baz? => :quux?
352
assert !@x.respond_to?(:foo=)
353
assert !@x.respond_to?(:bar=)
354
assert !@x.respond_to?(:baz=)
355
assert !@x.respond_to?(:quux=)
356
@x.instance_eval { @bar = true ; @quux = false }
363
def test_define_opposite_writers
364
@X.define_opposite_writers :foo? => :bar?, :baz => :quux