module Ecoportal module API module Common class BaseModel class UnlinkedModel < Exception def initialize (msg = "Something went wrong when linking the document.") super(msg) end end extend BaseClass class << self def passthrough(*methods, to: :doc) methods.each do |method| method = method.to_s define_method method do send(to)[method] end define_method "#{method}=" do |value| send(to)[method] = value end end end def embeds_one(method, key: method, nullable: false, klass:) method = method.to_s.freeze var = "@#{method}".freeze key = key.to_s.freeze define_method(method) do return instance_variable_get(var) if instance_variable_defined?(var) 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 = doc @original_doc = JSON.parse(@doc.to_json) @initial_doc = JSON.parse(@doc.to_json) end end def doc raise UnlinkedModel.new unless linked? return @doc if is_root? _parent.doc.dig(*[_key].flatten) end def original_doc raise UnlinkedModel.new unless linked? return @original_doc if is_root? _parent.original_doc.dig(*[_key].flatten) end def initial_doc raise UnlinkedModel.new unless linked? return @initial_doc if is_root? _parent.initial_doc.dig(*[_key].flatten) end def as_json doc end def to_json(*args) doc.to_json(*args) end def as_update(ref = :last) new_doc = as_json ref_doc = ref == :total ? initial_doc : original_doc Common::HashDiff.diff(new_doc, ref_doc) end def dirty? as_update != {} end def consolidate! raise UnlinkedModel.new 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) end end def reset! raise UnlinkedModel.new unless linked? new_doc = JSON.parse(original_doc.to_json) if is_root? @doc = new_doc else dig_set(_parent.doc, [_key].flatten, new_doc) end end def pretty_print puts JSON.pretty_generate(as_json) self end protected def is_root? _parent == self && !!defined?(@doc) end def linked? is_root? || !!_parent.doc.dig(*[_key].flatten) end private def dig_set(obj, keys, value) if keys.length == 1 obj[keys.first] = value else dig_set(obj[keys.first], keys.slice(1..-1), value) end end end end end end