Parent

Class Index [+]

Quicksearch

DataMapper::Collection

The Collection class represents a list of resources persisted in a repository and identified by a query.

A Collection should act like an Array in every way, except that it will attempt to defer loading until the results from the repository are needed.

A Collection is typically returned by the Model#all method.

Attributes

query[R]

Returns the Query the Collection is scoped with

@return [Query]

  the Query the Collection is scoped with

@api semipublic

Public Class Methods

new(query, resources = nil) click to toggle source

Initializes a new Collection identified by the query

@param [Query] query

  Scope the results of the Collection

@param [Enumerable] resources (optional)

  List of resources to initialize the Collection with

@return [self]

@api private

      # File lib/dm-core/collection.rb, line 1039
1039:     def initialize(query, resources = nil)
1040:       raise "#{self.class}#new with a block is deprecated" if block_given?
1041: 
1042:       @query        = query
1043:       @identity_map = IdentityMap.new
1044:       @removed      = Set.new
1045: 
1046:       super()
1047: 
1048:       # TODO: change LazyArray to not use a load proc at all
1049:       remove_instance_variable(:@load_with_proc)
1050: 
1051:       set(resources) if resources
1052:     end

Public Instance Methods

&(other) click to toggle source
Alias for: intersection
+(other) click to toggle source
Alias for: union
-(other) click to toggle source
Alias for: difference
<<(resource) click to toggle source

Append one Resource to the Collection and relate it

@param [Resource] resource

  the resource to add to this collection

@return [self]

@api public

     # File lib/dm-core/collection.rb, line 535
535:     def <<(resource)
536:       super(resource_added(resource))
537:     end
[](*args) click to toggle source

Simulates Array#slice and returns a new Collection whose query has a new offset or limit according to the arguments provided.

If you provide a range, the min is used as the offset and the max minues the offset is used as the limit.

@param [Integer, Array(Integer), Range] *args

  the offset, offset and limit, or range indicating first and last position

@return [Resource, Collection, nil]

  The entry which resides at that offset and limit,
  or a new Collection object with the set limits and offset

@return [nil]

  The offset (or starting offset) is out of range

@raise [ArgumentError] “arguments may be 1 or 2 Integers,

  or 1 Range object, was: #{args.inspect}"

@api public

     # File lib/dm-core/collection.rb, line 389
389:     def [](*args)
390:       offset, limit = extract_slice_arguments(*args)
391: 
392:       if args.size == 1 && args.first.kind_of?(Integer)
393:         return at(offset)
394:       end
395: 
396:       query = sliced_query(offset, limit)
397: 
398:       if loaded? || partially_loaded?(offset, limit)
399:         new_collection(query, super)
400:       else
401:         new_collection(query)
402:       end
403:     end
Also aliased as: slice
[]=(*args) click to toggle source

Splice a list of Resources at a given offset or range

When nil is provided instead of a Resource or a list of Resources this will remove all of the Resources at the specified position.

@param [Integer, Array(Integer), Range] *args

  The offset, offset and limit, or range indicating first and last position.
  The last argument may be a Resource, a list of Resources or nil.

@return [Resource, Enumerable]

  the Resource or list of Resources that was spliced into the Collection

@return [nil]

  If nil was used to delete the entries

@api public

     # File lib/dm-core/collection.rb, line 453
453:     def []=(*args)
454:       orphans = Array(superclass_slice(*args[0..2]))
455: 
456:       # relate new resources
457:       resources = resources_added(super)
458: 
459:       # mark resources as removed
460:       resources_removed(orphans - loaded_entries)
461: 
462:       resources
463:     end
Also aliased as: splice
all(query = Undefined) click to toggle source

Returns a new Collection optionally scoped by query

This returns a new Collection scoped relative to the current Collection.

  cars_from_91 = Cars.all(:year_manufactured => 1991)
  toyotas_91 = cars_from_91.all(:manufacturer => 'Toyota')
  toyotas_91.all? { |car| car.year_manufactured == 1991 }       #=> true
  toyotas_91.all? { |car| car.manufacturer == 'Toyota' }        #=> true

If query is a Hash, results will be found by merging query with this Collection’s query. If query is a Query, results will be found using query as an absolute query.

@param [Hash, Query] query

  optional parameters to scope results with

@return [Collection]

  Collection scoped by +query+

@api public

     # File lib/dm-core/collection.rb, line 213
213:     def all(query = Undefined)
214:       if query.equal?(Undefined) || (query.kind_of?(Hash) && query.empty?)
215:         dup
216:       else
217:         # TODO: if there is no order parameter, and the Collection is not loaded
218:         # check to see if the query can be satisfied by the head/tail
219:         new_collection(scoped_query(query))
220:       end
221:     end
at(offset) click to toggle source

Lookup a Resource from the Collection by offset

@param [Integer] offset

  offset of the Resource in the Collection

@return [Resource]

  Resource which matches the supplied offset

@return [nil]

  No Resource matches the supplied offset

@api public

     # File lib/dm-core/collection.rb, line 346
346:     def at(offset)
347:       if loaded? || partially_loaded?(offset)
348:         super
349:       elsif offset == 0
350:         first
351:       elsif offset > 0
352:         first(:offset => offset)
353:       elsif offset == 1
354:         last
355:       else
356:         last(:offset => offset.abs - 1)
357:       end
358:     end
clean?() click to toggle source

