Created
September 1, 2022 19:44
-
-
Save estum/a5590fec0b21e19769a3a3d0c161d691 to your computer and use it in GitHub Desktop.
Dry::Core::Comparator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require 'dry/core/equalizer' | |
module Dry | |
# @api public | |
def self.Comparator(*keys, **options) | |
if keys.size == 0 && options.size > 0 | |
keys << options.dup | |
options.replace(keys[0].extract!(:immutable, :inspect, :compare)) | |
end | |
mapping = keys.extract_options! | |
keys.concat mapping.keys if mapping.present? | |
case keys.size | |
when 0 | |
raise ArgumentError, 'missing key to compare' | |
when 1 | |
key = keys[0] | |
Dry::Core::MonoComparator.new(key, **options, compare: mapping[key]) | |
else | |
Dry::Core::PolyComparator.new(*keys, **options, compare: mapping) | |
end | |
end | |
module Core | |
class Comparator < Equalizer | |
DEFAULT_MAPPING = begin | |
hsh = Hash.new | |
hsh.default = :<=> | |
hsh.freeze | |
end | |
module Methods | |
include Equalizer::Methods | |
include Comparable | |
def <=>(other) | |
other.is_a?(self.class) && cmp(other) | |
end | |
end | |
def self.inherited(subklass) | |
unless subklass.const_defined?(:Methods) | |
subklass.const_set(:Methods, const_get(:Methods).dup) | |
end | |
super if defined?(super) | |
end | |
private | |
# @api private | |
def included(descendant) | |
super | |
descendant.include self.class.const_get(:Methods) | |
end | |
# @see Dry::Core::Equalizer#define_methods | |
# @param compare [Hash{ key => Symbol }] | |
# maps comparation methods to keys | |
# @api private | |
def define_methods(inspect: false, immutable: false, compare: nil) | |
super(inspect: inspect, immutable: immutable) | |
define_cmp_op_method(compare: compare) | |
return | |
end | |
end | |
private_constant :Comparator | |
class MonoComparator < Comparator | |
def define_cmp_op_method(compare: nil) | |
compare ||= DEFAULT_MAPPING.default | |
fn = proc(&@keys[0]) | |
define_method(:cmp) do |other| | |
fn[self].public_send(compare, fn[other]) | |
end | |
private :cmp | |
end | |
end | |
class PolyComparator < Comparator | |
def define_cmp_op_method(compare: nil) | |
compare = | |
if compare.is_a?(Hash) && compare.size > 0 | |
compare.with_defaults(DEFAULT_MAPPING) | |
else | |
DEFAULT_MAPPING | |
end | |
keys = @keys | |
define_method(:cmp) do |other| | |
keys.inject(0) do |a, key| | |
break nil if a.nil? | |
a.nonzero? || __send__(key).public_send(compare[key], other.__send__(key)) | |
end | |
end | |
private :cmp | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment