tty-option 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,8 +23,10 @@ module TTY
23
23
  end
24
24
 
25
25
  convert :date do |val|
26
+ require "date" unless defined?(::Date)
27
+ next val if val.is_a?(::Date)
28
+
26
29
  begin
27
- require "date" unless defined?(::Date)
28
30
  ::Date.parse(val)
29
31
  rescue ArgumentError, TypeError
30
32
  Const::Undefined
@@ -48,13 +50,19 @@ module TTY
48
50
  end
49
51
 
50
52
  convert :pathname, :path do |val|
51
- require "pathname"
52
- ::Pathname.new(val.to_s)
53
+ require "pathname" unless defined?(::Pathname)
54
+ next val if val.is_a?(::Pathname)
55
+
56
+ begin
57
+ ::Pathname.new(val)
58
+ rescue TypeError
59
+ Const::Undefined
60
+ end
53
61
  end
54
62
 
55
63
  convert :regexp do |val|
56
64
  begin
57
- Regexp.new(val.to_s)
65
+ Regexp.new(val)
58
66
  rescue TypeError, RegexpError
59
67
  Const::Undefined
60
68
  end
@@ -63,14 +71,16 @@ module TTY
63
71
  convert :sym, :symbol do |val|
64
72
  begin
65
73
  String(val).to_sym
66
- rescue ArgumentError
74
+ rescue ArgumentError, TypeError
67
75
  Const::Undefined
68
76
  end
69
77
  end
70
78
 
71
79
  convert :uri do |val|
80
+ require "uri" unless defined?(::URI)
81
+ next val if val.is_a?(::URI)
82
+
72
83
  begin
73
- require "uri"
74
84
  ::URI.parse(val)
75
85
  rescue ::URI::InvalidURIError
76
86
  Const::Undefined
@@ -79,24 +89,24 @@ module TTY
79
89
 
80
90
  convert :list, :array do |val|
81
91
  next Const::Undefined if val.nil?
92
+ next Array(val) unless val.respond_to?(:split)
82
93
 
83
- (val.respond_to?(:map) ? val : val.to_s.split(/(?<!\\),/))
84
- .map { |v| v.strip.gsub(/\\,/, ",") }
85
- .reject(&:empty?)
94
+ val.split(/(?<!\\),/)
95
+ .map { |v| v.strip.gsub(/\\,/, ",") }
96
+ .reject(&:empty?)
86
97
  end
87
98
 
88
99
  convert :map, :hash do |val|
89
100
  next Const::Undefined if val.nil?
101
+ next val if val.is_a?(Hash)
90
102
 
91
- values = val.respond_to?(:each) ? val : val.to_s.split(/[& ]/)
103
+ values = val.respond_to?(:split) ? val.split(/[& ]/) : Array(val)
92
104
  values.each_with_object({}) do |pair, pairs|
93
- key, value = pair.split(/[=:]/, 2)
94
- if (current = pairs[key.to_sym])
95
- pairs[key.to_sym] = Array(current) << value
96
- else
97
- pairs[key.to_sym] = value
98
- end
99
- pairs
105
+ is_string = pair.respond_to?(:split)
106
+ key, value = is_string ? pair.split(/[=:]/, 2) : pair
107
+ new_key = is_string ? key.to_sym : key
108
+ current = pairs[new_key]
109
+ pairs[new_key] = current ? Array(current) << value : value
100
110
  end
101
111
  end
102
112
 
@@ -2,46 +2,70 @@
2
2
 
3
3
  module TTY
4
4
  module Option
5
+ # Responsible for deep copying an object
6
+ #
7
+ # @api private
5
8
  module DeepDup
6
9
  NONDUPLICATABLE = [
7
- Symbol, TrueClass, FalseClass, NilClass, Numeric, Method
10
+ Symbol, TrueClass, FalseClass, NilClass, Numeric, Method, UnboundMethod
8
11
  ].freeze
9
12
 
10
- # Duplicate an object making a deep copy
13
+ # Deep copy an object
14
+ #
15
+ # @example
16
+ # DeepDeup.deep_dup({foo: {bar: [1, 2]}})
11
17
  #
12
18
  # @param [Object] object
19
+ # the object to deep copy
20
+ # @param [Hash] cache
21
+ # the cache of copied objects
22
+ #
23
+ # @return [Object]
13
24
  #
