Class: Gorillib::Collection

Inherits:
Object show all
Defined in:
lib/gorillib/collection.rb,
lib/gorillib/collection/model_collection.rb

Overview

The Collection class encapsulates the minimum functionality to let you:

  • store items uniquely, in order added
  • retrieve items by label
  • iterate over its values

A collection is best used for representing 'plural' properties of models; it is not intended to be some radical reimagining of a generic array or hash. We've found its locked-down capabilities to particularly useful for constructing DSLs (Domain-Specific Languages). Collections are not intended to be performant: its abstraction layer comes at the price of additional method calls.

Gated admission

Collection provides a well-defended perimeter. Every item added to the collection (whether sent to the initializer, the passes through add method

Familiarity with its contents

Typically your model will have a familiar (but not intimate) relationship with its plural property:

  • items may have some intrinsic, uniquely-identifying feature: a name, id, or normalized representation. You'd like to be able to add an retrieve them by that intrinsic feature without having to manually juggle the correspondence of labels to intrinsic features.

In the case of a ModelCollection,

  • all its items may share a common type: "a post has many Comments".

  • a model may want items to hold a reference back to the containing model, or otherwise to share some common attributes. As an example, a Graph may have many Stage objects; the collection can inform newly-added stages which graph they belong to.

Barebones enumerable methods

The set of methods is purposefully sparse. If you want to use select, invert, etc, just invoke to_hash or to_a and work with the copy it gives you.

Collection responds to:

  • receive!, values, to_a, each and each_value;
  • length, size, empty?, blank?
  • [], []=, include?, fetch, delete, each_pair, to_hash.
  • key_method: called on items to get their key; to_key by default.
  • <<: adds item under its key_method key
  • receive!s an array by auto-keying the elements, or a hash by trusting what you give it

A ModelCollection adds:

  • factory: generates new items, converts received items
  • update_or_create: if absent, creates item with given attributes and key_method => key`; if present, updates with given attributes.

Direct Known Subclasses

ModelCollection

Defined Under Namespace

Modules: CommonAttrs, ItemsBelongTo

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Collection

include Gorillib::Model



77
78
79
80
81
# File 'lib/gorillib/collection.rb', line 77

def initialize(options={})
  @clxn       = Hash.new
  @key_method = options[:key_method] if options[:key_method]
  @belongs_to = options[:belongs_to] if options[:belongs_to]
end

Instance Attribute Details

#belongs_toObject (readonly)

Object that owns this collection, if any



70
71
72
# File 'lib/gorillib/collection.rb', line 70

def belongs_to
  @belongs_to
end

Class Method Details

.native?(obj) ⇒ true, false

A native object does not need any transformation; it is accepted directly. By default, an object is native if it is_a? this class

Parameters:

  • obj (Object)

    the object that will be received

Returns:

  • (true, false)

    true if the item does not need conversion



176
177
178
# File 'lib/gorillib/collection.rb', line 176

def self.native?(obj)
  obj.is_a?(self)
end

.receive(items, *args) ⇒ Object

Create a new collection and add the given items to it (if given an existing collection, just returns it directly)



164
165
166
167
168
169
# File 'lib/gorillib/collection.rb', line 164

def self.receive(items, *args)
  return items if native?(items)
  coll = new(*args)
  coll.receive!(items)
  coll
end

Instance Method Details

#<<(item) ⇒ Gorillib::Collection

Adds item, returning the collection itself.

Returns:



128
129
130
131
# File 'lib/gorillib/collection.rb', line 128

def <<(item)
  add(item)
  self
end

#==(other) ⇒ true, false

Two collections are equal if they have the same class and their contents are equal

Parameters:

Returns:

  • (true, false)

    True if attributes are equal and other is instance of the same Class



184
185
186
187
# File 'lib/gorillib/collection.rb', line 184

def ==(other)
  return false unless other.instance_of?(self.class)
  clxn == other.send(:clxn)
end

#[]=(label, item) ⇒ Object

add item with given label



134
135
136
# File 'lib/gorillib/collection.rb', line 134

def []=(label, item)
  add(item, label)
end

#add(item, label = nil) ⇒ Object

Adds an item in-place. Items added to the collection (via add, []=, initialize, etc) all pass through the add method: you should override this in subclasses to add any gatekeeper behavior.

If no label is supplied, we use the result of invoking key_method on the item (or raise an error if no label and no key_method).

It's up to you to ensure that labels make sense; this method doesn't demand the item's key_method match its label.

Returns:



94
95
96
97
# File 'lib/gorillib/collection.rb', line 94

def add(item, label=nil)
  label ||= label_for(item)
  @clxn[label] = item
end

#each(&block) ⇒ Object

iterate over each value in the collection



124
# File 'lib/gorillib/collection.rb', line 124

def each(&block); each_value(&block) ; end

#inspectString

Returns string describing the collection's array representation.

Returns:

  • (String)

    string describing the collection's array representation



192
193
194
195
196
# File 'lib/gorillib/collection.rb', line 192

def inspect
  key_width = [keys.map{|key| key.to_s.length + 1 }.max.to_i, 45].min
  guts = clxn.map{|key, val| "%-#{key_width}s %s" % ["#{key}:", val.inspect] }.join(",\n   ")
  ['c{ ', guts, ' }'].join
end

#inspect_compactObject



198
199
200
# File 'lib/gorillib/collection.rb', line 198

def inspect_compact
  ['c{ ', keys.join(", "), ' }'].join
end

#label_for(item) ⇒ Object



99
100
101
102
103
104
# File 'lib/gorillib/collection.rb', line 99

def label_for(item)
  if key_method.nil? then
    raise ArgumentError, "Can't add things to a #{self.class} without some sort of label: use foo[label] = obj, or set the collection's key_method" ;
  end
  item.public_send(key_method)
end

#receive!(other) ⇒ Gorillib::Collection

Receive items in-place, replacing any existing item with that label.

Individual items are added using #receive_item -- if you'd like to perform any conversion or modification to items, do it there

Parameters:

Returns:



145
146
147
148
149
150
151
152
153
154
# File 'lib/gorillib/collection.rb', line 145

def receive!(other)
  if other.respond_to?(:each_pair)
    other.each_pair{|label, item| receive_item(label, item) }
  elsif other.respond_to?(:each)
    other.each{|item|             receive_item(nil,   item) }
  else
    raise "A collection can only receive something that is enumerable: got #{other.inspect}"
  end
  self
end

#receive_item(label, item) ⇒ Object

items arriving from the outside world should pass through receive_item, not directly to add.



158
159
160
# File 'lib/gorillib/collection.rb', line 158

def receive_item(label, item)
  add(item, label)
end

#to_aArray

Returns an array holding the items.

Returns:

  • (Array)

    an array holding the items



119
# File 'lib/gorillib/collection.rb', line 119

def to_a    ; values    ; end

#to_hash{Symbol => Object}

Returns a hash of key=>item pairs.

Returns:



121
# File 'lib/gorillib/collection.rb', line 121

def to_hash ; clxn.dup  ; end

#to_sString

Returns string describing the collection's array representation.

Returns:

  • (String)

    string describing the collection's array representation



190
# File 'lib/gorillib/collection.rb', line 190

def to_s           ; to_a.to_s           ; end