Class: Formed::Associations::CollectionProxy

Inherits:
Relation
  • Object
show all
Defined in:
lib/formed/associations/collection_proxy.rb

Instance Attribute Summary

Attributes inherited from Relation

#klass, #skip_preloading_value

Instance Method Summary collapse

Methods inherited from Relation

#any?, #encode_with, #initialize_copy, #many?, #none?, #one?, #to_ary

Methods included from Relation::Delegation

delegated_classes, uncacheable_methods

Constructor Details

#initialize(klass, association) ⇒ CollectionProxy

:nodoc:



6
7
8
9
10
11
12
# File 'lib/formed/associations/collection_proxy.rb', line 6

def initialize(klass, association, **) # :nodoc:
  @association = association
  super klass

  extensions = association.extensions
  extend(*extensions) if extensions.any?
end

Instance Method Details

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

Adds one or more records to the collection by setting their foreign keys to the association’s primary key. Since << flattens its argument list and inserts each record, push and concat behave identically. Returns self so several appends may be chained together.

class Person < ActiveRecord::Base
  has_many :pets
end

person.pets.size # => 0
person.pets << Pet.new(name: 'Fancy-Fancy')
person.pets << [Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo')]
person.pets.size # => 3

person.id # => 1
person.pets
# => [
#      #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
#      #<Pet id: 2, name: "Spook", person_id: 1>,
#      #<Pet id: 3, name: "Choo-Choo", person_id: 1>
#    ]


429
430
431
# File 'lib/formed/associations/collection_proxy.rb', line 429

def <<(*records)
  proxy_association.concat(records) && self
end

#==(other) ⇒ Object

Equivalent to Array#==. Returns true if the two arrays contain the same number of elements and if each element is equal to the corresponding element in the other array, otherwise returns false.

class Person < ActiveRecord::Base
  has_many :pets
end

person.pets
# => [
#      #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
#      #<Pet id: 2, name: "Spook", person_id: 1>
#    ]

other = person.pets.to_ary

person.pets == other
# => true

other = [Pet.new(id: 1), Pet.new(id: 2)]

person.pets == other
# => false


360
361
362
# File 'lib/formed/associations/collection_proxy.rb', line 360

def ==(other)
  load_target == other
end

#build(attributes = {}, &block) ⇒ Object Also known as: new

Returns a new object of the collection type that has been instantiated with attributes and linked to this object, but have not yet been saved. You can pass an array of attributes hashes, this will return an array with the new objects.

class Person
  has_many :pets
end

person.pets.build
# => #<Pet id: nil, name: nil, person_id: 1>

person.pets.build(name: 'Fancy-Fancy')
# => #<Pet id: nil, name: "Fancy-Fancy", person_id: 1>

person.pets.build([{name: 'Spook'}, {name: 'Choo-Choo'}, {name: 'Brain'}])
# => [
#      #<Pet id: nil, name: "Spook", person_id: 1>,
#      #<Pet id: nil, name: "Choo-Choo", person_id: 1>,
#      #<Pet id: nil, name: "Brain", person_id: 1>
#    ]

person.pets.size  # => 5 # size of the collection
person.pets.count # => 0 # count from database


93
94
95
# File 'lib/formed/associations/collection_proxy.rb', line 93

def build(attributes = {}, &block)
  @association.build(attributes, &block)
end

#clearObject

Equivalent to delete_all. The difference is that returns self, instead of an array with the deleted objects, so methods can be chained. See delete_all for more information. Note that because delete_all removes records by directly running an SQL query into the database, the updated_at column of the object is not changed.



446
447
448
449
# File 'lib/formed/associations/collection_proxy.rb', line 446

def clear
  delete_all
  self
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.load.empty?.

class Person < ActiveRecord::Base
  has_many :pets
end

person.pets.count  # => 1
person.pets.empty? # => false

person.pets.delete_all

person.pets.count  # => 0
person.pets.empty? # => true

Returns:

  • (Boolean)


227
228
229
# File 'lib/formed/associations/collection_proxy.rb', line 227

def empty?
  @association.empty?
end

#include?(record) ⇒ Boolean

Returns true if the given record is present in the collection.

class Person < ActiveRecord::Base
  has_many :pets
end

person.pets # => [#<Pet id: 20, name: "Snoop">]

person.pets.include?(Pet.find(20)) # => true
person.pets.include?(Pet.find(21)) # => false

Returns:

  • (Boolean)


323
324
325
# File 'lib/formed/associations/collection_proxy.rb', line 323

def include?(record)
  !!@association.include?(record)
end

#inspectObject

:nodoc:



493
494
495
496
# File 'lib/formed/associations/collection_proxy.rb', line 493

def inspect # :nodoc:
  load_target if find_from_target?
  super
end

#last(limit = nil) ⇒ Object



32
33
34
35
36
37
38
# File 'lib/formed/associations/collection_proxy.rb', line 32

def last(limit = nil)
  if limit
    target.last(limit)
  else
    target.last
  end
end

#load_targetObject



18
19
20
# File 'lib/formed/associations/collection_proxy.rb', line 18

def load_target
  @association.load_target
end

#loaded?Boolean Also known as: loaded

Returns true if the association has been loaded, otherwise false.

person.pets.loaded? # => false
person.pets.records
person.pets.loaded? # => true

Returns:

  • (Boolean)


27
28
29
# File 'lib/formed/associations/collection_proxy.rb', line 27

def loaded?
  @association.loaded?
end

#prepend(*_args) ⇒ Object

:nodoc:

Raises:

  • (NoMethodError)


436
437
438
# File 'lib/formed/associations/collection_proxy.rb', line 436

def prepend(*_args) # :nodoc:
  raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
end

#proxy_associationObject

:nodoc:



327
328
329
# File 'lib/formed/associations/collection_proxy.rb', line 327

def proxy_association # :nodoc:
  @association
end

#recordsObject

:method: to_ary

:call-seq:

to_ary()

Returns a new array of objects from the collection. If the collection hasn’t been loaded, it fetches the records from the database.

class Person < ActiveRecord::Base
  has_many :pets
end

person.pets
# => [
#       #<Pet id: 4, name: "Benny", person_id: 1>,
#       #<Pet id: 5, name: "Brain", person_id: 1>,
#       #<Pet id: 6, name: "Boss",  person_id: 1>
#    ]

other_pets = person.pets.to_ary
# => [
#       #<Pet id: 4, name: "Benny", person_id: 1>,
#       #<Pet id: 5, name: "Brain", person_id: 1>,
#       #<Pet id: 6, name: "Boss",  person_id: 1>
#    ]

other_pets.replace([Pet.new(name: 'BooGoo')])

other_pets
# => [#<Pet id: nil, name: "BooGoo", person_id: 1>]

person.pets
# This is not affected by replace
# => [
#       #<Pet id: 4, name: "Benny", person_id: 1>,
#       #<Pet id: 5, name: "Brain", person_id: 1>,
#       #<Pet id: 6, name: "Boss",  person_id: 1>
#    ]


404
405
406
# File 'lib/formed/associations/collection_proxy.rb', line 404

def records # :nodoc:
  load_target
end

#reloadObject

Reloads the collection from the database. Returns self.

class Person < ActiveRecord::Base
  has_many :pets
end

person.pets # fetches pets from the database
# => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]

person.pets # uses the pets cache
# => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]

person.pets.reload # fetches pets from the database
# => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]


465
466
467
468
# File 'lib/formed/associations/collection_proxy.rb', line 465

def reload
  proxy_association.reload(true)
  reset_scope
end

#replace(other_array) ⇒ Object

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

class Person < ActiveRecord::Base
  has_many :pets
end

person.pets
# => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]

other_pets = [Pet.new(name: 'Puff', group: 'celebrities')]

person.pets.replace(other_pets)

person.pets
# => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]

If the supplied array has an incorrect association type, it raises an ActiveRecord::AssociationTypeMismatch error:

person.pets.replace(["doo", "ggie", "gaga"])
# => ActiveRecord::AssociationTypeMismatch: Pet expected, got String


124
125
126
# File 'lib/formed/associations/collection_proxy.rb', line 124

def replace(other_array)
  @association.replace(other_array)
end

#resetObject

Unloads the association. Returns self.

class Person < ActiveRecord::Base
  has_many :pets
end

person.pets # fetches pets from the database
# => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]

person.pets # uses the pets cache
# => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]

person.pets.reset # clears the pets cache

person.pets  # fetches pets from the database
# => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]


486
487
# File 'lib/formed/associations/collection_proxy.rb', line 486

def reset
end

#reset_scopeObject

:nodoc:



489
490
491
# File 'lib/formed/associations/collection_proxy.rb', line 489

def reset_scope # :nodoc:
  self
end

#scopeObject

Returns a Relation object for the records in this association



332
333
334
# File 'lib/formed/associations/collection_proxy.rb', line 332

def scope
  @scope ||= @association.scope
end

#sizeObject

Returns the size of the collection. If the collection hasn’t been loaded, it executes a SELECT COUNT(*) query. Else it calls collection.size.

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.

class Person < ActiveRecord::Base
  has_many :pets
end

person.pets.size # => 3
# executes something like SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" = 1

person.pets # This will execute a SELECT * FROM query
# => [
#       #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
#       #<Pet id: 2, name: "Spook", person_id: 1>,
#       #<Pet id: 3, name: "Choo-Choo", person_id: 1>
#    ]

person.pets.size # => 3
# Because the collection is already loaded, this will behave like
# collection.size and no SQL count query is executed.


178
179
180
# File 'lib/formed/associations/collection_proxy.rb', line 178

def size
  @association.size
end

#take(limit = nil) ⇒ Object

Gives a record (or N records if a parameter is supplied) from the collection using the same rules as ActiveRecord::Base.take.

class Person < ActiveRecord::Base
  has_many :pets
end

person.pets
# => [
#       #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
#       #<Pet id: 2, name: "Spook", person_id: 1>,
#       #<Pet id: 3, name: "Choo-Choo", person_id: 1>
#    ]

person.pets.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>

person.pets.take(2)
# => [
#      #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
#      #<Pet id: 2, name: "Spook", person_id: 1>
#    ]

another_person_without.pets         # => []
another_person_without.pets.take    # => nil
another_person_without.pets.take(2) # => []


65
66
67
# File 'lib/formed/associations/collection_proxy.rb', line 65

def take(limit = nil)
  target.take(limit)
end

#targetObject



14
15
16
# File 'lib/formed/associations/collection_proxy.rb', line 14

def target
  @association.target
end

#with_context(context) ⇒ Object



98
99
100
# File 'lib/formed/associations/collection_proxy.rb', line 98

def with_context(context)
  each { |record| record.with_context(context) }
end