Checks if all the resources have no changes to save

@return [Boolean]

  true if the resource may not be persisted

@api public

     # File lib/dm-core/collection.rb, line 954
954:     def clean?
955:       !dirty?
956:     end
clear() click to toggle source

Removes all Resources from the Collection

This should remove and orphan each Resource from the Collection

@return [self]

@api public

     # File lib/dm-core/collection.rb, line 726
726:     def clear
727:       if loaded?
728:         resources_removed(self)
729:       end
730:       super
731:     end
collect!() click to toggle source

Invoke the block for each resource and replace it the return value

@yield [Resource] Each resource in the collection

@return [self]

@api public

     # File lib/dm-core/collection.rb, line 521
521:     def collect!
522:       super { |resource| resource_added(yield(resource_removed(resource))) }
523:     end
Also aliased as: map!
concat(resources) click to toggle source

Appends the resources to self

@param [Enumerable] resources

  List of Resources to append to the collection

@return [self]

@api public

     # File lib/dm-core/collection.rb, line 547
547:     def concat(resources)
548:       super(resources_added(resources))
549:     end
create(attributes = {}) click to toggle source

Create a Resource in the Collection

@param [Hash(Symbol => Object)] attributes

  attributes to set

@return [Resource]

  the newly created Resource instance

@api public

     # File lib/dm-core/collection.rb, line 792
792:     def create(attributes = {})
793:       _create(attributes)
794:     end
create!(attributes = {}) click to toggle source

Create a Resource in the Collection, bypassing hooks

@param [Hash(Symbol => Object)] attributes

  attributes to set

@return [Resource]

  the newly created Resource instance

@api public

     # File lib/dm-core/collection.rb, line 805
805:     def create!(attributes = {})
806:       _create(attributes, false)
807:     end
delete(resource) click to toggle source

Remove Resource from the Collection

This should remove an included Resource from the Collection and orphan it from the Collection. If the Resource is not within the Collection, it should return nil.

@param [Resource] resource the Resource to remove from

  the Collection

@return [Resource]

  If +resource+ is within the Collection

@return [nil]

  If +resource+ is not within the Collection

@api public

     # File lib/dm-core/collection.rb, line 634
634:     def delete(resource)
635:       if resource = super
636:         resource_removed(resource)
637:       end
638:     end
delete_at(offset) click to toggle source

Remove Resource from the Collection by offset

This should remove the Resource from the Collection at a given offset and orphan it from the Collection. If the offset is out of range return nil.

@param [Integer] offset

  the offset of the Resource to remove from the Collection

@return [Resource]

  If +offset+ is within the Collection

@return [nil]

  If +offset+ is not within the Collection

@api public

     # File lib/dm-core/collection.rb, line 655
655:     def delete_at(offset)
656:       if resource = super
657:         resource_removed(resource)
658:       end
659:     end
delete_if() click to toggle source

Deletes every Resource for which block evaluates to true.

@yield [Resource] Each resource in the Collection

@return [self]

@api public

     # File lib/dm-core/collection.rb, line 668
668:     def delete_if
669:       super { |resource| yield(resource) && resource_removed(resource) }
670:     end
destroy() click to toggle source

Remove every Resource in the Collection from the repository

This performs a deletion of each Resource in the Collection from the repository and clears the Collection.

@return [Boolean]

  true if the resources were successfully destroyed

@api public

     # File lib/dm-core/collection.rb, line 894
894:     def destroy
895:       if destroyed = all? { |resource| resource.destroy }
896:         clear
897:       end
898: 
899:       destroyed
900:     end
destroy!() click to toggle source

Remove all Resources from the repository, bypassing validation

This performs a deletion of each Resource in the Collection from the repository and clears the Collection while skipping validation.

@return [Boolean]

  true if the resources were successfully destroyed

@api public

     # File lib/dm-core/collection.rb, line 912
912:     def destroy!
913:       repository = self.repository
914:       deleted    = repository.delete(self)
915: 
916:       if loaded?
917:         unless deleted == size
918:           return false
919:         end
920: 
921:         each do |resource|
922:           resource.persistence_state = Resource::PersistenceState::Immutable.new(resource)
923:         end
924: 
925:         clear
926:       else
927:         mark_loaded
928:       end
929: 
930:       true
931:     end
difference(other) click to toggle source

Return the difference with another collection

@param [Collection] other

  the other collection

@return [Collection]

  the difference of the collection and other

@api public

     # File lib/dm-core/collection.rb, line 123
123:     def difference(other)
124:       set_operation(:-, other)
125:     end
Also aliased as: -
dirty?() click to toggle source

Checks if any resources have unsaved changes

@return [Boolean]

 true if the resources have unsaved changed

@api public

     # File lib/dm-core/collection.rb, line 964
964:     def dirty?
965:       loaded_entries.any? { |resource| resource.dirty? } || @removed.any?
966:     end
each() click to toggle source

Iterate over each Resource

@yield [Resource] Each resource in the collection

@return [self]

@api public

     # File lib/dm-core/collection.rb, line 503
503:     def each
504:       super do |resource|
505:         begin
506:           original, resource.collection = resource.collection, self
507:           yield resource
508:         ensure
509:           resource.collection = original
510:         end
511:       end
512:     end
first(*args) click to toggle source

Return the first Resource or the first N Resources in the Collection with an optional query

