Archive for March, 2006

Posted on March 30, 2006 at 9:45 pm

It looks like edge Rails revision 4007 breaks has_many :through associations if the association name does not correspond to the associated model. Before today, you could use :class_name to clear things up:

has_many :members, :through => :memberships, :class_name => "User"

That will now throw a nice descriptive error:

ActionView::TemplateError: Could not find the source association(s)
:member or :members in model Membership.  Try 'has_many :members,
:through => :memberships, :source => '.  Is it one of :user or :group?

You now need to specify the target model with :source:

has_many :members, :through => :memberships, :source => :user

I would just like to add that the world needs more error messages like the one above. Not only does it give you the correct syntax to use, but even tells you the two valid options for :source based on the real data!

Posted on March 24, 2006 at 6:21 pm

Graphviz is a nice piece of software that automated the layout of directed or undirected graphs, given a set of connected nodes. Last night I decided to see if I could make a chart of all my ancestors using Graphviz. I didn’t want to have to parse a gedcom file for this, and I already have a database containing all the data I need, so I whipped up something in Ruby. I used the ActiveRecord gem so that I wouldn’t have to write any SQL. It took me about 30 minutes to come up with nice solution and here it is:

require 'rubygems'
require_gem 'activerecord'

ActiveRecord::Base.connection = {
  :adapter => 'mysql',
  :database => 'retrospect',
  :username => 'root',
  :password => ''
}
ActiveRecord::Base.table_name_prefix = 'rgds_'
ActiveRecord::Base.pluralize_table_names = false

class Indiv < ActiveRecord::Base
  set_primary_key 'indkey'
  has_and_belongs_to_many :families, :join_table => 'rgds_children',
                          :association_foreign_key => 'famkey',
                          :foreign_key => 'indkey'
end

class Family < ActiveRecord::Base
  set_primary_key 'famkey'
  belongs_to :father, :class_name => 'Indiv', :foreign_key => 'spouse1'
  belongs_to :mother, :class_name => 'Indiv', :foreign_key => 'spouse2'
end

File::open("/Users/keithm/Desktop/test.dot", "w+") do |file|

  indivs = Indiv.find_all
  file.puts "digraph G {"
  file.puts "node [style=filled, height=1, width=1]"

  indivs.each do |i|
    i.families.each do |f|
      if f.father || f.mother
        shape = (i.sex == 'M') ? 'box' : 'ellipse'
        color = (i.sex == 'M') ? 'lightblue' : 'lightpink'
        file.puts "#{i.id} [shape=#{shape}, fillcolor=#{color}, label=\"#{i.name}\"]"
      end

      if !f.father.nil?
        file.puts "#{f.father.id} [shape=box, fillcolor=lightblue, label=\"#{f.father.name}\"]"
        file.puts "#{f.father.id} -> #{i.id}"
      end

      if !f.mother.nil?
        file.puts "#{f.mother.id} [shape=ellipse, fillcolor=lightpink, label=\"#{f.mother.name}\"]"
        file.puts "#{f.mother.id} -> #{i.id}"
      end
    end
  end

  file.puts '}'

end

You just open up the resulting .dot file with Graphviz and a couple of seconds later you have a huge chart (4Mb). If you open up the full image, don’t be surprised when you find that it’s almost 20K pixels wide!

Posted on March 21, 2006 at 6:21 pm

One of the new features being introduced in Rails 1.1 is ActiveRecord#with_scope. The with_scope functionality lets you wrapper a bunch of calls to Model#find or Model#create with a particular set of conditions. These conditions will always be used, regardless of what additional conditions you call Model#find with. If you want to know more about with_scope you might want to read the author’s article on how it works.

Rails includes the base with_scope and with_exclusive scope functionality, but there is a plugin called scoped_access that gives you a few nice tools to make life easier. The article I mentioned about tells you how to get a hold of it.

I’m using ScopedAccess::Filter in Callbx to enforce different types of security. For example, a user should never be able to see tickets from groups that he does not belong to. Normally I would have to write the conditions to restrict access on every action in the controller. Using a ScopedAccess::Filter I can set the scope for Ticket.find on the entire controller. This frees me to concentrate on the functionality for each action, rather than having to be concerned with the access control while coding the rest of the controller.

class TicketsController < ApplicationController

  # limit the scope on all calls to Ticket.find in this controller
  around_filter ScopedAccess::Filter.new(Ticket, :group_conditions)

  def list
    @ticket = Ticket.find(:all, :conditions => "status_code_id < 10")
  end

  protected

  # only show tickets from groups that the user is a member of
  def group_conditions
    conditions = []
    session[:user].groups.each do |group|
      conditions << "group_id = '#{group.id}'"
    end
    return :find => {:conditions => "(#{conditions.join(' OR ')})"}
  end

end

Assuming that the logged in user belongs to groups 5 and 17, the SQL generated on that Ticket.find in the list method would be something like:

SELECT * FROM `tickets` WHERE status_code_id < ‘10′ AND (group_id = ‘5′ OR group_id = ‘17′)

Posted on March 14, 2006 at 5:08 pm

Today I subscribed to Stowe Boyd’s blog after finding it via a signal vs. noise entry and the first blog entry was titled “Basecamp and the Federation of Work”.

In the article Stowe is compaining about the fact that he has to have several logins to Basecamp, because some of the projects he is involved in are owned by others. Basecamp doesn’t have what he calls a “Federation of Work” model, where you can login to Basecamp once and have access to your own projects as well other people’s projects that you have access to.

This is something that I’ve always found annoying too, so last year when I started working on Callbx I decided that people need to have consolidated access to their own groups and other groups that they have been invited to join.

Callbx is a help desk/ticketing application that I’m gearing specifically for smaller companies and consultants. Consultants especially, often work for several companies at once and may need to be able to login to several groups in Callbx to support their clients. To have to open separate browser windows, each logged into a different account wouldn’t be much fun.

My brother and I were discussing various aspects of Callbx this weekend and I told him I was thinking of trimming out the federated account stuff in order to prep Callbx for the public launch more quickly. But, after reading Stowe’s article I decided that it really is critical to have it from the beginning, even if it does take longer to get it out the door.

Posted on March 9, 2006 at 5:37 pm

My new MacBook Pro is keeping me warm on this snowy day here in Portland. That’s two cores to keep me warm.

CoreDuo