ark-cli 0.6.0 → 0.6.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a54741976deb70204c112ab5ee3ec996463ddaf6
4
- data.tar.gz: bf135aed0b692a6d591dff1160773a83b896ad32
3
+ metadata.gz: 7c49459aed6110db0623fdd6a8404476f396e7c5
4
+ data.tar.gz: 0d928dbdf78fc4f50b8bc304b7bca4ba2897048c
5
5
  SHA512:
6
- metadata.gz: b84c3e4562701df554ab07d821140ff4cb02c765c6aebe8d633f4a54f30e595b8d5f7eda3bf0c3b6046f2c4bca6c03eee032c790139cdcc5b431f7ef72207811
7
- data.tar.gz: 819682f6427f6d4e0397a43f11ae91cb83ad54b6e4ef411dacded492c7384a6bfa43af917248cb9b1ff6f4fd8a57986f31ee3debb82f3ba59ad5242aa26b95c2
6
+ metadata.gz: dafceeb2dba2b2d27f70124c298814a750841dc51f8140c81df519cb5b6cdfabc3af7e2b8adc30e0a8f5bfafddb379bfac67fb4d3ce26d79cd40b7134927c09a
7
+ data.tar.gz: aad0ec6ed111baa93a6e430dc2b3050b474176c7f888e0d8252cfa551ecf5a5e69d0efd864049383ecfebdcacdbf93d3e168b271db73a3a0b43d50ea4e022823
@@ -18,8 +18,9 @@
18
18
  require 'ark/utility'
19
19
  include Ark::Log
20
20
 
21
- require_relative 'cli/interface.rb'
21
+ require_relative 'cli/argument.rb'
22
22
  require_relative 'cli/option.rb'
23
+ require_relative 'cli/interface.rb'
23
24
  require_relative 'cli/spec.rb'
24
25
  require_relative 'cli/report.rb'
25
26
 
