Chronicling the trials and tribulations of developing for the modern web.



Bells is Back

Posted by Pat Nakajima on March 24, 2008 in Ruby or Rails.

Remember Capistrano Bells? The Rails plugin I created so long ago, then neglected for so long? Well it’s back. And it has a bunch of new recipes for your deployment pleasure.

Nginx Recipes

You know about Nginx right? That hip new web server all the cool kids are clamoring about? Well now Bells has built-in recipes to make configuring new Nginx virtual hosts a snap:

cap deploy:nginx:configure
How does Bells know where you want your Nginx vhosts stored? Why with the new options added to the Capfile template of course!
set :nginx_sites_available, "/usr/local/nginx/sites-available" 
set :nginx_sites_enabled, "/usr/local/nginx/sites-enabled" 

Those are just the defaults. You can modify them however you wish.

Thin Recipes

Almost as trendy as Nginx is Thin, a new Ruby web server that takes Mongrel’s parser and mashes it up with EventMachine with Rack. It’s quick and simple, especially with Bells’ new Thin recipes.

cap thin:configure
cap thin:start
cap thin:stop
cap thin:restart
I’ve been working on making Bells a bit more app server agnostic, which allows you to use Mongrel or Thin almost interchangeably. Just choose which you want to use like so:
set :app_server, :mongrel # or :thin if you please
Then specify the app server settings:
set :app_servers, 1
set :app_server_port, 7007
set :app_environment, 'production'
set :app_server_address, '127.0.0.1'
set :app_server_conf, "#{shared_path}/config/mongrel_conf.yml" 
I’ve done away with Mongrel specific settings, since Thin and Mongrel’s settings are pretty much the same.

So that’s about it. The project is on GitHub now. You can check out its page, or clone a copy yourself like so:

git clone git://github.com/nakajima/capistrano-bells.git

If you’re running edge Rails, you can even install the plugin directly from GitHub:

script/plugin install git clone git://github.com/nakajima/capistrano-bells.git

If you have any questions/suggestions, hit the wiki.

Another Calendar Helper for Rails

Posted by Pat Nakajima on November 01, 2007 in Ruby or Rails.
I put together a calendar helper plugin for Rails recently. I think it’s pretty cool, and it does what I need it to do, so I thought I might write about it and see if it can help any of you too. You can install it using the following:
./script/plugin install \
http://footodo.com/open/plugins/calendar_maker

If you want to check out the code with pretty Warehouse syntax highlighting, you can do so here.

Here’s a little taste of how “calendar maker” works. To build a basic calendar with nothing on it, do the following.
# In your controller...
class PartyController < ApplicationController
  def index
    @calendar = Calendar.new
  end
end
<!-- In your view... -->
<%= @calendar.generate %>

Of course that would be pretty boring if that’s all it could do, right? So let’s say we have some items we want to add to the calendar. To add objects to the calendar, simply pass the add method an array of objects. We specify the date attribute of those objects to use with the schedule_for option (an option name that I’m not entirely happy with and could possibly get renamed at some point).

Calendar Maker denotes an object’s addition to a day by simply adding a class name to that day. By default, the HTML class name added will just be the name of the attribute that the calendar is looking at, but you can specify a different one using the html_class option. It’s not a significant addition, but enough that you’ll be able to style that day different, and even give it its own behavior via unobtrusive Javascript (gotta love unobtrusive Javascript).

# This time, in your controller...
class PartyController < ApplicationController
  def index
    @parties  = Party.find(:all)
    @calendar = Calendar.new
    @calendar.add @parties, :schedule_for => :created_at, :html_class => "party" 
  end
end
<!-- And your view looks pretty much the same... -->
<%= @calendar.generate %>

If you have any questions, just leave a comment on this post and I’ll see if I can’t help out.

Set hash keys to instance variables with ease

Posted by Pat Nakajima on September 28, 2007 in Ruby or Rails.

Like any Rubyist, I tend to pass hashes as arguments (or is it parameters?) when creating a new object, and I usually want the keys to said hashes to be instance variables for said object. So I usually end up with something like this:

Forgive the strange spacing, it’s the only way to keep the syntax highlighting from getting all screwey.

def initialize(options={})
  @clowns = options[ :clowns ]
  @output = options[ :output ]
  @settings = options[ :settings ]
  @user_id = options[ :user_id ]
end

