Ruby Rookies

As a freelance developer, I often work with developers from different programming backgrounds and of different skill-levels. Over time, I’ve learned to tell a programmer’s Ruby level of experience by looking at the code they’ve written. Not only that, I can also place a safe bet on the programmer’s coding background based on certain patterns within the code that give subtle clues as to the coder’s previous programming experiences. There are certain coding practices that Ruby Rookies tend to employ often. Let’s call these anti-patterns. This post enumerates the most common of these anti-patterns.

Concatenate, Not Interpolate

Ruby is one of a few languages that allow you to do string interpolation directly into the string without using any special methods. Unfortunately, many Ruby rookies prefer to concatenate strings:

puts “error description” + e.cause.to_s + “happened on: ” + Time.now.to_s

When, instead, they could write:

puts “error description #{e.cause} happened on: #{Time.now}”

Not only is this easier on the eye, but the #to_s method is automatically called for you on interpolated objects. Failure to explicitly call #to_s when concatenating will result in a TypeError. So, interpolation results in shorter, prettier, and safer code.

Also, if you want to show off to your concatenating friends, why not use Ruby’s formatted interpolation?

pets = %w{dog cat rabbit}
puts "My first pet is a %s, my second one a %s and my third is a %s" % pets
=> My first pet is a dog, my second one a cat and my third is a rabbit

The Truth be told

Many people coming into Ruby from other languages fail to realize how simple Ruby’s truth model really is. As such, they write code like this:

if an_object != nil && an_object.method? == true
  #do something
end

In Ruby, only the values false and nil evaluate to false (actually they’re both objects). Everything else evaluates to true. Thus, our conditional becomes much simpler:

if an_object && an_object.method?
  #do something
end

While we’re on the subject of boolean logic, here’s another anti-pattern I often come across.

if !an_object.active?
  #do something
end

There’s nothing wrong with negative assertions as such, apart from one thing: readability. That exclamation mark is too easy to miss, giving the reader the opposite idea about what the code does. Luckily, Ruby offers some nice syntactic sugar in the form of the unless statement:

unless an_object.active?
  #do something
end

This is a nice, emphatic statement that’s hard to miss.

AndNotOr

Many Ruby Rookies are so overjoyed by the English-like equivalent of the logical operators that they use them all the time, replacing the symbolic ones. However, they don’t always realize that [!, &&, || ] are not the same as [not, and, or]. The difference is one of precedence.

puts "hello" && "world"
 => world

In this instance, the && operator is evaluated before the puts, effectively reducing the expression to puts "world"

If we use the and operator instead, the expression is evaluated as (puts "hello") and ("world")

puts "hello" and "world"
 => hello

Observe that the symbolic operators are more ‘sticky’ than their natural-language counterparts. This can lead to subtle bugs:

arr = [1,2,3]
!arr[0].is_a?(String) && arr.length > 3
 => false
not arr[0].is_a?(String) && arr.length > 3
 => true

As a rule of thumb, only use the natural-language operators for controlling the flow of execution, while sticking to the symbolic ones for boolean operations.

Don’t Fear the (Duck Typing) Reaper

Ruby doesn’t care much about what class an object says it belongs to, it cares more about what the object can do. If I want to order a pizza, I want the pizza place to provide me with a way to order a pizza. The pizza place can call itself “Pizza Parlor”, “The Pizza Emporium”, or whatever it likes, I really don’t care that much about its name. I just want it to support the order_pizza method. Many developers from a statically-typed background have trouble coming to terms with this notion.

The average static-typing programmer who just started coding in Ruby thinks a bit like that. Here’s an example:

def my_method(arg)
  #hold on, as there are no type declarations in Ruby, arg could be anything at all
  #better be defensive about this
  if arg.is_a? MyClass #that will ensure I’m dealing with the right type
     # oh, but in Ruby one can delete methods from objects at run-time
     # better protect against that too
     if arg.respond_to? :my_method
        arg.my_method  # now I can safely call my method
     else
        # panic!
     end
  end
  #that’s nice, solid code. Well done me.
end

In reality, this is just bloated, redundant code. Just because something can happen doesn’t mean that it will. If you expect your object to behave in a certain way, chances are that it will. If the unexpected happens, well, most programming languages tend to deal with the unexpected by providing exception handling, and Ruby does just that. An exception will be raised if the object doesn’t support a method.

So, if you want to play it safe, just catch the exception:

def m(arg)
  begin
    arg.my_method
  rescue => e
  # handle exception here
  end
end

That’s defensive enough without going overboard. Furthermore, a more experienced Ruby developer would know that the method declaration itself can act as the begin block, so the code can be made even simpler.

