ActiveRecord.id does not work within named_scope block

Hi,

Trying to create a named_scope that would check that the record is not
itself.

I have companies and contacts, where a contact can be a primary
contact for a company:

class Company
  has_many :contacts
end

class Contact < ActiveRecord::Base
  belongs_to :company

  named_scope :primary, :conditions => { :is_primary => true } do
    def mark_as_non_primary!
      each { |p| p.update_attribute(:is_primary, false) }
    end
  end

  named_scope :but_not_itself, :conditions => ['contacts.id != ?',
id]
end

So I would like to do something like this from Contact model:

  company.contacts.primary.but_not_itself.mark_as_non_primary!

However this does not work because id method within but_not_itself
named_scope returns an Object.id (instance id) and not ActiveRecord id
(database id).

How do I make it work?

Thanks!

Hi,

Trying to create a named_scope that would check that the record is not
itself.

I have companies and contacts, where a contact can be a primary
contact for a company:

class Company
has_many :contacts
end

class Contact < ActiveRecord::Base
belongs_to :company

named_scope :primary, :conditions => { :is_primary => true } do
def mark_as_non_primary!
each { |p| p.update_attribute(:is_primary, false) }
end
end

named_scope :but_not_itself, :conditions => ['contacts.id != ?',
id]
end

So I would like to do something like this from Contact model:

    company\.contacts\.primary\.but\_not\_itself\.mark\_as\_non\_primary\!

However this does not work because id method within but_not_itself
named_scope returns an Object.id (instance id) and not ActiveRecord id
(database id).

The reason this does not work is because that expression "id" is
evaluated when named_scope is call to create the scope, ie when the
class is loaded, so self is the class Contact.
It's the same reason why doing :conditions => ['created_at < ?',
1.day.ago] won't work properly: the 1.day.ago is evaluated once, at
class load time and then never again.

Luckily instead of passing a hash of options you can pass a lambda
that returns a hash of options. Since that proc will be called when
the scope is loaded and in the right context you should be fine.

Fred

Hi Fred,

Thanks for your input.

So far I changed that named_scope into this:
named_scope :but_not_itself,
  lambda { |id| { :conditions => ['contacts.id != ?', id]} }

Is that what you meant?

Is there a better way to do this?

Now I have to do this:
company.contacts.primary.but_not_itself(self.id).mark_as_non_primary!

seems, to me at least, kind of ugly having to pass self.id... :slight_smile:

By the way my inspiration for this came from
http://ryandaigle.com/articles/2008/3/24/what-s-new-in-edge-rails-has-finder-functionality
Where the author has User.inactive.activate to very neatly activate
inactive users.