class Ecoportal::API::Common::Content::CollectionModel
CollectionModel
aims to deal with Arrays of actual objects. @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
Attributes
Public Class Methods
# File lib/ecoportal/api/common/content/collection_model.rb, line 79 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
# File lib/ecoportal/api/common/content/collection_model.rb, line 14 def items_key @items_key ||= "id" end
# File lib/ecoportal/api/common/content/collection_model.rb, line 18 def items_key=(value) @items_key = value && value.to_s.freeze end
Resolves to the nuclear `Class` of the elements @note
- use block to define `klass` callback
@param value [Hash] base `doc` (raw object) to create the object with @yield [doc] identifies the target `class` of the raw object @yieldparam doc [Hash] @yieldreturn [Klass] the target `class` @return [Klass] the target `class`
# File lib/ecoportal/api/common/content/collection_model.rb, line 30 def klass(value = NOT_USED, &block) if block @klass = block block.call(value) if value != NOT_USED 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 end
@return [Boolean] are there the factory logics to build item objects defined?
# File lib/ecoportal/api/common/content/collection_model.rb, line 75 def klass? @klass || @new_item end
Ecoportal::API::Common::Content::DoubleModel::new
# File lib/ecoportal/api/common/content/collection_model.rb, line 90 def initialize(ini_doc = [], parent: self, key: nil) 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) end
Generates a new object of the target class @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`
@param doc [Hash] doc to parse @note if block is given, it ignores `doc` @yield [doc, parent, key] creates an object instance of the target `klass` @yieldparam doc [Hash] @yieldreturn [Klass] instance object of the target `klass` @return [Klass] instance object of the target `klass`
# File lib/ecoportal/api/common/content/collection_model.rb, line 55 def new_item(doc = NOT_USED, parent: nil, key: nil, &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) 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
Public Instance Methods
Get an element usign the `key`. @param value [String, Hash, Ecoportal::API::Common::Content::DoubleModel] @return [Object] the `items_class` element object
# File lib/ecoportal/api/common/content/collection_model.rb, line 151 def [](value) items_by_key[get_key(value)] end
Transforms `value` into the actual `key` to access the object in the doc `Array` @note
- The name of the method is after the paren't class method - This method would have been better called `_doc_pos` :)
# File lib/ecoportal/api/common/content/collection_model.rb, line 116 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 raise UnlinkedModel.new("Can't find child: #{value}") end end
# File lib/ecoportal/api/common/content/collection_model.rb, line 138 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
Deletes `value` from this `CollectionModel` instance @param 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
# File lib/ecoportal/api/common/content/collection_model.rb, line 188 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
# File lib/ecoportal/api/common/content/collection_model.rb, line 133 def each(&block) return to_enum(:each) unless block _items.each(&block) end
# File lib/ecoportal/api/common/content/collection_model.rb, line 130 def empty?; count == 0; end
@return [Class] the class of the elements of the Collection
# File lib/ecoportal/api/common/content/collection_model.rb, line 108 def items_class self.class.klass end
# File lib/ecoportal/api/common/content/collection_model.rb, line 129 def length; count; end
# File lib/ecoportal/api/common/content/collection_model.rb, line 131 def present?; count > 0; end
Tries to find the element `value`, if it exists, it updates it
Otherwise it pushes it to the end
@value [Hash, Ecoportal::API::Common::Content::DoubleModel] the eleement to be added @return [Object] the `items_class` element object
# File lib/ecoportal/api/common/content/collection_model.rb, line 164 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" 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
@return [Array<Object>] the `items_class` element object
# File lib/ecoportal/api/common/content/collection_model.rb, line 156 def values_at(*keys) keys.map {|key| self[key]} end
Protected Instance Methods
# File lib/ecoportal/api/common/content/collection_model.rb, line 224 def _doc_items replace_doc([]) unless doc.is_a?(Array) doc end
Gets the `key` of the object `value`
# File lib/ecoportal/api/common/content/collection_model.rb, line 211 def get_key(value) case value when Content::DoubleModel value.key when Hash value[items_key] when String value when Numeric get_key(self.to_a[value]) end end
@note it does not support a change of `id` on an existing item
# File lib/ecoportal/api/common/content/collection_model.rb, line 230 def items_by_key return @items_by_key if @indexed {}.tap do |hash| variable_set(:@items_by_key, hash) _items.each {|item| hash[item.key] = item} @indexed = true end end
# File lib/ecoportal/api/common/content/collection_model.rb, line 203 def items_key; self.class.items_key; end
# File lib/ecoportal/api/common/content/collection_model.rb, line 205 def on_change @indexed = false #variables_remove! end
# File lib/ecoportal/api/common/content/collection_model.rb, line 201 def order_matters?; self.class.order_matters; end
# File lib/ecoportal/api/common/content/collection_model.rb, line 202 def uniq?; self.class.uniq; end
Private Instance Methods
Deletes `value` from `doc` (here referred as `_doc_items`) @return [Object] the element deleted from `doc`
# File lib/ecoportal/api/common/content/collection_model.rb, line 263 def _doc_delete(value) if current_pos = _doc_key(value) _doc_items.delete_at(current_pos) end end
# File lib/ecoportal/api/common/content/collection_model.rb, line 269 def _doc_upsert(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED) current_pos = if elem = self[value] _doc_key(elem) end pos = scope_position(pos: pos, before: before, after: after) pos ||= current_pos if current_pos && pos _doc_items.delete_at(current_pos) pos = (pos <= current_pos)? pos : pos - 1 end pos = (pos && pos < _doc_items.length)? pos : _doc_items.length pos.tap do |i| _doc_items.insert(pos, value) end end
# File lib/ecoportal/api/common/content/collection_model.rb, line 241 def new_item(value) self.class.new_item(value, parent: self) end
# File lib/ecoportal/api/common/content/collection_model.rb, line 289 def scope_position(pos: NOT_USED, before: NOT_USED, after: NOT_USED) case when used_param?(pos) if elem = self[pos] _doc_key(elem) - 1 end when used_param?(before) if elem = self[before] _doc_key(elem) - 1 end when used_param?(after) if elem = self[after] _doc_key(elem) end end end
Helper to remove tracked down instance variables
Ecoportal::API::Common::Content::DoubleModel#variable_remove!
# File lib/ecoportal/api/common/content/collection_model.rb, line 246 def variable_remove!(key) if @items_by_key && (k = get_key(key)) && (item = @items_by_key[k]) _items.delete(item) if _items.include?(item) @items_by_key.delete(k) else super(key) end end
Removes all the persistent variables
Ecoportal::API::Common::Content::DoubleModel#variables_remove!
# File lib/ecoportal/api/common/content/collection_model.rb, line 256 def variables_remove! @indexed = false super end