@@ -0,0 +1,127 @@
1
+ module Ark # :nodoc:
2
+ module CLI
3
+
4
+ # Represents an argument, either to the program itself or for options which
5
+ # take arguments.
6
+ class Argument
7
+
8
+ # Raised when invalid argument syntax is given
9
+ class ArgumentSyntaxError < ArgumentError
10
+ end
11
+
12
+ # Raised when an argument value is set improperly
13
+ class ArgumentSetError < RuntimeError
14
+ end
15
+
16
+ # Parse an argument name and return an Argument object
17
+ def self.parse(arg)
18
+ arg = arg.to_s
19
+ name = self.strip_arg(arg)
20
+ if self.has_default?(arg)
21
+ default = self.parse_default(arg)
22
+ return Argument.new(name, default)
23
+ elsif self.is_glob?(arg)
24
+ return Argument.new(name, variad: true)
25
+ else
26
+ return Argument.new(name)
27
+ end
28
+ end
29
+
30
+ # Strip any special syntax from a given argument name
31
+ def self.strip_arg(arg)
32
+ return arg.to_s[/^(\S+?)(:|\.\.\.|$)/, 1]
33
+ end
34
+
35
+ # Return +true+ if the given argument has a default value, like +'arg:defaultvalue'+
36
+ def self.has_default?(arg)
37
+ return !arg[/^\S+?:.+/].nil?
38
+ end
39
+
40
+ # Parse the default value from an arg with one
41
+ def self.parse_default(arg)
42
+ return arg[/^.+?:(.+)/, 1]
43
+ end
44
+
45
+ # Return +true+ if the given argument is a glob, like +'arg...'+
46
+ def self.is_glob?(arg)
47
+ return !arg[/\.\.\.$/].nil?
48
+ end
49
+
50
+ # Validate an option name. Names must be alphanumeric, and must begin with a
51
+ # letter.
52
+ def self.valid_name?(name)
53
+ return !name.to_s[/^[[:alpha:]][[:alnum:]]+$/].nil?
54
+ end
55
+
56
+ # Initialize a new Argument object. +name+ must be alphanumeric and must begin
57
+ # with a letter. If this argument is unfulfilled, +default+ will be returned
58
+ # as its value, if default is non-nil. If +variad+ is true, then this argument
59
+ # will act as a glob for all trailing args.
60
+ def initialize(name, default=nil, variad: false)
61
+ unless self.class.valid_name?(name)
62
+ raise ArgumentSyntaxError, "Invalid argument name: #{name}"
63
+ end
64
+ @name = name.to_s
65
+ @default = default
66
+ @variad = variad
67
+ if self.variadic?
68
+ @value = []
69
+ else
70
+ @value = nil
71
+ end
72
+ end
73
+
74
+ # Return the name of this Argument
75
+ attr_reader :name
76
+
77
+ # Return the value for this argument. The default value will be returned if
78
+ # the argument is unset and the default is non-nil. If the argument is unset
79
+ # and there is no default, return nil.
80
+ def value
81
+ if @value.nil?
82
+ return @default
83
+ else
84
+ return @value
85
+ end
86
+ end
87
+
88
+ # Push +val+ onto this argument. Only valid for variadic args. For normal
89
+ # arguments, use #set instead.
90
+ def push(val)
91
+ unless self.variadic?
92
+ raise ArgumentSetError, "Cannot push onto a normal argument. Use the #set method instead."
93
+ end
94
+ @value << val
95
+ end
96
+
97
+ # Set the value for this argument to +val+. Only valid for non-variadic
98
+ # arguments. For variadic args, use #push instead.
99
+ def set(val)
100
+ if self.variadic?
101
+ raise ArgumentSetError, "Cannot set the value of a glob, use the #push method instead."
102
+ end
103
+ @value = val
104
+ end
105
+
106
+ # Return true if this argument is a glob
107
+ def variadic?
108
+ return @variad
109
+ end
110
+
111
+ # Return true if this argument has a default value. Variadic arguments always
112
+ # return true
113
+ def has_default?
114
+ return !@default.nil? || self.variadic?
115
+ end
116
+
117
+ # Return true if this argument has been given a value, or if it has a default
118
+ # value. Variadic arguments will always return true, since they are never
119
+ # required and always have a default value of +[]+
120
+ def fulfilled?
121
+ return !@value.nil? || self.has_default?
122
+ end
123
+ end
124
+
125
+ end # module CLI
126
+ end # module Ark
127
+
@@ -32,7 +32,10 @@ class Interface
32
32
  @input = input
33
33
  @spec = Spec.new
34
34
  yield @spec
35
- @spec.opt :help, :h, desc: "Print usage information"
35
+ @spec.opt :help, :h, desc: "Print usage information and exit"
36
+ if @spec.get_version
37
+ @spec.opt :version, :V, desc: "Print version information and exit"
38
+ end
36
39
  self.parse
37
40
  end
38
41
 
@@ -40,13 +43,13 @@ class Interface
40
43
  def parse()
41
44
  taking_options = true
42
45
  last_opt = nil
43
- refargs = @spec.get_args.clone
44
46
 
45
47
  args = []
46
48
  trailing = []
47
49
  named = {}
48
50
  options = {}
49
51
  counts = {}
52
+ arg_index = 0
50
53
 
51
54
  @input.each do |word|
52
55
  dbg "Parsing '#{word}'"
@@ -82,48 +85,47 @@ class Interface
82
85
  else
83
86
  dbg "Parsed output arg", 1
84
87
  taking_options = false
85
- args << word
86
- key = refargs.shift
87
- if key
88
- if key == @spec.get_variad
89
- named[key] = []
90
- named[key] << word
91
- else
92
- named[key] = word
93
- end
88
+ arg = @spec.get_args.values[arg_index]
89
+ arg_index += 1
90
+ if arg && !arg.variadic?
91
+ arg.set(word)
94
92
  elsif @spec.is_variadic?
