lib/tty/prompt/converters.rb in tty-prompt-0.21.0 vs lib/tty/prompt/converters.rb in tty-prompt-0.22.0
- old
+ new
@@ -1,75 +1,182 @@
# frozen_string_literal: true
-require 'pathname'
-require 'necromancer'
+require_relative "const"
+require_relative "converter_dsl"
-require_relative 'converter_dsl'
-
module TTY
class Prompt
module Converters
extend ConverterDSL
- # Delegate Necromancer errors
- #
- # @api private
- def self.on_error
- if block_given?
- yield
- else
- raise ArgumentError, 'You need to provide a block argument.'
+ TRUE_VALUES = /^(t(rue)?|y(es)?|on|1)$/i.freeze
+ FALSE_VALUES = /^(f(alse)?|n(o)?|off|0)$/i.freeze
+
+ 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
+
+ converter(:boolean, :bool) do |input|
+ case input.to_s
+ when TRUE_VALUES then true
+ when FALSE_VALUES then false
+ else Const::Undefined
end
- rescue Necromancer::ConversionTypeError => e
- raise ConversionError, e.message
end
- converter(:bool) do |input|
- on_error { Necromancer.convert(input).to(:boolean, strict: true) }
- end
-
- converter(:string) do |input|
+ converter(:string, :str) do |input|
String(input).chomp
end
- converter(:symbol) do |input|
+ converter(:symbol, :sym) do |input|
input.to_sym
end
+ converter(:char) do |input|
+ String(input).chars.to_a[0]
+ end
+
converter(:date) do |input|
- on_error { Necromancer.convert(input).to(:date, strict: true) }
+ begin
+ require "date" unless defined?(::Date)
+ ::Date.parse(input)
+ rescue ArgumentError
+ Const::Undefined
+ end
end
converter(:datetime) do |input|
- on_error { Necromancer.convert(input).to(:datetime, strict: true) }
+ begin
+ require "date" unless defined?(::Date)
+ ::DateTime.parse(input.to_s)
+ rescue ArgumentError
+ Const::Undefined
+ end
end
- converter(:int) do |input|
- on_error { Necromancer.convert(input).to(:integer, strict: true) }
+ converter(:time) do |input|
+ begin
+ require "time"
+ ::Time.parse(input.to_s)
+ rescue ArgumentError
+ Const::Undefined
+ end
end
+ converter(:integer, :int) do |input|
+ begin
+ Integer(input)
+ rescue ArgumentError
+ Const::Undefined
+ end
+ end
+
converter(:float) do |input|
- on_error { Necromancer.convert(input).to(:float, strict: true) }
+ begin
+ Float(input)
+ rescue TypeError, ArgumentError
+ Const::Undefined
+ end
end
+ # Convert string number to integer or float
+ #
+ # @return [Integer,Float,Const::Undefined]
+ #
+ # @api private
+ def cast_to_num(num)
+ ([convert(:int, num), convert(:float, num)] - [Const::Undefined]).first ||
+ Const::Undefined
+ end
+ module_function :cast_to_num
+
converter(:range) do |input|
- on_error { Necromancer.convert(input).to(:range, strict: true) }
+ if input.is_a?(::Range)
+ input
+ elsif match = input.to_s.match(SINGLE_DIGIT_MATCHER)
+ digit = cast_to_num(match[:digit])
+ ::Range.new(digit, digit)
+ elsif match = input.to_s.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 = input.to_s.match(LETTER_MATCHER)
+ ::Range.new(match[:open], match[:close],
+ match[:sep].gsub(/\s*/, "") == "...")
+ else Const::Undefined
+ end
end
converter(:regexp) do |input|
Regexp.new(input)
end
- converter(:file) do |input|
- ::File.open(::File.join(Dir.pwd, input))
+ converter(:filepath, :file) do |input|
+ ::File.expand_path(input)
end
- converter(:path) do |input|
- Pathname.new(::File.join(Dir.pwd, input))
+ converter(:pathname, :path) do |input|
+ require "pathname" unless defined?(::Pathname)
+ ::Pathname.new(input)
end
- converter(:char) do |input|
- String(input).chars.to_a[0]
+ converter(:uri) do |input|
+ require "uri" unless defined?(::URI)
+ ::URI.parse(input)
+ end
+
+ converter(:list, :array) do |val|
+ (val.respond_to?(:to_a) ? val : val.split(/(?<!\\),/))
+ .map { |v| v.strip.gsub(/\\,/, ",") }
+ .reject(&:empty?)
+ end
+
+ converter(:hash, :map) do |val|
+ values = val.respond_to?(:to_a) ? val : val.split(/[& ]/)
+ values.each_with_object({}) do |pair, pairs|
+ key, value = pair.split(/[=:]/, 2)
+ if (current = pairs[key.to_sym])
+ pairs[key.to_sym] = Array(current) << value
+ else
+ pairs[key.to_sym] = value
+ end
+ pairs
+ end
+ end
+
+ converter_registry.keys.each do |type|
+ next if type =~ /list|array|map|hash/
+
+ [:"#{type}_list", :"#{type}_array", :"#{type}s"].each do |new_type|
+ converter(new_type) do |val|
+ converter_registry[:array].(val).map do |obj|
+ converter_registry[type].(obj)
+ end
+ end
+ end
+
+ [:"#{type}_map", :"#{type}_hash"].each do |new_type|
+ converter(new_type) do |val|
+ converter_registry[:hash].(val).each_with_object({}) do |(k, v), h|
+ h[k] = converter_registry[type].(v)
+ end
+ end
+ end
+
+ [:"string_#{type}_map", :"str_#{type}_map",
+ :"string_#{type}_hash", :"str_#{type}_hash"].each do |new_type|
+ converter(new_type) do |val|
+ converter_registry[:hash].(val).each_with_object({}) do |(k, v), h|
+ h[converter_registry[:string].(k)] = converter_registry[type].(v)
+ end
+ end
+ end
end
end # Converters
end # Prompt
end # TTY