Class: Formed::Associations::CollectionAssociation

Inherits:
Association
  • Object
show all
Defined in:
lib/formed/associations/collection_association.rb

Overview

:nodoc:

Direct Known Subclasses

HasManyAssociation

Instance Attribute Summary

Attributes inherited from Association

#disable_joins, #owner, #reflection, #target

Instance Method Summary collapse

Methods inherited from Association

#extensions, #initialize, #initialize_attributes, #klass, #loaded!, #loaded?, #marshal_dump, #marshal_load, #reload, #reset_negative_cache, #reset_scope, #set_inverse_instance, #stale_target?

Constructor Details

This class inherits a constructor from Formed::Associations::Association

Instance Method Details

#add_to_target(record, skip_callbacks: false, replace: true, &block) ⇒ Object



125
126
127
# File 'lib/formed/associations/collection_association.rb', line 125

def add_to_target(record, skip_callbacks: false, replace: true, &block)
  replace_on_target(record, skip_callbacks, replace: replace, &block)
end

#build(attributes = nil, &block) ⇒ Object



27
28
29
30
31
32
33
# File 'lib/formed/associations/collection_association.rb', line 27

def build(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| build(attr, &block) }
  else
    add_to_target(build_record(attributes, &block), replace: true)
  end
end

#concat(*records) ⇒ Object

Add records to this association. Since << flattens its argument list and inserts each record, push and concat behave identically.



37
38
39
40
41
# File 'lib/formed/associations/collection_association.rb', line 37

def concat(*records)
  records = records.flatten
  load_target if owner.new_record?
  concat_records(records)
end

#empty?Boolean

Returns true if the collection is empty.

If the collection has been loaded it is equivalent to collection.size.zero?. If the collection has not been loaded, it is equivalent to !collection.exists?. If the collection has not already been loaded and you are going to fetch the records anyway it is better to check collection.length.zero?.

Returns:

  • (Boolean)


74
75
76
77
78
79
80
# File 'lib/formed/associations/collection_association.rb', line 74

def empty?
  if loaded? || @association_ids || reflection.has_cached_counter?
    size.zero?
  else
    target.empty? && !scope.exists?
  end
end

#find_from_target?Boolean

Returns:

  • (Boolean)


149
150
151
152
153
154
155
# File 'lib/formed/associations/collection_association.rb', line 149

def find_from_target?
  loaded? ||
    owner.strict_loading? ||
    reflection.strict_loading? ||
    owner.new_record? ||
    target.any? { |record| record.new_record? || record.changed? }
end

#include?(record) ⇒ Boolean

Returns:

  • (Boolean)


106
107
108
109
110
111
112
113
114
115
116
# File 'lib/formed/associations/collection_association.rb', line 106

def include?(record)
  if record.is_a?(reflection.klass)
    if record.new_record?
      include_in_memory?(record)
    else
      loaded? ? target.include?(record) : scope.exists?(record.id)
    end
  else
    false
  end
end

#load_targetObject



118
119
120
121
122
123
# File 'lib/formed/associations/collection_association.rb', line 118

def load_target
  @target = merge_target_lists(find_target, target) if find_target?

  loaded!
  target
end

#null_scope?Boolean

Returns:

  • (Boolean)


145
146
147
# File 'lib/formed/associations/collection_association.rb', line 145

def null_scope?
  owner.new_record?
end

#readerObject

Implements the reader method, e.g. foo.items for Foo.has_many :items



7
8
9
10
11
12
13
14
# File 'lib/formed/associations/collection_association.rb', line 7

def reader
  ensure_klass_exists!

  reload if stale_target?

  @proxy ||= CollectionProxy.create(klass, self)
  @proxy.reset_scope
end

#replace(other_array) ⇒ Object

Replace this collection with other_array. This will perform a diff and delete/add only records that have changed.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/formed/associations/collection_association.rb', line 84

def replace(other_array)
  other_array = other_array.map do |other|
    if other.class < Formed::Base
      other
    else
      build_record(other)
    end
  end
  original_target = load_target.dup

  if owner.new_record?
    replace_records(other_array, original_target)
  else
    replace_common_records_in_memory(other_array, original_target)
    if other_array != original_target
      transaction { replace_records(other_array, original_target) }
    else
      other_array
    end
  end
end

#resetObject



20
21
22
23
24
25
# File 'lib/formed/associations/collection_association.rb', line 20

def reset
  super
  @target = []
  @replaced_or_added_targets = Set.new
  @association_ids = nil
end

#scopeObject



142
143
# File 'lib/formed/associations/collection_association.rb', line 142

def scope
end

#sizeObject

Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn’t been loaded, and calling collection.size if it has.

If the collection has been already loaded size and length are equivalent. If not and you are going to need the records anyway length will take one less query. Otherwise size is more efficient.

This method is abstract in the sense that it relies on count_records, which is a method descendants have to provide.



53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/formed/associations/collection_association.rb', line 53

def size
  if !find_target? || loaded?
    target.size
  elsif @association_ids
    @association_ids.size
  elsif !association_scope.group_values.empty?
    load_target.size
  else
    unsaved_records = target.select(&:new_record?)
    unsaved_records.size + count_records
  end
end

#target=(record) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/formed/associations/collection_association.rb', line 129

def target=(record)
  return super unless reflection.klass.has_many_inversing

  case record
  when nil
    # It's not possible to remove the record from the inverse association.
  when Array
    super
  else
    replace_on_target(record, true, replace: true, inversing: true)
  end
end

#writer(records) ⇒ Object



16
17
18
# File 'lib/formed/associations/collection_association.rb', line 16

def writer(records)
  replace(records)
end