14
25
  # @api public
15
- def self.deep_dup(object)
16
- case object
17
- when *NONDUPLICATABLE then object
18
- when Hash then deep_dup_hash(object)
19
- when Array then deep_dup_array(object)
20
- else object.dup
21
- end
26
+ def self.deep_dup(object, cache = {})
27
+ cache[object.object_id] ||=
28
+ case object
29
+ when *NONDUPLICATABLE then object
30
+ when Array then deep_dup_array(object, cache)
31
+ when Hash then deep_dup_hash(object, cache)
32
+ else object.dup
33
+ end
22
34
  end
23
35
 
24
- # A deep copy of hash
36
+ # Deep copy an array
25
37
  #
26
- # @param [Hash] object
38
+ # @param [Array] object
39
+ # the array object to deep copy
40
+ # @param [Hash] cache
41
+ # the cache of copied objects
42
+ #
43
+ # @return [Array]
27
44
  #
28
45
  # @api private
29
- def self.deep_dup_hash(object)
30
- object.each_with_object({}) do |(key, val), new_hash|
31
- new_hash[deep_dup(key)] = deep_dup(val)
46
+ def self.deep_dup_array(object, cache)
47
+ object.each_with_object(cache[object.object_id] = []) do |val, array|
48
+ array << deep_dup(val, cache)
32
49
  end
33
50
  end
51
+ private_class_method :deep_dup_array
34
52
 
35
- # A deep copy of array
53
+ # Deep copy a hash
36
54
  #
37
- # @param [Array] object
55
+ # @param [Hash] object
56
+ # the hash object to deep copy
57
+ # @param [Hash] cache
58
+ # the cache of copied objects
59
+ #
60
+ # @return [Hash]
38
61
  #
39
62
  # @api private
40
- def self.deep_dup_array(object)
41
- object.each_with_object([]) do |val, new_array|
42
- new_array << deep_dup(val)
63
+ def self.deep_dup_hash(object, cache)
64
+ object.each_with_object(cache[object.object_id] = {}) do |(k, v), hash|
65
+ hash[deep_dup(k, cache)] = deep_dup(v, cache)
43
66
  end
44
67
  end
68
+ private_class_method :deep_dup_hash
45
69
  end # DeepDup
46
70
  end # Option
47
71
  end # TTY
@@ -22,6 +22,22 @@ module TTY
22
22
  # Raised during command line input parsing
23
23
  class ParseError < Error
24
24
  attr_accessor :param
25
+
26
+ # Format value
27
+ #
28
+ # @param [Object] value
29
+ # the value to format
30
+ #
31
+ # @example
32
+ # format_value([:a, 1])
33
+ # # => a:1
34
+ #
35
+ # @return [String]
36
+ #
37
+ # @api public
38
+ def format_value(value)
39
+ value.respond_to?(:to_ary) ? value.join(":") : value.to_s
40
+ end
25
41
  end
26
42
 
27
43
  # Raised when found unrecognized parameter
@@ -39,7 +55,7 @@ module TTY
39
55
  @param = param_or_message
40
56
 
41
57
  message = format(MESSAGE,
42
- value: value,
58
+ value: format_value(value),
43
59
  name: param.name,
44
60
  type: param.to_sym)
45
61
  else
@@ -129,16 +145,30 @@ module TTY
129
145
  if param_or_message.is_a?(Parameter)
130
146
  @param = param_or_message
131
147
  message = format(MESSAGE,
132
- value: value,
148
+ value: format_value(value),
133
149
  name: param.name,
134
150
  type: param.to_sym,
135
- choices: param.permit.join(", "))
151
+ choices: format_choices(param.permit))
136
152
  else
137
153
  message = param_or_message
138
154
  end
139
155
 
140
156
  super(message)
141
157
  end
158
+
159
+ private
160
+
161
+ # Format permitted choices
162
+ #
163
+ # @param [Array<Object>] choices
164
+ # the choices to format
165
+ #
166
+ # @return [String]
167
+ #
168
+ # @api private
169
+ def format_choices(choices)
170
+ choices.map { |val| format_value(val) }.join(", ")
171
+ end
142
172
  end
143
173
  end # Option
144
174
  end # TTY