95
- named[@spec.get_variad] << word
93
+ @spec.get_variad.push(word)
96
94
  else
97
95
  trailing << word
98
96
  end
99
97
  end
100
98
  end
101
99
  end
102
- if @spec.trailing_error && !trailing.empty?
103
- raise InterfaceError, "Error: got trailing option(s): #{trailing.join(', ')}"
104
- end
100
+
105
101
  @spec.get_opts.each do |name, opt|
106
102
  options[name] = opt.value
107
103
  counts[name] = opt.count
108
104
  end
109
- @spec.get_args.each do |name|
110
- if named[name].nil?
111
- if @spec.has_default?(name)
112
- named[name] = @spec.get_default(name)
113
- args << named[name]
114
- else
115
- unless @spec.is_variadic? && @spec.get_variad == name
116
- raise InterfaceError, "Required argument '#{name.upcase}' was not given."
117
- end
118
- end
119
- end
120
- end
121
- if @spec.is_variadic?
122
- named[@spec.get_variad] ||= []
105
+
106
+ @spec.get_args.each do |name, arg|
107
+ args << arg.value
108
+ named[name] = arg.value
123
109
  end
110
+ args.flatten!
111
+ args += trailing
112
+
124
113
  @report = Report.new(args, named, trailing, options, counts)
114
+
125
115
  if @report.opt(:help)
126
- self.print_usage()
116
+ self.print_usage
117
+ end
118
+
119
+ if @report.opt(:version)
120
+ self.print_version
121
+ end
122
+
123
+ unless @spec.get_args.values.all? {|arg| arg.fulfilled? }
124
+ raise InterfaceError, "Required argument '#{name.upcase}' was not given."
125
+ end
126
+
127
+ if @spec.trailing_error && !@report.trailing.empty?
128
+ raise InterfaceError, "Error: got trailing argument(s): #{trailing.join(', ')}"
127
129
  end
128
130
  end
129
131
 
@@ -131,69 +133,72 @@ class Interface
131
133
  def usage()
132
134
  tb = TextBuilder.new()
133
135
 
134
- tb.next 'USAGE:'
135
- tb.push @spec.get_name if @spec.get_name
136
+ tb.next('USAGE:')
137
+ tb.push(@spec.get_name) if @spec.get_name
136
138
 
137
139
  if @spec.get_opts.values.uniq.length < 5 || @spec.option_listing
138
140
  @spec.get_opts.values.uniq.each do |opt|
139
- tb.push "[#{opt.header}]"
141
+ tb.push("[#{opt.header}]")
140
142
  end
141
143
  else
142
- tb.push '[OPTION...]'
144
+ tb.push('[OPTION...]')
143
145
  end
144
146
 
145
147
  if @spec.has_args?
146
148
  if @spec.is_variadic?
147
- singles = @spec.get_args[0..-2].map do |a|
148
- if @spec.has_default?(a)
149
- a = "[#{a}]"
149
+ singles = @spec.get_args.values[0..-2].map do |a|
150
+ name = a.name
151
+ if a.has_default?
152
+ name = "[#{name}]"
150
153
  end
151
- a.upcase
154
+ name.upcase
152
155
  end
153
- tb.push singles
154
- v = @spec.get_args.last.upcase
155
- tb.push "[#{v}1 #{v}2...]"
156
+ tb.push(singles)
157
+ v = @spec.get_variad.name.upcase
158
+ tb.push("[#{v}1 #{v}2...]")
156
159
  else
157
- argmap = @spec.get_args.map do |a|
158
- if @spec.has_default?(a)
159
- a = "[#{a}]"
160
+ argmap = @spec.get_args.values.map do |a|
161
+ name = a.name
162
+ if a.has_default?
163
+ name = "[#{name}]"
160
164
  end
161
- a.upcase
165
+ name.upcase
162
166
  end
163
- tb.push argmap
167
+ tb.push(argmap)
164
168
  end
