Книга: 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