lib/fortnox/api/models/base.rb in fortnox-api-0.1.0 vs lib/fortnox/api/models/base.rb in fortnox-api-0.2.0

- old
+ new

@@ -1,45 +1,50 @@ -require "virtus" +require "fortnox/api/types" require "ice_nine" module Fortnox module API module Model - class Base + class Base < Fortnox::API::Types::Model - # extend Forwardable - include Virtus.model + # TODO(jonas): Restructure this class a bit, it is not very readable. - attr_accessor :unsaved + attr_accessor :unsaved, :parent def self.attribute( name, *args ) - define_method( "#{name}?" ) do + define_method( "#{ name }?" ) do !send( name ).nil? end super end - def initialize( hash = {} ) - unsaved = hash.delete( :unsaved ){ true } - @saved = !unsaved - @new = hash.delete( :new ){ true } + def self.new( hash ) + begin + obj = preserve_meta_properties( hash ) do + super( hash ) + end + rescue Dry::Struct::Error => e + raise Fortnox::API::AttributeError.new e + end - # .each{|a| p a.name} + IceNine.deep_freeze( obj ) + end - super hash - IceNine.deep_freeze( self ) + def unique_id + send( self.class::UNIQUE_ID ) end def update( hash ) old_attributes = self.to_hash new_attributes = old_attributes.merge( hash ) return self if new_attributes == old_attributes new_hash = new_attributes.delete_if{ |_, value| value.nil? } new_hash[:new] = @new + new_hash[:parent] = self self.class.new( new_hash ) end # Generic comparison, by value, use .eql? or .equal? for object identity. def ==( other ) @@ -51,9 +56,47 @@ @new end def saved? @saved + end + + def parent? + not @parent.nil? + end + + def parent + @parent || self.class.new( self.class::STUB.dup ) + end + + def to_hash( recursive = false ) + return super() if recursive + + self.class.schema.keys.each_with_object({}) do |key, result| + result[key] = self[key] + end + end + + private_class_method + + # dry-types filter anything that isn't specified as an attribute on the + # class that is being instansiated. This wrapper preserves the meta + # properties we need to track object state during that initilisation and + # sets them on the object after dry-types is done with it. + def self.preserve_meta_properties( hash ) + is_unsaved = hash.delete( :unsaved ){ true } + is_new = hash.delete( :new ){ true } + parent = hash.delete( :parent ){ nil } + + obj = yield + + # TODO: remove new, unsaved, saved + obj.instance_variable_set( :@unsaved, is_unsaved ) + obj.instance_variable_set( :@saved, !is_unsaved ) + obj.instance_variable_set( :@new, is_new ) + obj.instance_variable_set( :@parent, parent ) + + return obj end private def private_attributes