165
169
  end
166
170
 
167
- tb.wrap indent: 7, indent_after: true, segments: true
171
+ tb.wrap(indent: 7, indent_after: true, segments: true)
168
172
 
169
173
  if @spec.get_desc
170
- tb.skip @spec.get_desc
174
+ tb.skip(@spec.get_desc)
171
175
  tb.wrap(indent: 4)
172
176
  end
173
177
 
174
- tb.skip 'OPTIONS:'
175
- tb.skip
178
+ tb.skip('OPTIONS:').skip
176
179
 
177
180
  @spec.get_opts.values.uniq.each do |opt|
178
- tb.indent 4
179
- tb.push opt.header
181
+ tb.indent(4).push(opt.header)
180
182
  if opt.desc
181
- tb.next
182
- tb.indent 8
183
- tb.push opt.desc
183
+ tb.next.indent(8).push(opt.desc)
184
184
  end
185
185
  tb.skip
186
186
  end
187
187
 
188
- return tb.print
188
+ return tb.to_s
189
189
  end
190
190
 
191
191
  # Print usage information and exit
192
- def print_usage()
192
+ def print_usage
193
193
  puts self.usage
194
194
  exit 0
195
195
  end
196
196
 
197
+ def print_version
198
+ puts @spec.get_version
199
+ exit 0
200
+ end
201
+
197
202
  end # class Interface
198
203
 
199
204
  end # module CLI
@@ -1,4 +1,4 @@
1
- module Ark
1
+ module Ark # :nodoc:
2
2
  module CLI
3
3
 
4
4
  # Represents an option and stores the option's current state, as well as
@@ -9,15 +9,14 @@ class Option
9
9
  # [+keys+] A list of names this option will be identified by
10
10
  # [+args+] A list of argument named this option will expect
11
11
  # [+desc+] A short description of this option
12
- def initialize(long, short=nil, args=nil, defaults=nil, desc=nil)
13
- @long = long
14
- @short = short
15
- @args = args || []
16
- @defaults = defaults || {}
17
- @vals = []
18
- @flag = false
19
- @count = 0
20
- @desc = desc || ''
12
+ def initialize(long, short=nil, args=nil, desc=nil)
13
+ @long = long
14
+ @short = short
15
+ @args = args || []
16
+ @flag = false
17
+ @count = 0
18
+ @desc = desc || ''
19
+ @arg_index = 0
21
20
  end
22
21
 
23
22
  # A count of how many times this option has been given on the command line.
@@ -39,19 +38,14 @@ class Option
39
38
  return @args.length
40
39
  end
41
40
 
42
- # Return a count of how many arguments this option still expects
43
- def vals_needed()
44
- if self.flag?
45
- return 0
46
- else
47
- return @args.length - @vals.length
48
- end
49
- end
50
-
51
41
  # True if this option has received all the arguments it expects, or if this
52
42
  # option expects no arguments
53
43
  def full?
54
- return self.vals_needed == 0
44
+ if self.flag?
45
+ return true
46
+ else
47
+ return (@args.length - @arg_index) < 1
48
+ end
55
49
  end
56
50
 
57
51
  # True if this option expects no arguments; opposite of #has_args?
@@ -71,9 +65,11 @@ class Option
71
65
  end
72
66
  end
73
67
 
74
- # Pass an argument +arg+ to this option
75
- def push(arg)
76
- @vals << arg
68
+ # Pass an argument to this option
69
+ def push(val)
70
+ arg = @args[@arg_index]
71
+ @arg_index += 1
72
+ arg.set(val)
77
73
  end
78
74
 
79
75
  # Return the current value of this option
@@ -81,17 +77,9 @@ class Option
81
77
  if self.flag?
82
78
  return @flag
83
79
  else
