Class: CouchFoo::Associations::AssociationCollection

Inherits:
AssociationProxy show all
Defined in:
lib/couch_foo/associations/association_collection.rb

Overview

:nodoc:

Instance Method Summary collapse

Methods inherited from AssociationProxy

#===, #conditions, #inspect, #loaded, #loaded?, #proxy_owner, #proxy_reflection, #proxy_respond_to?, #proxy_target, #reload, #respond_to?, #target, #target=

Constructor Details

#initialize(owner, reflection) ⇒ AssociationCollection

Returns a new instance of AssociationCollection.



6
7
8
9
# File 'lib/couch_foo/associations/association_collection.rb', line 6

def initialize(owner, reflection)
  super
  construct_conditions
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

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



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/couch_foo/associations/association_collection.rb', line 260

def method_missing(method, *args)
  if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
    if block_given?
      super { |*block_args| yield(*block_args) }
    else
      super
    end
  elsif @reflection.klass.scopes.include?(method)
    @reflection.klass.scopes[method].call(self, *args)
  else          
    with_scope(construct_scope) do
      if block_given?
        @reflection.klass.send(method, *args) { |*block_args| yield(*block_args) }
      else
        @reflection.klass.send(method, *args)
      end
    end
  end
end

Instance Method Details

#<<(*records) ⇒ Object Also known as: push, concat

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.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/couch_foo/associations/association_collection.rb', line 78

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

  @owner.transaction do
    flatten_deeper(records).each do |record|
      raise_on_type_mismatch(record)
      add_record_to_target_with_callbacks(record) do |r|
        result &&= insert_record(record) unless @owner.new_record?
      end
    end
  end

  result && self
end

#any?Boolean

Returns:



196
197
198
199
200
201
202
# File 'lib/couch_foo/associations/association_collection.rb', line 196

def any?
  if block_given?
    method_missing(:any?) { |*block_args| yield(*block_args) }
  else
    !empty?
  end
end

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



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

def build(attributes = {}, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| build(attr, &block) }
  else
    build_record(attributes) do |record|
      block.call(record) if block_given?
      set_belongs_to_association_for(record)
    end
  end
end

#clearObject

Removes all records from this association. Returns self so method calls may be chained.



132
133
134
135
136
137
138
139
140
141
142
# File 'lib/couch_foo/associations/association_collection.rb', line 132

def clear
  return self if length.zero? # forces load_target if it hasn't happened already

  if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy
    destroy_all
  else          
    delete_all
  end

  self
end

#create(attrs = {}) ⇒ Object



152
153
154
155
156
157
158
159
160
161
# File 'lib/couch_foo/associations/association_collection.rb', line 152

def create(attrs = {})
  if attrs.is_a?(Array)
    attrs.collect { |attr| create(attr) }
  else
    create_record(attrs) do |record|
      yield(record) if block_given?
      record.save
    end
  end
end

#create!(attrs = {}) ⇒ Object



163
164
165
166
167
168
# File 'lib/couch_foo/associations/association_collection.rb', line 163

def create!(attrs = {})
  create_record(attrs) do |record|
    yield(record) if block_given?
    record.save!
  end
end

#delete(*records) ⇒ Object

Remove records from this association. Does not destroy records.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/couch_foo/associations/association_collection.rb', line 114

def delete(*records)
  records = flatten_deeper(records)
  records.each { |record| raise_on_type_mismatch(record) }
  
  @owner.transaction do
    records.each { |record| callback(:before_remove, record) }
    
    old_records = records.reject {|r| r.new_record? }
    delete_records(old_records) if old_records.any?
    
    records.each do |record|
      @target.delete(record)
      callback(:after_remove, record)
    end
  end
end

#delete_allObject

Remove all records from this association



98
99
100
101
102
# File 'lib/couch_foo/associations/association_collection.rb', line 98

def delete_all
  load_target
  delete(@target)
  reset_target!
end

#destroy_allObject



144
145
146
147
148
149
150
# File 'lib/couch_foo/associations/association_collection.rb', line 144

def destroy_all
  @owner.transaction do
    each { |record| record.destroy }
  end

  reset_target!
end

#empty?Boolean

Returns:



