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.

Issues with scopes/association proxies lazy/enum behavior ...

#4589

This seems to have crapped up in trunk in recent days...

lets say I have a setup like this:

class User < ActiveRecord::Base
  has_many :channels
end

class Channel < ActiveRecord::Base
  scope :audio, where(:classification => 'audio')
end

Assuming each user has some related audio channels, if I do something like:

User.first.channels.audio.select &:nil?

I would expect an empty array. However it appears the 'select &:nil?' bit is not working, as it returns the same results as User.first.channels.audio (as I say above, assuming all users have channels)

If I take out the audio scope and do something like:

User.first.channel_user_permissions.select &:nil? that seems to work.

Reported by VirtualFunction · May 13th, 2010 @ 06:49 AM

State: invalid
Milestone: none
Assigned to: nobody
Importance: Low

Activity

  1. VirtualFunction
    VirtualFunction

    OK, looks like this is specifically a scope issue, association proxies without scopes seem OK. For instance this fails:

    Channel.audio.select &:nil?

    May 13th, 2010 @ 06:50 AM

  2. VirtualFunction
    VirtualFunction

    For your info, I'm using the trunk version of Rails, it's a streaming fresh copy, about few hours old.

    May 13th, 2010 @ 06:51 AM

  3. Neeraj Singh
    Neeraj Singh

    I spent some time looking into this issue and this is what I found.

    class Brake
      scope :good, where(:quality => 'good')
    end
    Brake.all.class #=> Array
    Brake.good.select #=> ActiveRecord::Relation
    

    Actually you were unlucky to encounter 'select'. Instead of 'select' if you try 'collect' or 'map' they will work fine.

    Why is it so?

    Well Brake.unscoped returns ActiveRecord::Relation. This class has following statement.

    ASSOCIATION_METHODS = [:includes, :eager_load, :preload]
    MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having]
    SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :create_with, :from]
    

    ActiveRecord::Relation declares a method for all the items in the array. That method returns an instance of ActiveRecor::Relation. So 'select' method returns an instance of ActiveRecord::Relation so that you can do things like this.

    rails console  
    > unscoped = Brake.unscoped; nil
    > unscoped.select(:id)
    SELECT id FROM "brakes"
    > unscoped.inspect
    SELECT "brakes".* FROM "brakes"
    

    So in this case 'select' is being used to select what columns should be picked up.

    You will also find following statement in ActiveRecord::Relation

    delegate :length, :collect, :map, :each, :all?, :include?, :to => :to_a
    

    It means that operations like 'map' and 'collect' would work fine.

    Back to original problem

    One solution could be to rename select to select_query so that select could be used for enumeration purpose.

    I will post it on the mailing list and will see what the core team says.

    May 14th, 2010 @ 04:17 AM

  4. VirtualFunction
    VirtualFunction

    OK, thanks for getting back. Yes I should I've realized this this myself tbh... My bad, probably due to a lack of sleep.

    Could be worth making this clear in the documentation when the final release is made given the ambiguity of expectation here. Just another idea that's all along with the select_query concept (given that the "find" method also has the same ambiguity).

    Given that enum has a find_all method that is aliased to select, I'll probably use that (or just call "all" prior to that to coerce things to an Enum)

    May 14th, 2010 @ 01:03 PM

  5. Santiago Pastorino
    Santiago Pastorino
    • State changed from new to invalid

    Yeah select at this point returns a Relation object which represents the query to be executed and doesn't execute it. Lazy Load :).

    May 14th, 2010 @ 11:24 PM