84
- if self.full? && @vals.length == 1
85
- return @vals[0]
86
- elsif self.full?
87
- return @vals
88
- else
89
- if !@defaults.compact.empty?
90
- return @defaults.length > 1 ? @defaults : @defaults.first
91
- else
92
- return nil
93
- end
94
- end
80
+ vals = @args.map {|a| a.value }
81
+ vals = vals.first if vals.length == 1
82
+ return vals
95
83
  end
96
84
  end
97
85
 
@@ -105,7 +93,7 @@ class Option
105
93
  if self.flag?
106
94
  args = ''
107
95
  else
108
- args = ' ' + @args.join(', ').upcase
96
+ args = ' ' + @args.map {|a| a.name }.join(', ').upcase
109
97
  end
110
98
  short = @short ? "-#{@short} " : ''
111
99
  return "#{short}--#{@long}#{args}"
@@ -27,6 +27,11 @@ class Report
27
27
  return @trailing
28
28
  end
29
29
 
30
+ # Get a hash of all options and their values
31
+ def opts
32
+ return @options
33
+ end
34
+
30
35
  # Get the value of an option by +name+
31
36
  def opt(name)
32
37
  return @options[name.to_s]
@@ -17,7 +17,7 @@ class Spec
17
17
 
18
18
  # Initialize a bare interface +Spec+
19
19
  def initialize()
20
- @args = []
20
+ @arguments = {}
21
21
  @options = {}
22
22
  @variadic = false
23
23
  @option_listing = false
@@ -31,49 +31,6 @@ class Spec
31
31
  # If true, an error will be raised if trailing arguments are given
32
32
  attr_reader :trailing_error
33
33
 
34
- private
35
-
36
- # Strip any special syntax from a given argument. Used when parsing arguments
37
- def strip(arg)
38
- return arg[/^(\S+?)(:|\.\.\.|$)/, 1]
39
- end
40
-
41
- # Return +true+ if the given argument has a default value, like +'arg:defaultvalue'+
42
- def defaulted?(arg)
43
- return !arg[/^\S+?:.+/].nil?
44
- end
45
-
46
- # Parse the default value from an arg with one
47
- def parse_default(arg)
48
- return arg[/^.+?:(.+)/, 1]
49
- end
50
-
51
- # Return +true+ if the given argument is a glob, like +'arg...'+
52
- def variadic?(arg)
53
- return !arg[/\.\.\.$/].nil?
54
- end
55
-
56
- # Parse a given argument, interpreting any special syntax, and storing
57
- # argument information as needed
58
- def parse_arg(arg, default: nil, last: false)
59
- stripped = strip(arg)
60
- @args << stripped
61
- if defaulted?(arg)
62
- @defaults[stripped] = parse_default(arg)
63
- end
64
- if variadic?(arg)
65
- if last
66
- @variadic = true
67
- @variad = stripped
68
- else
69
- raise ArgumentSyntaxError,
70
- "Variadic arguments must come last. Offending variad is '#{arg}'"
71
- end
72
- end
73
- end
74
-
75
- public
76
-
77
34
  # Get the name defined for this spec
78
35
  def get_name
79
36
  return @name
@@ -86,7 +43,12 @@ class Spec
86
43
 
87
44
  # Get an array of argument names defined for this spec
88
45
  def get_args
89
- return @args
46
+ return @arguments
47
+ end
48
+
49
+ # Get version information defined for this spec
50
+ def get_version
51
+ return @version
90
52
  end
91
53
 
92
54
  # Return +true+ if this interface has any options defined for it
@@ -108,6 +70,15 @@ class Spec
108
70
  return @options[name]
109
71
  end
110
72
 
73
+ # Get an +Argument+ object for the given argument +name+
74
+ def get_arg(name)
75
+ name = name.to_s
76
+ if !@arguments.keys.member?(name)
77
+ raise NoSuchArgumentError, "Error, no such argument: '#{name}'"
78
+ end
79
+ return @arguments[name]
80
+ end
81
+
111
82
  # Return +true+ if this interface is variadic
