Code tagged with activerecord

Intercepting activerecord attributes

# So, you wrote a Rails application to track Marathon events. In a sudden 
# change of heart, you pick up your company and move it from the United 
# States to Germany. Why rewrite your app to handle metric distances? 
# Rails provides a solution: write_attribute and read_attribute. 

class Marathon < ActiveRecord::Base
  def distance=(meters)
    write_attribute(:distance, meters * 1609.344)
  end

  def distance
    read_attribute(:distance) / 0.000621371192
  end
end
Language Ruby / Tagged with rails, activerecord

Production data loading with Capistrano

# I was working on a migration that had a decent chance of messing up my database 
# and wanted assurance it would work with production data, not just my fixtures.

# Building on Bojan Mihelac's code to download files, I wrote a Capistrano task 
# to load the production database into my local database. And then capistrano 
# 1.4.0 was released with a built-in get method for downloading files. So if 
# you have 1.4.0, you can just throw away that whole function.

# Get file remote_path from FIRST server targetted by
# the current task and transfer it to local machine as path, SFTP required
# http://source.mihelac.org/articles/2007/01/11/capistrano-get-method-download-files-from-server
def actor.get(remote_path, path, options = {})
  execute_on_servers(options) do |servers|
    self.sessions[servers.first].sftp.connect do |tsftp|
      logger.info "Get #{remote_path} to #{path}"
      tsftp.get_file remote_path, path
    end
  end
end

desc "Load production data into development database"
task :load_production_data, :roles => :db, :only => { :primary => true } do
  require 'yaml'
 
  database = YAML::load_file('config/database.yml')
 
  filename = "dump.#{Time.now.strftime '%Y-%m-%d_%H:%M:%S'}.sql"
  on_rollback { delete "/tmp/#{filename}" }
 
  run "mysqldump -u #{database['production']['username']} –password=#{database['production']['password']} #{database['production']['database']} > /tmp/#{filename}" do |channel, stream, data|
    puts data
  end
  get "/tmp/#{filename}", filename
  exec "/tmp/#{filename}"
  exec "mysql -u #{database['development']['username']} –password=#{database['development']['password']} #{database['development']['database']} < #{filename}; rm -f #{filename}"
end

# After adding the code to the tail of my config/deploy.rb, I ran cap 
# load_production_data. Then I had a full copy of the production database 
# to tinker with until I was content my migration was flawless.

# Production data is also useful for other things. I can run complicated 
# stats queries without worrying about bogging down the site, and see layout 
# bugs that don't happen unless there's a few dozen tags on the page
Language Ruby / Tagged with capistrano, rails, activerecord

Pre 0.8.0 Validation Checker for RSpec

Nothing special, just a simple helper I threw together in a few minutes.  It is used in model tests to verify that a column is required.  A few usages follow.

  def should_require_attribute(model, attrib_name, error_messages=[], invalid_value=nil)
    # Collect the valid value
    valid_value = model.send(attrib_name)
    error_messages = error_messages.to_a.sort
    
    # Set it to nil
    model.send("#{attrib_name.to_s}=", invalid_value)

    model.should_not_be_valid
    model.should_have(error_messages.size).errors
    model.errors.on(attrib_name).to_a.sort.should == error_messages
    
    # Put things back where you found them
    model.send("#{attrib_name.to_s}=", valid_value)
    model.should_be_valid
  end

# Simple case
specify "should be invalid without state" do
   should_require_attribute(your_model, :state, "is required")   
end

# More than one error expected
specify "should require a valid email address" do
   should_require_attribute(your_model, :email_address, ["is required", "should be a valid format"])   
end

# Invalid value other than nil
specify "should require a non .gov email address" do
   should_require_attribute(your_model, :email_address, "can't contain .gov domains", "bigbrother@government.gov")   
end
Language Ruby / Tagged with rspec, activerecord, rails

Flexible Session Sweeping

Need to clean up your old sessions?

I picked up on the idea of using a shell ActiveRecord class to sweep old sessions by someone on the mailing list a long time ago. I simply tweaked the idea to accept flexible time parameters.
class Session < ActiveRecord::Base

  # time_ago examples: 
  #   30m => 30 minutes
  #   1h  => 1 hour
  #   3d  => 3 days
  def self.sweep(time_ago = nil)
    time = case time_ago
      when /^(\d+)m$/ then Time.now.utc - $1.to_i.minute
      when /^(\d+)h$/ then Time.now.utc - $1.to_i.hour
      when /^(\d+)d$/ then Time.now.utc - $1.to_i.day
      else Time.now.utc - 1.hour
    end
    self.delete_all "updated_on < '#{time.to_s(:db)}'"
  end

end
Now just schedule with cron...
$ crontab -e

    # Delete old session after 3 hours of no use. Run every 15 minutes.
    */15 * * * * /usr/local/bin/ruby /var/www/apps/cool_app/current/script/runner -e production "Session.sweep('3h')"
Language Ruby / Tagged with sessions, activerecord, rails

backup and restore your database with rake

