Where your boss challenges you and Bill to write better code than she can.
After such an eventful week, you’re looking forward to a relaxing Friday. But as soon as you sit down with Bill and your cup of coffee, your boss appears.
“You guys did a good job this week,” she says. “Looking over your code, I got so excited about metaprogramming that I decided to learn it myself. But last night I got stuck on a difficult coding problem. Can you help me?”
Having a boss who used to be a programmer and still likes to get her hands dirty can sometimes make your life harder. But you’re new at this job, and you can’t say no when your boss is asking for your help.
A few days ago, your boss learned about the attr_accessor method that you read about in . Now she’s using attr_accessor all the time to generate her objects’ attributes. While she was at it, your boss also came up with the idea of writing her own Class Macro (), similar to attr_accessor, which generates a validated attribute. “I call it attr_checked,” she says.
Your boss explains how this attr_checked method should work, pointing out that it should take the name of the attribute, as well as a block. The block is used for validation. If you assign a value to the attribute and the block doesn’t return true for that value, then you get a runtime exception.
Your boss’ first requirement is an attr_checked Class Macro, and she explains her secondary requirement: “I don’t want this attr_checked method to be available to each and every class, because I don’t like the idea of cluttering standard classes with my own methods. Instead, a class should gain access to attr_checked only when it includes a CheckedAttributes module.” She provides this example:
| class Person |
* | include CheckedAttributes |
| |
| attr_checked :age do |v| |
| v >= 18 |
| end |
| end |
| |
| me = Person.new |
| me.age = 39 # OK |
| me.age = 12 # Exception |
Your task today is to write CheckedAttributes and attr_checked for your boss.
The boss’ challenge is a bit too much to handle in a single burst of coding. You’ll get to a solution in small steps.
Instead of engaging in pair programming, Bill proposes sharing roles: he’ll manage the development, and you’ll write the code. While you wonder what “managing the development” actually means, Bill quickly lists the steps you’ll take:
Write a Kernel Method () named add_checked_attribute using eval to add a super-simple validated attribute to a class.
Refactor add_checked_attribute to remove eval.
Validate attributes through a block.
Change add_checked_attribute to a Class Macro () named attr_checked that’s available to all classes.
Write a module adding attr_checked to selected classes through a hook.
“Aren’t we supposed to work as a pair?” you ask. “I don’t even understand these steps.”
“Don’t worry,” Bill says. “You really only need to learn two things before you start developing: one is a method named eval, and the other is the concept of a Hook Method.” He vows to tell you everything you need to know about eval, because eval is necessary for the first development step. You will deal with Hook Methods later.