112
83
  def is_variadic?
113
84
  return @variadic
@@ -118,34 +89,21 @@ class Spec
118
89
  return @variad
119
90
  end
120
91
 
121
- # Return +true+ if the given argument +arg+ has a default value
122
- def has_default?(arg)
123
- @defaults.key?(arg.to_s)
124
- end
125
-
126
- # Return a hash of all default values
127
- def get_defaults
128
- return @defaults
129
- end
130
-
131
- # Return the default value of the given argument +arg+
132
- def get_default(arg)
133
- @defaults[arg.to_s]
134
- end
135
-
136
92
  # Return +true+ if this interface has any arguments defined
137
93
  def has_args?
138
- @args.length > 0
94
+ @arguments.length > 0
139
95
  end
140
96
 
141
97
  # Specify general information about the program
142
98
  # [+name+] Name of the program
143
99
  # [+desc+] Short description of the program
144
100
  # [+args+] A list of named arguments
145
- def header(name: nil, desc: nil, args: [])
101
+ # [+version+] Current version information for the program
102
+ def header(name: nil, desc: nil, args: [], version: nil)
146
103
  self.name(name)
147
104
  self.desc(desc)
148
105
  self.args(args)
106
+ self.version(version)
149
107
  end
150
108
 
151
109
  # Set the name of the program to +str+
@@ -160,16 +118,26 @@ class Spec
160
118
 
161
119
  # Define what arguments the program will accept
162
120
  def args(*input)
163
- @args = []
164
- @defaults = {}
165
-
121
+ @arguments = {}
166
122
  input.flatten.each_with_index do |item, i|
167
123
  item = item.to_s
168
124
  last = (input.length - (i + 1)) == 0
169
- parse_arg(item, last: last)
125
+ arg = Argument.parse(item)
126
+ if arg.variadic?
127
+ if !last
128
+ raise ArgumentSyntaxError, "Variadic arguments must come last. Offending variad is '#{arg}'"
129
+ else
130
+ @variadic = true
131
+ @variad = arg
132
+ end
133
+ end
134
+ @arguments[arg.name] = arg
170
135
  end
136
+ end
171
137
 
172
- @refargs = @args.clone
138
+ # Set the version information for the program to +str+
139
+ def version(str)
140
+ @version = str.to_s if str
173
141
  end
174
142
 
175
143
  # Define an Option
@@ -180,20 +148,8 @@ class Spec
180
148
  long = long.to_s
181
149
  short = short.to_s if short
182
150
  args = [args] if args.is_a?(String)
183
- defaults = []
184
- if args
185
- args.map! do |arg|
186
- arg = arg.to_s
187
- if defaulted?(arg)
188
- defaults << parse_default(arg)
189
- arg = strip(arg)
190
- else
191
- defaults << nil
192
- end
193
- arg
194
- end
195
- end
196
- o = Option.new(long, short, args, defaults, desc)
151
+ args.map! {|a| Argument.parse(a) } if args
152
+ o = Option.new(long, short, args, desc)
197
153
  @options[long] = o
198
154
  @options[short] = o if short
199
155
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ark-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Macquarie Sharpless
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-09 00:00:00.000000000 Z
11
+ date: 2015-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ark-util
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.3'
19
+ version: '0.4'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 0.3.0
22
+ version: 0.4.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: '0.3'
29
+ version: '0.4'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 0.3.0
32
+ version: 0.4.0
33
33
  description: |
34
34
  A library for parsing options and arguments from the commandline. Parses ARGV
35
35
  and returns an object holding information about what options were set on the
@@ -42,6 +42,7 @@ extra_rdoc_files: []
42
42
  files:
43
43
  - README.md
44
44
  - lib/ark/cli.rb
45
+ - lib/ark/cli/argument.rb
45
46
  - lib/ark/cli/interface.rb
46
47
  - lib/ark/cli/option.rb
47
48
  - lib/ark/cli/report.rb