Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Array of records from ActiveRecord Association does not behave as expected

#3667

From my limited experience and knowledge of Ruby on Rails, it appears that an Array can perform just like an association array when it is created from an array of associated records.
Take the following example. (Though this is simple, it illustrates the point.)


Project has_and_belongs_to_many Contracts
Contract has_and_belongs_to_many Projects

@record = Project.first
all_contracts = @record.contracts
@cts = Contracts.find(:all, :limit=>5)

@cts.each do |c|
  all_contracts << c
end

Every record that is added to the all_contracts array is actually added to the @record.contracts association. It performs like I had called @record.contracts << c
In the console I can even see the records being inserted into the database.

My solution was like this:


@record = Project.first
all_contracts = Array.new()
all_contracts = all_contracts | @record.contracts
@cts = Contracts.find(:all, :limit=>5)

@cts.each do |c|
  all_contracts << c
end

My solution does not create any join records.

Is this a feature or a bug? Did I miss some documentation some where to this effect?

Reported by Kent T · January 7th, 2010 @ 05:37 PM

State: wontfix
Milestone: none
Assigned to: nobody
Importance: none

Activity

  1. Matt Jones
    Matt Jones
    • State changed from new to wontfix

    This behavior is by design - @record.contracts doesn't actually return an Array, but an instance of AssociationCollection that mostly pretends to be an array. This lets you do things like:

    @record.contracts.exists?(@some_contract)
    

    which does a specific SQL query if @record.contracts isn't loaded yet, avoiding that database overhead. It also permits you to chain named scopes onto an association (@record.contracts.some_scope) and build/create associated records (with @record.contracts.build, etc.).

    If you really want to remove all the magic, @record.contracts.to_a will do what you're looking for.

    January 7th, 2010 @ 07:04 PM

  2. Kent T
    Kent T

    I don't wish to remove any of the magic of @record.contracts.
    However, when I instantiate an array from @record.contracts, should the array be an Array or an instance of AssociationCollection?

    Example

    all_contracts = @record.contracts

    all_contracts would be an array

    @record.contracts would be an instance of AssociationCollection

    Interestingly enough, when I printed the class of all_contracts it shows as an array, but it behaves like AssociationCollection. This is what I don't understand.

    January 7th, 2010 @ 07:31 PM

  3. Matt Jones
    Matt Jones

    This is a common gotcha in Ruby - objects are pretty much passed around as references. An example:

    a = [1,2,3]
    b = a
    a << 4
    b # => [1,2,3,4]
    

    So in your example, all_contracts is still an instance of AssociationCollection (although it fibs in the .class method and claims to be an Array). There are some ways to tell the two apart, though - all_contracts.respond_to?(:proxy_reflection) will return true, not that one typically needs to figure out that difference in standard Rails code.

    Anyway, if you have further questions, feel free to ask on the rails-talk list to avoid cluttering up Lighthouse.

    January 7th, 2010 @ 09:32 PM

  4. Rohit Arondekar
    Rohit Arondekar
    • Tag changed from activerecord associations, habtm to activerecord, associations, habtm

    October 7th, 2010 @ 05:21 AM