lib/ecoportal/api/common/content/collection_model.rb in ecoportal-api-v2-1.1.7 vs lib/ecoportal/api/common/content/collection_model.rb in ecoportal-api-v2-1.1.8

- old
+ new

@@ -30,19 +30,22 @@ # This is an optimization to cut class lookups # @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` + # @return [Class, Proc, Hash] the target `class` + # - `Hash` tracks a symbol pending to be resovle from its referrer + # - `Class` an already resolve class + # - `Proc` a forker that pivots between multiple classes def klass(value = NOT_USED, &block) @klass = block if block_given? - if @klass && !@class.is_a?(Proc) - @klass = resolve_class(@klass, exception: false) unless @klass.is_a?(Class) - @klass - elsif @klass.is_a?(Proc) && used_param?(value) + if @klass.is_a?(Proc) && used_param?(value) @klass.call(value) + elsif @klass && !@klass.is_a?(Proc) && !@klass.is_a?(Class) + @klass = resolve_class(@klass, exception: false) + @klass else @klass end.tap do |result| next unless result.is_a?(Class) next unless result < Ecoportal::API::Common::Content::DoubleModel @@ -56,11 +59,11 @@ end # Optimization def new_item_class_based? return false if @new_item.is_a?(Proc) - return false if @klass.is_a?(Proc) + return false if klass.is_a?(Proc) return true if klass.is_a?(Class) false end # Generates a new object of the target class @@ -76,49 +79,45 @@ # @key [Symbol, String] the key value to access the item within collection # Please observe that items in a CollectionModel are identified via their key attr. # Meaning that there is actually no need to define this argument. # @return [Klass] instance object of the target `klass` def new_item(doc = NOT_USED, parent: nil, key: nil, read_only: false, &block) - return (@new_item = block; nil) if block_given + if block_given? + @new_item = block + return + end msg = "To define the 'new_item' callback (factory), you need to use a block" raise msg unless used_param?(doc) msg = "You should define either a 'klass' or a 'new_item' callback first" raise msg unless klass? return @new_item.call(doc, parent, key) if @new_item.is_a?(Proc) - raise "Could not find a class for: #{doc}" unless target_class = klass(doc) + raise "Could not find a class for: #{doc}" unless (target_class = klass(doc)) return doc if doc.is_a?(target_class) target_class.new(doc, parent: parent, key: key, read_only: read_only) end - - 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 end include Enumerable inheritable_class_vars :klass, :order_matters, :order_key, :items_key, :new_item 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 + msg = "Undefined base 'klass' or 'new_item' callback for #{self.class}" + raise msg unless self.class.klass? - ini_doc = case ini_doc - when Array - ini_doc - when Enumerable - ini_doc.to_a - else - [] - 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 # @return [Class] the class of the elements of the Collection @@ -135,11 +134,11 @@ # - The name of the method is after the paren't class method # - This method would have been better called `_doc_pos` :) 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) + if (id = get_key(value)) #print "^" _doc_items.index {|item| get_key(item) == id}.tap do |p| #print "{{#{p}}}" end else @@ -155,14 +154,22 @@ raise UnlinkedModel, "Can't find child: #{show_str}" end end - def length; count; end - def empty?; count == 0; end - def present?; count > 0; end + def length + count + end + def empty? + count&.zero? + end + + def present? + count&.positive? + end + def each(&block) return to_enum(:each) unless block _items.each(&block) end @@ -204,26 +211,26 @@ 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] + 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? + (item || self[item_doc]).tap do |itm| + yield(itm) if block_given? end end # Deletes all the elements of this `CollectionModel` instance def clear - self.to_a.each {|item| delete!(item)} + to_a.each {|item| delete!(item)} 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 @@ -231,23 +238,30 @@ # - When used as `DoubleModel`, it should be the specific object to be deleted 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 + return unless (item = self[value]) + _doc_delete(item.doc) + @indexed = false + _items.delete(item) end protected - def order_matters?; self.class.order_matters; end - def uniq?; self.class.uniq; end - def items_key; self.class.items_key; end + def order_matters? + self.class.order_matters + end + def uniq? + self.class.uniq + end + + def items_key + self.class.items_key + end + def on_change @indexed = false #variables_remove! end @@ -259,11 +273,11 @@ when Hash value[items_key] when String value when Numeric - get_key(self.to_a[value]) + get_key(to_a[value]) end end def _doc_items replace_doc([]) unless doc.is_a?(Array) @@ -282,13 +296,13 @@ private def new_item(value) if self.class.new_item_class_based? - self.class.klass.new(value, parent: self, read_only: self._read_only) + self.class.klass.new(value, parent: self, read_only: _read_only) else - self.class.new_item(value, parent: self, read_only: self._read_only) + self.class.new_item(value, parent: self, read_only: _read_only) end end # Helper to remove tracked down instance variables def variable_remove!(key) @@ -307,51 +321,49 @@ end # Deletes `value` from `doc` (here referred as `_doc_items`) # @return [Object] the element deleted from `doc` def _doc_delete(value) - if current_pos = _doc_key(value) - _doc_items.delete_at(current_pos) - end + return unless (current_pos = _doc_key(value)) + + _doc_items.delete_at(current_pos) end def _doc_upsert(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED) - current_pos = if elem = self[value] - _doc_key(elem) - end + 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 + pos = pos <= current_pos ? pos : pos - 1 end - pos = (pos && pos < _doc_items.length)? pos : _doc_items.length - pos.tap do |i| + pos = (pos && pos < _doc_items.length)? pos : _doc_items.length # rubocop:disable Style/TernaryParentheses + pos.tap do |_i| _doc_items.insert(pos, value) end - end def scope_position(pos: NOT_USED, before: NOT_USED, after: NOT_USED) - case - when used_param?(pos) - if elem = self[pos] + if used_param?(pos) + if (elem = self[pos]) _doc_key(elem) - 1 end - when used_param?(before) - if elem = self[before] + elsif used_param?(before) + if (elem = self[before]) _doc_key(elem) - 1 end - when used_param?(after) - if elem = self[after] + elsif used_param?(after) + if (elem = self[after]) _doc_key(elem) end end end - end end end end end