command_mapper 0.1.0.pre1

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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ruby.yml +27 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +1 -0
  5. data/.yardopts +1 -0
  6. data/ChangeLog.md +25 -0
  7. data/Gemfile +15 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.md +369 -0
  10. data/Rakefile +12 -0
  11. data/commnad_mapper.gemspec +61 -0
  12. data/gemspec.yml +23 -0
  13. data/lib/command_mapper/arg.rb +75 -0
  14. data/lib/command_mapper/argument.rb +142 -0
  15. data/lib/command_mapper/command.rb +606 -0
  16. data/lib/command_mapper/exceptions.rb +19 -0
  17. data/lib/command_mapper/option.rb +282 -0
  18. data/lib/command_mapper/option_value.rb +21 -0
  19. data/lib/command_mapper/sudo.rb +73 -0
  20. data/lib/command_mapper/types/enum.rb +35 -0
  21. data/lib/command_mapper/types/hex.rb +82 -0
  22. data/lib/command_mapper/types/input_dir.rb +35 -0
  23. data/lib/command_mapper/types/input_file.rb +35 -0
  24. data/lib/command_mapper/types/input_path.rb +29 -0
  25. data/lib/command_mapper/types/key_value.rb +131 -0
  26. data/lib/command_mapper/types/key_value_list.rb +45 -0
  27. data/lib/command_mapper/types/list.rb +90 -0
  28. data/lib/command_mapper/types/map.rb +64 -0
  29. data/lib/command_mapper/types/num.rb +50 -0
  30. data/lib/command_mapper/types/str.rb +85 -0
  31. data/lib/command_mapper/types/type.rb +102 -0
  32. data/lib/command_mapper/types.rb +6 -0
  33. data/lib/command_mapper/version.rb +4 -0
  34. data/lib/command_mapper.rb +2 -0
  35. data/spec/arg_spec.rb +137 -0
  36. data/spec/argument_spec.rb +513 -0
  37. data/spec/commnad_spec.rb +1175 -0
  38. data/spec/exceptions_spec.rb +14 -0
  39. data/spec/option_spec.rb +882 -0
  40. data/spec/option_value_spec.rb +17 -0
  41. data/spec/spec_helper.rb +6 -0
  42. data/spec/sudo_spec.rb +24 -0
  43. data/spec/types/enum_spec.rb +31 -0
  44. data/spec/types/hex_spec.rb +158 -0
  45. data/spec/types/input_dir_spec.rb +30 -0
  46. data/spec/types/input_file_spec.rb +34 -0
  47. data/spec/types/input_path_spec.rb +32 -0
  48. data/spec/types/key_value_list_spec.rb +100 -0
  49. data/spec/types/key_value_spec.rb +272 -0
  50. data/spec/types/list_spec.rb +143 -0
  51. data/spec/types/map_spec.rb +62 -0
  52. data/spec/types/num_spec.rb +90 -0
  53. data/spec/types/str_spec.rb +232 -0
  54. data/spec/types/type_spec.rb +59 -0
  55. metadata +118 -0