Lame right? Wouldn’t it be nicer to do something like this?

def initialize(options={})
  keys_to_instance_variables(options)
end
Darn right it would. So I wrote this:
module KeysToInstanceVariables

  def keys_to_instance_variables(hash={})
    hash.keys.each do |key|
      sym = "@#{key.to_s}".to_sym
      self.instance_variable_set(sym, hash[key])
    end
  end

end

Object.send( :include, KeysToInstanceVariables )

I plopped that in a file called keys_to_instance_variables.rb, then required it from my class, and wham bam, dryer object instantiation.

Don’t believe me? Let’s go to the tests:
# Meet the Person class.  It's about to be put to the test.
# People have names, ages and heights.
require 'keys_to_instance_variables'
class Person

  def initialize(options={})
    keys_to_instance_variables(options)
  end

end

# And now let's put our little method through the motions.
class KeysToInstanceVariablesTest < Test::Unit::TestCase

  def test_should_have_person_class
    assert_not_nil Person
  end

  def test_should_include_keys_to_instance_variables_module
    Person.include?(KeysToInstanceVariables)
  end

  def test_should_convert_parameter_keys_to_instance_variables
    person = Person.new( :name => "Pat", :age => 21, :height => 64 )
    assert_equal 'Pat', person.instance_variable_get( :@name )
    assert_equal 21, person.instance_variable_get( :@age )
    assert_equal 64, person.instance_variable_get( :@height )
  end

end

Fun times right? I’m not sure if I’ve committed some faux pas by playing with the object class like that, so let me know if I have. It works for me though, so hopefully it’ll work for you too.

Introducing: Capistrano Bells

Posted by Pat Nakajima on June 08, 2007 in Ruby or Rails.

Update: See this post for an update on Capistrano Bells.

Capistrano 2 is great. I’ve been using it since its first preview to deploy all of my projects, including devthatweb. Now, before I started using Capistrano 2, I would use the deprec gem to setup my Rails apps, since it had tasks to configure a Mongrel cluster and restart Apache. Unfortunately, the deprec gem isn’t compatible with Capistrano 2, and Capistrano 2 doesn’t have these features.

Rather than going back to doing these tasks on the remote server like a sucker, I decided to write some Capistrano 2 recipes to help myself out. With multiple projects though, copying the recipes and keeping them updated across the board became a nuisance.

I decided it was time to write a plugin. That way, I could keep everything updated across all my projects, as well as give back to the amazing Ruby and Rails community that have given so much to me.

So I did. I call it Capistrano Bells. I’ve been working on it for a few weeks now and I think it’s ready to see the light of day. Bells makes Capistrano 2’s task set look like this:
cap deploy                      # Deploys your project.
cap deploy:apache:reload_apache # Reload Apache webserver
cap deploy:apache:restart       # Restarts Apache webserver
cap deploy:apache:setup         # Setup an apache virtual server on remote se...
cap deploy:apache:start         # Starts Apache webserver
cap deploy:apache:stop          # Stops Apache webserver
cap deploy:check                # Test deployment dependencies.
cap deploy:cleanup              # Clean up old releases.
cap deploy:cold                 # Deploys and starts a `cold' application.
cap deploy:copy_config_files    # Copy config files to live app
cap deploy:migrate              # Run the migrate rake task.
cap deploy:migrations           # Deploy and run pending migrations.
cap deploy:mint                 # Deploy Mint stats tracking.
cap deploy:mint:copy_db_config  # Copy Mint DB config
cap deploy:mint:setup           # Configure Mint virtual server on remote app.
cap deploy:mongrel:configure    # Configure Mongrel processes on the app server.
cap deploy:mongrel:delete       # Deletes mongrel configuration file.
cap deploy:mongrel:restart      # Restart the Mongrel processes on the app se...
cap deploy:mongrel:start        # Start Mongrel processes on the app server.
cap deploy:mongrel:stop         # Stop the Mongrel processes on the app server.
cap deploy:pending              # Displays the commits since your last deploy.
cap deploy:pending:diff         # Displays the `diff' since your last deploy.
cap deploy:php                  # Deploy a PHP project.
cap deploy:php:setup            # Configure virtual server on remote app.
cap deploy:put_config_files     # Puts config files on remote server.
cap deploy:rollback             # Rolls back to a previous version and restarts.
cap deploy:rollback_code        # Rolls back to the previously deployed version.
cap deploy:setup                # Prepares one or more servers for deployment.
cap deploy:symlink              # Updates the symlink to the deployed version.
cap deploy:tail                 # Shows tail of production log
cap deploy:update               # Copies your project and updates the symlink.
cap deploy:update_code          # Copies your project to the remote servers.
cap deploy:upload               # Copy files to the currently deployed version.
cap deploy:web:disable          # Present a maintenance page to visitors.
cap deploy:web:enable           # Makes the application web-accessible again.
cap invoke                      # Invoke a single command on the remote servers.
cap local:apache:restart        # Restart apache on local machine
cap local:apache:start          # Start apache on local machine
cap local:apache:stop           # Stop apache on local machine
cap shell                       # Begin an interactive Capistrano session.
cap tools:aptitude:install      # Installs a package using the aptitude comma...
cap tools:aptitude:search       # Search for aptitude packages on remote server
cap tools:aptitude:update       # Runs aptitude update on remote server
cap tools:aptitude:upgrade      # Runs aptitude upgrade on remote server
cap tools:gems:install          # Install a gem on the remote server
cap tools:gems:list             # List gems on remote server
cap tools:gems:remove           # Uninstall a gem from the remote server
cap tools:gems:update           # Update gems on remote server
cap tools:look_for_commands     # Look for necessary commands for Rails deplo...
cap tools:ssh:setup             # Copies contents of ssh public keys into aut...
cap tools:svn:add               # Add new files to subversion
cap tools:svn:clean             # remove and ignore log files and tmp from su...
cap tools:svn:commit            # Commits changes to subversion repository
cap tools:uptime                # Displays server uptime

