tty-option 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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