lib/ecoportal/api/common/base_model.rb in ecoportal-api-0.9.7 vs lib/ecoportal/api/common/base_model.rb in ecoportal-api-0.10.0

- old
+ new

@@ -1,13 +1,14 @@ +# rubocop:disable Naming/PredicateName module Ecoportal module API module Common class BaseModel - class UnlinkedModel < Exception - def initialize (msg = "Something went wrong when linking the document.", from: nil, key: nil) - msg += " From: #{from}." if from - msg += " key: #{key}." if key + class UnlinkedModel < StandardError + def initialize(msg = "Something went wrong when linking the document.", from: nil, key: nil) + msg << " From: #{from}." if from + msg << " key: #{key}." if key super(msg) end end extend BaseClass @@ -23,75 +24,83 @@ send(to)[method] = value end end end - def embeds_one(method, key: method, nullable: false, klass:) + def embeds_one(method, klass:, key: method, nullable: false) method = method.to_s.freeze var = "@#{method}".freeze key = key.to_s.freeze + define_method(method) do if instance_variable_defined?(var) value = instance_variable_get(var) return value unless nullable return value if (value && doc[key]) || (!value && !doc[key]) remove_instance_variable(var) end + doc[key] ||= {} unless nullable + return instance_variable_set(var, nil) unless doc[key] self.class.resolve_class(klass).new( doc[key], parent: self, key: key ).tap {|obj| instance_variable_set(var, obj)} end end - end attr_reader :_parent, :_key def initialize(doc = {}, parent: self, key: nil) @_parent = parent @_key = key - if !_parent || !_key - @doc = JSON.parse(doc.to_json) - @original_doc = JSON.parse(@doc.to_json) - @initial_doc = JSON.parse(@doc.to_json) - end + + return unless !_parent || !_key + + @doc = JSON.parse(doc.to_json) + @original_doc = JSON.parse(@doc.to_json) + @initial_doc = JSON.parse(@doc.to_json) end def doc - raise UnlinkedModel.new(from: "#{self.class}#doc", key: _key) unless linked? + raise UnlinkedModel.new(from: "#{self.class}#doc", key: _key) unless linked? return @doc if is_root? + _parent.doc.dig(*[_key].flatten) end def original_doc - raise UnlinkedModel.new(from: "#{self.class}#original_doc", key: _key) unless linked? + raise UnlinkedModel.new(from: "#{self.class}#original_doc", key: _key) unless linked? return @original_doc if is_root? + _parent.original_doc&.dig(*[_key].flatten) end def initial_doc - raise UnlinkedModel.new(from: "#{self.class}#initial_doc", key: _key) unless linked? + raise UnlinkedModel.new(from: "#{self.class}#initial_doc", key: _key) unless linked? return @initial_doc if is_root? + _parent.initial_doc&.dig(*[_key].flatten) end # It replaces `doc` by `new_doc` # @return [Hash] `doc` before change def replace_doc!(new_doc) raise UnlinkedModel.new(from: "#{self.class}#replace_doc", key: _key) unless linked? + @doc.tap do @doc = new_doc end end # It replaces `original_doc` by `new_doc` # @return [Hash] `original_doc` before change def replace_original_doc!(new_doc) raise UnlinkedModel.new(from: "#{self.class}#replace_original_doc", key: _key) unless linked? + @original_doc.tap do @original_doc = new_doc end end @@ -113,11 +122,12 @@ as_update != {} end # It consolidates all the changes carried by `doc` by setting it as `original_doc`. def consolidate! - raise UnlinkedModel.new(from: "#{self.class}#consolidate!", key: _key) unless linked? + raise UnlinkedModel.new(from: "#{self.class}#consolidate!", key: _key) unless linked? + new_doc = JSON.parse(doc.to_json) if is_root? @original_doc = new_doc else dig_set(_parent.original_doc, [_key].flatten, new_doc) @@ -128,22 +138,23 @@ # @note # 1. When there are nullable properties, it may be required to apply `reset!` from the parent # i.e. `parent.reset!("child")` # when parent.child is `nil` # 2. In such a case, only immediate childs are allowed to be reset # @param key [String, Array<String>, nil] if given, it only resets the specified property - def reset!(key = nil) - raise "'key' should be a String. Given #{key}" unless !key || key.is_a?(String) - raise UnlinkedModel.new(from: "#{self.class}#reset!", key: _key) unless linked? + def reset!(key = nil) # rubocop:disable Metrics/AbcSize> + msg = "'key' should be a String. Given #{key}" + raise ArgumentError, msg unless !key || key.is_a?(String) + raise UnlinkedModel.new(from: "#{self.class}#reset!", key: _key) unless linked? if key - if self.respond_to?(key) && child = self.send(key) && child.is_a?(Ecoportal::API::Common::BaseModel) + if respond_to?(key) && (child = send(key)) && child.is_a?(Ecoportal::API::Common::BaseModel) child.reset! else new_doc = original_doc && original_doc[key] dig_set(doc, [key], new_doc && JSON.parse(new_doc.to_json)) # regenerate object if new_doc is null - self.send(key) if !new_doc && self.respond_to?(key) + send(key) if !new_doc && respond_to?(key) end else new_doc = JSON.parse(original_doc.to_json) if is_root? @doc = new_doc @@ -177,19 +188,19 @@ dig_set(obj[keys.first], keys.slice(1..-1), value) end end def set_uniq_array_keep_order(key, value) - unless value.is_a?(Array) - raise "#{key}= needs to be passed an Array, got #{value.class}" - end - ini_vals = (original_doc && original_doc[key]) || [] + msg = "#{key}= needs to be passed an Array, got #{value.class}" + raise ArgumentError, msg unless value.is_a?(Array) + ini_vals = (original_doc && original_doc[key]) || [] value = value.uniq # preserve original order to avoid false updates doc[key] = ((ini_vals & value) + (value - ini_vals)).compact end - end end end end + +# rubocop:enable Naming/PredicateName