Code tagged with rails

Computer: Where am I right now in my rails application?

Posted by Chad Humphries 9 days ago
# This came from 20 minutes of goofing off
before_filter :where_am_i_right_now

def where_am_i_right_now
  %x{say 'processing #{params[:action].humanize} on the #{params[:controller]} controller'}
end
Language Ruby / Tagged with rails, bad

Never do this please

Posted by Chad Humphries 24 days ago
# Stick this in a model, and enjoy.  Or don't.  Actually don't.  This was 
# written as a crazy experiment all about coulda, not about shoulda.
class ActiveRecord::Base
  def current_user
    ObjectSpace.each_object(ApplicationController) do |found_obj|
      return found_obj.send!(:current_user) if found_obj.respond_to?(:current_user) 
    end
  end
end
Language Ruby / Tagged with rails, ahhh

delegate or return something specific with the built-in delegate

Posted by Chad Humphries about 1 month ago / Source: http://dev.rubyonrails.org/ticket/4134
# You can get the short-circuit behavior you're looking for with the following 
# construct:
delegate :foo, :wibble, :to => '(bar or return nil)'

# Some people will probably find sticking an expression in the :to a bit ugly 
# but it does allow you greater freedom than just "always return nil if :to 
# evaluates to nil":
delegate :foo, :wibble, :to => '(bar or return "bar not there")'

# You could probably make a (strong) argument that it would be clearer to write 
# custom accessors - but personally I like the way delegate() works right now.
Language Ruby / Tagged with rails

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

Managing Memcache with Capistrano

Capistrano rocks for deployment. If you haven't ever used it to deploy an app, you're seriously missing out. There's a lot of good recipes floating around online and this stuff changes so often that there’s probably something out there that’s better. However I figured I'd share how I'm currently managing our memcache daemons at work.
memcached, rails wtf?

We are using defunkt's cache_fu plugin right now to cache models in our system. It's pretty straightforward to use; an acts_as class method, and a config/memcached.yml file. The memcached.yml file lets you provide one or more servers that the memcache clients can connect to. Unfortunately there's no centralized way to manage those memcache daemons on your deploy hosts. With our good friend cap, some ruby, and a little time invested we can roll our own solution. Whatcha Want?

