Rails Tips

Vote on HN

I have started, abandoned, and restarted many pet rails projects. All hype aside, I've collected a fair amount of rails idioms. Whenever I come across a problem I know I've dealt with in the past, I usually run a few greps through my past projects to look for an answer. The following pages are disorganized tips of things I have done that are useful.

  • if an ActiveRecord::Base object 'foo' doesn't agree with what's in the database, simply do foo.reload. To make changes in the instance go to the database, do foo.save. For many of the Base methods, you can append a ! to the end of the method name and it'll raise an exception instead of returning false on failure. For example, save vs save!

  • If you can't find a method or variable in a class, check the parent class (Class ChildClass < ParentClass), or look for 'include ModuleName', where ModuleName can exist in the lib/ or vendor/plugins directory.

  • Rails config variables

    • also good for setting vars for actionpack components
  • config.observer... - registers a callback for when something happens to a model. Can be done with before_save, but can also be done externally and shared between models.

  • ActiveRecord::Base.logger = Logger.new(STDOUT) - to just have SQL statements print to stdout.

Testing

  • autotest

    • This gem watches for filesystem changes and only runs tests that have been updated. It's fast and it colorizes results. I use it in conjunction with RSpec, and rspec-rails.
  • rails_rcov - wrapper that gives you rake tasks to rcov. To show test code coverage.

  • CruiseControl - continuous integration tool that reports build and test errors. The link is specific to ruby projects.

RSpec

RSpec is an implementation of Behavior Driven Design (BDD). It's a declarative form of design and test. When I first learned testing, I learned it as imperatively setting up state, doing some actions, and then verifing the end state. The big problems I had with testing was always getting tangled up in complex dependencies between different components. This made it really hard to focus on the system being tested. I'm looking more into using mocks and stubs to replace fixtures and real models when it makes sense to. BDD helps with this because it focuses on beahavior and specification rather than implementation.

  • RSpec - the library itself

    • gem install rspec, rspec-rails, diff-lcs, ZenTest
    • script/generate spec
    • rake -T | grep spec
    • should, should_not are Spec::Expectations
  • mock_model

    • good for mocking up ActiveRecord objects

Plugins and Gems

  • schema-browser

  • restful_authentication

    • straightforward plugin that requires just a little bit of tweaking and config. Works great if your user model acts_as_state_machine.
  • role_requirement - plays well with restful_authentication. I haven't tried this one before. I did use Active RBAC with positive results before.

  • acts_as_state_machine - amazing mixin that can make your models act like a state machine. It allows you to register callbacks for when a model's state changes.

  • SyslogLogger - logger that goes to syslog. Not a fan of this one, but I've seen it in use.

  • SimpleConfig

    • haven't used it, but will work.

Gem Dependencies

I like to have my projects be self-contained. This means that when you check out one of my projects, you should be able to setup your database, do a rake or two, and be on your way.

There are several fixes to this problem. The cleanest one that I've choosen is to update to Edge Rails with:

rake rails:freeze:edge

then follow Ryan's Scraps Gem Dependencies article.

Unfortunately, upgrading to Edge Rails might not be feasible. In this case, one solution is to mimic what Edge Rails does:

mkdir RAILS_ROOT/vendor/gems
gem install FOO --install-dir RAILS_ROOT/vendor/gems

To make your rails app recognize the installed gems, update your environment.rb to look in the newly created subdirectory with config.load_paths.

Another interesting plugin that I haven't looked into is piston which gives you all the advantages of svn:externals, but also allows you to commit local changes to your own repo.

Escaping HTML

See ERB::Util for the following:

  • html_escape
  • url_encode
  • url_decode

Remember that if you wanted to use these in your controller, you have to require and include it. I recommend against using the shorthand versions because it sucks for readability.

Metaprogramming

When I was trying to create a RESTful TagsController with the acts_as_taggable plugin, I created a form that looked like:

<input id="tag" type="text" size="17" name="tag"/>
<input id="taggable[id]" type="hidden" value="14" name="taggable[id]"/>
<input id="taggable[type]" type="hidden" value="Place" name="taggable[type]"/>

When this form was posted, I would get a string for the class of the object that I wanted to tag in taggable[type]. In normal Ruby, you can call Kernel.const_get(classname) to get the Class object for classname. In Rails, this has been simplified further to be just classname.constantize

Inheritance

Namespaced Models:

class Pet < ActiveRecord::Base
  self.abstract_class = true

  belongs_to :person
  validates_presence_of :name
end

class Dog < Pet
  def bark
    "baaw"
  end
end

Cool Syntax

assert_equal 1, Developer.connection.select_value(<<-end_sql).to_i
  SELECT count(*) FROM developers_projects
  WHERE project_id = #{project.id}
  AND developer_id = #{developer.id}
end_sql

Miscellanous

  • Hash.symbolize_keys! - turns all keys to symbols
comments powered by Disqus