TODO and Custom Annotations in Rails Applications

While writing software it’s common to leave comments for your future self. For instance, if you have written some code but realize that it should be refactored to be more efficient, you may place something along the lines of “TODO: change active record find method and replace w/ a custom sql select finder “. With rails, if you follow this convention, you can get a list of your annotations with a rake task.

rake notes:todo

which will print out the file where the the todo was found along with the line number and the comment…

app/controllers/application_controller.rb:
  * [  8] fix me

Rails defines several other annotation types for you

rake notes                                # Enumerate all annotations
rake notes:fixme                          # Enumerate all FIXME annotations
rake notes:optimize                       # Enumerate all OPTIMIZE annotations
rake notes:todo                           # Enumerate all TODO annotations

And you also may define your own

# SEAN: please rewrite this method to query only chunky bacon

you may find all instances of “SEAN” by running

rake notes:custom ANNOTATION=SEAN

Rake DB Everything, Dump, Destroy, Create, Load

I’m a big fan of the yaml_db plugin. But I don’t like running rake db:data:load, only to find that my db columns mismatch my model attributes, thus aborting the data import task. To quickly add/remove columns/attributes from a model and rebuild the database with previous db info, I wrote this simple rake task. It saves model records as yaml in db/seandb.yml, and reloads them in the same task but with the db rebuilt. I nest the save method in begin/rescue so that if there are any conflicts the task will continue.

This biggest challenge was to get a list of all the active record models in the Rails app. This problem has been posted a lot and I didn’t find an easy solution. For now, I just look in the app/models directory and ‘classify’ and ‘constantize’ the file name. This will load the model so that I can iterate over all the active record subclasses and call the appropriate Model.find(:all) method. I could maybe do the same w/ all files in the Rails.configuration.load_paths, but my models are, at least for now, under app/models. Plugin models are loaded and available from the rake task if you require the :environment.

It’s as simple as running

rake db:everything
SAVEDB = "#{RAILS_ROOT}/db/seandb.yml"
namespace :db do
   task :everything => [:environment, :spit, :drop, :create, :migrate, :populate] do
     desc "spit out model records as yaml, rebuild the database and repopulate the db"
   end

   task :spit do
    Dir["#{RAILS_ROOT}/app/models/*"].each {|file| (File.basename(file,".*").classify.constantize)}
    File.open(SAVEDB, "w") do |f|
      ActiveRecord::Base.send(:subclasses).each do |model|
        begin
          model.find(:all).each do |record|
              f.write(record.to_yaml)
              print "\twrote #{record.class}\n"
          end
        rescue
        end
      end
    end
   end

   task :populate do
    File.open( SAVEDB ) do |yf|
      YAML.each_document( yf ) do |ydoc|
        begin
          m = ydoc.clone
          m.save
          p "...saved #{m.id}"
        rescue
        end
      end
    end
   end
end