Class: ActiveRecord::Associations::AssociationProxy

Inherits:
Object
  • Object
show all
Defined in:
activerecord/lib/active_record/associations/association_proxy.rb

Overview

Active Record Associations

This is the root class of all association proxies:

AssociationProxy
  BelongsToAssociation
    HasOneAssociation
  BelongsToPolymorphicAssociation
  AssociationCollection
    HasAndBelongsToManyAssociation
    HasManyAssociation
      HasManyThroughAssociation
         HasOneThroughAssociation

Association proxies in Active Record are middlemen between the object that holds the association, known as the @owner, and the actual associated object, known as the @target. The kind of association any proxy is about is available in @reflection. That's an instance of the class ActiveRecord::Reflection::AssociationReflection.

For example, given

class Blog < ActiveRecord::Base
  has_many :posts
end

blog = Blog.find(:first)

the association proxy in blog.posts has the object in blog as @owner, the collection of its posts as @target, and the @reflection object represents a :has_many macro.

This class has most of the basic instance methods removed, and delegates unknown methods to @target via method_missing. As a corner case, it even removes the class method and that's why you get

blog.posts.class # => Array

though the object behind blog.posts is not an Array, but an ActiveRecord::Associations::HasManyAssociation.

The @target object is not loaded until needed. For example,

blog.posts.count

is computed directly through SQL and does not trigger by itself the instantiation of the actual post records.

Instance Method Summary collapse

Constructor Details

#initialize(owner, reflection) ⇒ AssociationProxy

Returns a new instance of AssociationProxy



58
59
60
61
62
63
64
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 58

def initialize(owner, reflection)
  @owner, @reflection = owner, reflection
  @updated = false
  reflection.check_validity!
  Array.wrap(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
  reset
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object (private)

Forwards any missing method call to the target.



208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 208

def method_missing(method, *args)
  if load_target
    unless @target.respond_to?(method)
      message = "undefined method `#{method.to_s}' for \"#{@target}\":#{@target.class.to_s}"
      raise NoMethodError, message
    end

    if block_given?
      @target.send(method, *args)  { |*block_args| yield(*block_args) }
    else
      @target.send(method, *args)
    end
  end
end

Instance Method Details

#===(other) ⇒ Object

Forwards === explicitly to the target because the instance method removal above doesn't catch it. Loads the target if needed.



89
90
91
92
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 89

def ===(other)
  load_target
  other === @target
end

#aliased_table_nameObject

Returns the name of the table of the related class:

post.comments.aliased_table_name # => "comments"


98
99
100
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 98

def aliased_table_name
  @reflection.klass.table_name
end

#conditionsObject Also known as: sql_conditions

Returns the SQL string that corresponds to the :conditions option of the macro, if given, or nil otherwise.



104
105
106
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 104

def conditions
  @conditions ||= interpolate_sql(@reflection.sanitized_conditions) if @reflection.sanitized_conditions
end

#inspectObject

Forwards the call to the target. Loads the target if needed.



144
145
146
147
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 144

def inspect
  load_target
  @target.inspect
end

#loadedObject

Asserts the target has been loaded setting the loaded flag to true.



128
129
130
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 128

def loaded
  @loaded = true
end

#loaded?Boolean

Has the target been already loaded?

Returns:

  • (Boolean)


123
124
125
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 123

def loaded?
  @loaded
end

#proxy_ownerObject

Returns the owner of the proxy.



67
68
69
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 67

def proxy_owner
  @owner
end

#proxy_reflectionObject

Returns the reflection object that represents the association handled by the proxy.



73
74
75
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 73

def proxy_reflection
  @reflection
end

#proxy_respond_to?Object

:nodoc:



53
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 53

alias_method :proxy_respond_to?, :respond_to?

#proxy_targetObject

Returns the target of the proxy, same as target.



78
79
80
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 78

def proxy_target
  @target
end

#reloadObject

Reloads the target and returns self on success.



116
117
118
119
120
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 116

def reload
  reset
  load_target
  self unless @target.nil?
end

#resetObject

Resets the loaded flag to false and sets the target to nil.



110
111
112
113
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 110

def reset
  @loaded = false
  @target = nil
end

#respond_to?(*args) ⇒ Boolean

Does the proxy or its target respond to symbol?

Returns:

  • (Boolean)


83
84
85
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 83

def respond_to?(*args)
  proxy_respond_to?(*args) || (load_target && @target.respond_to?(*args))
end

#send(method, *args) ⇒ Object



149
150
151
152
153
154
155
156
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 149

def send(method, *args)
  if proxy_respond_to?(method)
    super
  else
    load_target
    @target.send(method, *args)
  end
end

#targetObject

Returns the target of this proxy, same as proxy_target.



133
134
135
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 133

def target
  @target
end

#target=(target) ⇒ Object

Sets the target of this proxy to \target, and the loaded flag to true.



138
139
140
141
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 138

def target=(target)
  @target = target
  loaded
end