views:

616

answers:

2

Hi,

I have two ActiveRecord models with a hasMany / belongsTo association:

class User < ActiveRecord::Base
  has_many :letters
end

class Letter < ActiveRecord::Base
  belongs_to :user
end

The User model has a revision_number attribute, to which I would like to scope the belongs_to association, so the letter is associated to a User by both user.id and user.revision_number.

I tried using the :conditions key as documented in the API docs:

class Letter < ActiveRecord::Base
  belongs_to :user, :conditions => "revision_number = #{client_revision}"
end

but this attempts to call client-revision on the Letter class, not the instance of Letter. Could anyone point me in the right direction for scoping the belongs_to association correctly?

I'm using the acts-as-revisable plugin to version the User model.

A: 

I am having a hard time understanding why you would want to scope the belongs_to in this way. Correct me if I am wrong, but it might be better to do something like this. I am assuming you want some sort of version control system:

class User < ActiveRecord::Base
  has_many :letters
end

class Letter < ActiveRecord::Base
  has_many :revisions, :class_name => "LetterVersion"
  belongs_to :current, :class_name => "LetterVersion"
end

class LetterVersion < ActiveRecord::Base
  belongs_to :letter
end
Blake Chambers
If you want, you could scope current to the most recently updated_at revision and use a has_one. That would avoid adding another foreign key to the letter model.
Blake Chambers
Hi,Thanks for your responses. I'm using the acts_as_revisable plugin. In my database, Users are versioned so that any changes to the user model are auditable. Letters are not versioned.A Letter's text contains bound values, e.g the user's name. When viewed, the letter should display the client's name at the time the letter was created.Perhaps a better example would be products on invoices; the product's price needs to be captured within the invoice so that viewing an invoice several months down the line will show the correct price at the time the invoice was created.Thanks,Chris
Chris
A: 

Finally figured out what I needed was something like composite keys, which Rails ActiveRecord doesn't support. The solution (for now at least) was to write custom client accessors on the Letter to support the composite keys (id and revision_number):

class Letter < ActiveRecord::Base
  def client
    Client.find_by_id(self.client_id).try(:find_revision, self.client_revision)
  end

  def client=(c)
    self.client_id = c.id
    self.client_revision = c.revision_number
  end
end

class Client < ActiveRecord::Base
  acts_as_revisable

  has_many :letters
end

With this setup, Client#1.letters will retrieve an array of both letters, but Letter#2.client will retrieve Client#1r2, whilst Letter#2.client will retrieve Client#1r4:

Client         id:    1    1    1    1    1    1
       rev_number:    1    2    3    4    5    6

Letter         id:         1              2
        client_id:         1              1
  client_revision:         2              5

Still not sure if this is the best approach to this problem, but it seems to work for now.

Chris