Freshly/spicerack

View on GitHub
lib/spicerack/hash_model.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
# frozen_string_literal: true

require "active_model"

module Spicerack
  # Provides on-demand synchronization between a `data' object and ActiveModel::Attributes
  module HashModel
    extend ActiveSupport::Concern

    included do
      include ActiveModel::Attributes

      class_attribute :_fields, instance_writer: false, default: []

      attr_accessor :data
    end

    class_methods do
      def inherited(base)
        base._fields = _fields.dup
        super
      end

      private

      def field(name, type = Type::Value.new, **options)
        name = name.to_s
        _fields << name
        attribute(name, type, **options)
        define_field_methods(name)
      end

      def define_field_methods(name)
        define_method("#{name}?".to_sym) { data[name].present? }
        define_method("#{name}=".to_sym) do |value|  
          data[name] = value
          synchronize_attribute_from_datastore(name)
        end
        define_method(name) do
          synchronize_attribute_from_datastore(name)
          attribute(name)
        end
      end
    end
      
    private

    # The data object can be anything from a normal hash to a hash-like object backed by redis.
    def synchronize_attribute_from_datastore(name)
      value_from_datastore = data[name]
      value_from_attribute = attribute(name)
      return if value_from_datastore == value_from_attribute
      
      value = value_from_datastore || value_from_attribute
      
      # Otherwise, the dataset is value takes priority and is written as the attribute value
      # ActiveModel changed the interface to this method between Rails 6.0 and 6.1
      # This method is a patch which allows this class to work with either version
      # Once support for pre rails 6.0 is sunset this should likely be removed
      if respond_to?(:_write_attribute, true)
        _write_attribute(name, value)
      else
        write_attribute(name, value)
      end
    end
  end
end