Installation

To get Capistrano Bells, run the following command:
script/plugin install http://svn.nakadev.com/plugins/bells
Then to install it, run the following rake task:
rake bells:install

In the Capfile, enter whatever settings you need. You can also comment out recipes that you don’t need.

Warning:

Capistrano Bells is still in its beta stage.
I make no guarantees regarding its stability nor suggest that it is fit for anything.

With that being said, I will tell you that I deployed my latest project, called Infotation, using nothing but this plugin.

Usage

While I plan to write much more in depth about the many uses of Capistrano Bells in future posts, the basic uses of the plugin should be self-explanatory to anybody familiar with Capistrano 2. To view available tasks, run:

cap -T

I tried to adhere to the principle that the ideal depth for task commands was 3 namespaces, as you can see here:
cap deploy:apache:restart

In that example, “deploy” is the task family, “apache” is the task type, and “restart” is the task itself. This system has proven itself to be intuitive enough for me to not have to constantly be running the cap -T command.

Help wanted

Feel free to sift through the recipes and code. If you see something that ought to be done differently, please let me know in the comments of this post. Again, I must warn you that this plugin is still in its beta stage. If you’d like to help me test it, I’d appreciate it.

How to update your Rails plugins' subversion externals URLs

Posted by Pat Nakajima on May 26, 2007 in Ruby or Rails.

11 hours ago, DHH committed a change to the Rails plugin repository that may or may not have wreaked havoc on your deployment setup. I know it did mine. He moved the simply_helpful, resource_feeder, and acts_as_taggable plugins into a new folder named “legacy.” When I tried to deploy a few unrelated changed I made to this blog, the externals weren’t able to be found, and my deployment broke the blog.

To fix things, I had to change my svn:externals settings, a task not very well explained by the Subversion book which is so frequently recommended for such problems.

To change the way Subversion updates externals in your repository you must run the following command:
svn propedit svn:externals vendor/plugins --editor-cmd vim
That command will open the externals file for that particular directory, and since I used the --editor-cmd vim option, it will use the vim editor. Once in the file, you’ll see something that looks like the following:
attachment_fu  http://svn.techno-weenie.net/projects/plugins/attachment_fu
simply_helpful  http://dev.rubyonrails.com/svn/rails/plugins/legacy/simply_helpful
The first part is the name of the directory that will be added to the folder whose externals file you’re editing. The second part is the corresponding repository URL. In other words, an externals file with the above text will create an attachment_fu directory as well as a simply_helpful directory, each of which will be updated with code from their respective repository URLs.

To fix the recent update made by DHH, just update the URL for the broken plugins, as you can see I’ve done for the simply helpful plugin.