Class: DatastaxRails::Associations::CollectionAssociation

Inherits:
Association show all
Defined in:
lib/datastax_rails/associations/collection_association.rb

Overview

DatastaxRails Association Collection

CollectionAssociation is an abstract class that provides common stuff to ease the implementation of association proxies that represent collections. See the class hierarchy in AssociationProxy.

You need to be careful with assumptions regarding the target: The proxy does not fetch records from the database until it needs them, but new ones created with build are added to the target. So, the target may be non-empty and still lack children waiting to be read from the database. If you look directly to the database you cannot assume that's the entire collection because new records may have been added to the target, etc.

If you need to work on all current children, new and existing records, load_target and the loaded flag are your friends.

Instance Attribute Summary collapse

Attributes inherited from Association

#loaded, #owner, #reflection, #target

Instance Method Summary collapse

Methods inherited from Association

#aliased_column_family, #association_scope, #klass, #loaded!, #reload, #reset_scope, #scoped, #set_inverse_instance, #stale_target?, #target_scope

Constructor Details

#initialize(owner, reflection) ⇒ CollectionAssociation


23
24
25
26
# File 'lib/datastax_rails/associations/collection_association.rb', line 23

def initialize(owner, reflection)
  super
  @proxy = CollectionProxy.new(self)
end

Instance Attribute Details

#proxyObject (readonly)

:nodoc:


19
20
21
# File 'lib/datastax_rails/associations/collection_association.rb', line 19

def proxy
  @proxy
end

Instance Method Details

#add_to_target(record) {|record| ... } ⇒ Object

Yields:

  • (record)

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/datastax_rails/associations/collection_association.rb', line 159

def add_to_target(record)
  callback(:before_add, record)
  yield(record) if block_given?

  if options[:uniq] && (index = @target.index(record))
    @target[index] = record
  else
    @target << record
  end

  callback(:after_add, record)
  set_inverse_instance(record)

  record
end

#any?Boolean


44
45
46
# File 'lib/datastax_rails/associations/collection_association.rb', line 44

def any?
  size > 0
end

#build(attributes = {}, options = {}, &block) ⇒ Object


87
88
89
90
91
92
93
94
95
# File 'lib/datastax_rails/associations/collection_association.rb', line 87

def build(attributes = {}, options = {}, &block)
  if attributes.is_a?(Array)
    attributes.map { |attr| build(attr, options, &block) }
  else
    add_to_target(build_record(attributes, options)) do |record|
      yield(record) if block_given?
    end
  end
end

#concat(*records) ⇒ Object

Add records to this association. Returns self so method calls may be chained. Since << flattens its argument list and inserts each record, push and concat behave identically.


192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/datastax_rails/associations/collection_association.rb', line 192

def concat(*records)
  result = true
  load_target if owner.new_record?

  records.flatten.each do |record|
    raise_on_type_mismatch(record)
    add_to_target(record) do |_r|
      result &&= insert_record(record) unless owner.new_record?
    end
  end

  result && records
end

#create(attributes = {}, options = {}, &block) ⇒ Object


97
98
99
# File 'lib/datastax_rails/associations/collection_association.rb', line 97

def create(attributes = {}, options = {}, &block)
  create_record(attributes, options, &block)
end

#create!(attributes = {}, options = {}, &block) ⇒ Object


101
102
103
# File 'lib/datastax_rails/associations/collection_association.rb', line 101

def create!(attributes = {}, options = {}, &block)
  create_record(attributes, options, true, &block)
end

#delete(*records) ⇒ Object

Removes records from this association calling before_remove and after_remove callbacks.

This method is abstract in the sense that delete_records has to be provided by descendants. Note this method does not imply the records are actually removed from the database, that depends precisely on delete_records. They are in any case removed from the collection.


132
133
134
# File 'lib/datastax_rails/associations/collection_association.rb', line 132

def delete(*records)
  delete_or_destroy(records, options[:dependent])
end

#delete_allObject

Remove all records from this association

See delete for more info.


108
109
110
111
112
113
# File 'lib/datastax_rails/associations/collection_association.rb', line 108

def delete_all
  delete(load_target).tap do
    reset
    loaded!
  end
end

#destroy(*records) ⇒ Object

Destroy records and remove them from this association calling before_remove and after_remove callbacks.

Note that this method will always remove records from the database ignoring the :dependent option.


141
142
143
144
# File 'lib/datastax_rails/associations/collection_association.rb', line 141

def destroy(*records)
  records = find(records) if records.any? { |record| record.is_a?(Fixnum) || record.is_a?(String) }
  delete_or_destroy(records, :destroy)
end

#destroy_allObject

Destroy all the records from this association.

See destroy for more info.


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

def destroy_all
  destroy(load_target).tap do
    reset
    loaded!
  end
end

#empty?Boolean


40
41
42
# File 'lib/datastax_rails/associations/collection_association.rb', line 40

def empty?
  size == 0
end

#ids_readerObject

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


64
65
66
67
68
69
70
71
72
73
74
# File 'lib/datastax_rails/associations/collection_association.rb', line 64

def ids_reader
  if loaded?
    load_target.map do |record|
      record.send(reflection.association_primary_key)
    end
  else
    scoped.map! do |record|
      record.send(reflection.association_primary_key)
    end
  end
end

#ids_writer(ids) ⇒ Object

Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items


77
78
79
80
# File 'lib/datastax_rails/associations/collection_association.rb', line 77

def ids_writer(ids)
  ids = Array.wrap(ids).reject(&:blank?)
  replace(klass.find(ids).index_by(&:id).values_at(*ids))
end

#load_targetObject


153
154
155
156
157
# File 'lib/datastax_rails/associations/collection_association.rb', line 153

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

#many?Boolean


48
49
50
# File 'lib/datastax_rails/associations/collection_association.rb', line 48

def many?
  size > 1
end

#reader(force_reload = false) ⇒ Object

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


53
54
55
56
# File 'lib/datastax_rails/associations/collection_association.rb', line 53

def reader(force_reload = false)
  reload if force_reload || stale_target?
  proxy
end

#replace(other_array) ⇒ Object

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


177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/datastax_rails/associations/collection_association.rb', line 177

def replace(other_array)
  other_array.each { |val| raise_on_type_mismatch(val) }
  original_target = load_target.dup

  delete(target - other_array)

  unless concat(other_array - target) # rubocop:disable Style/GuardClause
    @target = original_target
    fail RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
                          'new records could not be saved.'
  end
end

#resetObject


82
83
84
85
# File 'lib/datastax_rails/associations/collection_association.rb', line 82

def reset
  @loaded = false
  @target = []
end

#sizeObject Also known as: count


28
29
30
31
32
33
34
35
36
37
# File 'lib/datastax_rails/associations/collection_association.rb', line 28

def size
  if !find_target? || loaded?
    target.size
  elsif !loaded? && target.is_a?(Array)
    unsaved_records = target.select(&:new_record?)
    unsaved_records.size + scoped.count
  else
    scoped.count
  end
end

#uniq(collection = load_target) ⇒ Object


146
147
148
149
150
151
# File 'lib/datastax_rails/associations/collection_association.rb', line 146

def uniq(collection = load_target)
  seen = {}
  collection.select do |record|
    seen[record.id] = true unless seen.key?(record.id)
  end
end

#writer(records) ⇒ Object

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


59
60
61
# File 'lib/datastax_rails/associations/collection_association.rb', line 59

def writer(records)
  replace(records)
end