10 Ruby on Rails Best Practices

If you’re new to Ruby on Rails, one of the most daunting aspects is knowing what’s the preferred way of accomplishing a given task. While a lot of techniques and libraries have come and gone as the community’s preferred way of doing something, there are some best practices that remain, and can lead to writing the cleanest, most secure and maintainable Rails code possible.

Listed here today are ten of the most popular and useful best practices you can use as a Ruby developer.

Fat Model, Skinny Controller

Arguably one of the most important ways to write clear and concise code in Ruby on Rails, the motto “Fat Model, Skinny Controller” refers to how the M and C parts of MVC ideally work together. Namely, any non-response-related logic should go in the model, ideally in a nice, testable method. Meanwhile, the “skinny” controller is simply a nice interface between the view and model.

In practice, this can require a range of different types of refactoring, but it all comes down to one idea: by moving any logic that isn’t about the response (for example, setting a flash message, or choosing whether to redirect or render a view) to the model (instead of the controller), not only have you promoted reuse where possible but you’ve also made it possible to test your code outside of the context of a request.

Let’s look at a simple example. Say you have code like this:

def index
  @published_posts = Post.all :conditions => {['published_at <= ?', Time.now]}
  @unpublished_posts = Post.all :conditions => {['published_at IS NULL OR published_at > ?', Time.now]}
end

You can change it to this:

def index
  @published_posts = Post.all_published
  @unpublished_posts = Post.all_unpublished
end

Then, you can move the logic to your post model, where it might look like this:

def self.all_published
  all :conditions => {['published_at <= ?', Time.now]}
end

def self.all_unpublished
  all :conditions => {['published_at IS NULL OR published_at > ?', Time.now]}
end

With the methods Post.all_published and Post.all_unpublished, we’ve not only made it simpler to test our code, we’ve also made it possible to reuse that same set of conditions in another location. But as we’ll see shortly, even this is still not ideal.

Reusable Scopes and Relations

In the example above, we still weren’t quite at the optimal level. For example, what if we wanted to fetch a single published post? We’d have to duplicate the conditions in another method—which just leads to more and more junk code.

Fortunately, Rails provides a better way—scopes (in older versions of Rails they were called named scopes). Put simply, a scope is a set of constraints on database interactions (such as a condition, limit, or offset) that are chainable and reusable. As a result, I can call MyModel.my_scope.another_scope, orMyModel.my_scope.first, or MyModel.my_scope.all.

So, taking our previous example again, we could rewrite it as follows in Rails 3:

scope :published, lambda { where('published_at < = ?', Time.now) }
scope :unpublished, lambda { where('published_at > ?', Time.now) }

And in Rails 2:

named_scope :published, lambda { {:conditions => ['published_at < = ?', Time.now]} }
named_scope :unpublished, lambda { {:conditions => ['published_at > ?', Time.now]} }

This would then allow us to use Post.published.all andPost.unpublished.all where needed.

Even better, as of Rails 3, Rails now supports relations—essentially, arbitrary scopes that can be used anywhere. For example, Post.where(:title => 'Hello World').first instead of Post.first :conditions => {:title => 'Hello World'}, meaning you now get powerful features such as chaining for arbitrary database calls.

This leads to better constructed code and more reusable queries—you start to think in terms of what a scope or combination of scopes can achieve—in other words, the big picture—rather than just what your one query is. As a bonus, it makes it much nicer to compose very complex queries such as searches by simply adding the relations and scopes you need.

Package Your Code into Gems and Plugins

If you’ve used Ruby on Rails a decent amount, you’ve most likely noticed the wealth of rubygems available to Rails developers. If there is a relatively general problem, it’s highly likely another developer has already solved it.

So, when you write code you think is general enough—which usually just means you’ve written it more than once before in another application, stop and think about how you can extract it into a plugin or gem suitable for a wider range of purposes. This not only pays off the next time you need said functionality, but it also forces you to stop and evaluate your approach to the problem—more often than not, I’ve found that extracting code from an application has led to a simpler design. You also shouldn’t forget that as a developer, releasing open source code can pay off in other ways.

When it comes to using that code next time, as an added bonus it’ll generally be tested already and well explored—resulting in generally better code from the multiple stages of refactoring.

Along the same lines, spend some time looking at open source gems that already solve your problems. If you’re not sure where to start, a GitHub search is usually a good jumping-off point, and Ruby Toolbox contains a listing of the most popular plugins in the community.

Use the Built-in Ruby Duck Typing Methods

