Class: Module
- Inherits:
-
Object
- Object
- Module
- Defined in:
- lib/chattr.rb
Overview
The checked attribute methods are added to Module. You can use them as a safer replacement for attr_accessor
.
Instance Method Summary collapse
-
#array_attr(*names, &block) ⇒ Object
call-seq: array_attr :attr array_attr Class, :attr, Class2, :attr2 array_attr Class, :attr do |val| val.isvalid end array_attr(:attr) {|val| val.isvalid }.
-
#typed_attr(*names, &block) ⇒ Object
call-seq: typed_attr :attr typed_attr nil, :attr typed_attr Class, :attr typed_attr Class, nil, ‘default’, :attr typed_attr Class, nil, :attr do |val| val.isvalid end typed_attr(Integer, 0, :attr) {|val| (0..5).include?(val) } …
Instance Method Details
#array_attr(*names, &block) ⇒ Object
call-seq:
array_attr :attr
array_attr Class, :attr, Class2, :attr2
array_attr Class, :attr do |val| val.isvalid end
array_attr(:attr) {|val| val.isvalid }
array_attr is like attr_accessor, but the attributes it creates are checked Arrays, see checked Array.
The parameter list is processed in order, and may contain:
-
a class. The following array attributes will require values that are
kind_of?
this class -
a Symbol, which is used as the name for an array attribute.
In addition, array_attr may be given a block. Any value to be used as a member of the array will be passed to this block, which must not return false/nil or an exception will be raised. You’ll need to parenthesize the parameter list for a {|| } block, or just use a do…end block.
There’s no need to initialize the attribute with an empty array, that comes for free.
nil checking and default values are not provided as with typed_attr
.
Here’s an example:
class MyClass
array_attr String, :attr1 do|s|
s.size >= 5 # All array members must have >=5 characters
end
array_attr(:attr2) {|e| # Array members must be Integers or Strings
e.kind_of?(String) || e.kind_of?(Integer)
}
end
c = MyClass.new
c.attr1 << "ouch" # Error, string is too short
c.attr2 << { :hi => "there" } # Error, value must be Integer or String
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
# File 'lib/chattr.rb', line 358 def array_attr(*names, &block) klass = Object names.each{|name| case name when Class klass = name when Symbol define_method(name) { instance_variable_get("@#{name}") || instance_variable_set("@#{name}", Array(klass, &block).new) } define_method("#{name}=") {|val| a = instance_variable_get("@#{name}") || instance_variable_set("@#{name}", Array(klass, &block).new) saved = a.clone a.clear begin a.concat(val) # If conversion is legal, this will do it rescue instance_variable_set("@#{name}", saved) raise end } else raise "Parameter to array_attr must be Class or Symbol" end } end |
#typed_attr(*names, &block) ⇒ Object
call-seq:
typed_attr :attr
typed_attr nil, :attr
typed_attr Class, :attr
typed_attr Class, nil, 'default', :attr
typed_attr Class, nil, :attr do |val| val.isvalid end
typed_attr(Integer, 0, :attr) {|val| (0..5).include?(val) }
... and combinations
typed_attr is like attr_accessor, but with optional value checking using either class.kind_of? or by calling your block, or both. Assignment of any value that fails the checks causes an assertion to be raised.
The parameter list is processed in order, and may contain:
-
a class. The following attributes will require values that are
kind_of?
this class -
nil
. Addingnil
to the argument list allowsnil
as a value for the following attributes, which otherwise would disallownil
, -
a Symbol, which is used as the name for an attribute,
-
any other value, which is used as a default value for following attributes.
In addition, typed_attr may be given a block. Any value to be assigned will be passed to this block, which must not return false/nil or an exception will be raised. You’ll need to parenthesize the parameter list for a {|| } block, or just use a do…end block.
Here’s an example:
class MyClass
typed_attr nil, :attr1 # This attribute is unchecked
typed_attr :attr2 # This attribute may be assigned any non-nil value
typed_attr String, "hi", :attr3 # This attribute must be a string and defaults to "hi"
typed_attr Integer, 0, :attr4 do |i|
(0..5).include?(i) # This attribute must be an Integer in 0..5
end
typed_attr String, :foo, Integer, :bar # Two attributes of different types. Don't do this please!
typed_attr(String, :attr5) {|s|
s.size >= 4 # Values must have at least 4 characters
}
end
Note that if you don’t allow nil, you should use a default value, or a new object will break your rules. This won’t cause an exception, as typed_attr assumes you’ll initialise the value in the object’s constructor.
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/chattr.rb', line 281 def typed_attr(*names, &block) klass = Object nil_ok = false def_val = nil names.each{|name| case name when Class klass = name when NilClass nil_ok = true when Symbol define_method(name) { v = instance_variable_get("@#{name}") # This is awkward if nil is a valid value: if (v == nil && (!nil_ok || !instance_variables.include?("@#{name}"))) v = instance_variable_set("@#{name}", def_val) end v } define_method("#{name}=") {|val| if val == nil unless nil_ok raise "Can't assign nil to #{name} which is restricted to class #{klass}" end else if !val.kind_of?(klass) raise "Can't assign #{val.inspect} of class #{val.class} to attribute #{name} which is restricted to class #{klass}" elsif (block && !block.call(val)) raise "Invalid value assigned to #{name}: #{val.inspect}" end end instance_variable_set("@#{name}", val) } else def_val = name # Save this as a default value end } end |