Книга: Metaprogramming Ruby 2
Назад: Hook Methods
Дальше: Wrap-Up

Quiz: Checked Attributes (Step 5)

Where you finally earn Bill’s respect and the title of Master of Metaprogramming.

The following is the code that we wrote in the previous development step:

 
class​ Class
 
def​ attr_checked(attribute, &validation)
 
define_method ​"​#{attribute}​="​ ​do​ |value|
 
raise ​'Invalid attribute'​ ​unless​ validation.call(value)
 
instance_variable_set(​"@​#{attribute}​"​, value)
 
end
 
 
define_method attribute ​do
 
instance_variable_get ​"@​#{attribute}​"
 
end
 
end
 
end

This code defines a Class Macro () named attr_checked. This Class Macro is an instance method of Class, so it’s available to all classes. The final step in your development plan asks you to restrict access to attr_checked: it should be available only to those classes that include a module named CheckedAttributes. The test suite for this step is pretty much the same one you used in step 4, with a single additional line:

 
require ​'test/unit'
 
 
class​ Person
*
include CheckedAttributes
 
 
attr_checked :age ​do​ |v|
 
v >= 18
 
end
 
end
 
 
class​ TestCheckedAttributes < Test::Unit::TestCase
 
def​ setup
 
@bob = Person.new
 
end
 
 
def​ test_accepts_valid_values
 
@bob.age = 18
 
assert_equal 18, @bob.age
 
end
 
 
def​ test_refuses_invalid_values
 
assert_raises RuntimeError, ​'Invalid attribute'​ ​do
 
@bob.age = 17
 
end
 
end
 
end

Can you remove attr_checked from Class, write the CheckedAttributes module, and solve the boss’ challenge?

Quiz Solution

You can copy the trick that you learned in . CheckedAttributes defines attr_checked as a class method on its includers:

*
module​ CheckedAttributes
*
def​ self.included(base)
*
base.extend ClassMethods
*
end
*
*
module​ ClassMethods
 
def​ attr_checked(attribute, &validation)
 
define_method ​"​#{attribute}​="​ ​do​ |value|
 
raise ​'Invalid attribute'​ ​unless​ validation.call(value)
 
instance_variable_set(​"@​#{attribute}​"​, value)
 
end
 
 
define_method attribute ​do
 
instance_variable_get ​"@​#{attribute}​"
 
end
 
end
*
end
*
end

Your boss will be delighted. These are the same Class Macro and module that she challenged you to write this morning. If you can write code like this, you’re on your way to mastering the art of metaprogramming.

Назад: Hook Methods
Дальше: Wrap-Up