# frozen_string_literal: true require 'luna_park/extensions/comparable_debug' require 'luna_park/errors' module LunaPark module Extensions # @example # class Money # include LunaPark::Extensions::Comparable # # attr_reader :amount, :currency, :meta # # comparable_attributes :amount, :currency # # def initialize(amount, currency, meta = nil) # @amount = amount # @currency = currency # @meta = meta # end # end # # Money.new(1, 'USD') == Money.new(2, 'USD') # => false # Money.new(1, 'USD') == Money.new(1, 'USD') # => true # Money.new(1, 'USD', 'meta') == Money.new(1, 'USD', 'feta') # => true module Comparable def self.included(base) base.extend ClassMethods base.include InstanceMethods end module ClassMethods ## # Enable debug mode (just include debug methods) def enable_debug include ComparableDebug unless include?(ComparableDebug) self end alias debug enable_debug ## # Describe methods list that will be used for comparsion via `#==` method def comparable_attributes(*names) raise 'No attributes given' if names.compact.empty? @comparable_attributes_list ||= [] @comparable_attributes_list |= names end ## # List of methods that will be used for comparsion via `#==` method def comparable_attributes_list return @comparable_attributes_list if @comparable_attributes_list raise Errors::NotConfigured, "You must set at least one comparable attribute using #{self}.comparable_attributes(*names)" end private def inherited(child) super child.instance_variable_set(:@comparable_attributes_list, @comparable_attributes_list&.dup) end end module InstanceMethods ## # Compare this object with other using methids, described with `::comparable_attributes` method def eql?(other) return false unless other.is_a?(self.class) self.class.comparable_attributes_list.all? { |attr| send(attr) == other.send(attr) } end alias == eql? ## # Enable debug mode (just include debug methods) def enable_debug self.class.enable_debug self end alias debug enable_debug end end end end