Return type from a ActiveRecord query

activerecord, ruby-on-rails

Solution

You know, Rails sometimes lies to you -- all magicians do :)

Rails allows you to build complex queries by chaining your `has_many` associations. The core of this functionality is a bunch of XXXAssocation (like `HasManyAssociation`) classes. When you call `.class` on a `has_many` association your call is applied in fact for `HasManyAssociation` instance. But here's the magic starts:

# collection_proxy.rb
instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }

Rails undefs (hides) methods of `HasManyAssociation` instance (except the few, as you can see in the regular expression) and then uses delegation and `method_missing` to pass your call to some underlying array (if you're trying to fetch records) or to association itself (if you're chaining your association):

  delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from,
           :lock, :readonly, :having, :pluck, :to => :scoped 

  delegate :target, :load_target, :loaded?, :to => :@association

  delegate :select, :find, :first, :last,
           :build, :create, :create!,
           :concat, :replace, :delete_all, :destroy_all, :delete, :destroy, :uniq,
           :sum, :count, :size, :length, :empty?,
           :any?, :many?, :include?,
           :to => :@association

  def method_missing(method, *args, &block)
    match = DynamicFinderMatch.match(method)
    if match && match.instantiator?
      send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |r|
        proxy_association.send :set_owner_attributes, r
        proxy_association.send :add_to_target, r
        yield(r) if block_given?
      end
    end

    if target.respond_to?(method) || (!proxy_association.klass.respond_to?(method) && Class.respond_to?(method))
      if load_target
        if target.respond_to?(method)
          target.send(method, *args, &block)
        else
          begin
            super
          rescue NoMethodError => e
            raise e, e.message.sub(/ for #<.*$/, " via proxy for #{target}")
          end
        end
      end

    else
      scoped.readonly(nil).send(method, *args, &block)
    end
  end

So, `HasManyAssociation` instance decides what to handle by itself and what needs to be accomplished via hidden array (`class` method isn't what `HasManyAssociation` interested in so it will be called on this hidden array. The result, of course, will be `Array`, which is a little deception).

Problem

What will let me know if I am going to get a Relation, Array, or some other type back from an ActiveRecord call? I know I can type .class in the console and figure it out, but is there something in the call itself that will let me know what I am asking for?

Original source