kxi 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a376bd573bfb44dbeae16f773f883a1f55daedd7
4
+ data.tar.gz: 4cbb2897d3c73fe15b882a26d79f8d3bdc387244
5
+ SHA512:
6
+ metadata.gz: 7d040d8a1a051f68268ba981ea84e69fcf5bd547f801c3411511bcb3b3edd2da4e23befcb9171fe6b43655bdd2adca2ef6b8de387fa5874f8d8b76b79cbabac7
7
+ data.tar.gz: c35695494016d0625076379ffcc9370e5c4299cc47cc19f4979d10ee0cd4f9ac01c3d4f3e27051f1c2859c18a4099aa830324545fb1ef0d0ab45ec671c405ed8
@@ -0,0 +1,51 @@
1
+ # Created by Matyáš Pokorný on 2018-01-20.
2
+
3
+ module KXI
4
+ module CLI
5
+ # Represents anonymous argument (eg.: verb VALUE)
6
+ class AnonymousArgument < KXI::CLI::Argument
7
+ # Gets default value of argument
8
+ # @return [Object] Default value of argument
9
+ def default
10
+ @def
11
+ end
12
+
13
+ # Gets whether argument is variadic
14
+ # @return [Bool] True if argument is variadic; false otherwise
15
+ def variadic?
16
+ @var
17
+ end
18
+
19
+ # Gets syntax of argument
20
+ # @return [String] Syntax of argument
21
+ def syntax
22
+ "#{required? ? '<' : '['}#{name}#{required? ? '>' : ']'}"
23
+ end
24
+
25
+ # Gets full descriptive name of argument
26
+ # @return [String] Full name of argument
27
+ def headline
28
+ super.upcase
29
+ end
30
+
31
+ # Instantiates the {KXI::CLI::AnonymousArgument} class
32
+ # @param nm [String] Name of argument
33
+ # @param desc [String] Description of argument
34
+ # @param rq [Bool] Indicates whether argument is required
35
+ # @param df Default value of argument
36
+ # @param var [Bool] Indicates whether argument is variadic
37
+ def initialize(nm, desc, rq = true, df = nil, var = false, &validator)
38
+ super(nm, desc, rq, 5 - (var ? 1 : 0) - (rq ? 0 : 1))
39
+ @def = df
40
+ @var = var
41
+ @val = validator
42
+ end
43
+
44
+ # Validates value of argument
45
+ # @param val [String, Array<String>] Value of argument
46
+ def validate(val)
47
+ @val.call(val) if @val != nil
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,57 @@
1
+ # Created by Matyáš Pokorný on 2018-01-20.
2
+
3
+ module KXI
4
+ module CLI
5
+ # Represents argument of command-line interface
6
+ class Argument
7
+ # Gets the name of argument
8
+ # @return [String] Name of argument
9
+ def name
10
+ @name
11
+ end
12
+
13
+ # Gets syntax of argument
14
+ # @return [String] Syntax of argument
15
+ def syntax
16
+ name
17
+ end
18
+
19
+ # Gets full descriptive name of argument
20
+ # @return [String] Full name of argument
21
+ def headline
22
+ name
23
+ end
24
+
25
+ # Gets the description of argument
26
+ # @return [String] Description of argument
27
+ def description
28
+ @desc
29
+ end
30
+
31
+ # Indicates whether argument is mandatory
32
+ # @return [Bool] True if argument is mandatory; otherwise false
33
+ def required?
34
+ @req
35
+ end
36
+
37
+ # Gets the order of argument (in descending order)
38
+ # @return [Number] Order of argument
39
+ def order
40
+ @order
41
+ end
42
+
43
+ # Instantiates the {KXI::CLI::Argument} class
44
+ # @param nm [String] Name of argument
45
+ # @param desc [String] Description of argument
46
+ # @param req [Bool] Determines whether argument is mandatory
47
+ # @param order [Number] Order of argument
48
+ def initialize(nm, desc, req, order)
49
+ raise(Exception.new('Invalid argument name!')) unless /^[A-Za-z0-9\-]+$/m =~ nm
50
+ @name = nm.downcase
51
+ @desc = desc
52
+ @req = req
53
+ @order = order
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,84 @@
1
+ # Created by Matyáš Pokorný on 2018-01-21.
2
+
3
+ module KXI
4
+ module CLI
5
+ # Manages values of arguments
6
+ class ArgumentValues
7
+ # Instantiates the {KXI::CLI::ArgumentValues} class
8
+ # @param args [Array<KXI::CLI::Argument>] Expected arguments
9
+ def initialize(args)
10
+ @args = args
11
+ @vals = {}
12
+ args.each do |a|
13
+ if a.is_a?(KXI::CLI::FlagArgument)
14
+ @vals[a.name] = { :argument => a, :value => false }
15
+ end
16
+ end
17
+ end
18
+
19
+ # Assigns (or adds) a value to argument
20
+ # @param arg [KXI::CLI::Argument] Argument to set
21
+ # @param val [String] Value to assign
22
+ def set(arg, val)
23
+ if arg.is_a?(KXI::CLI::FlagArgument)
24
+ raise(KXI::Exceptions::ArgumentException.new(arg.name, 'Flag set multiple times!')) if @vals[arg.name][:value]
25
+ @vals[arg.name][:value] = val
26
+ else
27
+ if arg.variadic?
28
+ @vals[arg.name] = { :argument => arg, :value => [] } if @vals[arg.name] == nil
29
+ @vals[arg.name][:value].push(val)
30
+ else
31
+ raise(KXI::Exceptions::ArgumentException.new(arg.name, 'Argument set multiple times!')) if @vals[arg.name] != nil
32
+ begin
33
+ arg.validate(val)
34
+ rescue Exception => ex
35
+ raise(KXI::Exceptions::ArgumentException.new(arg.name, ex.message))
36
+ end
37
+ @vals[arg.name] = { :argument => arg, :value => val }
38
+ end
39
+ end
40
+ end
41
+
42
+ # Gets value of argument
43
+ # @param index [String, Symbol, KXI::CLI::Argument] Name of argument
44
+ # @return [String] Value of argument
45
+ def []=(index)
46
+ if index.is_a?(Symbol)
47
+ index = index.to_s
48
+ elsif index.is_a?(KXI::CLI::Argument)
49
+ index = index.name
50
+ end
51
+ raise(Exception.new("Undefined argument #{index}!")) if @vals[index] == nil
52
+ return @vals[index][:value]
53
+ end
54
+
55
+ # Converts class to {Hash}
56
+ # @return [Hash] Equivalent hash
57
+ def to_h
58
+ ret = {}
59
+ @vals.each_pair do |k, v|
60
+ ret[k] = v[:value]
61
+ end
62
+ return ret
63
+ end
64
+
65
+ # Validates variadic arguments and checks for minimal argument requirements
66
+ def finish
67
+ @args.each do |arg|
68
+ if @vals[arg.name] != nil
69
+ unless arg.is_a?(KXI::CLI::FlagArgument)
70
+ begin
71
+ arg.validate(@vals[arg.name][:value])
72
+ rescue Exception => ex
73
+ raise(KXI::Exceptions::ArgumentException.new(arg.name, ex.message))
74
+ end
75
+ end
76
+ else
77
+ raise(KXI::Exceptions::ArgumentException.new(arg.name, 'Argument is mandatory!')) if arg.required?
78
+ @vals[arg.name] = arg.default
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,39 @@
1
+ # Created by Matyáš Pokorný on 2018-01-20.
2
+
3
+ module KXI
4
+ module CLI
5
+ # Represents argument specified explicitly with name
6
+ class ExplicitArgument < KXI::CLI::Argument
7
+ # Gets full descriptive name of argument
8
+ # @return [String] Full name of argument
9
+ def headline
10
+ ret = ''
11
+ ret = "-#{@sh}, " if @sh != nil
12
+ ret += "--#{name}"
13
+ return ret
14
+ end
15
+
16
+ # Gets syntax of argument
17
+ # @return [String] Syntax of argument
18
+ def syntax
19
+ headline
20
+ end
21
+
22
+ # Gets the shortcut symbol of argument
23
+ # @return [String] Shortcut symbol of argument
24
+ def shortcut
25
+ @sh
26
+ end
27
+
28
+ # Instantiates the {KXI::CLI::ExplicitArgument} class
29
+ # @param nm [String] Name of argument
30
+ # @param desc [String] Description of argument
31
+ # @param sh [String, nil] Shortcut of argument
32
+ # @param req [Bool] Specifies whether argument is mandatory
33
+ def initialize(nm, desc, sh = nil, req = false)
34
+ super(nm, desc, req, req ? 2 : 1)
35
+ @sh = sh
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,16 @@
1
+ # Created by Matyáš Pokorný on 2018-01-20.
2
+
3
+ module KXI
4
+ module CLI
5
+ # Represents the flag argument (eg.: -f, --flag)
6
+ class FlagArgument < KXI::CLI::ExplicitArgument
7
+ # Instantiates the {KXI::CLI::FlagArgument} class
8
+ # @param nm [String] Name of argument
9
+ # @param desc [String] Description of argument
10
+ # @param sh [String] Shortcut of argument
11
+ def initialize(nm, desc, sh = nil)
12
+ super(nm, desc, sh)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,60 @@
1
+ # Created by Matyáš Pokorný on 2018-01-20.
2
+
3
+ module KXI
4
+ module CLI
5
+ # Represents named argument (eg.: -a VALUE, --argument VALUE)
6
+ class NamedArgument < KXI::CLI::ExplicitArgument
7
+ # Gets default value of argument
8
+ # @return [Object] Default value of argument
9
+ def default
10
+ @def
11
+ end
12
+
13
+ # Gets whether argument is variadic
14
+ # @return [Bool] True if argument is variadic; false otherwise
15
+ def variadic?
16
+ @var
17
+ end
18
+
19
+ # Gets name of argument value
20
+ # @return [String] Name of argument value
21
+ def value_name
22
+ @vnm
23
+ end
24
+
25
+ # Gets full descriptive name of argument
26
+ # @return [String] Full name of argument
27
+ def headline
28
+ "#{super} #{@var ? '...' : ''}#{@vnm.upcase}"
29
+ end
30
+
31
+ # Gets syntax of argument
32
+ # @return [String] Syntax of argument
33
+ def syntax
34
+ "-#{(shortcut != nil ? shortcut : "-#{name}")} #{required? ? '<' : '['}#{@var ? '...' : ''}#{@vnm}#{required? ? '>' : ']'}"
35
+ end
36
+
37
+ # Instantiates the {KXI::CLI::NamedArgument} class
38
+ # @param nm [String] Name of argument
39
+ # @param vn [String] Name of value
40
+ # @param desc [String] Description of argument
41
+ # @param sh [String] Shortcut of argument
42
+ # @param rq [Bool] Indicates whether argument is required
43
+ # @param df Default value of argument
44
+ # @param var [Bool] Indicates whether argument is variadic
45
+ def initialize(nm, vn, desc, sh = nil, rq = true, df = nil, var = false, &validator)
46
+ super(nm, desc, sh, rq)
47
+ @def = df
48
+ @vnm = vn
49
+ @var = var
50
+ @val = validator
51
+ end
52
+
53
+ # Validates value of argument
54
+ # @param val [String, Array<String>] Value of argument
55
+ def validate(val)
56
+ @val.call(val) if @val != nil
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,281 @@
1
+ # Created by Matyáš Pokorný on 2018-01-20.
2
+
3
+ module KXI
4
+ module CLI
5
+ # Represents a verb of console interface
6
+ class Verb
7
+ # Gets the name of verb
8
+ # @return [String] Name of verb
9
+ def name
10
+ @name
11
+ end
12
+
13
+ # Gets the description of verb
14
+ # @return [String] Description of verb
15
+ def description
16
+ @desc
17
+ end
18
+
19
+ # Assigns action to this verb
20
+ # @yield [args] Action of verb
21
+ # @yieldparam args [KXI::CLI::ArgumentValues] Parsed values of arguments
22
+ def action(&block)
23
+ @action = block
24
+ end
25
+
26
+ # Gets the arguments assigned to this verb
27
+ # @return [KXI::Collections::ProtectedCollection] Arguments of verb
28
+ def arguments
29
+ KXI::Collections::ProtectedCollection.new(@args)
30
+ end
31
+
32
+ # Enables help for this verb and it's children
33
+ def enable_help
34
+ unless @help
35
+ @help = true
36
+ @verbs.foreach { |i| i.enable_help }
37
+ end
38
+ end
39
+
40
+ # Instantiates the {KXI::CLI::Verb} class
41
+ # @param nm [String] Name of verb
42
+ # @param desc [String] Description of verb
43
+ # @param ctx [String] Context of verb
44
+ # @yield [verb] Initialization body of verb
45
+ # @yieldparam verb [KXI::CLI::Verb] Initialized verb
46
+ # @raise [Exception] When name of verb is invalid
47
+ def initialize(nm, desc, ctx = nil)
48
+ raise(Exception.new('Invalid name of verb!')) unless /^[A-Za-z0-9][A-Za-z0-9\-]*$/m =~ nm
49
+ @name = nm.downcase
50
+ @desc = desc
51
+ @verbs = KXI::Collections::ArrayCollection.new
52
+ @args = KXI::Collections::ArrayCollection.new
53
+ @action = nil
54
+ @ctx = ctx
55
+ @help = false
56
+ yield(self) if block_given?
57
+ end
58
+
59
+ # Creates a new child verb
60
+ # @param name [String] Name of verb
61
+ # @param desc [String] Description of verb
62
+ # @return [KXI::CLI::Verb] Child verb
63
+ def verb(name, desc)
64
+ check_name(name)
65
+ add_verb(Verb.new(name, desc, @ctx == nil ? @name : "#{@ctx} #{@name}") { |v| yield(v) if block_given? })
66
+ end
67
+
68
+ # Creates new optional anonymous argument
69
+ # @param name [String] Name of argument
70
+ # @param desc [String] Description of argument
71
+ # @param df [Object, nil] Default value of argument
72
+ # @param var [Bool] Determines whether argument is variadic
73
+ # @yield [val] Validator function for value of argument
74
+ # @yieldparam val [String, Array<String>] Value of argument
75
+ # @return [KXI::CLI::AnonymousArgument] Anonymous argument
76
+ def anonymous(name, desc, df = nil, var = false, &validator)
77
+ add_argument(KXI::CLI::AnonymousArgument.new(name, desc, false, df, var, &validator))
78
+ end
79
+
80
+
81
+ # Creates new mandatory anonymous argument
82
+ # @param name [String] Name of argument
83
+ # @param desc [String] Description of argument
84
+ # @param var [Bool] Determines whether argument is variadic
85
+ # @yield [val] Validator function for value of argument
86
+ # @yieldparam val [String, Array<String>] Value of argument
87
+ # @return [KXI::CLI::AnonymousArgument] Anonymous argument
88
+ def anonymous!(name, desc, var = false, &validator)
89
+ add_argument(KXI::CLI::AnonymousArgument.new(name, desc, true, nil, var, &validator))
90
+ end
91
+
92
+ # Creates new optional named argument
93
+ # @param name [String] Name of argument
94
+ # @param val [String] Name of value
95
+ # @param desc [String] Description of argument
96
+ # @param sh [String, nil] Shortcut symbol
97
+ # @param df [Object, nil] Default value of argument
98
+ # @param var [Bool] Determines whether argument is variadic
99
+ # @yield [val] Validator function for value of argument
100
+ # @yieldparam val [String, Array<String>] Value of argument
101
+ # @return [KXI::CLI::NamedArgument] Named argument
102
+ def named(name, val, desc, sh = nil, df = nil, var = false, &validator)
103
+ add_argument(KXI::CLI::NamedArgument.new(name, val, desc, sh, false, df, var, &validator))
104
+ end
105
+
106
+
107
+ # Creates new mandatory named argument
108
+ # @param name [String] Name of argument
109
+ # @param val [String] Name of value
110
+ # @param desc [String] Description of argument
111
+ # @param sh [String, nil] Shortcut symbol
112
+ # @param var [Bool] Determines whether argument is variadic
113
+ # @yield [val] Validator function for value of argument
114
+ # @yieldparam val [String, Array<String>] Value of argument
115
+ # @return [KXI::CLI::NamedArgument] Named argument
116
+ def named!(name, val, desc, sh = nil, var = false, &validator)
117
+ add_argument(KXI::CLI::NamedArgument.new(name, val, desc, sh, true, nil, var, &validator))
118
+ end
119
+
120
+
121
+ # Creates new flag argument
122
+ # @param name [String] Name of flag
123
+ # @param desc [String] Description of flag
124
+ # @param sh [String, nil] Shortcut symbol
125
+ # @return [KXI::CLI::FlagArgument] Flag argument
126
+ def flag(name, desc, sh = nil)
127
+ add_argument(KXI::CLI::FlagArgument.new(name, desc, sh))
128
+ end
129
+
130
+ # Parses and executes action of verb
131
+ # @param args [Array<String>] Given arguments
132
+ def act(*args)
133
+ args = ARGV if args.length == 0
134
+ hf = args.include?('-h') or args.include?('--help') or args.include?('-?')
135
+ if @verbs.count > 0
136
+ v = args.shift
137
+ if @help and (v == 'help' or hf)
138
+ if v == 'help' or v == nil
139
+ help
140
+ else
141
+ verb = @verbs.first { |i| i.name == v.downcase }
142
+ if verb == nil
143
+ help
144
+ else
145
+ verb.act(*args)
146
+ end
147
+ end
148
+ else
149
+ raise(KXI::Exceptions::VerbExpectedException.new(@verbs.select { |i| i.name }.to_array)) if v == nil
150
+ verb = @verbs.first { |i| i.name == v.downcase }
151
+ raise(KXI::Exceptions::VerbExpectedException.new(@verbs.select { |i| i.name }.to_array)) if verb == nil
152
+ verb.act(*args)
153
+ end
154
+ else
155
+ if @help and hf
156
+ help
157
+ else
158
+ vals = KXI::CLI::ArgumentValues.new(@args.to_array)
159
+ anon = @args.of_type(KXI::CLI::AnonymousArgument).order_by_descending { |a, b| a.order <=> b.order }
160
+
161
+ ar = nil
162
+ args.each do |val|
163
+ if val.start_with?('-')
164
+ raise(KXI::Exceptions::ArgumentException.new(ar.name, 'Expected value!')) if ar != nil
165
+ raise(KXI::Exceptions::ArgumentException.new(val, 'Invalid syntax!')) if val.start_with?('---')
166
+ if val.start_with?('--')
167
+ raise(KXI::Exceptions::ArgumentException.new(val, 'Expected argument name!')) if val.length == 2
168
+ nm = val[2..val.length - 1].downcase
169
+ arg = @args.first { |i| i.name == nm }
170
+ raise(KXI::Exceptions::ArgumentException.new(nm, 'No such argument exists!')) if arg == nil
171
+ if arg.is_a?(KXI::CLI::FlagArgument)
172
+ vals.set(arg, true)
173
+ elsif arg.is_a?(KXI::CLI::NamedArgument)
174
+ ar = arg
175
+ else
176
+ raise(KXI::Exceptions::ArgumentException.new(nm, 'Argument cannot be assigned explicitly!'))
177
+ end
178
+ else
179
+ raise(KXI::Exceptions::ArgumentException.new(val, 'Expected short argument name!')) if val.length == 1
180
+ nm = val[1]
181
+ arg = @args.first { |i| i.shortcut == nm }
182
+ raise(KXI::Exceptions::ArgumentException.new(nm, 'No such argument exists!')) if arg == nil
183
+ if arg.is_a?(KXI::CLI::FlagArgument)
184
+ vals.set(arg, true)
185
+ elsif arg.is_a?(KXI::CLI::NamedArgument)
186
+ if val.length == 2
187
+ ar = arg
188
+ else
189
+ vals.set(arg, val[2..val.length - 1])
190
+ end
191
+ end
192
+ end
193
+ else
194
+ if ar != nil
195
+ vals.set(ar, val)
196
+ ar = nil
197
+ else
198
+ an = anon.first
199
+ raise(KXI::Exceptions::NoArgumentException.new(val)) if an == nil
200
+ vals.set(an, val)
201
+ anon.remove_at(0)
202
+ end
203
+ end
204
+ end
205
+ if @action == nil
206
+ raise(KXI::Exceptions::NotImplementedException.new)
207
+ else
208
+ vals.finish
209
+ @action.call(vals)
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ # Prints help message of verb
216
+ def help
217
+ puts(@name)
218
+ puts("\t#{@desc}")
219
+ puts
220
+ puts('Usage:')
221
+ if @verbs.count == 0
222
+ print("\t")
223
+ print("#{@ctx} ") if @ctx != nil
224
+ print(@name)
225
+ args = @args.order_by_descending { |a, b| a.order <=> b.order }
226
+ args.where { |i| i.required? or i.is_a?(KXI::CLI::AnonymousArgument) }.foreach do |i|
227
+ print(" #{i.syntax}")
228
+ end
229
+ puts
230
+ if args.count > 0
231
+ puts
232
+ max = args.max { |a, b| a.headline.length <=> b.headline.length }.headline.length
233
+ args.foreach do |a|
234
+ puts("\t#{a.headline.ljust(max)} #{a.description}")
235
+ end
236
+ end
237
+ else
238
+ max = @verbs.max { |a, b| a.name.length <=> b.name.length }.name.length
239
+ @verbs.foreach do |v|
240
+ print("\t")
241
+ print("#{@ctx} ") if @ctx != nil
242
+ puts("#{@name} #{v.name.ljust(max)} #{v.description}")
243
+ end
244
+ end
245
+ end
246
+
247
+ # Adds argument
248
+ # @param arg [KXI::CLI::Argument] Argument to add
249
+ def add_argument(arg)
250
+ check_name(arg.name)
251
+ if arg.is_a?(KXI::CLI::ExplicitArgument) and arg.shortcut != nil
252
+ raise(Exception.new("Short name '#{arg.shortcut}' is already in use!")) if @args.of_type(KXI::CLI::ExplicitArgument).any { |i| i.shortcut == arg.shortcut }
253
+ end
254
+ if arg.is_a?(KXI::CLI::AnonymousArgument) and arg.variadic?
255
+ raise(Exception.new("Verb can have only one variadic anonymous argument! (argument '#{arg.name}')")) if @args.of_type(KXI::CLI::AnonymousArgument).any { |i| i.variadic? }
256
+ raise(Exception.new("Verb cannot contain both optional and variadic anonymous arguments! (argument '#{arg.name}')")) if @args.of_type(KXI::CLI::AnonymousArgument).any { |i| not i.required? }
257
+ end
258
+ @args.add(arg)
259
+ @verbs.foreach { |i| i.add_argument(arg) }
260
+ return arg
261
+ end
262
+
263
+ # Adds child verb
264
+ # @param v [KXI::CLI::Verb] Verb to add
265
+ def add_verb(v)
266
+ @args.foreach { |a| v.add_argument(a) }
267
+ @verbs.add(v)
268
+ v.enable_help if @help
269
+ return v
270
+ end
271
+
272
+ def check_name(nm)
273
+ raise(Exception.new('Name must contain at least two characters!')) if nm == nil or nm.length < 2
274
+ nm = nm.downcase
275
+ raise(Exception.new("Name '#{nm}' is already in use!")) unless @verbs.all { |i| i.name != nm } && @args.all { |i| i.name != nm }
276
+ end
277
+
278
+ private :check_name
279
+ end
280
+ end
281
+ end