class Deferring::DeferredAssociation

Attributes

dependent[R]
inverse_name[R]
klass[R]
load_state[R]
parent_record[R]

Public Class Methods

new(original_association, klass, parent_record, inverse_name, dependent) click to toggle source
Calls superclass method
# File lib/deferring/deferred_association.rb, line 13
def initialize(original_association, klass, parent_record, inverse_name, dependent)
  super(original_association)
  @load_state    = :ghost
  @klass         = klass
  @parent_record = parent_record
  @inverse_name  = inverse_name
  @dependent     = dependent
end

Public Instance Methods

<<(*records) click to toggle source
# File lib/deferring/deferred_association.rb, line 119
def <<(*records)
  # TODO: Do we want to prevent including the same object twice? Not sure,
  # but it will probably be filtered after saving and retrieving as well.
  records.flatten.compact.uniq.each do |record|
    run_deferring_callbacks(:link, record) do
      if inverse_name && record.class.reflect_on_association(inverse_name)
        record.send(:"#{inverse_name}=", parent_record)
      end

      objects << record
    end
  end
  self
end
Also aliased as: push, concat, append
add_callback_listener(event_name, callback_method) click to toggle source
# File lib/deferring/deferred_association.rb, line 211
def add_callback_listener(event_name, callback_method)
  (@listeners ||= []) << DeferredCallbackListener.new(event_name, parent_record, callback_method)
end
append(*records)
Alias for: <<
build(*args, &block) click to toggle source
# File lib/deferring/deferred_association.rb, line 156
def build(*args, &block)
  klass.new(*args, &block).tap do |record|
    run_deferring_callbacks(:link, record) do
      if inverse_name && record.class.reflect_on_association(inverse_name)
        record.send(:"#{inverse_name}=", parent_record)
      end

      objects.push(record)
    end
  end
end
changed_for_autosave?() click to toggle source

Returns true if there are links that will be created or deleted when saving the parent of the association.

# File lib/deferring/deferred_association.rb, line 207
def changed_for_autosave?
  links.any? || unlinks.any?
end
concat(*records)
Alias for: <<
count(*args, &block) click to toggle source

Delegates Ruby's Enumerable#count method to the original association.

The delegation has to be explicit in this case, because the inclusion of Enumerable also defines the count-method on DeferredAssociation.

# File lib/deferring/deferred_association.rb, line 65
def count(*args, &block)
  if block_given?
    objects.count(*args, &block)
  else
    original_association.count(*args)
  end
end
create(*args, &block) click to toggle source
# File lib/deferring/deferred_association.rb, line 168
def create(*args, &block)
  association.create(*args, &block).tap do |_|
    @load_state = :ghost
    load_objects
  end
end
create!(*args, &block) click to toggle source
# File lib/deferring/deferred_association.rb, line 175
def create!(*args, &block)
  association.create!(*args, &block).tap do |_|
    @load_state = :ghost
    load_objects
  end
end
delete(*records) click to toggle source
# File lib/deferring/deferred_association.rb, line 137
def delete(*records)
  records.flatten.compact.uniq.each do |record|
    run_deferring_callbacks(:unlink, record) { objects.delete(record) }
  end
  self
end
destroy(*records) click to toggle source
# File lib/deferring/deferred_association.rb, line 144
def destroy(*records)
  records.flatten.compact.uniq.each do |record|
    record = record.to_i if record.is_a? String
    record = objects.detect { |o| o.id == record } if record.is_a? Integer

    run_deferring_callbacks(:unlink, record) {
      objects.delete(record)
      record.mark_for_destruction if dependent && [:destroy, :delete_all].include?(dependent)
    }
  end
end
each(&block) click to toggle source
# File lib/deferring/deferred_association.rb, line 31
def each(&block)
  objects.each(&block)
end
find(*args) click to toggle source

Delegates Ruby's Enumerable#find method to the original association.

The delegation has to be explicit in this case, because the inclusion of Enumerable also defines the find-method on DeferredAssociation.

# File lib/deferring/deferred_association.rb, line 57
def find(*args)
  original_association.find(*args)
end
ids() click to toggle source
# File lib/deferring/deferred_association.rb, line 115
def ids
  objects.map(&:id)
end
inspect() click to toggle source
# File lib/deferring/deferred_association.rb, line 23
def inspect
  objects.inspect
end
Also aliased as: pretty_inspect
objects() click to toggle source
# File lib/deferring/deferred_association.rb, line 93
def objects
  load_objects
  @objects
end
objects=(records) click to toggle source
# File lib/deferring/deferred_association.rb, line 98
def objects=(records)
  @objects = records.compact.map do |record|
    if inverse_name && record.class.reflect_on_association(inverse_name)
      record.send(:"#{inverse_name}=", parent_record)
    end
    record
  end

  @original_objects = original_association.to_a.clone
  objects_loaded!

  pending_deletes.each { |record| run_deferring_callbacks(:unlink, record) }
  pending_creates.each { |record| run_deferring_callbacks(:link, record) }

  @objects
end
pending_creates()
Alias for: links
pending_deletes()
Alias for: unlinks
pretty_inspect()
Alias for: inspect
push(*records)
Alias for: <<
reload() click to toggle source
# File lib/deferring/deferred_association.rb, line 182
def reload
  original_association.reload
  @load_state = :ghost
  self
end
Also aliased as: reset
reset()
Alias for: reload
select(value = nil, &block) click to toggle source

Delegates Ruby's Enumerable#select method to the original association when no block has been given. Rails' select-method does not accept a block, so we know that in that case the select-method has to be called on our deferred association.

The delegation has to be explicit in this case, because the inclusion of Enumerable also defines the select-method on DeferredAssociation.

# File lib/deferring/deferred_association.rb, line 80
def select(value = nil, &block)
  if block_given?
    objects.select { |*block_args| block.call(*block_args) }
  else
    original_association.select(value)
  end
end
set_inverse_instance(associated_record, parent_record) click to toggle source

Rails 3.0 specific, not needed anymore for Rails 3.0+

# File lib/deferring/deferred_association.rb, line 89
def set_inverse_instance(associated_record, parent_record)
  original_association.__send__(:set_inverse_instance, associated_record, parent_record)
end

Private Instance Methods

association() click to toggle source
# File lib/deferring/deferred_association.rb, line 217
def association
  load_objects
  original_association
end
load_objects() click to toggle source
# File lib/deferring/deferred_association.rb, line 222
def load_objects
  return if objects_loaded?

  @objects = original_association.to_a.clone
  @original_objects = @objects.clone.freeze
  objects_loaded!
end
notify_callback_listeners(event_name, record) click to toggle source
# File lib/deferring/deferred_association.rb, line 249
def notify_callback_listeners(event_name, record)
  @listeners && @listeners.each do |listener|
    if listener.event_name == event_name
      listener.public_send(event_name, record)
    end
  end
end
objects_loaded!() click to toggle source
# File lib/deferring/deferred_association.rb, line 234
def objects_loaded!
  @load_state = :loaded
end
objects_loaded?() click to toggle source
# File lib/deferring/deferred_association.rb, line 230
def objects_loaded?
  @load_state == :loaded
end
original_objects() click to toggle source
# File lib/deferring/deferred_association.rb, line 238
def original_objects
  load_objects
  @original_objects
end
run_deferring_callbacks(event_name, record) { || ... } click to toggle source
# File lib/deferring/deferred_association.rb, line 243
def run_deferring_callbacks(event_name, record)
  notify_callback_listeners(:"before_#{event_name}", record)
  yield if block_given?
  notify_callback_listeners(:"after_#{event_name}", record)
end