joshuabowers/opium

View on GitHub
lib/opium/model/persistable.rb

Summary

Maintainability
A
2 hrs
Test Coverage
module Opium
  module Model
    module Persistable
      extend ActiveSupport::Concern
      
      included do
      end
      
      class InvalidError < StandardError
      end
      
      module ClassMethods
        def create( attributes = {} )
          new( attributes ).tap {|model| model.save}
        end
        
        def create!( attributes = {} )
          new( attributes ).tap {|model| model.save!}
        end
        
        def destroy_all( query = nil )
          
        end
        
        def delete_all( query = nil )
          
        end
        
        def added_headers
          @added_headers ||= {}.with_indifferent_access
        end
        
        def add_header_to( methods, header, value, options = {} )
          Array( methods ).each do |method|
            added_headers[method] = { header: header, value: value, options: options }
            
            added_headers[method][:options][:only] = Array( options[:only] )
            added_headers[method][:options][:except] = Array( options[:except] )
          end
          nil
        end
        
        def get_header_for( method, context, owner = nil )
          return {} unless added_headers[method]
          
          eval_only = !added_headers[method][:options][:only].empty?
          eval_except = !added_headers[method][:options][:except].empty?
          
          within_only = added_headers[method][:options][:only].include?( context )
          within_except = added_headers[method][:options][:except].include?( context )
          
          value = added_headers[method][:value]
          value = value.call( owner ) if owner && value.respond_to?( :call )
          
          if value && ( ( !eval_only && !eval_except ) || ( eval_only && within_only ) || ( eval_except && !within_except ) )
            { headers: { added_headers[method][:header] => value } } 
          else
            {}
          end
        end
      end
      
      def save( options = {} )
        create_or_update( options )
      rescue => e
        errors.add( :base, e.to_s )
        false
      end
      
      def save!
        create_or_update( validates: true ) || raise( InvalidError, 'failed to save, as model is invalid' )
      end
      
      def update_attributes( attributes = {} )
        self.attributes = attributes
        save
      end
      
      def update_attributes!( attributes = {} )
        self.attributes = attributes
        save!
      end
      
      alias_method :update, :update_attributes
      alias_method :update!, :update_attributes!
      
      def touch
        save( validates: false )
      end
      
      def delete( options = {} )
        self.tap do
          attributes_or_headers( :delete, :delete, options ) do |headers|
            self.class.http_delete id, headers unless new_record?
          end
          self.freeze
        end
      end
      alias_method :destroy, :delete
      
      def new_record?
        self.id.nil?
      end
      
      def persisted?
        !new_record? && !self.changed?
      end
      
      def pointer
        @pointer ||= Pointer.new( model_name: model_name, id: id ) unless new_record?
      end
      
      def to_parse
        pointer.to_parse
      end
            
      private
      
      def create_or_update( options )
        if options[:validates] == false || valid?
          if new_record?
            _create( options )
          else
            _update( options )
          end
        end.present?
      end
      
      def _create( options = {} )
        self.attributes = attributes_or_headers( :post, :create, options ) do |headers|
          self.class.http_post self.attributes_to_parse( not_readonly: true ), headers
        end
      end
      
      def _update( options = {} )
        self.attributes = attributes_or_headers( :put, :update, options ) do |headers|
          self.class.http_put id, self.attributes_to_parse( only: changes.keys ), headers
        end
      end
      
      def sent_headers
        @_sent_headers || {}
      end
      
      def attributes_or_headers( method, action, options, &block )
        result = block.call( options.deep_merge self.class.get_header_for( method, action, self ) )
        if options[:sent_headers]
          @_sent_headers = result
          {}
        else
          result
        end 
      end
    end
  end
end