When there are no arguments, return the first Resource in the Collection. When the first argument is an Integer, return a Collection containing the first N Resources. When the last (optional) argument is a Hash scope the results to the query.

@param [Integer] limit (optional)

  limit the returned Collection to a specific number of entries

@param [Hash] query (optional)

  scope the returned Resource or Collection to the supplied query

@return [Resource, Collection]

  The first resource in the entries of this collection,
  or a new collection whose query has been merged

@api public

     # File lib/dm-core/collection.rb, line 240
240:     def first(*args)
241:       first_arg = args.first
242:       last_arg  = args.last
243: 
244:       limit_specified = first_arg.kind_of?(Integer)
245:       with_query      = (last_arg.kind_of?(Hash) && !last_arg.empty?) || last_arg.kind_of?(Query)
246: 
247:       limit = limit_specified ? first_arg : 1
248:       query = with_query      ? last_arg  : {}
249: 
250:       query = self.query.slice(0, limit).update(query)
251: 
252:       # TODO: when a query provided, and there are enough elements in head to
253:       # satisfy the query.limit, filter the head with the query, and make
254:       # sure it matches the limit exactly.  if so, use that result instead
255:       # of calling all()
256:       #   - this can probably only be done if there is no :order parameter
257: 
258:       loaded = loaded?
259:       head   = self.head
260: 
261:       collection = if !with_query && (loaded || lazy_possible?(head, limit))
262:         new_collection(query, super(limit))
263:       else
264:         all(query)
265:       end
266: 
267:       return collection if limit_specified
268: 
269:       resource = collection.to_a.first
270: 
271:       if with_query || loaded
272:         resource
273:       elsif resource
274:         head[0] = resource
275:       end
276:     end
first_or_create(conditions = {}, attributes = {}) click to toggle source

Finds the first Resource by conditions, or creates a new Resource with the attributes if none found

@param [Hash] conditions

  The conditions to be used to search

@param [Hash] attributes

  The attributes to be used to create the resource with if none found

@return [Resource]

  The instance found by +query+, or created with +attributes+ if none found

@api public

     # File lib/dm-core/collection.rb, line 764
764:     def first_or_create(conditions = {}, attributes = {})
765:       first(conditions) || create(conditions.merge(attributes))
766:     end
first_or_new(conditions = {}, attributes = {}) click to toggle source

Finds the first Resource by conditions, or initializes a new Resource with the attributes if none found

@param [Hash] conditions

  The conditions to be used to search

@param [Hash] attributes

  The attributes to be used to initialize the resource with if none found

@return [Resource]

  The instance found by +query+, or created with +attributes+ if none found

@api public

     # File lib/dm-core/collection.rb, line 749
749:     def first_or_new(conditions = {}, attributes = {})
750:       first(conditions) || new(conditions.merge(attributes))
751:     end
get(*key) click to toggle source

Lookup a Resource in the Collection by key

This looksup a Resource by key, typecasting the key to the proper object if necessary.

  toyotas = Cars.all(:manufacturer => 'Toyota')
  toyo = Cars.first(:manufacturer => 'Toyota')
  toyotas.get(toyo.id) == toyo                  #=> true

@param [Enumerable] *key

  keys which uniquely identify a resource in the Collection

@return [Resource]

  Resource which matches the supplied key

@return [nil]

  No Resource matches the supplied key

@api public

     # File lib/dm-core/collection.rb, line 147
147:     def get(*key)
148:       assert_valid_key_size(key)
149: 
150:       key   = model_key.typecast(key)
151:       query = self.query
152: 
153:       @identity_map[key] || if !loaded? && (query.limit || query.offset > 0)
154:         # current query is exclusive, find resource within the set
155: 
156:         # TODO: use a subquery to retrieve the Collection and then match
157:         #   it up against the key.  This will require some changes to
158:         #   how subqueries are generated, since the key may be a
159:         #   composite key.  In the case of DO adapters, it means subselects
160:         #   like the form "(a, b) IN(SELECT a, b FROM ...)", which will
161:         #   require making it so the Query condition key can be a
162:         #   Property or an Array of Property objects
163: 
164:         # use the brute force approach until subquery lookups work
165:         lazy_load
166:         @identity_map[key]
167:       else
168:         # current query is all inclusive, lookup using normal approach
169:         first(model.key_conditions(repository, key).update(:order => nil))
170:       end
171:     end
get!(*key) click to toggle source

Lookup a Resource in the Collection by key, raising an exception if not found

This looksup a Resource by key, typecasting the key to the proper object if necessary.

@param [Enumerable] *key

  keys which uniquely identify a resource in the Collection

@return [Resource]

  Resource which matches the supplied key

@return [nil]

  No Resource matches the supplied key

@raise [ObjectNotFoundError] Resource could not be found by key

@api public

     # File lib/dm-core/collection.rb, line 189
189:     def get!(*key)
190:       get(*key) || raise(ObjectNotFoundError, "Could not find #{model.name} with key #{key.inspect}")
191:     end
hash() click to toggle source

@api semipublic

     # File lib/dm-core/collection.rb, line 980
980:     def hash
981:       self.class.hash ^ query.hash
982:     end
insert(offset, *resources) click to toggle source

Inserts the Resources before the Resource at the offset (which may be negative).

@param [Integer] offset

  The offset to insert the Resources before

@param [Enumerable] *resources

  List of Resources to insert

