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 ('+ Foo' signifies an included module Foo):

AssociationProxy
  SingularAssociaton
    HasOneAssociation
      HasOneThroughAssociation + ThroughAssociation
    BelongsToAssociation
      BelongsToPolymorphicAssociation
  AssociationCollection
    HasAndBelongsToManyAssociation
    HasManyAssociation
      HasManyThroughAssociation + ThroughAssociation

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 Attribute Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (AssociationProxy) initialize(owner, reflection)

A new instance of AssociationProxy



58
59
60
61
62
63
64
65
# 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
  construct_scope
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

- (Object) method_missing(method, *args, &block)

Forwards any missing method call to the target.



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

def method_missing(method, *args, &block)
  if load_target
    return super unless @target.respond_to?(method)
    @target.send(method, *args, &block)
  end
rescue NoMethodError => e
  raise e, e.message.sub(/ for #<.*$/, " via proxy for #{@target}")
end

Instance Attribute Details

- (Object) target Also known as: proxy_target

Returns the target of this proxy, same as proxy_target.



147
148
149
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 147

def target
  @target
end

Instance Method Details

- (Object) ===(other)

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



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

def ===(other)
  other === load_target
end

- (Object) aliased_table_name

Returns the name of the table of the related class:

post.comments.aliased_table_name # => "comments"


107
108
109
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 107

def aliased_table_name
  @reflection.klass.table_name
end

- (Object) inspect

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



159
160
161
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 159

def inspect
  load_target.inspect
end

- (Object) loaded!

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



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

def loaded!
  @loaded      = true
  @stale_state = stale_state
end

- (Boolean) loaded?

Has the target been already loaded?

Returns:

  • (Boolean)


126
127
128
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 126

def loaded?
  @loaded
end

- (Object) proxy_owner

Returns the owner of the proxy.



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

def proxy_owner
  @owner
end

- (Object) proxy_reflection

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



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

def proxy_reflection
  @reflection
end

- (Object) reload

Reloads the target and returns self on success.



118
119
120
121
122
123
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 118

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

- (Object) reset

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



112
113
114
115
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 112

def reset
  @loaded = false
  @target = nil
end

- (Boolean) respond_to?(*args)

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)
  super || (load_target && @target.respond_to?(*args))
end

- (Object) scoped



168
169
170
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 168

def scoped
  target_scope & @association_scope
end

- (Object) send(method, *args)



163
164
165
166
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 163

def send(method, *args)
  return super if respond_to?(method)
  load_target.send(method, *args)
end

- (Boolean) stale_target?

The target is stale if the target no longer points to the record(s) that the relevant foreign_key(s) refers to. If stale, the association accessor method on the owner will reload the target. It's up to subclasses to implement the state_state method if relevant.

Note that if the target has not been loaded, it is not considered stale.

Returns:

  • (Boolean)


142
143
144
# File 'activerecord/lib/active_record/associations/association_proxy.rb', line 142

def stale_target?
  loaded? && @stale_state != stale_state
end

- (Object) to_param



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

def to_param
  proxy_target.to_param
end