Class: Ecoportal::API::Common::Content::CollectionModel

Inherits:
DoubleModel
  • Object
show all
Includes:
Enumerable
Defined in:
lib/ecoportal/api/common/content/collection_model.rb

Overview

Note:

to be able to refer to the correct element of the Collection, it is required that those elements have a unique key that allows to identify them

CollectionModel aims to deal with Arrays of actual objects.

Constant Summary

Constants inherited from DoubleModel

DoubleModel::NOT_USED

Constants included from ClassHelpers

Ecoportal::API::Common::Content::ClassHelpers::NOT_USED

Class Attribute Summary collapse

Attributes inherited from DoubleModel

#_key, #_parent, #_read_only

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from DoubleModel

#as_json, #as_update, #consolidate!, #dirty?, #doc, embeds_many, embeds_one, enforce!, #key, #key=, key?, new_uuid, #original_doc, pass_reader, pass_writer, passarray, passboolean, passdate, passforced, passkey, passthrough, #print_pretty, #replace_doc, #reset!, #root, #to_json

Methods included from ClassHelpers

#inheritable_attrs, #inheritable_class_vars, #inherited, #instance_variable_name, #new_class, #resolve_class, #to_constant, #to_time, #uid, #used_param?

Constructor Details

#initialize(ini_doc = [], parent: self, key: nil, read_only: false) ⇒ CollectionModel

Returns a new instance of CollectionModel.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 100

def initialize(ini_doc = [], parent: self, key: nil, read_only: false)
  unless self.class.klass?
    raise "Undefined base 'klass' or 'new_item' callback for #{self.class}"
  end

  ini_doc = case ini_doc
            when Array
              ini_doc
            when Enumerable
              ini_doc.to_a
            else
              []
            end

  super(ini_doc, parent: parent, key: key, read_only: read_only)
end

Class Attribute Details

.klass(value = NOT_USED) {|doc| ... } ⇒ Klass

Note:
  • use block to define klass callback
Note:

When klass is resolved, if the items are of type DoubleModel, it sets on the collection class the items_key

Resolves to the nuclear Class of the elements

Parameters:

  • value (Hash) (defaults to: NOT_USED)

    base doc (raw object) to create the object with

Yields:

  • (doc)

    identifies the target class of the raw object

Yield Parameters:

  • doc (Hash)

Yield Returns:

  • (Klass)

    the target class

Returns:

  • (Klass)

    the target class



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 34

def klass(value = NOT_USED, &block)
  if block
    @klass = block
    block.call(value) if value != NOT_USED
    @klass
  elsif used_param?(value)
    if @klass.is_a?(Proc)
      @klass.call(value)
    else
      resolve_class(@klass, exception: false)
    end
  else
    resolve_class(@klass, exception: false)
  end.tap do |result|
    next unless result.is_a?(Class)
    next unless result < Ecoportal::API::Common::Content::DoubleModel
    self.items_key = result.key
  end
end

.order_keyObject

Returns the value of attribute order_key.



12
13
14
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 12

def order_key
  @order_key
end

.order_mattersObject

Returns the value of attribute order_matters.



12
13
14
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 12

def order_matters
  @order_matters
end

Class Method Details

.doc_class(name) ⇒ Object



88
89
90
91
92
93
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 88

def doc_class(name)
  dim_class = new_class(name, inherits: Common::Content::ArrayModel) do |klass|
    klass.order_matters = order_matters
    klass.uniq          = uniq
  end
end

.items_keyObject

The attr that has been defined as passkey in the item class



16
17
18
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 16

def items_key
  @items_key ||= "id"
end

.items_key=(value) ⇒ Object



20
21
22
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 20

def items_key=(value)
  @items_key = value && value.to_s.freeze
end

.klass?Boolean

Returns are there the factory logics to build item objects defined?.

Returns:

  • (Boolean)

    are there the factory logics to build item objects defined?



84
85
86
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 84

def klass?
  @klass || @new_item
end

.new_item(doc = NOT_USED, parent: nil, key: nil, read_only: false) {|doc, parent, key| ... } ⇒ Klass

Note:
  • use block to define new_item callback, which will prevail over klass
  • if new_item callback was not defined, it is required to defnie klass
Note:

if block is given, it ignores doc

Generates a new object of the target class

Parameters:

  • doc (Hash) (defaults to: NOT_USED)

    doc to parse

Yields:

  • (doc, parent, key)

    creates an object instance of the target klass

Yield Parameters:

  • doc (Hash)

Yield Returns:

  • (Klass)

    instance object of the target klass

Returns:

  • (Klass)

    instance object of the target klass



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 64