@return [self]

@api public

     # File lib/dm-core/collection.rb, line 591
591:     def insert(offset, *resources)
592:       super(offset, *resources_added(resources))
593:     end
inspect() click to toggle source

Gets a Human-readable representation of this collection, showing all elements contained in it

@return [String]

  Human-readable representation of this collection, showing all elements

@api public

     # File lib/dm-core/collection.rb, line 975
975:     def inspect
976:       "[#{map { |resource| resource.inspect }.join(', ')}]"
977:     end
intersection(other) click to toggle source

Return the intersection with another collection

@param [Collection] other

  the other collection

@return [Collection]

  the intersection of the collection and other

@api public

     # File lib/dm-core/collection.rb, line 108
108:     def intersection(other)
109:       set_operation(:&, other)
110:     end
Also aliased as: &
last(*args) click to toggle source

Return the last Resource or the last N Resources in the Collection with an optional query

When there are no arguments, return the last Resource in the Collection. When the first argument is an Integer, return a Collection containing the last N Resources. When the last (optional) argument is a Hash scope the results to the query.

@param [Integer] limit (optional)

  limit the returned Collection to a specific number of entries

@param [Hash] query (optional)

  scope the returned Resource or Collection to the supplied query

@return [Resource, Collection]

  The last resource in the entries of this collection,
  or a new collection whose query has been merged

@api public

     # File lib/dm-core/collection.rb, line 295
295:     def last(*args)
296:       first_arg = args.first
297:       last_arg  = args.last
298: 
299:       limit_specified = first_arg.kind_of?(Integer)
300:       with_query      = (last_arg.kind_of?(Hash) && !last_arg.empty?) || last_arg.kind_of?(Query)
301: 
302:       limit = limit_specified ? first_arg : 1
303:       query = with_query      ? last_arg  : {}
304: 
305:       query = self.query.slice(0, limit).update(query).reverse!
306: 
307:       # tell the Query to prepend each result from the adapter
308:       query.update(:add_reversed => !query.add_reversed?)
309: 
310:       # TODO: when a query provided, and there are enough elements in tail to
311:       # satisfy the query.limit, filter the tail with the query, and make
312:       # sure it matches the limit exactly.  if so, use that result instead
313:       # of calling all()
314: 
315:       loaded = loaded?
316:       tail   = self.tail
317: 
318:       collection = if !with_query && (loaded || lazy_possible?(tail, limit))
319:         new_collection(query, super(limit))
320:       else
321:         all(query)
322:       end
323: 
324:       return collection if limit_specified
325: 
326:       resource = collection.to_a.last
327: 
328:       if with_query || loaded
329:         resource
330:       elsif resource
331:         tail[tail.empty? ? 0 : 1] = resource
332:       end
333:     end
map!() click to toggle source
Alias for: collect!
model() click to toggle source

Returns the Model

@return [Model]

  the Model the Collection is associated with

@api semipublic

    # File lib/dm-core/collection.rb, line 45
45:     def model
46:       query.model
47:     end
new(attributes = {}) click to toggle source

Initializes a Resource and appends it to the Collection

@param [Hash] attributes

  Attributes with which to initialize the new resource

@return [Resource]

  a new Resource initialized with +attributes+

@api public

     # File lib/dm-core/collection.rb, line 777
777:     def new(attributes = {})
778:       resource = repository.scope { model.new(attributes) }
779:       self << resource
780:       resource
781:     end
pop(*) click to toggle source

Removes and returns the last Resource in the Collection

@return [Resource]

  the last Resource in the Collection

@api public

     # File lib/dm-core/collection.rb, line 601
601:     def pop(*)
602:       if removed = super
603:         resources_removed(removed)
604:       end
605:     end
push(*resources) click to toggle source

Append one or more Resources to the Collection

This should append one or more Resources to the Collection and relate each to the Collection.

@param [Enumerable] *resources

  List of Resources to append

@return [self]

@api public

     # File lib/dm-core/collection.rb, line 562
562:     def push(*resources)
563:       super(*resources_added(resources))
564:     end
reject!() click to toggle source

Deletes every Resource for which block evaluates to true

@yield [Resource] Each resource in the Collection

@return [Collection]

  If resources were removed

@return [nil]

  If no resources were removed

@api public

     # File lib/dm-core/collection.rb, line 682
682:     def reject!
683:       super { |resource| yield(resource) && resource_removed(resource) }
684:     end
reload(other_query = Undefined) click to toggle source

Reloads the Collection from the repository

If query is provided, updates this Collection’s query with its conditions

  cars_from_91 = Cars.all(:year_manufactured => 1991)
  cars_from_91.first.year_manufactured = 2001   # note: not saved
  cars_from_91.reload
  cars_from_91.first.year                       #=> 1991

@param [Query, Hash] query (optional)

  further restrict results with query

@return [self]

@api public

    # File lib/dm-core/collection.rb, line 64
64:     def reload(other_query = Undefined)
65:       query = self.query
66:       query = other_query.equal?(Undefined) ? query.dup : query.merge(other_query)
67: 
68:       # make sure the Identity Map contains all the existing resources
69:       identity_map = repository.identity_map(model)
70: 
71:       loaded_entries.each do |resource|
72:         identity_map[resource.key] = resource
73:       end
74: 
75:       # sort fields based on declared order, for more consistent reload queries
76:       properties = self.properties
77:       fields     = properties & (query.fields | model_key | [ properties.discriminator ].compact)
78: 
79:       # replace the list of resources
80:       replace(all(query.update(:fields => fields, :reload => true)))
81:     end
replace(other) click to toggle source

