Little Helpers on Rails

Ruby on Rails provides a whole haystack full of convenient helper methods. We found some hidden needles that help you sew more maintainable applications.

Everybody knows about the vast amount of helper methods in Ruby on Rails. Going from a database table name to a human readable label? table_name.humanize! The date two weeks ago? 2.weeks.ago! These are the famous ones. But there are also more subtle helpers to simplify your life and keep your code maintainable. This post is dedicated to the little gems.

Half Full or Half Empty?

After a few projects with Ruby, you are used to retrieve the sorted last names of your friends list with friends.collect(&:lastname).uniq.sort or to select the ones living in your home town with friends.select {|f| f.town == ‚Bern‘}. You are even prepared to handle the situation when they would all leave you, with friends.empty?. This is what you know from other languages like Smalltalk (collect, select, …) or even Java (isEmpty(
)
).

But then, as life only makes sense if you have a couple of friends (no no, they haven’t left you), you go ahead with

if !friends.empty?      # go meet some

Here, the eager Ruby programmer comes along to tell you

unless friends.empty?   # go meet some

whereas the seasoned developer might counter with

if friends.any?         # go meet some

Besides eliminating the repetition of the common !x.empty? statement (DRY!), the last method greatly increases readability. This is no big issue in a one-liner. In a complex if-statement, with (several) else/elsif clauses, you may want to handle the more common cases first. This helps to understand the primary control flow when maintaining your application at a later date. So, do not write

bad:
if friends.empty?
  # huh, what now?
else
  # do as usual
end

better:
if friends.any?
  # do as usual
else
  # huh, what now?
end

Last but not least, as a non-native english speaker, I never quite got accustomed to unless. I still replace it with ‘if not’ in my mind, resolve all the multiple negations and hope that the tests catch all the special cases. The any? method provides an optimal solution that addresses all these issues. (Little caveat: any? actually tests if at least one element of the collection is not nil or true. So it will not work as above on a collection of booleans.)

Just Follow the Rails…

There is one case that is not handled so far. What if you did not have the time to set up your friends list yet? Another check is needed:

if !friends.nil? && friends.any?

or maybe just

if friends && friends.any?

This is where Rails takes over:

if friends.present?

This method just works, no matter if
friends is nil or an empty collection. In these cases, it returns false, otherwise it returns true. Exactly what we want, and even for collections of booleans!

present? has a sister as well: blank?. From the API Documentation: “An object is blank if it’s false, empty, or a whitespace string. For example, „“, “ „, nil, false, [], and {} are blank.” Actually, present? is defined as !blank?. For collections, hashes and strings, these methods make many nil checks pretty redundant. Check if your required input fields all have some content? [field_a, field_b, field_c].collect(&:present?).all? !

… And Go Everywhere

To eliminate preceding nil checks for any object, Rails offers another goodie:

if friend.try(:town) == ‚Bern‘

is short for

if friend && friend.town == ‘Bern’

If friend would be nil, the try call will directly return nil. Otherwise, the passed method will be called. This is especially handy when navigating through object structures:

first_zip = friends.try(:first).try(:town).try(:zip)

which would otherwise look like

first_zip = friends && friends.first && friends.first.town ? friends.first.town.zip : nil

No more null pointer exceptions! In Groovy, a hybrid of Ruby and Java, this functionality even got its own operator.

Behind the Scenes

Ok, let’s think about it. If I call the method blank?, introduced by Rails, on nil, it returns true. Calling a method on nil? Defining new methods for core classes? Big magic!

Not so much. The names of these two language concepts implemented by Ruby are Everything is an Object and Open Classes. Yes, even nil is an object. It is a singleton, you may send messages to it and there is a corresponding class (NilClass). And Ruby allows you to define methods for every class in the environment just by inserting an additional class definition with the new methods into your code. Feel free to have a look at the implementation of this magic blank? method.

Summing Up

Ruby provides a rich set of dynamic language features that are used for best in Ruby on Rails. Even simple methods from basic objects help to reduce duplication and enhance readability, and thus increase productivity and maintainability. It is an enlightening experience to get familiar with these little helpers and even more with the powerfull Ruby language constructs. Just do not forget Spiderman: With great power comes great responsibility.

Kommentare sind geschlossen.