def new_item(doc = NOT_USED, parent: nil, key: nil, read_only: false, &block)
  if block
    @new_item = block
  elsif used_param?(doc)
    raise "You should define either a 'klass' or a 'new_item' callback first" unless klass?
    if @new_item
      @new_item.call(doc, parent, key)
    else
      if target_class = self.klass(doc)
        doc.is_a?(target_class) ? doc : target_class.new(doc, parent: parent, key: key, read_only: read_only)
      else
        raise "Could not find a class for: #{doc}"
      end
    end
  else
    raise "To define the 'new_item' callback (factory), you need to use a block"
  end
end

Instance Method Details

#[](value) ⇒ Object

Get an element usign the key.

Parameters:

Returns:

  • (Object)

    the items_class element object



175
176
177
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 175

def [](value)
  items_by_key[get_key(value)]
end

#_doc_key(value) ⇒ Object

Note:
  • The name of the method is after the paren't class method
  • This method would have been better called _doc_pos :)

Transforms value into the actual key to access the object in the doc Array



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 130

def _doc_key(value)
  #print "*(#{value.class})"
  return super(value) unless value.is_a?(Hash) || value.is_a?(Content::DoubleModel)
  if id = get_key(value)
    #print "^"
    _doc_items.index {|item| get_key(item) == id}.tap do |p|
      #print "{{#{p}}}"
    end
  else
    show_str = \
      case value
      when Hash
        value.pretty_inspect
      when Content::DoubleModel
        "#{value} with key: #{value.class.key} (items_key: #{self.class.items_key})"
      else
        value
      end

    raise UnlinkedModel, "Can't find child: #{show_str}"
  end
end

#_doc_pos(value) ⇒ Object



122
123
124
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 122

def _doc_pos(value)
  _doc_key(value)
end

#_itemsObject



162
163
164
165
166
167
168
169
170
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 162

def _items
  return @_items if @_items
  [].tap do |elements|
    variable_set(:@_items, elements)
    _doc_items.each do |item_doc|
      elements << new_item(item_doc)
    end
  end
end

#clearObject

Deletes all the elements of this CollectionModel instance



215
216
217
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 215

def clear
  self.to_a.each {|item| delete!(item)}
end

#delete!(value) ⇒ Object

Deletes value from this CollectionModel instance

Parameters:

  • value (String, Hash, Ecoportal::API::Common::Content::DoubleModel)
    • When used as String, the key value (i.e. id value) is expected
    • When used as Hash, it should be the doc of the target element
    • When used as DoubleModel, it should be the specific object to be deleted


224
225
226
227
228
229
230
231
232
233
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 224

def delete!(value)
  unless value.is_a?(Hash) || value.is_a?(Content::DoubleModel) || value.is_a?(String)
    raise "'Content::DoubleModel' or 'Hash' doc required"
  end
  if item = self[value]
    _doc_delete(item.doc)
    @indexed = false
    _items.delete(item)
  end
end

#each(&block) ⇒ Object



157
158
159
160
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 157

def each(&block)
  return to_enum(:each) unless block
  _items.each(&block)
end

#empty?Boolean

Returns:

  • (Boolean)


154
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 154

def empty?;   count == 0; end

#include?(value) ⇒ Boolean

Checks if an element exists in the collection

Parameters:

Returns:

  • (Boolean)

    whether or not it is included



182
183
184
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 182

def include?(value)
  items_by_key.key?(get_key(value))
end

#items_classClass

Returns the class of the elements of the Collection.

Returns:

  • (Class)

    the class of the elements of the Collection



118
119
120
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 118

def items_class
  self.class.klass
end

#lengthObject



153
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 153

def length;   count;      end

#present?Boolean

Returns:

  • (Boolean)


155
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 155

def present?; count > 0;  end

#upsert!(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED) ⇒ Object

Tries to find the element value, if it exists, it updates it Otherwise it pushes it to the end

Returns:

  • (Object)

    the items_class element object



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 195

def upsert!(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
  unless value.is_a?(Hash) || value.is_a?(Content::DoubleModel)
    raise "'Content::DoubleModel' or 'Hash' doc required. Given #{value.class}"
  end
  item_doc = value.is_a?(Content::DoubleModel)? value.doc : value
  item_doc = JSON.parse(item_doc.to_json)
  if item = self[value]
    item.replace_doc(item_doc)
  else
    _doc_upsert(item_doc, pos: pos, before: before, after: after).tap do |pos_idx|
      _items.insert(pos_idx, new_item(item_doc))
      @indexed = false
    end
  end
  (item || self[item_doc]).tap do |item|
    yield(item) if block_given?
  end
end

#values_at(*keys) ⇒ Array<Object>

Returns the items_class element object.

Returns:

  • (Array<Object>)

    the items_class element object



187
188
189
# File 'lib/ecoportal/api/common/content/collection_model.rb', line 187

def values_at(*keys)
  keys.map {|key| self[key]}
end