"Higher-order functions are the very essence of functional programming languages – they breathe fire into the belly of the code." – Joe Armstrong

Tuesday, April 22, 2008

5 Rails Tips

If you're not already watching Ryan Bates' screencasts at Railscasts.com, I strongly recommend it. They're really quite well done and interesting. He's running a contest, in which participants suggest 5 Rails tips. For my examples, I've decided to focus on areas that are either informed by ideas from functional programming (which this blog is ostensibly about), or areas in which Ryan and I have different approaches (or both). Here are my tips:

Use Module methods

Here's where I disagree with something Ryan said in his 101st Railscast, in which he suggests using Class (or instance) methods with variables over using Module methods. I prefer to use Module methods. Here's a typical (truncated) example:

As I said above, Ryan and I have different preferences regarding Class vs. Module. I like having helper methods that are testable in a more "purely functional" paradigm, wherein we mock a request, but don't need to mess around within the base controller object or the like. We just pass our mock request model into the get_stylesheets_by_request method and take it from there. Either approach works, you may find that one or the other matches your own thought process more closely.

Use Constants

In the code example above, notice the use of the STYLESHEETS_FOR Constant, which we can define as follows:

It gives us access to either normal_browser.css or mobile.css, as appropriate. This is a somewhat contrived example, but you could use whatever list of stylesheets you want for the values in each pair, expanding to customize for Safari, Epiphany, Opera, etc.

This technique could be used for any data that is unlikely to change in the course of an app running, but is not tied specifically enough to a given model to be stored in the DB. This allows you to avoid continually reconstructing never-changing local variables inside a method call. Some more realistic examples appear again in the MessageHelper tip below.

Return guards can simplify flow control

I loath if - elseif - else - end flow control, and strongly prefer return guards. It's a personal bigotry that I freely admit, because I think return guards make code more readable, shorter, and more in line with how I think. Here's a rewrite of Ryan's star_type method from Railscast #101 that uses return guards rather than an if block.

These are obviously truncated. I use DESCRIPTION_OF_RESOURCE and EXPLAIN for hover explanations and the like. Notice also that the EXPLAIN and MESSAGE[:confirm_long] values are Procs, allowing you to reuse them abstractly. The purpose of the ERROR, MESSAGE and TITLE hashes should be obvious from their names.1 The main benefit of this practice is that you can conform to DRY principles, while also separating your messages logically according to what they'll be needed for.

Of course, you can wrap the use of each of these hashes inside helper methods, like explain, message, or what have you.

Whenever assigning into multiple variables, it can often be helpful to do simultaneous parallel assignments using pattern matching. Here's an example from an RSpec file. I have a resource called Preceptor which has many Rotations, and a helper method called create_preceptor_and_rotations that does what you (hopefully) expect:

In the first case, I want to use both @preceptor and @preceptor_rotations for some purpose. In the 2nd case, I want to keep the @preceptor, but the name of the rotations variable informs anyone reading the code that I don't care about the rotations in this particular spec instance. In both cases, I've done assignments into two variables simultaneously.

So there's my list. These are all obviously fairly subjective points, but I find that these approaches work well for me, and my co-workers seem pretty agreeable to them. Maybe they'll work for you, too.