-
# frozen_string_literal: true
-
-
1
module Wholeable
-
# Provides core equality behavior.
-
1
class Builder < Module
-
1
def self.add_aliases descendant
-
84
descendant.alias_method :deconstruct, :to_a
-
84
descendant.alias_method :deconstruct_keys, :to_h
-
84
descendant.alias_method :to_s, :inspect
-
end
-
-
1
def initialize *keys, kind: :immutable
-
84
super()
-
84
@keys = keys.uniq
-
84
@kind = kind
-
84
@members = []
-
84
setup
-
end
-
-
1
def included descendant
-
84
super
-
84
coalesce_members descendant
-
-
84
descendant.class_eval <<-METHODS, __FILE__, __LINE__ + 1
-
1
def self.[](...) = new(...)
-
-
2
def self.new(...) = #{mutable?} ? super.dup : super.freeze
-
-
1
def self.members = #{members}
-
-
1
then: 7
else: 77
#{mutable? ? :attr_accessor : :attr_reader} #{keys.map(&:inspect).join ", "}
-
METHODS
-
-
84
self.class.add_aliases descendant
-
end
-
-
1
private
-
-
1
attr_reader :keys, :kind, :members
-
-
1
def setup
-
840
private_methods.grep(/\A(define)_/).sort.each { |method| __send__ method }
-
84
freeze
-
end
-
-
1
def coalesce_members descendant
-
84
then: 15
else: 69
members.replace(descendant.respond_to?(:members) ? (descendant.members + keys).uniq : keys)
-
end
-
-
1
def mutable? = kind == :mutable
-
-
1
def define_diff
-
84
define_method :diff do |other|
-
6
then: 4
if other.is_a? self.class
-
12
to_h.merge!(other.to_h) { |_, one, two| [one, two].uniq }
-
8
.select { |_, diff| diff.size == 2 }
-
else: 2
else
-
6
to_h.each.with_object({}) { |(key, value), diff| diff[key] = [value, nil] }
-
end
-
end
-
end
-
-
1
def define_eql
-
90
define_method(:eql?) { |other| instance_of?(other.class) && hash == other.hash }
-
end
-
-
1
def define_equality
-
90
define_method(:==) { |other| other.is_a?(self.class) && hash == other.hash }
-
end
-
-
1
def define_hash
-
84
define_method :hash do
-
78
members.map { |key| public_send key }
-
.prepend(self.class)
-
.hash
-
end
-
end
-
-
1
def define_inspect
-
84
define_method :inspect do
-
5
klass = self.class
-
5
name = klass.name || klass.inspect
-
-
15
members.map { |key| "@#{key}=#{public_send(key).inspect}" }
-
.join(", ")
-
5
.then { |pairs| "#<#{name} #{pairs}>" }
-
end
-
end
-
-
61
def define_members(local_members = members) = define_method(:members) { local_members }
-
-
1
def define_to_a
-
84
define_method :to_a do
-
12
members.reduce([]) { |collection, key| collection.append public_send(key) }
-
end
-
end
-
-
1
def define_to_h
-
84
define_method :to_h do
-
60
members.each.with_object({}) { |key, attributes| attributes[key] = public_send key }
-
end
-
end
-
-
1
def define_with
-
90
define_method(:with) { |**attributes| self.class.new(**to_h.merge!(attributes)) }
-
end
-
end
-
end