lib/ecoportal/api/common/content/double_model.rb in ecoportal-api-v2-0.9.7 vs lib/ecoportal/api/common/content/double_model.rb in ecoportal-api-v2-1.0.1
- old
+ new
@@ -16,10 +16,13 @@
msg += " key: #{key}." if key
super(msg)
end
end
+ class NoKeyMethod < StandardError
+ end
+
class << self
attr_reader :key
def key?
!!key
@@ -169,11 +172,11 @@
klass.uniq = uniq
end
define_method method do
return instance_variable_get(var) if instance_variable_defined?(var)
- new_obj = dim_class.new(parent: self, key: method)
+ new_obj = dim_class.new(parent: self, key: method, read_only: self._read_only)
variable_set(var, new_obj)
end
end
end
@@ -192,11 +195,14 @@
# @param method [Symbol] the method that exposes the embeded object
# @param key [Symbol] the `key` that embeds it to the underlying `Hash` model
# @param order_matters [Boolean] to state if the order will matter
# @param klass [Class, String] the class of the individual elements it embeds
# @param enum_class [Class, String] the class of the collection that will hold the individual elements
- def embeds_many(method, key: method, order_matters: false, order_key: nil, klass: nil, enum_class: nil)
+ # @param read_only [Boolean] whether or not should try to **work around** items `klass` missing a `key`
+ # - If set to `true` this is meant only for read purposes (won't be able to successufully insert)
+ def embeds_many(method, key: method, klass: nil, enum_class: nil,
+ order_matters: false, order_key: nil, read_only: false)
if enum_class
eclass = enum_class
elsif klass
eclass = new_class("#{method}::#{klass}", inherits: Common::Content::CollectionModel) do |dim_class|
dim_class.klass = klass
@@ -204,21 +210,21 @@
dim_class.order_key = order_key
end
else
raise "You should either specify the 'klass' of the elements or the 'enum_class'"
end
- embed(method, key: key, multiple: true, klass: eclass) do |instance_with_called_method|
+ embed(method, key: key, multiple: true, klass: eclass, read_only: read_only) do |instance_with_called_method|
# keep reference to the original class to resolve the `klass` dependency
# See stackoverflow: https://stackoverflow.com/a/73709529/4352306
referrer_class = instance_with_called_method.class
eclass.klass = {referrer_class => klass} if klass
end
end
private
- def embed(method, key: method, nullable: false, multiple: false, klass:, &block)
+ def embed(method, key: method, nullable: false, multiple: false, klass:, read_only: false, &block)
method = method.to_s.freeze
var = instance_variable_name(method).freeze
k = key.to_s.freeze
# retrieving method (getter)
@@ -228,13 +234,26 @@
unless nullable
doc[k] ||= multiple ? [] : {}
end
return variable_set(var, nil) unless doc[k]
- self.class.resolve_class(klass).new(
- doc[k], parent: self, key: k
- ).tap {|obj| variable_set(var, obj)}
+ embedded_class = self.class.resolve_class(klass)
+
+ if multiple && read_only
+ if doc[k].is_a?(Array) && embedded_class < Common::Content::CollectionModel
+ if (item_class = embedded_class.klass) && !item_class.key?
+ item_class.passkey :id
+ doc[k].each_with_index do |item_doc, i|
+ item_doc["id"] = "#{i}" unless item_doc.key?("id")
+ end
+ end
+ end
+ end
+
+ embedded_class.new(doc[k], parent: self, key: k, read_only: self._read_only || read_only).tap do |obj|
+ variable_set(var, obj)
+ end
end
end
# The list of keys that will be forced in the model
def model_forced_keys
@@ -242,20 +261,21 @@
end
end
inheritable_class_vars :forced_model_keys, :key
- attr_reader :_parent, :_key
+ attr_reader :_parent, :_key, :_read_only
- def initialize(doc = {}, parent: self, key: nil)
- @_dim_vars = []
- @_parent = parent || self
- @_key = key || self
+ def initialize(doc = {}, parent: self, key: nil, read_only: false)
+ @_dim_vars = []
+ @_parent = parent || self
+ @_key = key || self
+ @_read_only = read_only
self.class.enforce!(doc)
- if _parent == self
+ if (_parent == self) || read_only
@doc = doc
@original_doc = JSON.parse(@doc.to_json)
end
if key_method? && doc && doc.is_a?(Hash)
@@ -269,17 +289,17 @@
_parent.root
end
# @return [String] the `value` of the `key` method (i.e. `id` value)
def key
- raise "No key_method defined for #{self.class}" unless key_method?
+ raise NoKeyMethod.new "No key_method defined for #{self.class}" unless key_method?
self.method(key_method).call
end
# @param [String] the `value` of the `key` method (i.e. `id` value)
def key=(value)
- raise "No key_method defined for #{self.class}" unless key_method?
+ raise NoKeyMethod.new "No key_method defined for #{self.class}" unless key_method?
method = "#{key_method}="
self.method(method).call(value)
end
# Offers a method for child classes to transform the key,
@@ -294,10 +314,11 @@
end
end
# @return [nil, Hash] the underlying `Hash` model as is (carrying current changes)
def doc
+ return @doc if doc_var?
raise UnlinkedModel.new(from: "#{self.class}#doc", key: _key) unless linked?
if is_root?
@doc
else
_parent.doc.dig(*[_doc_key(_key)].flatten)
@@ -376,11 +397,15 @@
end
end
protected
+ def doc_var?
+ !!defined?(@doc)
+ end
+
def is_root?
- _parent == self && !!defined?(@doc)
+ _parent == self && doc_var?
end
def linked?
is_root? || !!_parent.doc
end