The requirements for this task are dead simple.

  • use cap to get out to the remote hosts
  • the memcache daemon list comes from cache_fu’s yml file
  • write a script to start, stop, restart, get the status of, and commit mass genocide against(I want all processes kill -9'd right now).
It turned out to be a pretty simple hack; parse the yaml, get the host's ip address, see if the host is in the server list, if it is do whatever command line operation we were given. Check it out. #!/usr/bin/env ruby # this goes in your script/ directory # it parses your memcached.yml file and hooks you up w/ some info # it keeps you from having to mess w/ stale memcached daemons for whatever reason. require 'yaml' class MemcachedCtl attr_accessor :memcached, :memory, :pids, :servers, :ip_address, :ethernet_device def initialize env = ENV['RAILS_ENV'] || 'development' self.memcached = `which memcached`.chomp self.servers = [ ] self.pids = { } self.ethernet_device = ENV['ETH'] || 'eth0' self.ip_address = get_ip_address || '0.0.0.0' self.memory = '128' config = YAML.load_file(File.expand_path(File.dirname(__FILE__) + "/../config/memcached.yml")) self.servers = [ config['defaults']['servers'] ].flatten rescue ['127.0.0.1:11211'] self.servers = [ config[env]['servers'] ].flatten if config[env]['servers'] self.servers.reject! { |server| host,port = server.split(/:/); self.ip_address == host } self.memory = config[env]['memory'] unless config[env]['memory'].nil? each_server do |host,port| `ps auwwx | grep memcached | grep '\\-l #{ip_address} \\-p #{port}' | grep -v grep`.split(/\n/).each do |line| self.pids[port] = line.split(/\s+/)[1] end self.pids[port] ||= 'Down' end end def execute(cmd) send(cmd) end def restart; stop; sleep 1; start; end def status each_server { |host,port| puts "Port #{port} -> #{pids[port] =~ /\d+/ ? 'Up' : 'Down'}" } end def kill each_server { |host,port| `kill -9 #{pids[port]} > /dev/null 2>&1` if pids[port] =~ /\d+/ } end def stop; kill; end def start each_server do |host,port| `#{memcached} -d -m #{memory} -l #{ip_address} -p #{port}` STDERR.puts "Try memcached_ctl status" unless $? == 0 end end protected def each_server(&block) servers.each do |server| host,port = server.split(/:/) yield host, port end end def get_ip_address # this works on linux you might have to tweak this on other oses line = `/sbin/ifconfig #{ethernet_device} | grep inet | grep -v inet6`.chomp if line =~ /\s*inet addr:((\d+\.){3}\d+)\s+.*/ self.ip_address = $1 end end end ########################################################################### cmd = ARGV.shift unless cmd.nil? MemcachedCtl.new.execute(cmd) end

Get Your Cap On

So I named the script memcached_ctl and added it to my script/ directory in svn and redeployed. Now I can start poppin caps in our memcache daemons. If I throw the following code at the bottom of my config/deploy.rb I can see what’s up with my memcache daemons on my production hosts. If I were smart/motivated I’d make a gem and let you good people include these tasks, you know like you should be doing for mongrel_cluster. # ==================================== # Memcached Server TASKS # ==================================== %w(start stop restart kill status).each do |cmd| desc "#{cmd} your memcached servers" task "memcached_#{cmd}".to_sym, :roles => :app do run "RAILS_ENV=production #{ruby} #{current_path}/script/memcached_ctl #{cmd}" end end
Language Ruby / Tagged with rails, capistrano, memcache, caching

Automatically tagging releases with capistrano

Even though Capistrano “tags” each release by creating a new folder on the production server(s), it might be interesting to have a historical perspective in your repository anyway.

This makes it easier to know exactly what went up for a release.

I would like to share the following Capistrano recipe for your pleasure:
config/deploy.rb

require 'uri'
task :after_deploy do
  source = repository
  dest = URI.parse(repository).merge("../releases/#{File.basename(release_path)}")
  cmd = "svn copy --revision=#{revision} --quiet --message \"Auto tagging release #{release_path}\" #{source} #{dest}"
  puts cmd
  `#{cmd}`
end

First, we start by requiring uri, because Subversion does not like relative URLs.

Next, we find the location into which to tag the release, and finally, we just do it.

Simple, effective.

Enjoy !
Language Ruby / Tagged with rails, subversion, capistrano

Backup your production database with capistrano

Say you have a remote server and you want to back up your remote DB to your home machine but you're behind a firewall. Here's my quick solution/hack of the default capistrano backup task. Remotely, it will dump your database to /tmp and bzip2 it. Locally, it will scp the file to your RAILS_ROOT/backups. You need to have all your ssh keys set up for this to work. It probably will fail if you're using a gateway.

role :db,  "caboo.se", :primary => true
set :user, "bananas"

desc "Backup the database"
task :backup, :roles => :db, :only => { :primary => true } do
  filename = "/tmp/#{application}.dump.#{Time.now.to_f}.sql.bz2"

  on_rollback { delete filename }
  run "mysqldump -u root -p mephisto_production | bzip2 -c > #{filename}" do |ch, stream, out|
    ch.send_data "assword\n" if out =~ /^Enter password:/
    # set this to your db password.. yuk!
  end
  `rsync #{user}@#{roles[:db][0].host}:#{filename} #{File.dirname(__FILE__)}/../backups/`
  delete filename
end

Invoke with rake remote:exec ACTION=backup Suggested improvements: - is it necessary to shell out to scp? (update: now uses rsync) - is there a better way of doing the db password? - any way to use the 'backup gem', a favorite of rails professionals everywhere?
Language Ruby / Tagged with rails, capistrano, backup

Manage Gems within Capistrano

Posted by Chad Humphries about 1 year ago
require 'capistrano'
# Installs within Capistrano as the plugin _gem_.
# Prefix all calls to the library with gem.
# Manages installing gems and versioned gems.
module GemInstaller

  # Default install command
  #
  # * doesn't install documentation
  # * installs all required dependencies automatically.
  #
  GEM_INSTALL="gem install -y --no-rdoc"

  # Upgrade the *gem* system to the latest version. Runs via *sudo*
  def update_system
    sudo "gem update --system"
  end

  # Updates all the installed gems to the latest version. Runs via *sudo*.
  # Don't use this command if any of the gems require a version selection.
  def upgrade
    sudo "gem update --no-rdoc"
  end

  # Removes old versions of gems from installation area.
  def cleanup
    sudo "gem cleanup" 
  end

  # Installs the gems detailed in +packages+, selecting version +version+ if
  # specified.
  #
  # +packages+ can be a single string or an array of strings.
  #  
  def install(packages, version=nil)
    sudo "#{GEM_INSTALL} #{if version then '-v '+version.to_s end} #{packages.to_a.join(' ')}"
  end

  # Auto selects a gem from a list and installs it.
  #
  # *gem* has no mechanism on the command line of disambiguating builds for
  # different platforms, and instead asks the user. This method has the necessary
  # conversation to select the +version+ relevant to +platform+ (or the one nearest
  # the top of the list if you don't specify +version+).
  def select(package, version=nil, platform='ruby')
    selections={}
    cmd="#{GEM_INSTALL} #{if version then '-v '+version.to_s end} #{package}"
    sudo cmd do |channel, stream, data|
      data.each_line do | line |
  case line
  when /\s(\d+).*\(#{platform}\)/
    if selections[channel[:host]].nil?
      selections[channel[:host]]=$1.dup+"\n"
      logger.info "Selecting #$&", "#{stream} :: #{channel[:host]}"
    end
  when /\s\d+\./
    # Discard other selections from data stream
  when /^>/
    channel.send_data selections[channel[:host]]
    logger.debug line, "#{stream} :: #{channel[:host]}"
  else
    logger.info line, "#{stream} :: #{channel[:host]}"
  end
      end
    end
  end

end

Capistrano.plugin :gem, GemInstaller
Language Ruby / Tagged with capistrano, rubygems, rails

Renaming your .rhtml to .erb on EdgeRails

Posted by Chad Humphries about 1 year ago
## just slap this in your rake file or in lib/tasks

namespace 'views' do
  desc 'Renames all your rhtml views to erb'
  task 'rename' do
    Dir.glob('app/views/**/*.rhtml').each do |file|
      puts `svn mv #{file} #{file.gsub(/\.rhtml$/, '.erb')}`
    end
  end
end

## or if you have rhtml and rxml you could probably use the following (untested)

namespace 'views' do
  desc 'Renames all your rhtml views to erb'
  task 'rename' do
    Dir.glob('app/views/**/*.rhtml').each do |file|
      puts `svn mv #{file} #{file.gsub(/\.(rhtml|rxml)$/, '.erb')}`
    end
  end
end
Language Ruby / Tagged with rake, views, rails