def m(arg)
  arg.my_method
rescue => e
 # handle exception here
end

That’s safe code without the bloat. Ain’t Ruby brilliant?

A Nest of Ifs

Too many ifs and elsifs make our code look ugly and hard to read, especially if they’re nested. For Rookies, if..else statements are often used as a hammer to crack any kind of nut. There are many ways to avoid the duplication and messiness of multiple conditionals. A very common solution is to simply refactor at least one of the conditional statements as a separate method. However, one of my favorite techniques is the use of Hashes as business rules objects. Simply put, we can abstract some of our conditional logic in a Hash object, thus making our code easier on the eye and reusable.

Consider this:

if @document.save
   if current_user.role == "admin"
     redirect_to admin_path
   elsif current_user.role == "reviewer"
     redirect_to reviewer_path
   elsif current_user.role == "collaborator"
     redirect_to collaborator
   else
     redirect_to user_path
   end
 else
   render :new
 end

We can use a Logic Hash object and the ternary operator to make this code more concise and maintainable:

redirection_path = Hash.new{|hash,key| hash[key] = user_path}
redirection_path[:admin] =  admin_path
redirection_path[:reviewer] =  reviewer_path
redirection_path[:collaborator] =  collaborator_path

@document.save ? redirect_to redirection_path[current_user.role.to_sym]
    :  (render :new)

We whittled the 5 potential execution paths down to 2. But, wait a minute…we can take advantage of Ruby’s metaprogramming abilities to take this to the next level of terseness.

redirection_path = Hash.new{|hash,key| hash[key] = ( %w(reviewer admin collaborator).include?(key.to_s) ?
                                instance_eval("#{key}_path") : instance_eval('user_path') )}

@document.save ? redirect_to redirection_path[current_user.role.to_sym]
    :  (render :new)

OK, I admit we’re drifting a little bit off Rookie territory right now. The point is: Ruby gives the power to choose succinct and unambiguous statements over nested conditionals, so just go ahead and use it.

List In-comprehensions

The functional aspects of Ruby, realized by blocks, are woefully underused by Ruby Rookies, especially when it comes to constructing lists based on other lists. Say, we need to extract the even numbers from a list and multiply them by 3. Then, we only want to keep the numbers below a certain threshold. Many Rookies will take this approach:

arr =[1,2,3,4,5,6,7,8,9,10]

new_arr = []
arr.each do |x|
  if x % 2 == 0
    new_arr << x * 3 if x * 3 < 20
  end
end

Which works fine, but doesn’t quite compare to the expressiveness of:

arr.select{|x| x % 2 == 0 }.map{|x| x * 3}.reject{|x| x > 19}

By using blocks, we are effectively passing functions to functions. Many Ruby methods take advantage of this to allow for some concise, powerful, yet very readable code. A Ruby Rookie might write some code that conditionally loads a number of Rake tasks like this:

load "project/tasks/annotations.rake"
load "project/tasks/dev.rake"
load "project/tasks/framework.rake"
load "project/tasks/initializers.rake"
load "project/tasks/log.rake"
load "project/tasks/middleware.rake"
load "project/tasks/misc.rake"
load "project/tasks/restart.rake"
load "project/tasks/routes.rake"
load "project/tasks/tmp.rake"
load "project/tasks/statistics.rake" if Rake.application.current_scope.empty?

Now take a look at how Rails is achieving the same thing in one single, elegant swoop:

%w(
  annotations
  dev
  framework
  initializers
  log
  middleware
  misc
  restart
  routes
  tmp
).tap { |arr|
  arr << 'statistics' if Rake.application.current_scope.empty?
}.each do |task|
  load "rails/tasks/#{task}.rake"
end

Leveraging the self-referential tap method and an Enumerator (each) to produce clean and maintainable code. Beautiful!

What’s Next?

This article covered coding anti-patterns. In an upcoming article, I’ll take a look at design anti-patterns, that is the way Ruby Rookies structure their code in a not-so-savvy manner.