Replace the Resources within the Collection

@param [Enumerable] other

  List of other Resources to replace with

@return [self]

@api public

     # File lib/dm-core/collection.rb, line 700
700:     def replace(other)
701:       other = resources_added(other)
702:       resources_removed(entries - other)
703:       super(other)
704:     end
Also aliased as: superclass_replace
repository() click to toggle source

Returns the Repository

@return [Repository]

  the Repository this Collection is associated with

@api semipublic

    # File lib/dm-core/collection.rb, line 35
35:     def repository
36:       query.repository
37:     end
respond_to?(method, include_private = false) click to toggle source

Check to see if collection can respond to the method

@param [Symbol] method

  method to check in the object

@param [Boolean] include_private

  if set to true, collection will check private methods

@return [Boolean]

  true if method can be responded to

@api public

     # File lib/dm-core/collection.rb, line 944
944:     def respond_to?(method, include_private = false)
945:       super || model.respond_to?(method) || relationships.named?(method)
946:     end
reverse() click to toggle source

Return a copy of the Collection sorted in reverse

@return [Collection]

  Collection equal to +self+ but ordered in reverse

@api public

     # File lib/dm-core/collection.rb, line 473
473:     def reverse
474:       dup.reverse!
475:     end
reverse!() click to toggle source

Return the Collection sorted in reverse

@return [self]

@api public

     # File lib/dm-core/collection.rb, line 482
482:     def reverse!
483:       query.reverse!
484: 
485:       # reverse without kicking if possible
486:       if loaded?
487:         @array.reverse!
488:       else
489:         # reverse and swap the head and tail
490:         @head, @tail = tail.reverse!, head.reverse!
491:       end
492: 
493:       self
494:     end
save() click to toggle source

Save every Resource in the Collection

@return [Boolean]

  true if the resources were successfully saved

@api public

     # File lib/dm-core/collection.rb, line 871
871:     def save
872:       _save
873:     end
save!() click to toggle source

Save every Resource in the Collection bypassing validation

@return [Boolean]

  true if the resources were successfully saved

@api public

     # File lib/dm-core/collection.rb, line 881
881:     def save!
882:       _save(false)
883:     end
set(resources) click to toggle source

(Private) Set the Collection

@param [Array] resources

  resources to add to the collection

@return [self]

@api private

     # File lib/dm-core/collection.rb, line 714
714:     def set(resources)
715:       superclass_replace(resources_added(resources))
716:       self
717:     end
shift(*) click to toggle source

Removes and returns the first Resource in the Collection

@return [Resource]

  the first Resource in the Collection

@api public

     # File lib/dm-core/collection.rb, line 613
613:     def shift(*)
614:       if removed = super
615:         resources_removed(removed)
616:       end
617:     end
slice(*args) click to toggle source
Also aliased as: superclass_slice
Alias for: []
slice!(*args) click to toggle source

Deletes and Returns the Resources given by an offset or a Range

@param [Integer, Array(Integer), Range] *args

  the offset, offset and limit, or range indicating first and last position

@return [Resource, Collection]

  The entry which resides at that offset and limit, or
  a new Collection object with the set limits and offset

@return [Resource, Collection, nil]

  The offset is out of range

@api public

     # File lib/dm-core/collection.rb, line 419
419:     def slice!(*args)
420:       removed = super
421: 
422:       resources_removed(removed) unless removed.nil?
423: 
424:       # Workaround for Ruby <= 1.8.6
425:       compact! if RUBY_VERSION <= '1.8.6'
426: 
427:       unless removed.kind_of?(Enumerable)
428:         return removed
429:       end
430: 
431:       offset, limit = extract_slice_arguments(*args)
432: 
433:       query = sliced_query(offset, limit)
434: 
435:       new_collection(query, removed)
436:     end
splice(*args) click to toggle source
Alias for: []=
superclass_replace(other) click to toggle source

Access LazyArray#replace directly

@api private

Alias for: replace
superclass_slice(*args) click to toggle source

Access LazyArray#slice directly

Collection#[]= uses this to bypass Collection#slice and access the resources directly so that it can orphan them properly.

@api private

Alias for: slice
union(other) click to toggle source

Return the union with another collection

@param [Collection] other

  the other collection

@return [Collection]

  the union of the collection and other

@api public

    # File lib/dm-core/collection.rb, line 92
92:     def union(other)
93:       set_operation(:|, other)
94:     end
Also aliased as: |, +
unshift(*resources) click to toggle source

Prepend one or more Resources to the Collection

This should prepend one or more Resources to the Collection and relate each to the Collection.

@param [Enumerable] *resources

  The Resources to prepend

@return [self]

@api public

     # File lib/dm-core/collection.rb, line 577
577:     def unshift(*resources)
578:       super(*resources_added(resources))
579:     end
update(attributes) click to toggle source

Update every Resource in the Collection

  Person.all(:age.gte => 21).update(:allow_beer => true)

@param [Hash] attributes

  attributes to update with

@return [Boolean]

  true if the resources were successfully updated

@api public

     # File lib/dm-core/collection.rb, line 820
