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.”