As a language, Ruby uses several conventions that can make development easier. For example, implementing a to_s instance method on an object will give you a standard way of getting a string representation of your object.

By implementing these standard type conversions—in addition to to_s, there’s also to_i for integers and to_a for arrays—you make it possible for your Ruby code to be more concise. As an example, have a look at the following string interpolation:

"Hello there, #{user.name}"

If you alias the name attribute to to_s, you could instead write simply:

"Hello there, #{user}"

Other places in Ruby that use to_s (and, for other situations, to_i and the like) will automatically take advantage of this string representation of your object.

Alongside this, you can also implement the Enumerable module for any of your classes that you want to provide with useful iteration features. All you need to write in your class are the each and < => methods. These two simple additions give you a whole heap of extra functionality for free: methods like map, inject,sort, max, min, and a number of others.

Manage Attribute Access

By default, when using mass assignment in Rails—that is, code similar toUser.new(params[:user]) and @user.update_attributes params[:user]—Rails will assign every attribute without doing any checking. Your validations prevent bad data but they don’t, for example, prevent you from overwriting an attribute that you don’t want to change.

To solve this, ActiveRecord uses two methods—attr_protected andattr_accessibile. Using attr_protected, you declare a blacklist of variables you don’t want assigned (for instance, attr_protected :admin, :password_hash). Using attr_accessible, which is generally prefered, you declare the ones you do want to be able to assign (for instance,attr_accessible :login, :email, :password, :password_confirmation).

By doing this, you prevent any mass assignment that could occur via your application’s forms—just because you don’t have a field for a given attribute doesn’t mean a hacker can’t add it to the request. This way you’re either forced you to manually set certain attribute values or, more usefully, provide a protected method to use when you want to set the value.

From a security perspective, using attr_accessible and attr_protectedforces you to think about what should be editable and how to protect the ways in which your class’s attributes are set.

Use Non-database-backed Models

Although models in Rails are mostly based on ActiveRecord::Base or some other type of object mapper for a database, it’s important to remember that in MVC, the M isn’t restricted to database-backed models.

Using non-database-backed models can help to organize logic which might otherwise become muddy. For example, there are libraries that give you anActiveRecord-like interface for contact form emails.

Using ActiveModel (available in Rails 3 and higher), it’s possible to take arbitrary objects that encapsulate a set of common behavior and use them as your models. Adding virtual models also makes it easier to adhere to RESTful controller design, as you can represent data other than database entries as resources. As a prime example, several popular authentication libraries in Rails now represent the user’s current authenticated session as a model, and I’ve personally implemented a password reset as a model.

When it comes time to interact with these models in your controller code, your code will be that much cleaner, as you can use the exact same approach as with database-backed models.

Virtual Attributes

If you find that you’re manipulating data before passing it to a model (for example, converting the type of an object), it’s likely time you started structuring your code to take advantage of virtual attributes.

Virtual attributes are a very simple idea—essentially, all you’re doing is defining your own getter and setter methods.

Let’s say you were using the following code to set a user’s name:

@user = User.new(params[:user])
@user.first_name, @user.last_name = params[:user][:full_name].split(" ", 2)

You could remove the second line, and instead add the following to your User model:

def full_name=(value)
  self.first_name, self.last_name = value.to_s.split(" ", 2)
end

Whenever you set the full_name attribute, your model will now automatically set the first_name and last_name attributes for you as well, even thoughfull_name doesn’t exist in the database. Likewise, you’ll typically want to define a getter method, full_name, that returns "#{first_name} #{last_name}".

Using virtual attributes, you can use alternative representations of data in forms with relatively little effort. It’s also much simpler to test the logic in isolation, which is always a good thing.

Use Translations

As of Rails 2.2, the framework itself has shipped with strong support for internationalization (or i18n) out of the box. All you need to do is maintain a YAML file of translations, and use I18n.t / t in your code where there is data shown to the user. Essentially, the Rails i18n framework makes it easy to declare the map of an abstract context to a string.

From a developer’s perspective, using I18n is useful for more than just rolling your app out to more locales. When it comes time to rename something in your interface, having a single place to look for the string in question in order to replace it across the whole application is much quicker than scouring your code for every occurrence.

If you want to get started with Rails’ I18n framework, there is a very thorough guide made freely available on it by the community.

In Conclusion

There are literally hundreds of coding practices or techniques that can make your life as a Ruby on Rails developer easier, but I’ve tried to pick out ten that are broadly applicable to just about every project, and which are often ignored. Do you follow these practices already? Will you start? Do you have any others you’d add? Let me know in the comments.