820:     def update(attributes)
821:       assert_update_clean_only(:update)
822: 
823:       dirty_attributes = model.new(attributes).dirty_attributes
824:       dirty_attributes.empty? || all? { |resource| resource.update(attributes) }
825:     end
update!(attributes) click to toggle source

Update every Resource in the Collection bypassing validation

  Person.all(:age.gte => 21).update!(:allow_beer => true)

@param [Hash] attributes

  attributes to update

@return [Boolean]

  true if the resources were successfully updated

@api public

     # File lib/dm-core/collection.rb, line 838
838:     def update!(attributes)
839:       assert_update_clean_only(:update!)
840: 
841:       model = self.model
842: 
843:       dirty_attributes = model.new(attributes).dirty_attributes
844: 
845:       if dirty_attributes.empty?
846:         true
847:       elsif dirty_attributes.any? { |property, value| !property.valid?(value) }
848:         false
849:       else
850:         unless _update(dirty_attributes)
851:           return false
852:         end
853: 
854:         if loaded?
855:           each do |resource|
856:             dirty_attributes.each { |property, value| property.set!(resource, value) }
857:             repository.identity_map(model)[resource.key] = resource
858:           end
859:         end
860: 
861:         true
862:       end
863:     end
|(other) click to toggle source
Alias for: union

Protected Instance Methods

loaded_entries() click to toggle source

Loaded Resources in the collection

@return [Array]

  Resources in the collection

@api private

      # File lib/dm-core/collection.rb, line 1002
1002:     def loaded_entries
1003:       (loaded? ? self : head + tail).reject { |resource| resource.destroyed? }
1004:     end
model_key() click to toggle source

Returns the model key

@return [PropertySet]

  the model key

@api private

     # File lib/dm-core/collection.rb, line 992
992:     def model_key
993:       model.key(repository_name)
994:     end
properties() click to toggle source

Returns the PropertySet representing the fields in the Collection scope

@return [PropertySet]

  The set of properties this Collection's query will retrieve

@api private

      # File lib/dm-core/collection.rb, line 1012
1012:     def properties
1013:       model.properties(repository_name)
1014:     end
relationships() click to toggle source

Returns the Relationships for the Collection’s Model

@return [Hash]

  The model's relationships, mapping the name to the
  Associations::Relationship object

@api private

      # File lib/dm-core/collection.rb, line 1023
1023:     def relationships
1024:       model.relationships(repository_name)
1025:     end

Private Instance Methods

_create(attributes, execute_hooks = true) click to toggle source

Creates a resource in the collection

@param [Boolean] execute_hooks

  Whether to execute hooks or not

@param [Hash] attributes

  Attributes with which to create the new resource

@return [Resource]

  a saved Resource

@api private

      # File lib/dm-core/collection.rb, line 1210
1210:     def _create(attributes, execute_hooks = true)
1211:       resource = repository.scope { model.send(execute_hooks ? :create : :create!, default_attributes.merge(attributes)) }
1212:       self << resource if resource.saved?
1213:       resource
1214:     end
_save(execute_hooks = true) click to toggle source

Saves a collection

@param [Boolean] execute_hooks

  Whether to execute hooks or not

@return [Boolean]

  Returns true if collection was updated

@api private

      # File lib/dm-core/collection.rb, line 1236
1236:     def _save(execute_hooks = true)
1237:       loaded_entries = self.loaded_entries
1238:       loaded_entries.each { |resource| set_default_attributes(resource) }
1239:       @removed.clear
1240:       loaded_entries.all? { |resource| resource.__send__(execute_hooks ? :save : :save!) }
1241:     end
_update(dirty_attributes) click to toggle source

Updates a collection

@return [Boolean]

  Returns true if collection was updated

@api private

      # File lib/dm-core/collection.rb, line 1222
1222:     def _update(dirty_attributes)
1223:       repository.update(dirty_attributes, self)
1224:       true
1225:     end
assert_update_clean_only(method) click to toggle source

Raises an exception if # is performed on a dirty resource

@raise [UpdateConflictError]

  raise if the resource is dirty

@return [undefined]

@api private

      # File lib/dm-core/collection.rb, line 1488
1488:     def assert_update_clean_only(method)
1489:       if dirty?
1490:         raise UpdateConflictError, "#{self.class}##{method} cannot be called on a dirty collection"
1491:       end
1492:     end
assert_valid_key_size(key) click to toggle source

Raises an exception if # receives the wrong number of arguments

@param [Array] key

  the key value

@return [undefined]

@raise [UpdateConflictError]

  raise if the resource is dirty

@api private

      # File lib/dm-core/collection.rb, line 1505
1505:     def assert_valid_key_size(key)
1506:       expected_key_size = model_key.size
1507:       actual_key_size   = key.size
1508: 
1509:       if actual_key_size != expected_key_size
1510:         raise ArgumentError, "The number of arguments for the key is invalid, expected #{expected_key_size} but was #{actual_key_size}"
1511:       end
1512:     end
default_attributes() click to toggle source

Returns default values to initialize new Resources in the Collection

@return [Hash] The default attributes for new instances in this Collection

@api private

      # File lib/dm-core/collection.rb, line 1248
