Class: Dux::Comparable
- Inherits:
-
Module
- Object
- Module
- Dux::Comparable
- Defined in:
- lib/dux/comparable.rb
Overview
Simplify the creation of comparable objects by specifying
a list of attributes (with optional ordering) that
determines how they should be compared, without having
to define a spaceship (<=>
) operator in the given class.
Defined Under Namespace
Classes: Attribute
Constant Summary collapse
- ORDERS =
Valid orders for sorting
Dux.enum(:asc, :desc, aliases: { ascending: :asc, descending: :desc })
- ATTRIBUTE_TUPLE =
Checks if an attribute argument is a valid tuple
Dux.yard('(Symbol, Symbol)')
Instance Method Summary collapse
-
#build_spaceship_method ⇒ String
private
Generates the spaceship method body.
-
#initialize(*attributes, sort_order: :asc, type_guard: true, **options) ⇒ Comparable
constructor
A new instance of Comparable.
-
#inspect ⇒ String
Display the attributes used to compare for clarity in class ancestor listings.
-
#join_attributes ⇒ String
private
Join the attributes to be checked in #build_spaceship_method.
-
#many? ⇒ Boolean
Determine if we are operating on many attributes.
- #parse_attributes(attributes) ⇒ <Dux::Enum::Attribute> private
-
#same_type_guard? ⇒ Boolean
Determine if we have a type guard that requires another of the same class.
-
#single? ⇒ Boolean
Determine if we are operating on a single attribute.
- #spaceship_method ⇒ String private
-
#specific_type_guard? ⇒ Boolean
Determine if.
-
#type_guard? ⇒ Boolean
Determine if we have any kind of type guard.
-
#type_guard_value ⇒ String
private
Provides the value for type guards used by #build_spaceship_method.
- #validate_order(sort_order) ⇒ Symbol private
- #validate_type_guard(type_guard) ⇒ Boolean, ... private
Constructor Details
#initialize(*attributes, sort_order: :asc, type_guard: true, **options) ⇒ Comparable
Returns a new instance of Comparable.
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/dux/comparable.rb', line 20 def initialize(*attributes, sort_order: :asc, type_guard: true, **) @default_sort_order = validate_order sort_order @type_guard = validate_type_guard type_guard @attributes = parse_attributes(attributes) if @attributes.one? @default_sort_order = @attributes.first.order end include ::Comparable class_eval spaceship_method, __FILE__, __LINE__ + 1 end |
Instance Method Details
#build_spaceship_method ⇒ String (private)
Generates the spaceship method body.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/dux/comparable.rb', line 98 def build_spaceship_method ''.tap do |body| body << <<-RUBY def <=>(other) RUBY if type_guard? body << <<-RUBY unless other.kind_of?(#{type_guard_value}) raise TypeError, "\#{other.inspect} must be kind of \#{#{type_guard_value}}" end RUBY end body << <<-RUBY #{join_attributes} RUBY body << <<-RUBY end RUBY end end |
#inspect ⇒ String
Display the attributes used to compare for clarity in class ancestor listings.
40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/dux/comparable.rb', line 40 def inspect attribute_list = @attributes.map do |attribute| attribute.to_inspect(many: many?, default: @default_sort_order) end.join(', ') if many? attribute_list = "[#{attribute_list}]" attribute_list << ", default_order: #{@default_sort_order.to_s.upcase}" end "Dux::Comparable(#{attribute_list})" end |
#join_attributes ⇒ String (private)
Join the attributes to be checked in #build_spaceship_method
127 128 129 130 131 |
# File 'lib/dux/comparable.rb', line 127 def join_attributes @attributes.map do |attribute| attribute.to_comparison(wrap: @attributes.length > 1) end.join('.nonzero? || ') end |
#many? ⇒ Boolean
Determine if we are operating on many attributes
Boolean complement of #single?.
57 58 59 |
# File 'lib/dux/comparable.rb', line 57 def many? @attributes.length > 1 end |
#parse_attributes(attributes) ⇒ <Dux::Enum::Attribute> (private)
147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/dux/comparable.rb', line 147 def parse_attributes(attributes) raise ArgumentError, "Must provide at least one attribute" if attributes.empty? attributes.map do |attribute| case attribute when Symbol, String Attribute.new(attribute, @default_sort_order) when ATTRIBUTE_TUPLE Attribute.new(attribute[0], validate_order(attribute[1])) else raise ArgumentError, "Don't know what to do with #{attribute.inspect}" end end.freeze end |
#same_type_guard? ⇒ Boolean
Determine if we have a type guard that requires another of the same class.
70 71 72 |
# File 'lib/dux/comparable.rb', line 70 def same_type_guard? @type_guard == true end |
#single? ⇒ Boolean
Determine if we are operating on a single attribute
Boolean complement of #many?.
64 65 66 |
# File 'lib/dux/comparable.rb', line 64 def single? @attributes.one? end |
#spaceship_method ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
89 90 91 |
# File 'lib/dux/comparable.rb', line 89 def spaceship_method @spaceship_method ||= build_spaceship_method end |
#specific_type_guard? ⇒ Boolean
Determine if
75 76 77 |
# File 'lib/dux/comparable.rb', line 75 def specific_type_guard? @type_guard.kind_of?(String) && Dux.presentish?(@type_guard) end |
#type_guard? ⇒ Boolean
Determine if we have any kind of type guard.
83 84 85 |
# File 'lib/dux/comparable.rb', line 83 def type_guard? same_type_guard? || specific_type_guard? end |
#type_guard_value ⇒ String (private)
Provides the value for type guards used by #build_spaceship_method
135 136 137 138 139 140 141 142 143 |
# File 'lib/dux/comparable.rb', line 135 def type_guard_value raise 'Cannot get value for non-type guard' unless type_guard? if same_type_guard? 'self.class' elsif specific_type_guard? @type_guard end end |
#validate_order(sort_order) ⇒ Symbol (private)
165 166 167 168 169 |
# File 'lib/dux/comparable.rb', line 165 def validate_order(sort_order) ORDERS[sort_order] rescue Dux::Enum::NotFound => e raise ArgumentError, "invalid sort order: #{sort_order.inspect}" end |
#validate_type_guard(type_guard) ⇒ Boolean, ... (private)
174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/dux/comparable.rb', line 174 def validate_type_guard(type_guard) case type_guard when true, false, nil then type_guard when Class, Module type_guard.name when String, Symbol, Dux::IndifferentString type_guard.to_s else raise TypeError, "Don't know what to do with type guard: #{type_guard.inspect}" end end |