Where you sprinkle some flexibility over today’s project.
To solve the boss’ challenge, you and Bill still need to implement a few important features. One of these features is described in the third step of your development plan: “validate attributes through a block.” Right now, your generated attribute raises an exception if you assign it nil or false. But it’s supposed to support flexible validation through a block.
Because this step changes the interface of add_checked_attribute, it also calls for an update of the test suite. Bill replaces the two test cases that checked for nil or false attributes with a single new test case:
| | require 'test/unit' |
| | |
| | class Person; end |
| | |
| | class TestCheckedAttribute < Test::Unit::TestCase |
| | def setup |
* | add_checked_attribute(Person, :age) {|v| v >= 18 } |
| | @bob = Person.new |
| | end |
| | |
| | def test_accepts_valid_values |
| | @bob.age = 20 |
| | assert_equal 20, @bob.age |
| | end |
| | |
* | def test_refuses_invalid_values |
* | assert_raises RuntimeError, 'Invalid attribute' do |
* | @bob.age = 17 |
* | end |
* | end |
| | end |
| | |
* | def add_checked_attribute(klass, attribute, &validation) |
| | # ... (The code here doesn't pass the test. Modify it.) |
| | end |
Can you modify add_checked_attribute so that it passes the new tests?
You can pass the tests and solve the quiz by changing a couple of lines in add_checked_attribute:
* | def add_checked_attribute(klass, attribute, &validation) |
| | klass.class_eval do |
| | 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 |
“Step 3 was quick,” Bill notes. “Let’s move on to step 4.”