Tags: Best Practices
Fred is a software jack of all trades, having worked at every stage of the software development life-cycle. He loves: solving tricky problems, Ruby, Agile methods, meta-programming, Behaviour-Driven Development, the semantic web. Fred works as a freelance developer, and consultant, speaks at conferences and blogs here .

  • http://careersreport.com Raymond Dubois

    I’d like to show you a real way you can make a lot of money every week by completing basic work

  • http://careersreport.com Raymond Dubois

    over internet from comfort of your home for several h a daily / See more info by visiting

  • http://careersreport.com Raymond Dubois

    HGFG

  • http://careersreport.com Raymond Dubois

    Check out a real way how you can earn a lot of extra money by finishing basic jobs online from home

  • http://careersreport.com Raymond Dubois

    vdcyj

  • http://careersreport.com Raymond Dubois

    I want to show you a real way to get paid a little extra money by working straight-forward tasks over

  • http://careersreport.com Raymond Dubois

    information

  • http://careersreport.com Raymond Dubois

    Allow me to show you a real way how you can make a lot of extra money on weekly basis by

  • http://careersreport.com Raymond Dubois

    working straightforward jobs over internet from your house for few short hrs /day – Click on the

  • http://careersreport.com Raymond Dubois

    SFGSDFGSD

  • http://careersreport.com Raymond Dubois

    Allow me to show you a magical way to earn a lot of extra money by finishing basic tasks from your house for few short hours a day — See more info by visiting

  • http://careersreport.com Raymond Dubois

    >MY;#)~~&~diqsus~~^~(#ACCOUNT;:

  • http://careersreport.com Raymond Dubois

    Allow me to show you a magical way to earn a lot of extra money by finishing basic tasks from your house for few short hours a day — See more info by visiting >MY;#)~~&~diqsus~~^~(#ACCOUNT;:

  • Test McTest

    Thanks for this article, I enjoyed reading through it. I didn’t know about the def as a begin block!

    • Fred@Bootstrap

      Thanks for the kind comment. I too learn something new about Ruby every day :)

  • biff

    I like the metaprogramming example. Though I wouldn’t call it “succinct and unambiguous”.

    • Fred@Bootstrap

      Oh come on, you have to at least give me ‘succinct’ :)

  • Fred

    Dig it, thanks for sharing! Great refresher.

    • Fred@Bootstrap

      Thank you! Great name, BTW!

  • http://seawolfsanctuary.com/ ben

    I like the Hash selector pattern too much:


    if @document.save
    redirect_to {
    admin: admin_path,
    reviewer: reviewer_path,
    collaborator: collaborator_path
    }[current_user.role.to_sym]
    else
    render :new
    end

    But, of course, the receiving method, redirect_to in this case, will have to handle nil if the role isn’t in the list !

    • Fred@Bootstrap

      You’re right Ben, that’s why I prefer to set an explicit Hash and use the Hash initializer block, as in the article’s example. That way you can set a default value for any role that isn’t in the list (i.e. any key that hasn’t been assigned). You’ll never get back a nil. Hashes in Ruby are pretty awesome!

      • http://seawolfsanctuary.com/ ben

        Getting into {…}[:…] || :default to get around it is a little messy!

  • Tueksta

    load "rails/tasks/#{task}.rake"

    should probably be

    load "projects/tasks/#{task}.rake"

    • Tueksta

      btw: in the last example, i think the first version is way more clean and concise, while the latter is more powerful.

      • Fred@Bootstrap

        See, I kind of disagree with that. The first version is full of duplication. If the task path changes, then I have to update a dozen lines. In the second version I only have to update one. Also the first version doesn’t provide a single, focused load mechanism . Are there more load statements scattered around the code base? There could be. The second version provides us with what I call a single point of action. All the tasks to be loaded are included in that array. If I want to modify, add or delete tasks, I know exactly where to find them. Hope that makes sense.

        • Tueksta

          yes it does. but here are my objections to your version:
          -there is no clear visual seperation of data and logic
          -the bigger variety and complexity of statements in use, needs the reader to think more to understand what is going on
          -it creates an additional array
          -it has added risk with the use of .tap and .each, that someone might have overwritten those methods

          I guess the ideal version would be something in between those two:

          ::DATA::
          -define data (task-path and task-names)
          ::LOWLEVELLOGIC::

          -define method to load a task
          ::ABSTRACTLOGIC::

          -conditional adding of statistics
          -loop each element and load

          you’re almost there, but dropped a bit short :)

    • Fred@Bootstrap

      yes, you’re right of course, that second part was pasted from Rails’ source, hence the ‘rails’ bit :)

  • Brian

    Some great examples here! However, in the section “List In-comprehensions”, you can see the performance trade-off of the “Ruby way”.

    The first solution (the Rookie way) only iterates over the array once and is fast, whereas the expressive way iterates 3 times, which takes almost twice as long (based on my simple benchmark tests).

    A compromise of speed + expressiveness might look something like:
    arr.map{|x| x * 3 if x % 2 == 0 && x * 3 < 20}.compact

    • Michel Pigassou

      Also, Ruby 2 introduced lazy enumerators that allows to chain instructions, only doing one pass at the end.

Special Offer
Free course!

Git into it! Bonus course Introduction to Git is yours when you take up a free 14 day SitePoint Premium trial.