192
193
194
# File 'lib/couch_foo/associations/association_collection.rb', line 192

def empty?
  size.zero?
end

#find(*args) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/couch_foo/associations/association_collection.rb', line 11

def find(*args)
  options = args.extract_options!
  
  options[:conditions] = @association_conditions.merge(options[:conditions] || {})
  
  # Multiple ordering not supported at the minute
  #if options[:order] && @reflection.options[:order]
  #  options[:order] = "#{options[:order]}, #{@reflection.options[:order]}"
  #elsif @reflection.options[:order]
  if @reflection.options[:order]
    options[:order] = @reflection.options[:order]
  end

  # Build options specific to association
  construct_find_options!(options)
  
  merge_options_from_reflection!(options)
  
  # Pass through args exactly as we received them.
  args << options

  @reflection.klass.find(*args)
end

#first(*args) ⇒ Object

Fetches the first element directly if it can



36
37
38
39
40
41
42
43
# File 'lib/couch_foo/associations/association_collection.rb', line 36

def first(*args)
  if fetch_first_or_last_using_find? args
    find(:first, *args)
  else
    load_target unless loaded?
    @target.first(*args)
  end
end

#include?(record) ⇒ Boolean

Returns:



230
231
232
233
234
235
# File 'lib/couch_foo/associations/association_collection.rb', line 230

def include?(record)
  return false unless record.is_a?(@reflection.klass)
  load_target if !loaded?
  return @target.include?(record) if loaded?
  exists?(record)
end

#last(*args) ⇒ Object

Fetches the last element directly if it can



46
47
48
49
50
51
52
53
# File 'lib/couch_foo/associations/association_collection.rb', line 46

def last(*args)
  if fetch_first_or_last_using_find? args
    find(:last, *args)
  else
    load_target unless loaded?
    @target.last(*args)
  end
end

#lengthObject

Returns the size of the collection by loading it and calling size on the array. If you want to use this method to check whether the collection is empty, use collection.length.zero? instead of collection.empty?



188
189
190
# File 'lib/couch_foo/associations/association_collection.rb', line 188

def length
  load_target.size
end

#replace(other_array) ⇒ Object

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



217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/couch_foo/associations/association_collection.rb', line 217

def replace(other_array)
  other_array.each { |val| raise_on_type_mismatch(val) }

  load_target
  other   = other_array.size < 100 ? other_array : other_array.to_set
  current = @target.size < 100 ? @target : @target.to_set

  @owner.transaction do
    delete(@target.select { |v| !other.include?(v) })
    concat(other_array.select { |v| !current.include?(v) })
  end
end

#resetObject



60
61
62
63
# File 'lib/couch_foo/associations/association_collection.rb', line 60

def reset
  reset_target!
  @loaded = false
end

#sizeObject

Returns the size of the collection by executing a count query if the collection hasn’t been loaded and calling collection.size if it has. If it’s more likely than not that the collection does have a size larger than zero and you need to fetch that collection afterwards, it’ll take one database query if you use length.



174
175
176
177
178
179
180
181
182
183
# File 'lib/couch_foo/associations/association_collection.rb', line 174

def size
  if @owner.new_record? || (loaded? && !@reflection.options[:uniq])
    @target.size
  elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array)
    unsaved_records = @target.select { |r| r.new_record? }
    unsaved_records.size + count_records
  else
    count_records
  end
end

#sum(*args) ⇒ Object

Calculate sum



105
106
107
108
109
110
111
# File 'lib/couch_foo/associations/association_collection.rb', line 105

def sum(*args)
  if block_given?
    calculate(:sum, *args) { |*block_args| yield(*block_args) }
  else
    calculate(:sum, *args)
  end
end

#to_aryObject



55
56
57
58
# File 'lib/couch_foo/associations/association_collection.rb', line 55

def to_ary
  load_target
  @target.to_ary
end

#uniq(collection = self) ⇒ Object



204
205
206
207
208
209
210
211
212
213
# File 'lib/couch_foo/associations/association_collection.rb', line 204

def uniq(collection = self)
  seen = Set.new
  collection.inject([]) do |kept, record|
    unless seen.include?(record.id)
      kept << record
      seen << record.id
    end
    kept
  end
end