The code that you never write will work forever. — : 1648
Engineering is all about trade-offs. Just about every engineering decision involves getting something, but at a price, — : 1769
Now, a skeptic might observe that neither replace_firstname nor replace_lastcarmodelbought have added any particularly new capability to the FormLetter class.1 These two methods, however they are implemented, simply expose an existing feature of the FormLetter class in a slightly different package. So why should we bother? We bother because our users asked us to bother. If you reread my tale of intrigue and junk mail, you’ll see that it was the coders who were using the FormLetter class that asked for the convenience methods. Those methods make the code generating the form letters cleaner and easier for the people who count—the programmers who need to deal with it. One of the key values of the Ruby programming culture is that the look of the code matters. It matters because the people who use the code, read the code, and maintain the code matter. Good software engineering is all about making everyone’s job easier, not just because we want to go home on time but because we all want to turn out the best possible end product. So we add convenience methods, build method_missing methods, and go to enormous lengths to make our APIs easy to use because programmers with easy-to-use APIs tend to have the time to craft easy-to-use—and working—systems. — : 4139
If you feel like this technique has left you in a confusing maze of twisty passages,3 here are some signposts that can help. First, keep your goal firmly in mind. In our example, we wanted to make it easy to add paragraph-generating methods to StructuredDocument subclasses. Second, know when things happen. For example, when you load the StructuredDocument code, perhaps with this: require 'structured_document' You end up with the generic StructuredDocument class, which has the paragraph_type class method on it. A bit later (perhaps even on the very next line of code) you load the Instructions class: require 'instructions' As the Instructions class definition is getting executed, it will fire off calls to the paragraph_type method up in the StructuredDocument class. This will add methods with names like introduction, warning, and step to the Instructions class. Only when all of this defining is over do you make an instance of Instructions and call the generated methods. The third thing to know is that the value of self is at every stage of this process. This starts out relatively straightforward but gets a little hairy as you go along. The straightforward bit is in the superclass: — : 4647
Fixing this kind of location independence problem is cheap; knowing that you have the problem in the first place is priceless. — : 5220
good workman also learns from the past. All too often when a new technology comes along—Ruby, for example—we tend to toss out the hard-won lessons of experience along with the old code. Take the time to learn from the smart people who came before you. You might start with Paul Graham’s 1993 book, On LISP. The entire text of this book is available at www.paulgraham.com/onlisp.html. It is worth reading even if you never type a single parenthesis of LISP. In many ways this book, especially the chapter on object equality, was inspired by: Bloch, J. Effective Java, Second Edition. Boston, MA: Addison-Wesley, 2008. — : 5408