RubyConf 2008

My talk got accepted to RubyConf 2008. If you would like to see me speak, you can do so Thursday Nov 6th at 3:05 in Orlando.

I will be speaking on rush. The official title of my talk is, “rush, a shell that will yield to you.” Needless to say, I am excited and I hope to see a lot of you there. Please contact me at schluter [at] gmail.com if you want to meet up at the conf.

[ANN] Rushmate

On and off over the last few months I have been using rush. Basically, rush is a replacement for the unix shell (bash, zsh, etc) which uses pure Ruby syntax. It adds a lot of convenience to the commandline for people familiar with ruby.

It brings pure ruby syntax to searching (grep), copying files, killing processes, changing permissions and globbing. You can even do all this remotely and it still feels like you are working locally. Here is a quick example.

rush> myproject
=> localhost:/Users/schlueter/myproject
rush> myproject["**/*.rb"].search(/class Bill/).mate

This will open up all the files that contain the string “class Bill” in TextMate. You could just do a find in project in this instance but, with this you can target certain files quicker.

I recently started writing a Textmate command and really wanted to use a few things in rush.

That us why I am announcing Rushmate, the first gem I have written individually. Rushmate provides a Command object that allows you to seamlessly go back and forth between Textmate and rush. It adds a few accessors for TextMate Environment variables so you can access the project dir, current word, etc.

Here is a quick example:

#!/usr/bin/env ruby
require 'rubygems'
require 'rushmate'
Rushmate::Command.new {
  # find ruby files with the current word in textmate
  found_files = project_directory[
    "**/#{current_word.downcase}.rb"]
  if found_files.empty?
    # if you can't find any files show a tool tip
    exit.show_tool_tip(
      "can't find #{current_word.downcase}.rb in project")
  else
    if found_files.size == 1
      # if there is only 1 file go ahead and switch to that file
      found_files.mate
    else
      # if there are multiple files prompt the user for
      # which file they want to switch to
      # then switch to their selection
      menu_files = found_files.collect {
        |f| f.full_path.gsub(project_directory.full_path, "")
      }
      project_directory[
        user_input.quick_menu_from_array(menu_files)
      ].mate
    end
  end
}

It is pretty well commented, but basically it looks at the current word in TextMate then tries to find a ruby file with that name and switch to it. If it finds multiple files with that name, it puts a little menu up and waits for the user to select.

For more information on Rushmate you can view the rdoc online.

Clean Up Controllers with Before Filters

I think everyone get’s it by now. In rubyland, we generally like Skinny Controllers and Fat Models. I just wanted to share a little before_filter I use on my CRUD controllers to make each action smaller even if it doesn’t make the whole controller smaller.

def load_record
  unless params[:id].blank?
    @record = SomeActiveRecordClass.find(params[:id])
  end
  if @record.nil?
    @record = SomeActiveRecordClass.new
  end
  @record.attributes = params[:record] if params[:record]
end

I run this little before filter before new, create, edit, update, and destroy. In any of those actions you can depend on @record being set in the proper state, ready for saving, populating a form, or deleting.

It isn’t anything special just a way to keep boilerplate code out of the actions themselves.

Do you have any before_filter patterns? How do you keep your controllers looking slim and trim?

On an unrelated note, RideCharge, my employer, is actively seeking a QA Specialist. Our ideal QA person would be able to write automated tests in addition to normal QA activity. If you have anymore questions contact me schlueter [at] gmail [.] com.

has_one weirdness

I am not quite prepared to call this a rails bug. But has_one definitely behaves weirdly in rails 2.1.

Lets say you have the following two models:

class Email < ActiveRecord::Base
  belongs_to :user
end

class User < ActiveRecord::Base
  has_one :email
end

A very straight forward has_one association. Now give the following from a script/console session:

>> user = User.first
=> #<User id: 1, created_at: "2008-07-09 01:19:11", updated_at: "2008-07-09 01:19:11">
>> user.email = Email.find(user.email.id)
=> #<Email id: 1, user_id: 1, created_at: "2008-07-09 01:19:26", updated_at: "2008-07-09 01:19:26">
>> user.reload
=> #<User id: 1, created_at: "2008-07-09 01:19:11", updated_at: "2008-07-09 01:19:11">
>> user.email
=> nil

That is weird. There is no reason you would do this in production, but if you were doing this you wouldn’t expect it to update the email row to set user_id to nil. Unfortunately, that is exactly what is happening.

Personally, I wouldn’t expect it to write anything to the database until I called save on user.

Anyone have a reasonable explanation, or should I submit it to Lighthouse.

Rails 2.1 Eager Loading

As long as I can remember you could always eager load your associations in rails. If you wanted to find all your users companies, you could tell rails to do it in an efficient way.

There is no change in the code you write

User.all(:include => :company)

The SQL it used to generate was:

SELECT
  `users`.`id`      AS t0_r0
  `users`.`email   AS t0_r1
  . . .
FROM
  `users`
  LEFT OUTER JOIN `companies` ON `companies`.id = `users`.company_id

Fine, that isn’t really news, but now active_record splits that sql statement up into two statements.

SELECT * FROM `users`;
SELECT * FROM `companies`
  WHERE `companies`.id IN ('1','2', . . . );

Apparently this will be faster in most cases. I don’t have anything to say about that but, there are two interesting things I want to show you. They both involve :conditions.

If you put conditions on the users table it still generates two queries.

User.all(:include => :company,
  :conditions => "`users`.`email` LIKE '%gmail%'")
SELECT * FROM `users`
  WHERE `users`.`email` LIKE '%gmail%';
SELECT * FROM `companies`
  WHERE `companies`.id IN ('1','2', . . . );

But if you put conditions on the companies table it fails over to the old style and generates one statement

User.all(:include => :company,
  :conditions => "`companies`.`name` LIKE '%google%'")
SELECT
  `users`.`id`      AS t0_r0
  `users`.`email   AS t0_r1
  . . .
FROM
  `users`
  LEFT OUTER JOIN `companies` ON `companies`.id = `users`.company_id
WHERE
  `companies`.`name` LIKE '%google%'

I don’t know if they have to do it that way to get the correct results or what. But I thought it was interesting.

Feeds/Syndication