1248:     def default_attributes
1249:       return @default_attributes if @default_attributes
1250: 
1251:       default_attributes = {}
1252: 
1253:       conditions = query.conditions
1254: 
1255:       if conditions.slug == :and
1256:         model_properties = properties.dup
1257:         model_key        = self.model_key
1258: 
1259:         if model_properties.to_set.superset?(model_key.to_set)
1260:           model_properties -= model_key
1261:         end
1262: 
1263:         conditions.each do |condition|
1264:           next unless condition.slug == :eql
1265: 
1266:           subject = condition.subject
1267:           next unless model_properties.include?(subject) || (condition.relationship? && subject.source_model == model)
1268: 
1269:           default_attributes[subject] = condition.loaded_value
1270:         end
1271:       end
1272: 
1273:       @default_attributes = default_attributes.freeze
1274:     end
delegate_to_model(method, *args, &block) click to toggle source

Delegate the method to the Model

@param [Symbol] method

  the name of the method in the model to execute

@param [Array] *args

  the arguments for the method

@return [Object]

  the return value of the model method

@api private

      # File lib/dm-core/collection.rb, line 1463
1463:     def delegate_to_model(method, *args, &block)
1464:       model = self.model
1465:       model.send(:with_scope, query) do
1466:         model.send(method, *args, &block)
1467:       end
1468:     end
delegate_to_relationship(relationship, query = nil) click to toggle source

Delegate the method to the Relationship

@return [Collection]

  the associated Resources

@api private

      # File lib/dm-core/collection.rb, line 1476
1476:     def delegate_to_relationship(relationship, query = nil)
1477:       relationship.eager_load(self, query)
1478:     end
filter(other_query) click to toggle source

Filter resources in the collection based on a Query

@param [Query] query

  the query to match each resource in the collection

@return [Array]

  the resources that match the Query

@return [nil]

  nil if no resources match the Query

@api private

      # File lib/dm-core/collection.rb, line 1375
1375:     def filter(other_query)
1376:       query  = self.query
1377:       fields = query.fields.to_set
1378:       unique = other_query.unique?
1379: 
1380:       # TODO: push this into a Query#subset? method
1381:       if other_query.links.empty?                 &&
1382:         (unique || (!unique && !query.unique?))   &&
1383:         !other_query.reload?                      &&
1384:         !other_query.raw?                         &&
1385:         other_query.fields.to_set.subset?(fields) &&
1386:         other_query.condition_properties.subset?(fields)
1387:       then
1388:         other_query.filter_records(to_a.dup)
1389:       end
1390:     end
initialize_copy(original) click to toggle source

Copies the original Collection state

@param [Collection] original

  the original collection to copy from

@return [undefined]

@api private

      # File lib/dm-core/collection.rb, line 1062
1062:     def initialize_copy(original)
1063:       super
1064:       @query        = @query.dup
1065:       @identity_map = @identity_map.dup
1066:       @removed      = @removed.dup
1067:     end
initialize_resource(resource) click to toggle source

Initialize a resource from a Hash

@param [Resource, Hash] resource

  resource to process

@return [Resource]

  an initialized resource

@api private

      # File lib/dm-core/collection.rb, line 1078
1078:     def initialize_resource(resource)
1079:       resource.kind_of?(Hash) ? new(resource) : resource
1080:     end
lazy_load() click to toggle source

Lazy loads a Collection

@return [self]

@api private

      # File lib/dm-core/collection.rb, line 1106
1106:     def lazy_load
1107:       if loaded?
1108:         return self
1109:       end
1110: 
1111:       mark_loaded
1112: 
1113:       head  = self.head
1114:       tail  = self.tail
1115:       query = self.query
1116: 
1117:       resources = repository.read(query)
1118: 
1119:       # remove already known results
1120:       resources -= head          if head.any?
1121:       resources -= tail          if tail.any?
1122:       resources -= @removed.to_a if @removed.any?
1123: 
1124:       query.add_reversed? ? unshift(*resources.reverse) : concat(resources)
1125: 
1126:       # TODO: DRY this up with LazyArray
1127:       @array.unshift(*head)
1128:       @array.concat(tail)
1129: 
1130:       @head = @tail = nil
1131:       @reapers.each { |resource| @array.delete_if(&resource) } if @reapers
1132:       @array.freeze if frozen?
1133: 
1134:       self
1135:     end
method_missing(method, *args, &block) click to toggle source

Delegates to Model, Relationships or the superclass (LazyArray)

When this receives a method that belongs to the Model the Collection is scoped to, it will execute the method within the same scope as the Collection and return the results.

When this receives a method that is a relationship the Model has defined, it will execute the association method within the same scope as the Collection and return the results.

Otherwise this method will delegate to a method in the superclass (LazyArray) and return the results.

@return [Object]

  the return values of the delegated methods

@api public

      # File lib/dm-core/collection.rb, line 1440
1440:     def method_missing(method, *args, &block)
1441:       relationships = self.relationships
1442: 
1443:       if model.respond_to?(method)
1444:         delegate_to_model(method, *args, &block)
1445:       elsif relationship = relationships[method] || relationships[DataMapper::Inflector.singularize(method.to_s).to_sym]
1446:         delegate_to_relationship(relationship, *args)
1447:       else
1448:         super
1449:       end
1450:     end
new_collection(query, resources = nil, &block) click to toggle source

Initializes a new Collection

@return [Collection]

  A new Collection object

@api private

      # File lib/dm-core/collection.rb, line 1153
