1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
|
# frozen_string_literal: true
require_relative "../converter"
require_relative "../null_converter"
module Necromancer
# Container for Range converter classes
module RangeConverters
SINGLE_DIGIT_MATCHER = /^(?<digit>-?\d+(\.\d+)?)$/.freeze
DIGIT_MATCHER = /^(?<open>-?\d+(\.\d+)?)
\s*(?<sep>(\.\s*){2,3}|-|,)\s*
(?<close>-?\d+(\.\d+)?)$
/x.freeze
LETTER_MATCHER = /^(?<open>\w)
\s*(?<sep>(\.\s*){2,3}|-|,)\s*
(?<close>\w)$
/x.freeze
# An object that converts a String to a Range
class StringToRangeConverter < Converter
# Convert value to Range type with possible ranges
#
# @param [Object] value
#
# @example
# converter.call("0,9") # => (0..9)
#
# @example
# converter.call("0-9") # => (0..9)
#
# @api public
def call(value, strict: config.strict)
if match = value.match(SINGLE_DIGIT_MATCHER)
digit = cast_to_num(match[:digit])
::Range.new(digit, digit)
elsif match = value.match(DIGIT_MATCHER)
open = cast_to_num(match[:open])
close = cast_to_num(match[:close])
::Range.new(open, close, match[:sep].gsub(/\s*/, "") == "...")
elsif match = value.match(LETTER_MATCHER)
::Range.new(match[:open], match[:close],
match[:sep].gsub(/\s*/, "") == "...")
else
strict ? raise_conversion_type(value) : value
end
end
# Convert range end to numeric value
#
# @api private
def cast_to_num(str)
Integer(str)
rescue ArgumentError
Float(str)
rescue ArgumentError
nil
end
end
def self.load(conversions)
[
StringToRangeConverter.new(:string, :range),
NullConverter.new(:range, :range)
].each do |converter|
conversions.register converter
end
end
end # RangeConverters
end # Necromancer
|