5.10. Concluding Remarks: Languages, Productivity, and Beauty

This chapter showed two examples of using language features to support the productive creation of beautiful and concise code. The first is the use of metaprogramming, closures and higher-order functions to allow model validations and controller filters to be DRYly declared in a single place, yet called from multiple points in the code. Validations and filters are an example of aspect-oriented programming (AOP), a methodology that has been criticized because it obfuscates control flow but whose well-circumscribed use can enhance DRYness. All in all, validations, filters, and association helper methods are worth studying as successful examples of tastefully exploiting programming language features to enhance code beauty and productivity.

The second example is the design choices reflected in the association helper methods. For example, you may have noticed that while the foreign key field for a Movie object associated with a review is called movie_id, the association helper methods allow us to reference review.movie, allowing our code to focus on the architectural association between Movies and Reviews rather than the implementation detail of the foreign key names. You could certainly manipulate the movie_id or review_id fields in the database directly, as Web applications based on less-powerful frameworks are often forced to do, or do so in your Rails app, as in review.movie_id=some_movie.id. But besides being harder to read, this code hardwires the assumption that the foreign key field is named movie_id, which may not be true if your models are using advanced Rails features such as polymorphic associations, or if ActiveRecord has been configured to interoperate with a legacy database that follows a differ- ent naming convention. In such cases, review.movie and review.movie= will still work, but referring to review.movie_id will fail. Since someday your code will be legacy code, help your successors be productive—keep the logical structure of your entities as separate as possible from the database representation.

We might similarly ask, now that we know how associations are stored in the RDBMS, why movie.save actually also causes a change to the reviews table when we save a movie after adding a review to it. In fact, calling save on the new review object would also work, but having said that a Movie has many Reviews, it just makes more sense to think of saving the Movie when we update which Reviews it has. In other words, it’s designed this way in order to make sense to programmers and make the code more beautiful.

Finally, as we saw in Section 5.8, an application framework provides direct support for the major architectural components of the application (in our case, models, views, and con- trollers), but any large software system contains many other kinds of code as well. Indeed, some of the examples of that section are best understood as additional patterns for solving software problems—a theme to which we will frequently return, and that we treat in depth in Chapter 11.