1153:     def new_collection(query, resources = nil, &block)
1154:       if loaded?
1155:         resources ||= filter(query)
1156:       end
1157: 
1158:       # TOOD: figure out a way to pass not-yet-saved Resources to this newly
1159:       # created Collection.  If the new resource matches the conditions, then
1160:       # it should be added to the collection (keep in mind limit/offset too)
1161: 
1162:       self.class.new(query, resources, &block)
1163:     end
partially_loaded?(offset, limit = 1) click to toggle source

Test if the collection is loaded between the offset and limit

@param [Integer] offset

  the offset of the collection to test

@param [Integer] limit

  optional limit for how many entries to be loaded

@return [Boolean]

  true if the collection is loaded from the offset to the limit

@api private

      # File lib/dm-core/collection.rb, line 1093
1093:     def partially_loaded?(offset, limit = 1)
1094:       if offset >= 0
1095:         lazy_possible?(head, offset + limit)
1096:       else
1097:         lazy_possible?(tail, offset.abs)
1098:       end
1099:     end
repository_name() click to toggle source

Returns the Query Repository name

@return [Symbol]

  the repository name

@api private

      # File lib/dm-core/collection.rb, line 1143
1143:     def repository_name
1144:       repository.name
1145:     end
resource_added(resource) click to toggle source

Track the added resource

@param [Resource] resource

  the resource that was added

@return [Resource]

  the resource that was added

@api private

      # File lib/dm-core/collection.rb, line 1299
1299:     def resource_added(resource)
1300:       resource = initialize_resource(resource)
1301: 
1302:       if resource.saved?
1303:         @identity_map[resource.key] = resource
1304:         @removed.delete(resource)
1305:       else
1306:         set_default_attributes(resource)
1307:       end
1308: 
1309:       resource
1310:     end
resource_removed(resource) click to toggle source

Track the removed resource

@param [Resource] resource

  the resource that was removed

@return [Resource]

  the resource that was removed

@api private

      # File lib/dm-core/collection.rb, line 1338
1338:     def resource_removed(resource)
1339:       if resource.saved?
1340:         @identity_map.delete(resource.key)
1341:         @removed << resource
1342:       end
1343: 
1344:       resource
1345:     end
resources_added(resources) click to toggle source

Track the added resources

@param [Array] resources

  the resources that were added

@return [Array]

  the resources that were added

@api private

      # File lib/dm-core/collection.rb, line 1321
1321:     def resources_added(resources)
1322:       if resources.kind_of?(Enumerable)
1323:         resources.map { |resource| resource_added(resource) }
1324:       else
1325:         resource_added(resources)
1326:       end
1327:     end
resources_removed(resources) click to toggle source

Track the removed resources

@param [Array] resources

  the resources that were removed

@return [Array]

  the resources that were removed

@api private

      # File lib/dm-core/collection.rb, line 1356
1356:     def resources_removed(resources)
1357:       if resources.kind_of?(Enumerable)
1358:         resources.each { |resource| resource_removed(resource) }
1359:       else
1360:         resource_removed(resources)
1361:       end
1362:     end
scoped_query(query) click to toggle source

Return the absolute or relative scoped query

@param [Query, Hash] query

  the query to scope the collection with

@return [Query]

  the absolute or relative scoped query

@api private

      # File lib/dm-core/collection.rb, line 1401
1401:     def scoped_query(query)
1402:       if query.kind_of?(Query)
1403:         query.dup
1404:       else
1405:         self.query.relative(query)
1406:       end
1407:     end
set_default_attributes(resource) click to toggle source

Set the default attributes for a non-frozen resource

@param [Resource] resource

  the resource to set the default attributes for

@return [undefined]

@api private

      # File lib/dm-core/collection.rb, line 1284
1284:     def set_default_attributes(resource)
1285:       unless resource.readonly?
1286:         resource.attributes = default_attributes
1287:       end
1288:     end
set_operation(operation, other) click to toggle source

Apply a set operation on self and another collection

@param [Symbol] operation

  the set operation to apply

@param [Collection] other

  the other collection to apply the set operation on

@return [Collection]

  the collection that was created for the set operation

@api private

      # File lib/dm-core/collection.rb, line 1176
1176:     def set_operation(operation, other)
1177:       resources   = set_operation_resources(operation, other)
1178:       other_query = Query.target_query(repository, model, other)
1179:       new_collection(query.send(operation, other_query), resources)
1180:     end
set_operation_resources(operation, other) click to toggle source

Prepopulate the set operation if the collection is loaded

@param [Symbol] operation

  the set operation to apply

@param [Collection] other

  the other collection to apply the set operation on

@return [nil]

  nil if the Collection is not loaded

@return [Array]

  the resources to prepopulate the set operation results with

@api private

      # File lib/dm-core/collection.rb, line 1195
1195:     def set_operation_resources(operation, other)
1196:       entries.send(operation, other.entries) if loaded?
1197:     end
sliced_query(offset, limit) click to toggle source

@api private

      # File lib/dm-core/collection.rb, line 1410
1410:     def sliced_query(offset, limit)
1411:       query = self.query
1412: 
1413:       if offset >= 0
1414:         query.slice(offset, limit)
1415:       else
1416:         query = query.slice((limit + offset).abs, limit).reverse!
1417: 
1418:         # tell the Query to prepend each result from the adapter
1419:         query.update(:add_reversed => !query.add_reversed?)
1420:       end
1421:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.