lib/axiom/algebra/rename/aliases.rb
# encoding: utf-8
module Axiom
module Algebra
class Rename
# Aliases that map old attributes to new renamed attributes
class Aliases
extend Aliasable
include Enumerable
include Equalizer.new(:to_hash)
inheritable_alias(:| => :union)
# Instantiate new set of Aliases
#
# @example
# aliases = Aliases.new(aliases)
#
# @param [Hash{Attribute => Attribute}] aliases
#
# @return [Aliases]
#
# @api public
def self.new(aliases)
assert_unique_aliases(aliases)
super
end
# Asset the aliases are unique
#
# @param [Hash{Attribute => Attribute}] aliases
#
# @return [undefined]
#
# @raise [DuplicateAliasError]
# raised when the aliases are duplicates
#
# @api private
def self.assert_unique_aliases(aliases)
if aliases.values.uniq!
fail DuplicateAliasError, 'the aliases must be unique'
end
end
private_class_method :assert_unique_aliases
# Initialize rename aliases
#
# @param [Hash] aliases
# the old and new attributes
#
# @return [undefined]
#
# @api public
def initialize(aliases)
@aliases = self.class.freezer.call(aliases)
end
# Lookup the new attribute given the old attribute
#
# @example
# new_attribute = aliases[old_attribute]
#
# @param [Attribute] attribute
# the old attribute
#
# @return [Attribute]
#
# @api public
def [](attribute)
@aliases.fetch(attribute, attribute)
end
# Union the aliases with another set of aliases
#
# @example
# new_aliases = aliases.union(other)
#
# @param [Aliases] other
# the aliases to union with
#
# @return [Aliases]
#
# @api public
def union(other)
other_aliases = other.to_hash.dup
inverted = other_aliases.invert
# Remove aliases that cancel out, and preserve different aliases
each do |old_attribute, new_attribute|
old_attribute = inverted.fetch(old_attribute, old_attribute)
other_aliases.delete(old_attribute)
next if old_attribute.eql?(new_attribute)
other_aliases[old_attribute] = new_attribute
end
self.class.new(other_aliases)
end
# Iterate over each old and new attribute
#
# @example
# aliases = Aliases.new(old => new)
# aliases.each { |old, new| ... }
#
# @yield [old, new]
#
# @yieldparam [Attribute] old_attribute
# the old attribute
# @yieldparam [Attribute] new_attribute
# the new attribute
#
# @return [self]
#
# @api public
def each
return to_enum unless block_given?
@aliases.each { |old_attribute, new_attribute| yield old_attribute, new_attribute }
self
end
# Test if there are no aliases
#
# @example
# aliases.empty? # => true or false
#
# @return [Boolean]
#
# @api public
def empty?
@aliases.empty?
end
# Return the inverse aliases
#
# @example
# inverse = aliases.inverse
#
# @return [Aliases]
#
# @api public
def inverse
self.class.new(@aliases.invert)
.memoize(inverse: self)
end
# Compare the aliases with other aliases for equivalency
#
# @example
# aliases == other # => true or false
#
# @param [Aliases] other
# the other aliases to compare with
#
# @return [Boolean]
#
# @api public
def ==(other)
cmp?(__method__, other)
end
# Convert the aliases to a Hash
#
# @example
# hash = aliases.to_hash
#
# @return [Hash]
#
# @api public
def to_hash
@aliases
end
# Coerce a Hash of old and new attributes into Aliases
#
# @param [Header] attributes
# the header containing the old attributes
# @param [Aliases, #map] aliases
# the aliases to coerce
#
# @return [Aliases]
#
# @api private
def self.coerce(attributes, aliases)
return aliases if aliases.kind_of?(Aliases)
header = Relation::Header.coerce(attributes)
renames = aliases.map do |old_attr, new_attr|
coerce_alias_pair(header, old_attr, new_attr)
end
new(Hash[renames])
end
# Coerce old and new attributes into Attribute objects
#
# @param [Header] attributes
# the header containing the old attributes
# @param [Symbol, Attribute] old_attr
# the old attribute name or Attribute object
# @param [Symbol, Attribute] new_attr
# the new attribute name or Attribute object
#
# @return [Array(Attribute, Attribute)]
#
# @api private
def self.coerce_alias_pair(attributes, old_attr, new_attr)
old_attr = attributes[old_attr]
new_attr = old_attr.rename(new_attr) if new_attr.kind_of?(Symbol)
[old_attr, new_attr]
end
private_class_method :coerce_alias_pair
memoize :inverse
end # class Aliases
end # class Rename
end # module Algebra
end # module Axiom