And if you enjoyed reading this post, you’ll love Learnable; the place to learn fresh skills and techniques from the masters. Members get instant access to all of SitePoint’s ebooks and interactive online courses, like Learning Ruby on Rails 3.

Comments on this article are closed. Have a question about Ruby on Rails? Why not ask it on our forums?

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • Ed Ruder

    This article is a good guideline for Rails beginners–thanks for writing it.

    Your example of Duck Typing, however, doesn’t describe Duck Typing. It’s describing Ruby’s automatic invocation of an object’s to_s method when in a string context. Since Object responds to to_s, all objects respond to it, so this is more an example of inheritance, or sub-classing, which is not unique to Ruby. (E.g., Java has similar behavior.)

    Duck Typing refers to the somewhat-unique feature of Ruby that any object that responds to a given method can have that method invoked on it, regardless of its class. “If it walks like a duck, …”

    An example of Duck Typing would be a method that invokes an object’s << method to append a string to it. The method will work correctly whether the object is a File object, an Array, or a String.

    • Darcy Laycock

      I think it doesn’t quite cover it as well as it could of (namely, bad examples) but the intention was to describe exactly that – It’s up to the author of the code to ensure the to_s method ‘quacks’ correctly (it just so happens that is one of the methods is implemented all over the place).

      Based on that, it is mostly an example of inheritance and subclassing but it still describes duck typing (although it is badly worded / could do with more explanation) as inheritance still makes up duck typing to a degree (it’s primarily just that ruby gives you a little bit more flexibility; At the core of it, duck typing is ultimate about a simple interface that specifies how a method should be invoked and the type and a small context of the information returned.

  • adrian

    Thanks heaps for this man. Nice list to keep in mind, and nicely written. I haven’t done any of my own gems yet, so that get pushed to the top of the list for me! Thanks heaps for taking the time to put this together.

  • http://www.marc-remolt.de/ Marc Remolt

    Really nice introductory article!

    Personally I prefer class methods to scopes in Rails3. They are simply better to read than lambdas.

    Your examples as class methods:

    def published
    where(‘published_at ?’, Time.now)
    end

    As long as they return a relation, you can chain them the same way as scopes, so you loose nothing:

    MyClass.published.where(‘title = ?’, ‘Testartikel’).limit(3)

    Cheers
    Marc

    • http://www.marc-remolt.de/ Marc Remolt

      Sorry, made a typo in my last comment. Of course it must be

      def self.published

      for a class method.

    • http://www.thinkfuse.com Brandon Bloom

      Actually, you do lose the ability to use with_scope.

      See http://ryandaigle.com/articles/2006/07/20/a-rails-feature-you-should-be-using-with_scope and http://asciicasts.com/episodes/5-using-with-scope

      One thing not mentioned in those articles, but probably even more useful is that you can call with_scope without arguments to inherit the scope of the calling context. This enables you to write class methods which operate on each record in a scope chain. For example, see this gist:

      https://gist.github.com/1172981

    • http://active-active.blogspot.com Ryan Ransford

      Another thing to consider: Using a scope explicitly declares an intent to use that name for the purposes of selecting a sub-set of records. Using a method to declare a “scope” does not explicitly declare that purpose. I understand that these two ducks quack alike, but when scanning a chunk of code (i.e. learning it for the first time, or as a tools builder), the scope jumps out a lot more than the method definition.

  • http://raghubetter.blogspot.com Raghavendra Shet

    Cool ones to read and understand.

  • http://www.wellbeinginthecity.me Dan

    Great article – thanks!

    That’s only 8 though, are you keeping the other two to yourself? :–)

    • Benjamin Lewis

      lol, if you count the conclusion, it’s an off by 1 error.

  • Phil

    Regarding Fat Model, Skinny Controller: I really doubt that this can be a general rule. The most obvious reason is the word “fat”. You don’t really want any part of your code fat. Fat code usually means code that is hard to follow.

    If you really want reusuable code that would otherwise make your models fat, I suggest putting it into ./lib. That’s what this folder is for, right?

    MVC is nice but in reality complex stuff cannot be separated clearly so I guess it doesn’t make sense to try so hard just to comply MVC by 100%.

    Just my 2 cents…

  • http://www.akashtiwary.com Akash

    Hi, I am from India and I would like to thank you for so many useful articles on Ruby On Rails on your website. I have just started learning the basics of Ruby On Rails and will love to use it some day. To be honest, I am still struggling in fully comprehending it. But, you have so many helpful articles for beginners like me. Thanks and have a great day!