namespace :db do
  namespace :backup do
    
    def interesting_tables
      ActiveRecord::Base.connection.tables.sort.reject! do |tbl|
        ['schema_info', 'sessions', 'public_exceptions'].include?(tbl)
      end
    end
  
    desc "Dump entire db."
    task :write => :environment do 

      dir = RAILS_ROOT + '/db/backup'
      FileUtils.mkdir_p(dir)
      FileUtils.chdir(dir)
    
      interesting_tables.each do |tbl|

        klass = tbl.classify.constantize
        puts "Writing #{tbl}..."
        File.open("#{tbl}.yml", 'w+') { |f| YAML.dump klass.find(:all).collect(&:attributes), f }      
      end
    
    end
  
    task :read => [:environment, 'db:schema:load'] do 

      dir = RAILS_ROOT + '/db/backup'
      FileUtils.mkdir_p(dir)
      FileUtils.chdir(dir)
    
      interesting_tables.each do |tbl|

        klass = tbl.classify.constantize
        ActiveRecord::Base.transaction do 
        
          puts "Loading #{tbl}..."
          YAML.load_file("#{tbl}.yml").each do |fixture|
            ActiveRecord::Base.connection.execute "INSERT INTO #{tbl} (#{fixture.keys.join(",")}) VALUES (#{fixture.values.collect { |value| ActiveRecord::Base.connection.quote(value) }.join(",")})", 'Fixture Insert'
          end        
        end
      end
    
    end
  
  end
end
Language Ruby / Tagged with rails, activerecord, database, rake

Overriding Attributes in ActiveRecord

I love that super calls method_missing if the method is not defined on the superclass.

Consider this case. You have some ActiveRecord named Account, which has an associated email_address. However, an account owner may optionally give a special “notification” email address, which will be used for things like newsletter emails and security issues and such. If no notification address has been explicitly given, it should fall back to the account’s primary email address. It’s as simple as this:

class Account < ActiveRecord::Base
  def notification_address
    super || email_address
  end
end

Calling super forces the superclass, ActiveRecord::Base, to be sent the notification_address message, which it won’t understand. This causes method_missing to be called on AR::Base, which looks for the notification_address attribute in the record’s attribute set. If that has not been set, it will be nil, in which case we then default to the email_address value.

Just as you’d expect.
Language Ruby / Tagged with rails, activerecord

Encrypted database passwords in database.yml

Some people are upset that database.yml can expose passwords in plaintext. However, there is a pretty simple way to get encryption into database.yml. Because the database.yml file is actually run through an ERB interpreter by Rails, we can put code into our file:
# database.yml
production:
  adapter: mysql
  username: db_user
  password: <%= custom_method_to_obtain_password %>
  host: your_db_host
Language Ruby / Tagged with rails, activerecord, erb

Validating all of your fixtures at once

This snippet will validate all of your fixtures all at once.

# file: validate_models.rake
# task: rake db:validate_models
namespace :db do
  desc "Run model validations on all model records in database"
  task :validate_models => :environment do
    puts "-- records - model --"
    Dir.glob(RAILS_ROOT + '/app/models/**/*.rb').each { |file| require file }
    Object.subclasses_of(ActiveRecord::Base).select { |c|
          c.base_class == c}.sort_by(&:name).each do |klass|
      total = klass.count
      printf "%10d - %s\n", total, klass.name
      chunk_size = 1000
      (total / chunk_size + 1).times do |i|
        chunk = klass.find(:all, :offset => (i * chunk_size), :limit => chunk_size)
        chunk.reject(&:valid?).each do |record|
          puts "#{record.class}: id=#{record.id}"
          p record.errors.full_messages
          puts
        end rescue nil
      end
    end
  end
end
Language Ruby / Tagged with rake, activerecord

Active Record Relationship Design Patterns

In the world of web applications, eventually you�??ve seen it all, and you start to see the same relational patterns occur over and over. To help out the newbies, here�??s a list of various relationships (in Ruby on Rails syntax) that we see all the time.
Can you think of any more common relationship patterns?
Language Ruby / Tagged with activerecord

Speed Up Inserts With Transactions

Wrap a set of inserts in a transaction to speed up inserts in Rails.

We recently put in a tag system that allows users of our database to tag people or organizations and then send out emails or create address labels from those tags. A problem came up when we implemented functionality to �??Tag All�?? listings on a certain page. Usually this meant 50-100 inserts like so:

ids.each do |i|
  Tag.create :user_id = uid, :entity_id = i
end

This is all well and good, except for the fact that Rails wraps each insert in a transaction, which slows things down a bit. The solution was to wrap the entire loop in a transaction, that way the inserts are committed all at once, assuming they all are successful.

Tag.transaction do
  ids.each do |i|
    Tag.create :user_id = uid, :entity_id = i
  end
end

Here are some numbers. In development mode, with ~40 inserts:
- Without wrapping it in a transaction, it takes 3.66 seconds to complete the 40 inserts
- When wrapped in a transaction, it takes 0.534 seconds for the same set of inserts

Another approach:

Tag.transaction do
  ids.each do |i|
    t = Tag.new :user_id = uid, :entity_id = i
    t.save!
  end
end

This rolls back the entire operation if any inserts are invalid. Depending on the situation, this may be more desirable then having those rows silently fail.
Language Ruby / Tagged with activerecord