@@ -0,0 +1,131 @@
1
+ require 'command_mapper/types/type'
2
+ require 'command_mapper/types/str'
3
+
4
+ module CommandMapper
5
+ module Types
6
+ #
7
+ # Represents a key-value type.
8
+ #
9
+ class KeyValue < Type
10
+
11
+ # The separator String between the key and value.
12
+ #
13
+ # @return [String]
14
+ attr_reader :separator
15
+
16
+ # The key's type.
17
+ #
18
+ # @return [Type]
19
+ attr_reader :key
20
+
21
+ # The value's type.
22
+ #
23
+ # @return [Type]
24
+ attr_reader :value
25
+
26
+ #
27
+ # Initializes the key-value value type.
28
+ #
29
+ # @param [String] separator
30
+ # The key-value separator.
31
+ #
32
+ # @param [Type, Hash] key
33
+ # The key's value type.
34
+ #
35
+ # @param [Type, Hash] value
36
+ # The value's value type.
37
+ #
38
+ # @param [Hash{Symbol => Object}]
39
+ # Additional keyword arguments for {Type#initialize}.
40
+ #
41
+ def initialize(separator: '=', key: Str.new, value: Str.new)
42
+ @separator = separator
43
+
44
+ if key.nil?
45
+ raise(ArgumentError,"key: keyword cannot be nil")
46
+ end
47
+
48
+ if value.nil?
49
+ raise(ArgumentError,"value: keyword cannot be nil")
50
+ end
51
+
52
+ @key = Types::Type(key)
53
+ @value = Types::Type(value)
54
+ end
55
+
56
+ #
57
+ # Valides the given value.
58
+ #
59
+ # @param [Object] value
60
+ #
61
+ # @return [true, (false, String)]
62
+ #
63
+ def validate(value)
64
+ case value
65
+ when Hash
66
+ if value.length < 1
67
+ return [false, "cannot be empty"]
68
+ end
69
+
70
+ if value.length > 1
71
+ return [false, "cannot contain multiple key:value pairs (#{value.inspect})"]
72
+ end
73
+
74
+ key, value = value.first
75
+ when Array
76
+ if value.length < 2
77
+ return [false, "must contain two elements (#{value.inspect})"]
78
+ end
79
+
80
+ if value.length > 2
81
+ return [false, "cannot contain more than two elements (#{value.inspect})"]
82
+ end
83
+
84
+ key, value = value
85
+ else
86
+ return [false, "must be a Hash or an Array (#{value.inspect})"]
87
+ end
88
+
89
+ valid, message = @key.validate(key)
90
+
91
+ unless valid
92
+ return [false, "key #{message}"]
93
+ end
94
+
95
+ valid, message = @value.validate(value)
96
+
97
+ unless valid
98
+ return [false, "value #{message}"]
99
+ end
100
+
101
+ return true
102
+ end
103
+
104
+ #
105
+ # Formats a value into a key-value pair.
106
+ #
107
+ # @param [Hash, Array, #to_s] value
108
+ # The given value.
109
+ #
110
+ # @return [String]
111
+ # The formatted key-value pair.
112
+ #
113
+ def format(value)
114
+ case value
115
+ when Hash, Array
116
+ case value
117
+ when Hash
118
+ key, value = value.first
119
+ when Array
120
+ key, value = value
121
+ end
122
+
123
+ "#{@key.format(key)}#{@separator}#{@value.format(value)}"
124
+ else
125
+ super(value)
126
+ end
127
+ end
128
+
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,45 @@
1
+ require 'command_mapper/types/list'
2
+ require 'command_mapper/types/key_value'
3
+
4
+ module CommandMapper
5
+ module Types
6
+ #
7
+ # Represents a list of `key=value` pairs.
8
+ #
9
+ class KeyValueList < List
10
+
11
+ #
12
+ # Initializes the key-value list.
13
+ #
14
+ # @param [String] separator
15
+ # The list separator character (ex: `,`).
16
+ #
17
+ # @param [String] key_value_separator
18
+ # The key-value separator (ex: `=`).
19
+ #
20
+ # @param [Hash{Symbol => Object}] kwargs
21
+ # Additional keyword arguments for {KeyValue#initialize}.
22
+ #
23
+ def initialize(separator: ',', key_value_separator: '=', **kwargs)
24
+ super(
25
+ type: KeyValue.new(separator: key_value_separator, **kwargs),
26
+ separator: separator
27
+ )
28
+ end
29
+
30
+ #
31
+ # Formats the value.
32
+ #
33
+ # @param [Hash, Array((key, value))] value
34
+ # The list of key-value pairs.
35
+ #
36
+ # @return [String]
37
+ # The formatted key-value list.
38
+ #
39
+ def format(value)
40
+ super(Array(value).map(&@type.method(:format)))
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,90 @@
1
+ require 'command_mapper/types/type'
2
+ require 'command_mapper/types/str'
3
+
4
+ module CommandMapper
5
+ module Types
6
+ #
7
+ # Represents a list type.
8
+ #
9
+ class List < Type
10
+
11
+ # The seperator character.
12
+ #
13
+ # @return [String]
14
+ attr_reader :separator
15
+
16
+ # The list element type.
17
+ #
18
+ # @return [Type]
19
+ attr_reader :type
20
+
21
+ #
22
+ # Initializes the list type.
23
+ #
24
+ # @param [String] separator
25
+ # The list separator character.
26
+ #
27
+ # @param [Type, Hash] value
28
+ # The list's value type.
29
+ #
30
+ def initialize(separator: ',', type: Str.new, allow_empty: false)
31
+ if type.nil?
32
+ raise(ArgumentError,"type: keyword cannot be nil")
33
+ end
34
+
35
+ @separator = separator
36
+ @type = Types::Type(type)
37
+
38
+ @allow_empty = allow_empty
39
+ end
40
+
41
+ #
42
+ # Specifies whether the option's value may accept empty values.
43
+ #
44
+ # @return [Boolean]
45
+ #
46
+ def allow_empty?
47
+ @allow_empty
48
+ end
49
+
50
+ #
51
+ # Validates the value.
52
+ #
53
+ # @param [Object] value
54
+ #
55
+ # @return [true, (false, String)]
56
+ #
57
+ def validate(value)
58
+ values = Array(value)
59
+
60
+ if values.empty?
61
+ unless allow_empty?
62
+ return [false, "cannot be empty"]
63
+ end
64
+ end
65
+
66
+ values.each do |element|
67
+ valid, message = @type.validate(element)
68
+
69
+ unless valid
70
+ return [false, "element #{message}"]
71
+ end
72
+ end
73
+
74
+ return true
75
+ end
76
+
77
+ #
78
+ # Formats the value into a list.
79
+ #
80
+ # @param [Object] value
81
+ #
82
+ # @return [String]
83
+ #
84
+ def format(value)
85
+ Array(value).map(&@type.method(:format)).join(@separator)
86
+ end
87
+
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,64 @@
1
+ require 'command_mapper/types/type'
2
+
3
+ module CommandMapper
4
+ module Types
5
+ class Map < Type
6
+
7
+ # @return [Hash{Object => String}]
8
+ attr_reader :map
9
+
10
+ #
11
+ # Initializes the map value type.
12
+ #
13
+ # @param [Hash{Object => String}] map
14
+ # The map of values to Strings.
15
+ #
16
+ def initialize(map)
17
+ @map = map
18
+ end
19
+
20
+ #
21
+ # Creates a new map.
22
+ #
23
+ # @param [Hash{Object => String}] map
24
+ # The map of values to Strings.
25
+ #
26
+ # @return [Map]
27
+ #
28
+ def self.[](map)
29
+ new(map)
30
+ end
31
+
32
+ # Maps boolean values to "yes" and "no"
33
+ YesNo = new(true => 'yes', false => 'no')
34
+
35
+ # Maps boolean values to "enabled" and "disabled"
36
+ EnabledDisabled = new(true => 'enabled', false => 'disabled')
37
+
38
+ #
39
+ # Validates a value.
40
+ #
41
+ # @param [Object] value
42
+ #
43
+ # @return [true, (false, String)]
44
+ #
45
+ def validate(value)
46
+ unless @map.has_key?(value)
47
+ return [false, "unknown value (#{value.inspect})"]
48
+ end
49
+
50
+ return true
51
+ end
52
+
53
+ #
54
+ # @param [Object] value
55
+ #
56
+ # @return [String]
57
+ #
58
+ def format(value)
59
+ super(@map.fetch(value))
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,50 @@
1
+ require 'command_mapper/types/type'
2
+
3
+ module CommandMapper
4
+ module Types
5
+ #
6
+ # Represents a numeric value.
7
+ #
8
+ class Num < Type
9
+
10
+ #
11
+ # Validates a value.
12
+ #
13
+ # @param [String, Integer] value
14
+ #
15
+ # @return [true, (false, String)]
16
+ #
17
+ def validate(value)
18
+ case value
19
+ when Integer
20
+ return true
21
+ when String
22
+ unless value =~ /\A\d+\z/
23
+ return [false, "contains non-numeric characters (#{value.inspect})"]
24
+ end
25
+ else
26
+ unless value.respond_to?(:to_i)
27
+ return [false, "cannot be converted into an Integer (#{value.inspect})"]
28
+ end
29
+ end
30
+
31
+ return true
32
+ end
33
+
34
+ #
35
+ # Formats a numeric value.
36
+ #
37
+ # @param [String, Integer, #to_i] value
38
+ #
39
+ # @return [String]
40
+ #
41
+ def format(value)
42
+ case value
43
+ when Integer, String then value.to_s
44
+ else value.to_i.to_s
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,85 @@
1
+ require 'command_mapper/types/type'
2
+
3
+ module CommandMapper
4
+ module Types
5
+ class Str < Type
6
+ #
7
+ # Initializes the value.
8
+ #
9
+ # @param [Boolean] allow_empty
10
+ # Specifies whether the argument may accept empty values.
11
+ #
12
+ # @param [Boolean] allow_blank
13
+ # Specifies whether the argument may accept blank values.
14
+ #
15
+ def initialize(allow_empty: false, allow_blank: false)
16
+ @allow_empty = allow_empty
17
+ @allow_blank = allow_blank
18
+ end
19
+
20
+ #
21
+ # Specifies whether the option's value may accept empty values.
22
+ #
23
+ # @return [Boolean]
24
+ #
25
+ def allow_empty?
26
+ @allow_empty
27
+ end
28
+
29
+ #
30
+ # Specifies whether the option's value may accept blank values.
31
+ #
32
+ # @return [Boolean]
33
+ #
34
+ def allow_blank?
35
+ @allow_blank
36
+ end
37
+
38
+ #
39
+ # Validates the given value.
40
+ #
41
+ # @param [Object] value
42
+ # The given value to validate.
43
+ #
44
+ # @return [true, (false, String)]
45
+ # Returns true if the valid is considered valid, or false and a
46
+ # validation message if the value is not valid.
47
+ # * If `nil` is given and a value is required, then `false` will be
48
+ # returned.
49
+ # * If an empty value is given and empty values are not allowed, then
50
+ # `false` will be returned.
51
+ # * If an empty value is given and blank values are not allowed, then
52
+ # `false` will be returned.
53
+ #
54
+ def validate(value)
55
+ case value
56
+ when nil
57
+ unless allow_empty?
58
+ return [false, "cannot be nil"]
59
+ end
60
+ when Enumerable
61
+ return [false, "cannot convert a #{value.class} into a String (#{value.inspect})"]
62
+ else
63
+ unless value.respond_to?(:to_s)
64
+ return [false, "does not define a #to_s method (#{value.inspect})"]
65
+ end
66
+
67
+ string = value.to_s
68
+
69
+ if string.empty?
70
+ unless allow_empty?
71
+ return [false, "does not allow an empty value"]
72
+ end
73
+ elsif string =~ /\A\s+\z/
74
+ unless allow_blank?
75
+ return [false, "does not allow a blank value (#{value.inspect})"]
76
+ end
77
+ end
78
+ end
79
+
80
+ return true
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,102 @@
1
+ module CommandMapper
2
+ module Types
3
+ #
4
+ # The base type for all command-line argument types.
5
+ #
6
+ # ## Custom Types
7
+ #
8
+ # Custom types can be defined by extending the {Type} class.
9
+ #
10
+ # class PortRange < CommandMapper::Types::Type
11
+ #
12
+ # def validate(value)
13
+ # case value
14
+ # when Integer
15
+ # true
16
+ # when Range
17
+ # if value.begin.kind_of?(Integer)
18
+ # true
19
+ # else
20
+ # [false, "port range can only contain Integers"]
21
+ # end
22
+ # else
23
+ # [false, "port range must be an Integer or a Range of Integers"]
24
+ # end
25
+ # end
26
+ #
27
+ # def format(value)
28
+ # case value
29
+ # when Integer
30
+ # "#{value}"
31
+ # when Range
32
+ # "#{value.begin}-#{value.end}"
33
+ # end
34
+ # end
35
+ #
36
+ # end
37
+ #
38
+ #
39
+ # The custom type can define the following methods:
40
+ #
41
+ # * `#initialize` - accepts additional configuration options.
42
+ # * `#validate` - accepts a value object and returns `true` (indicating the
43
+ # value is valid) or `[false, message]` (indicating the value is invalid).
44
+ # * `#format` - accepts a validated value and returns a formatted String.
45
+ #
46
+ # Once defined, custom {Type} classes can be used with `option` or
47
+ # `argument` and passed in via the `type:` keyword argument.
48
+ #
49
+ # option "--ports", value: {required: true, type: PortRange.new}
50
+ #
51
+ # argument :ports, required: true, type: PortRange.new
52
+ #
53
+ class Type
54
+
55
+ #
56
+ # The default `validate` method for all types.
57
+ #
58
+ # @param [Object]
59
+ #
60
+ # @return [true, (false, String)]
61
+ #
62
+ def validate(value)
63
+ true
64
+ end
65
+
66
+ #
67
+ # The default `format` method for all types.
68
+ #
69
+ # @param [#to_s] value
70
+ #
71
+ # @return [String]
72
+ # The String version of the value.
73
+ #
74
+ def format(value)
75
+ value.to_s
76
+ end
77
+
78
+ end
79
+
80
+ require 'command_mapper/types/str'
81
+
82
+ #
83
+ # Converts a value into a {Type} object.
84
+ #
85
+ # @param [Type, Hash, nil] value
86
+ #
87
+ # @return [Type]
88
+ #
89
+ # @raise [ArgumentError]
90
+ #
91
+ def self.Type(value)
92
+ case value
93
+ when Type then value
94
+ when Hash then Str.new(**value)
95
+ when nil then nil
96
+ else
97
+ raise(ArgumentError,"value must be a #{Type}, Hash, or nil: #{value.inspect}")
98
+ end
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,6 @@
1
+ require 'command_mapper/types/type'
2
+ require 'command_mapper/types/str'
3
+ require 'command_mapper/types/map'
4
+ require 'command_mapper/types/list'
5
+ require 'command_mapper/types/key_value'
6
+ require 'command_mapper/types/key_value_list'
@@ -0,0 +1,4 @@
1
+ module CommandMapper
2
+ # Version of command_mapper
3
+ VERSION = '0.1.0.pre1'
4
+ end
@@ -0,0 +1,2 @@
1
+ require 'command_mapper/command'
2
+ require 'command_mapper/version'