consoler 1.0.3 → 1.1.0

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
- SHA1:
3
- metadata.gz: 317d28b9a15f0ea07c01410191cac9d0d59a41dd
4
- data.tar.gz: f9a363ba2b596a02df673c1600441f2f6d7b7010
2
+ SHA256:
3
+ metadata.gz: 49ca5e9011acc8b6817962ab10c90bae3b91b8f2e0fe351001bdd55b84d0338d
4
+ data.tar.gz: f8ced34f84e72074986fad4b16fcfd1ae0725ffaaade46b69d876b028f327ffd
5
5
  SHA512:
6
- metadata.gz: b42c0a91ed2df03ef7d76796f1da5e986782d2d0415a14167159f34b10b76eb939e97f378109328bdd768c7f234607907871bcd03e2e5ad097180c7edaf385c6
7
- data.tar.gz: df6627a850424d6053c4525bea35014c1c8ec42bbe9cbe43f981119b535a747af53c4130dec92242309d36891c9e7368450c49838c37c55ea2662a682582fb26
6
+ metadata.gz: 3ab6c3d9629accdc064f3b74cb0458f65a14615e89a81e892e07e9498a7fd28607efcd439fc53278ad4e8be86c54b5031b891b0b9d67c5e6496c456e2f049b74
7
+ data.tar.gz: 14c391a50764df0359e0483fd5e59fcd2d765c982e69ea3d15e4b0ff6c5a00270b5f31df397cc2b5275f20d7d9c4e6e56e3000479f78d5e416f5c6287445bc5c
@@ -0,0 +1,45 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.2
3
+
4
+ Exclude:
5
+ - 'test/**/*'
6
+
7
+ # 80 is not enough these days
8
+ Metrics/LineLength:
9
+ Max: 100
10
+
11
+ # Not ready for 100 yet
12
+ Metrics/ClassLength:
13
+ Max: 250
14
+
15
+ # Not ready for 20 yet
16
+ Metrics/MethodLength:
17
+ Max: 100
18
+
19
+ Metrics/AbcSize:
20
+ Enabled: false
21
+
22
+ # not ready for 10 just yet
23
+ Metrics/CyclomaticComplexity:
24
+ Max: 30
25
+
26
+ Metrics/BlockNesting:
27
+ Enabled: false
28
+
29
+ Metrics/PerceivedComplexity:
30
+ Enabled: false
31
+
32
+ # I prefer regular if statements in some cases
33
+ Style/IfUnlessModifier:
34
+ Enabled: false
35
+ Style/GuardClause:
36
+ Enabled: false
37
+
38
+ # `super` makes no sense in this case
39
+ # `#respond_to_missing?` is implemented
40
+ Style/MethodMissingSuper:
41
+ Enabled: false
42
+
43
+ # I like the comma
44
+ Style/TrailingCommaInArguments:
45
+ Enabled: false
@@ -1,10 +1,11 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.2.9
5
- - 2.3.6
6
- - 2.4.3
7
- - 2.5.0
4
+ - 2.2.10
5
+ - 2.3.8
6
+ - 2.4.5
7
+ - 2.5.3
8
+ - 2.6.1
8
9
  cache:
9
10
  - bundler
10
11
  script:
data/README.md CHANGED
@@ -9,17 +9,19 @@
9
9
  app = Consoler::Application.new description: 'A simple app'
10
10
 
11
11
  # define a command
12
- app.build '[--clean] output_dir' do |clean, output_dir|
12
+ app.build '[--clean] <output_dir>' do |clean, output_dir|
13
13
  # clean contains a boolean
14
14
  clean_up if clean
15
15
 
16
16
  # output_dir contains a string
17
17
  build_project output_dir
18
18
  end
19
- app.run(['build', 'production', '--clean'])
19
+
20
+ # run with own args
21
+ app.run ['build', 'production', '--clean']
20
22
 
21
23
  # this does not match, nothing is executed and the usage message is printed
22
- app.run(['deploy', 'production'])
24
+ app.run ['deploy', 'production']
23
25
 
24
26
  # defaults to ARGV
25
27
  app.run
@@ -30,6 +32,7 @@ app.run
30
32
  - No fiddling with `ARGV` and friends
31
33
  - Arguments filled based on their name, for easy access
32
34
  - Grouped optionals
35
+ - Easy option aliasing
33
36
 
34
37
  ## Requirements
35
38
 
@@ -39,6 +42,7 @@ Tests are run against multiple ruby versions (latest supported):
39
42
  - `2.3.x`
40
43
  - `2.4.x`
41
44
  - `2.5.x`
45
+ - `2.6.x`
42
46
 
43
47
  No other requirements exist.
44
48
 
@@ -51,20 +55,20 @@ gem install consoler
51
55
  or add to your `Gemfile` for applications
52
56
 
53
57
  ```ruby
54
- gem 'consoler', '~> 1.0.3'
58
+ gem 'consoler', '~> 1.1.0'
55
59
  ```
56
60
 
57
61
  or to your `.gemspec` file for gems
58
62
 
59
63
  ```ruby
60
64
  Gem::Specification.new do |spec|
61
- spec.add_dependency 'consoler', '~> 1.0.3'
65
+ spec.add_dependency 'consoler', '~> 1.1.0'
62
66
  end
63
67
  ```
64
68
 
65
69
  ## Docs
66
70
 
67
- Full API documentation can the found here: http://www.rubydoc.info/github/justim/consoler-rb/
71
+ Full API documentation can the found here: https://www.rubydoc.info/github/justim/consoler-rb/
68
72
 
69
73
  ### API
70
74
 
@@ -93,11 +97,11 @@ app.build do
93
97
  end
94
98
 
95
99
  # to add options to your command, provide a options definition as an argument
96
- app.build '[--clean] [--env=] [-v] output_dir' do |clean, env, v, output_dir|
100
+ app.build '[--clean] [--env=] [-v|--verbose] <output_dir>' do |clean, env, verbose, output_dir|
97
101
  # parameters are matched based on name
98
102
  # `clean` contains a boolean
99
103
  # `env` contains a string or nil if not provided
100
- # `v` contains a number, counting the times it occurred in the arguments
104
+ # `verbose` contains a number, counting the times it occurred in the arguments
101
105
  # `output_dir` contains a string, if needed to match this command
102
106
  end
103
107
  ```
@@ -111,23 +115,23 @@ end
111
115
  app = Consoler::Application.new
112
116
 
113
117
  # a build command
114
- app.build '[--clean] [--env=] [-v] output_dir' do |clean, env, v, output_dir|
115
- puts 'Starting build...' if v > 0
118
+ app.build '[--clean] [--env=] [-v|--verbose] <output_dir>' do |clean, env, verbose, output_dir|
119
+ puts 'Starting build...' if verbose > 0
116
120
 
117
121
  do_clean_up if clean
118
122
 
119
123
  do_build env || 'development', output_dir
120
124
 
121
- puts 'Build complete' if v > 0
125
+ puts 'Build complete' if verbose > 0
122
126
  end
123
127
 
124
128
  # a deploy command
125
- app.build '[-v] [--env=]' do |env|
126
- puts 'Starting deploy...' if v > 0
129
+ app.build '[-v|--verbose] [--env=]' do |env|
130
+ puts 'Starting deploy...' if verbose > 0
127
131
 
128
132
  do_deploy env || 'development'
129
133
 
130
- puts 'Deploy complete' if v > 0
134
+ puts 'Deploy complete' if verbose > 0
131
135
  end
132
136
  ```
133
137
 
@@ -137,29 +141,34 @@ _Shell commands:_
137
141
  ruby app.rb build --env production dist
138
142
 
139
143
  # deploy
140
- ruby app.rb deploy
144
+ ruby app.rb deploy --verbose
141
145
  ```
142
146
 
143
147
  #### Options definition
144
148
 
145
- | Option | Meaning | Example |
146
- | ------------ | ------------------------------- | -------------------------------------- |
147
- | `-f` | Short option with name `f` | `ruby app.rb build -f` |
148
- | `--clean` | Long option with name `clean` | `ruby app.rb build --clean` |
149
- | `output_dir` | Argument with name `output_dir` | `ruby app.rb build dist/` |
150
- | `--env=` | Long option with value | `ruby app.rb build --env production` |
151
- | `-e=` | Short option with value | `ruby app.rb build -e production` |
152
- | `[ .. ]` | Optional options/arguments | `ruby app.rb build` would match `[-v]` |
149
+ | Option | Meaning | Example |
150
+ | -------------- | ------------------------------- | -------------------------------------- |
151
+ | `-f` | Short option with name `f` | `ruby app.rb build -f` |
152
+ | `--clean` | Long option with name `clean` | `ruby app.rb build --clean` |
153
+ | `-v|--verbose` | Alias option for `v` | `ruby app.rb build --verbose` |
154
+ | `<output_dir>` | Argument with name `output_dir` | `ruby app.rb build dist/` |
155
+ | `--env=` | Long option with value | `ruby app.rb build --env production` |
156
+ | `-e=` | Short option with value | `ruby app.rb build -e production` |
157
+ | `[ .. ]` | Optional options/arguments | `ruby app.rb build` would match `[-v]` |
153
158
 
154
- Optional-tokens can occur anywhere in the options, as long as they are not nested and properly closed. You can group optional parts, meaning that both options/arguments should be available or both not.
159
+ Some notes about these options:
155
160
 
156
- Options and/or arguments are mandatory unless specified otherwise.
161
+ - The `<`, `>` around the argument name a optional, but are always shown in de usage message for better legibility
162
+ - Optional-tokens can occur anywhere in the options, as long as they are not nested and properly closed. You can group optional parts, meaning that both options/arguments should be available or both not.
163
+ - Options and/or arguments are mandatory unless specified otherwise.
164
+ - Aliases should be the same "type", meaning that if the original option expands an value, the alias must do as well
165
+ - Aliases are only allowed for short and long options, with or without value and can be optional, just like regular options
157
166
 
158
167
  Grouping of optionals allows you do things like this:
159
168
 
160
169
  ```ruby
161
170
  app = Consoler::Application.new
162
- app.shout '[first_name last_name] [name]' do |first_name, last_name, name|
171
+ app.shout '[<first_name> <last_name>] [<name>]' do |first_name, last_name, name|
163
172
  # by definition, `last_name` is also filled
164
173
  unless first_name.nil? then
165
174
  puts "Hello #{first_name} #{last_name}!"
@@ -172,11 +181,11 @@ end
172
181
 
173
182
  # calling with two arguments can fill the first group
174
183
  # prints "Hello John Doe!"
175
- app.run(['shout', 'John', 'Doe'])
184
+ app.run ['shout', 'John', 'Doe']
176
185
 
177
186
  # calling with one argument it is not possible to fill the first group
178
187
  # prints "Hello Mr. White!"
179
- app.run(['shout', 'Mr. White!'])
188
+ app.run ['shout', 'Mr. White!']
180
189
  ```
181
190
 
182
191
  #### Return types in action block
@@ -188,6 +197,8 @@ app.run(['shout', 'Mr. White!'])
188
197
  | Value | `String` (`production`) | `nil` |
189
198
  | Argument | `String` (`dist/`) | `nil` |
190
199
 
200
+ Aliased keep the properties as the original options; with a definition of `-v|--verbose` and providing `--verbose` as an argument, both `v` and `verbose` contain a number (`1` in this case). Same goes the other way around; with a definition of `--force|-f` and providing `-f` as an argument, both `force` and `f` contain a boolean (`true` in this case).
201
+
191
202
  #### Subapplications
192
203
 
193
204
  To make application nesting possible you can provide a complete application to a command, instead of an action block.
@@ -200,7 +211,7 @@ android = Consoler::Application.new
200
211
  android.build do; end
201
212
 
202
213
  # options are supported just like regular apps
203
- android.clean '--force' do; end
214
+ android.clean '--force|-f' do; end
204
215
 
205
216
  # create an application
206
217
  app = Consoler::Application.new
@@ -245,7 +256,7 @@ app = Consoler::Application.new
245
256
  app.db db
246
257
  app.cache cache
247
258
 
248
- app.build '[--clean] [--env=] [-v] output_dir -- build the project' do |clean, env, v, output_dir|
259
+ app.build '[--clean] [--env=] [-v] <output_dir> -- build the project' do |clean, env, v, output_dir|
249
260
  puts 'Starting build...' if v > 0
250
261
 
251
262
  if clean
@@ -274,10 +285,6 @@ Make the file executable with `chmod a+x app.rb`, you can now call it with `./ap
274
285
  ./app.rb
275
286
  ```
276
287
 
277
- ### Current wish-list
278
-
279
- - Aliases, mostly to "document" short options, `-v|--verbose`
280
-
281
288
  ## Final notes
282
289
 
283
290
  If you like what you see, feel free to use it anyway you like. Also, don't hold back if you have suggestions/question and create an issue to let me know, and we can talk about it. And if you don't like what you see, PRs are welcome. You should probably file an issue first to make sure it's something worth doing, and the right way to do it.
@@ -22,9 +22,10 @@ Gem::Specification.new do |spec|
22
22
  # build docs on install
23
23
  spec.metadata['yard.run'] = 'yri'
24
24
 
25
+ spec.required_ruby_version = '>= 2.2'
25
26
  spec.add_development_dependency 'bundler', '~> 1.15'
26
- spec.add_development_dependency 'rake', '~> 12.3.0'
27
27
  spec.add_development_dependency 'minitest', '~> 5.11.3'
28
- spec.add_development_dependency 'yard', '~> 0.9.12'
28
+ spec.add_development_dependency 'rake', '~> 12.3.0'
29
29
  spec.add_development_dependency 'simplecov', '~> 0.15.1'
30
+ spec.add_development_dependency 'yard', '~> 0.9.12'
30
31
  end
@@ -4,7 +4,6 @@ require_relative 'options'
4
4
  require_relative 'arguments'
5
5
 
6
6
  module Consoler
7
-
8
7
  # Consoler application
9
8
  #
10
9
  # @example A simple application
@@ -24,16 +23,25 @@ module Consoler
24
23
  # # this does not match, nothing is executed and the usage message is printed
25
24
  # app.run(['deploy', 'production'])
26
25
  class Application
27
-
28
26
  # Create a consoler application
29
27
  #
30
28
  # @param options [Hash] Options for the application
31
29
  # @option options [String] :description The description for the application (optional)
32
- def initialize(options={})
30
+ def initialize(options = {})
33
31
  @description = options[:description]
34
32
  @commands = []
35
33
  end
36
34
 
35
+ # Accept all method_missing call
36
+ #
37
+ # We use the name as a command name, thus we accept all names
38
+ #
39
+ # @param _method_name [String] Name of the method
40
+ # @param _include_private [bool] Name of the method
41
+ def respond_to_missing?(_method_name, _include_private = false)
42
+ true
43
+ end
44
+
37
45
  # Register a command for this app
38
46
  #
39
47
  # @param command_name [Symbol] Name of the command
@@ -44,21 +52,21 @@ module Consoler
44
52
  action = nil
45
53
  options_def = ''
46
54
 
47
- unless block.nil? then
55
+ unless block.nil?
48
56
  action = block
49
57
  options_def = input
50
58
 
51
- if not options_def.nil? and not options_def.instance_of? String then
59
+ if !options_def.nil? && !options_def.instance_of?(String)
52
60
  raise 'Invalid options'
53
61
  end
54
62
  end
55
63
 
56
- if input.instance_of? Consoler::Application then
64
+ if input.instance_of? Consoler::Application
57
65
  action = input
58
66
  options_def = ''
59
67
  end
60
68
 
61
- if action.nil? then
69
+ if action.nil?
62
70
  raise 'Invalid subapp/block'
63
71
  end
64
72
 
@@ -66,7 +74,7 @@ module Consoler
66
74
 
67
75
  _add_command(command, options_def, action)
68
76
 
69
- return nil
77
+ nil
70
78
  end
71
79
 
72
80
  # Run the application with a list of arguments
@@ -75,15 +83,15 @@ module Consoler
75
83
  # @param disable_usage_message [Boolean] Disable the usage message when nothing it matched
76
84
  # @return [mixed] Result of your matched command, <tt>nil</tt> otherwise
77
85
  def run(args = ARGV, disable_usage_message = false)
78
- # TODO signal handling of some kind?
86
+ # TODO: signal handling of some kind?
79
87
 
80
88
  result, matched = _run(args.dup)
81
89
 
82
- if not matched and not disable_usage_message
90
+ if !matched && !disable_usage_message
83
91
  usage
84
92
  end
85
93
 
86
- return result
94
+ result
87
95
  end
88
96
 
89
97
  # Show the usage message
@@ -93,7 +101,7 @@ module Consoler
93
101
  puts "#{@description}\n\n" unless @description.nil?
94
102
  puts 'Usage:'
95
103
 
96
- _commands_usage $0
104
+ _commands_usage $PROGRAM_NAME
97
105
  end
98
106
 
99
107
  protected
@@ -107,13 +115,13 @@ module Consoler
107
115
  arguments = Consoler::Arguments.new args
108
116
 
109
117
  @commands.each do |command|
110
- if command.command == arg then
118
+ if command.command == arg
111
119
  # the matched command contains a subapp, run subapp with the same
112
120
  # arguments (excluding the arg that matched this command)
113
- if command.action.instance_of? Consoler::Application then
121
+ if command.action.instance_of?(Consoler::Application)
114
122
  result, matched = command.action._run(args)
115
123
 
116
- if matched then
124
+ if matched
117
125
  return result, true
118
126
  end
119
127
  else
@@ -126,26 +134,26 @@ module Consoler
126
134
  end
127
135
  end
128
136
 
129
- return nil, false
137
+ [nil, false]
130
138
  end
131
139
 
132
140
  # Print the usage message for this command
133
141
  #
134
142
  # @param [String] prefix A prefix for the command from a parent app
135
143
  # @return [Consoler::Application]
136
- def _commands_usage(prefix='')
144
+ def _commands_usage(prefix = '')
137
145
  @commands.each do |command|
138
146
  # print the usage message of a subapp with a prefix from the current command
139
- if command.action.instance_of? Consoler::Application then
147
+ if command.action.instance_of?(Consoler::Application)
140
148
  command.action._commands_usage "#{prefix} #{command.command}"
141
149
  else
142
150
  print " #{prefix} #{command.command}"
143
151
 
144
- if command.options.size then
152
+ if command.options.size
145
153
  print " #{command.options.to_definition}"
146
154
  end
147
155
 
148
- unless command.options.description.nil? then
156
+ unless command.options.description.nil?
149
157
  print " -- #{command.options.description}"
150
158
  end
151
159
 
@@ -165,11 +173,13 @@ module Consoler
165
173
  # @param [Proc, Consoler::Application] action Action or subapp
166
174
  # @return [Consoler::Application]
167
175
  def _add_command(command, options_def, action)
168
- @commands.push(Consoler::Command.new(
169
- command: command,
170
- options: Consoler::Options.new(options_def),
171
- action: action,
172
- ))
176
+ @commands.push(
177
+ Consoler::Command.new(
178
+ command: command,
179
+ options: Consoler::Options.new(options_def),
180
+ action: action,
181
+ )
182
+ )
173
183
 
174
184
  self
175
185
  end
@@ -183,7 +193,7 @@ module Consoler
183
193
  arguments = action.parameters.map do |parameter|
184
194
  parameter_name = parameter[1].to_s
185
195
 
186
- if match.has_key? parameter_name then
196
+ if match.key? parameter_name
187
197
  match[parameter_name]
188
198
  else
189
199
  # check for the normalized name of every match to see
@@ -191,7 +201,7 @@ module Consoler
191
201
  match.each do |name, value|
192
202
  normalized_name = _normalize name
193
203
 
194
- if parameter_name == normalized_name then
204
+ if parameter_name == normalized_name
195
205
  return value
196
206
  end
197
207
  end
@@ -212,7 +222,7 @@ module Consoler
212
222
  # also be an syntax error, like starting with a number.
213
223
  # this normalization is more of a comvenience than anything
214
224
  # else
215
- name.gsub('-', '_')
225
+ name.tr('-', '_')
216
226
  end
217
227
  end
218
228
  end
@@ -3,7 +3,6 @@
3
3
  require_relative 'matcher'
4
4
 
5
5
  module Consoler
6
-
7
6
  # Arguments
8
7
  #
9
8
  # @attr_reader [Array<String>] args Raw arguments
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Consoler
4
-
5
4
  # Consoler command
6
5
  #
7
6
  # Basically a named hash
@@ -1,12 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Consoler
4
-
5
4
  # Argument/Options matcher
6
5
  #
7
6
  # Given a list of arguments and a list option try to match them
8
7
  class Matcher
9
-
10
8
  # Create a matcher
11
9
  #
12
10
  # @param [Consoler::Arguments] arguments List of arguments
@@ -27,14 +25,14 @@ module Consoler
27
25
  parse_options = true
28
26
 
29
27
  _loop_args do |arg|
30
- unless parse_options then
28
+ unless parse_options
31
29
  @argument_values.push arg
32
30
  next
33
31
  end
34
32
 
35
33
  # when "argument" is --, then stop parsing the rest of the arguments
36
34
  # and treat the rest as regular arguments
37
- if arg == '--' then
35
+ if arg == '--'
38
36
  parse_options = false
39
37
  next
40
38
  end
@@ -49,12 +47,20 @@ module Consoler
49
47
  remaining = _match_arguments
50
48
  _fill_defaults
51
49
 
52
- if @matched_options.size == @options.size then
50
+ if @matched_options.size == @options.size
53
51
  @matched_options['remaining'] = remaining
52
+
53
+ # make sure all aliases are also filled
54
+ @options.each do |option|
55
+ option.aliases.each do |alias_|
56
+ @matched_options[alias_.name] = @matched_options[option.name]
57
+ end
58
+ end
59
+
54
60
  return @matched_options
55
61
  end
56
62
 
57
- return nil
63
+ nil
58
64
  end
59
65
 
60
66
  private
@@ -68,59 +74,58 @@ module Consoler
68
74
  is_short = false
69
75
  name = nil
70
76
 
71
- if arg[0..1] == '--' then
77
+ if arg[0..1] == '--'
72
78
  is_long = true
73
79
  name = arg[2..-1]
74
- elsif arg[0] == '-' then
80
+ elsif arg[0] == '-'
75
81
  is_short = true
76
82
  name = arg[1..-1]
77
83
  end
78
84
 
79
85
  # arg is not a long/short option, add to arguments values
80
- unless is_long or is_short then
86
+ unless is_long || is_short
81
87
  @argument_values.push arg
82
88
  return true
83
89
  end
84
90
 
85
- unless name.nil? then
91
+ unless name.nil?
86
92
  # get the name of the option, short options use the first character
87
- option_name = if is_short then
93
+ option_name = if is_short
88
94
  name[0]
89
95
  else
90
96
  name
91
97
  end
92
98
 
93
- option = @options.get option_name
99
+ option, matched = @options.get_with_alias option_name
94
100
 
95
101
  # no option by this name in options
96
102
  return nil if option.nil?
97
103
 
98
- needs_short = option.is_short
99
- needs_long = option.is_long
100
-
101
104
  # see if the type if right, short or long
102
- if needs_long and not is_long then
105
+ if matched.is_long && !is_long
103
106
  return nil
104
- elsif needs_short and not is_short then
107
+ elsif matched.is_short && !is_short
105
108
  return nil
106
109
  end
107
110
 
108
- if is_long then
109
- if option.is_value then
111
+ if is_long
112
+ if option.is_value
110
113
  # is_value needs a next argument for its value
111
114
  return nil if _peek_next.nil?
112
- @matched_options[name] = _peek_next
115
+
116
+ @matched_options[option.name] = _peek_next
113
117
  _skip_next
114
118
  else
115
- @matched_options[name] = true
119
+ option_value! option
116
120
  end
117
121
  end
118
122
 
119
- if is_short then
120
- if name.size == 1 and option.is_value then
123
+ if is_short
124
+ if name.size == 1 && option.is_value
121
125
  # is_value needs a next argument for its value
122
126
  return nil if _peek_next.nil?
123
- @matched_options[name] = _peek_next
127
+
128
+ @matched_options[option.name] = _peek_next
124
129
  _skip_next
125
130
  else
126
131
  # for every character (short option) increment the option value
@@ -128,17 +133,30 @@ module Consoler
128
133
  short_option = @options.get n
129
134
  return nil if short_option.nil?
130
135
 
131
- if @matched_options[n].nil? then
132
- @matched_options[n] = 0
133
- end
134
-
135
- @matched_options[n] += 1
136
+ option_value! short_option
136
137
  end
137
138
  end
138
139
  end
139
140
  end
140
141
 
141
- return true
142
+ true
143
+ end
144
+
145
+ # Set the value of an option
146
+ #
147
+ # Long or short option needed
148
+ #
149
+ # @param [Consoler::Option]
150
+ def option_value!(option)
151
+ if option.is_short
152
+ if @matched_options[option.name].nil?
153
+ @matched_options[option.name] = 0
154
+ end
155
+
156
+ @matched_options[option.name] += 1
157
+ else
158
+ @matched_options[option.name] = true
159
+ end
142
160
  end
143
161
 
144
162
  # Loop through the arguments
@@ -151,7 +169,7 @@ module Consoler
151
169
 
152
170
  # use an incrementing index, to be able to peek to the next in the list
153
171
  # and to skip an item
154
- while @index < size do
172
+ while @index < size
155
173
  yield @arguments.args[@index]
156
174
 
157
175
  _skip_next
@@ -197,7 +215,7 @@ module Consoler
197
215
  # arguments supplied (info available from optionals map)
198
216
  optionals.each do |_, optional|
199
217
  optional.each do |before|
200
- if before[:included] then
218
+ if before[:included]
201
219
  @matched_options[before[:name]] = @argument_values[argument_values_index]
202
220
  argument_values_index += 1
203
221
  end
@@ -205,7 +223,7 @@ module Consoler
205
223
  end
206
224
 
207
225
  # only fill mandatory argument if its not the :REMAINING key
208
- if mandatory_arg_name != :REMAINING then
226
+ if mandatory_arg_name != :REMAINING
209
227
  @matched_options[mandatory_arg_name] = @argument_values[argument_values_index]
210
228
  argument_values_index += 1
211
229
  end
@@ -214,7 +232,7 @@ module Consoler
214
232
  remaining = []
215
233
 
216
234
  # left over arguments
217
- while argument_values_index < @argument_values.size do
235
+ while argument_values_index < @argument_values.size
218
236
  remaining.push @argument_values[argument_values_index]
219
237
  argument_values_index += 1
220
238
  end
@@ -229,18 +247,18 @@ module Consoler
229
247
  @optionals_before = {}
230
248
  tracker = {}
231
249
 
232
- @options.each do |option, key|
250
+ @options.each do |option, _key|
233
251
  next unless option.is_argument
234
252
 
235
- if option.is_optional then
253
+ if option.is_optional
236
254
  # setup tracker for optional group
237
255
  tracker[option.is_optional] = [] if tracker[option.is_optional].nil?
238
256
 
239
257
  # mark all optionals as not-included
240
- tracker[option.is_optional].push({
258
+ tracker[option.is_optional].push(
241
259
  included: false,
242
260
  name: option.name,
243
- })
261
+ )
244
262
  else
245
263
  @optionals_before[option.name] = tracker
246
264
  tracker = {}
@@ -248,7 +266,7 @@ module Consoler
248
266
  end
249
267
 
250
268
  # make sure all optionals are accounted for in the map
251
- if tracker != {} then
269
+ if tracker != {}
252
270
  # use a special key so we can handle it differently in the filling process
253
271
  @optionals_before[:REMAINING] = tracker
254
272
  @optionals_before_has_remaining = true
@@ -267,7 +285,7 @@ module Consoler
267
285
  mandatories_matched = @optionals_before.size
268
286
 
269
287
  # there are optionals at the end of the options, don't match the void
270
- if @optionals_before_has_remaining then
288
+ if @optionals_before_has_remaining
271
289
  mandatories_matched -= 1
272
290
  end
273
291
 
@@ -276,11 +294,11 @@ module Consoler
276
294
  # loop through optional map
277
295
  _each_optional_before_sorted do |before|
278
296
  # are there enough arguments left to fill this optional group
279
- if (total + before.size + mandatories_matched) <= @argument_values.size then
297
+ if (total + before.size + mandatories_matched) <= @argument_values.size
280
298
  total += before.size
281
299
 
282
300
  before.each do |val|
283
- val[:included] = true;
301
+ val[:included] = true
284
302
  end
285
303
  end
286
304
  end
@@ -293,10 +311,10 @@ module Consoler
293
311
  # @return [Consoler::Matcher]
294
312
  def _fill_defaults
295
313
  @options.each do |option|
296
- if option.is_optional then
297
- unless @matched_options.has_key? option.name then
298
- @matched_options[option.name] = option.default_value
299
- end
314
+ next unless option.is_optional
315
+
316
+ unless @matched_options.key? option.name
317
+ @matched_options[option.name] = option.default_value
300
318
  end
301
319
  end
302
320
 
@@ -312,10 +330,10 @@ module Consoler
312
330
  @optionals_before.each do |_, optionals|
313
331
  tmp = []
314
332
  optionals.each do |optional_index, before|
315
- tmp.push({
333
+ tmp.push(
316
334
  count: before.size,
317
335
  index: optional_index,
318
- })
336
+ )
319
337
  end
320
338
 
321
339
  tmp.sort! { |a, b| b[:count] - a[:count] }.each do |item|
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Consoler
4
-
5
4
  # Represents an option
6
5
  #
7
6
  # @attr_reader [String] name Name of the options
@@ -10,6 +9,7 @@ module Consoler
10
9
  # @attr_reader [Boolean] is_argument Is the option an argument
11
10
  # @attr_reader [Boolean] is_value Does the option need a value (<tt>--option=</tt>)
12
11
  # @attr_reader [Integer] is_optional Is the option optional (> 0) (<tt>[option]</tt>)
12
+ # @attr_reader [Array] aliases List of aliases of option (<tt>-v|--verbose</tt>)
13
13
  class Option
14
14
  attr_reader :name
15
15
  attr_reader :is_long
@@ -17,6 +17,7 @@ module Consoler
17
17
  attr_reader :is_argument
18
18
  attr_reader :is_value
19
19
  attr_reader :is_optional
20
+ attr_reader :aliases
20
21
 
21
22
  # Create a option
22
23
  #
@@ -28,13 +29,13 @@ module Consoler
28
29
  option = Option.new option_def, tracker
29
30
 
30
31
  # split short options with more than 1 char in multiple options
31
- if option.is_short and option.name.size > 1 then
32
+ if option.is_short && option.name.size > 1
32
33
  # remember state
33
34
  old_tracking = tracker.is_tracking
34
35
  old_is_value = option.is_value
35
36
 
36
37
  # if the complete option is optional, fake the tracker
37
- if option.is_optional then
38
+ if option.is_optional
38
39
  tracker.is_tracking = true
39
40
  end
40
41
 
@@ -44,7 +45,7 @@ module Consoler
44
45
  new_name = "-#{name}"
45
46
 
46
47
  # if the short option should have a value, this only counts for the last option
47
- if old_is_value and i == names.count - 1 then
48
+ if old_is_value && i == names.count - 1
48
49
  new_name = "#{new_name}="
49
50
  end
50
51
 
@@ -67,14 +68,20 @@ module Consoler
67
68
  def to_definition
68
69
  definition = name
69
70
 
70
- if is_long then
71
+ if is_long
71
72
  definition = "--#{definition}"
72
- elsif is_short then
73
+ elsif is_short
73
74
  definition = "-#{definition}"
74
75
  end
75
76
 
76
- if is_value then
77
+ if is_value
77
78
  definition = "#{definition}="
79
+ elsif is_argument
80
+ definition = "<#{definition}>"
81
+ end
82
+
83
+ aliases.each do |alias_|
84
+ definition = "#{definition}|#{alias_.to_definition}"
78
85
  end
79
86
 
80
87
  definition
@@ -88,7 +95,7 @@ module Consoler
88
95
  return 0 if is_short
89
96
  return false if is_long
90
97
 
91
- return nil
98
+ nil
92
99
  end
93
100
 
94
101
  protected
@@ -103,19 +110,33 @@ module Consoler
103
110
  # Check for multiple attributes in the option definition till we got the
104
111
  # final name and all of its attributes
105
112
 
106
- option, @is_optional = _is_optional option_def, tracker
113
+ # make sure we don't wrongly process any alias
114
+ alias_defs = option_def.split '|'
115
+ option = alias_defs.shift || ''
116
+
117
+ option, @is_optional = _is_optional option, tracker
107
118
  option, @is_long = _is_long option
108
119
  option, @is_short = _is_short option
109
- @is_argument = (not @is_long and not @is_short)
120
+ @is_argument = (!@is_long && !@is_short)
110
121
  option, @is_value = _value option, @is_argument
122
+ option, @aliases = _aliases option, alias_defs, tracker
123
+
124
+ if option[0] == '<'
125
+ raise 'Invalid <, missing >' if option[-1] != '>'
126
+ raise 'Only arguments support <, > around name' unless @is_argument
127
+
128
+ option = option[1..-2]
129
+ end
130
+
131
+ raise 'Missing starting <' if option[-1] == '>'
111
132
 
112
133
  @name = option
113
134
 
114
- if @name.empty? then
135
+ if @name.empty?
115
136
  raise 'Option must have a name'
116
137
  end
117
138
 
118
- if @is_long and @is_short
139
+ if @is_long && @is_short
119
140
  raise 'Option can not be a long and a short option'
120
141
  end
121
142
  end
@@ -135,8 +156,8 @@ module Consoler
135
156
  # @raise [RuntimeError] if you try to close an unopened optional
136
157
  # @return [(String, Integer|nil)] Remaining option definition, and, optional group if available
137
158
  def _is_optional(option, tracker)
138
- if option[0] == '[' then
139
- if !tracker.is_tracking then
159
+ if option[0] == '['
160
+ if !tracker.is_tracking
140
161
  # mark tracker as tracking
141
162
  tracker.is_tracking = true
142
163
  tracker.index += 1
@@ -147,14 +168,12 @@ module Consoler
147
168
  end
148
169
 
149
170
  # get optional group index from tracking, if tracking
150
- optional = if tracker.is_tracking then
171
+ optional = if tracker.is_tracking
151
172
  tracker.index
152
- else
153
- nil
154
173
  end
155
174
 
156
- if option[-1] == ']' then
157
- if tracker.is_tracking then
175
+ if option[-1] == ']'
176
+ if tracker.is_tracking
158
177
  # mark tracker as non-tracking
159
178
  tracker.is_tracking = false
160
179
  option = option[0..-2]
@@ -163,7 +182,7 @@ module Consoler
163
182
  end
164
183
  end
165
184
 
166
- return option, optional
185
+ [option, optional]
167
186
  end
168
187
 
169
188
  # Check long definition
@@ -171,14 +190,14 @@ module Consoler
171
190
  # @param [String] option Option definition
172
191
  # @return [(String, Boolean)]
173
192
  def _is_long(option)
174
- if option[0..1] == '--' then
193
+ if option[0..1] == '--'
175
194
  long = true
176
195
  option = option[2..-1]
177
196
  else
178
197
  long = false
179
198
  end
180
199
 
181
- return option, long
200
+ [option, long]
182
201
  end
183
202
 
184
203
  # Check short definition
@@ -186,14 +205,14 @@ module Consoler
186
205
  # @param [String] option Option definition
187
206
  # @return [(String, Boolean)]
188
207
  def _is_short(option)
189
- if option[0] == '-' then
208
+ if option[0] == '-'
190
209
  short = true
191
210
  option = option[1..-1]
192
211
  else
193
212
  short = false
194
213
  end
195
214
 
196
- return option, short
215
+ [option, short]
197
216
  end
198
217
 
199
218
  # Check value definition
@@ -202,8 +221,8 @@ module Consoler
202
221
  # @raise [RuntimeError] if you try to assign a value to an argument
203
222
  # @return [(String, Boolean)]
204
223
  def _value(option, argument)
205
- if option[-1] == '=' then
206
- if argument then
224
+ if option[-1] == '='
225
+ if argument
207
226
  raise 'Arguments can\'t have a value'
208
227
  end
209
228
 
@@ -213,7 +232,36 @@ module Consoler
213
232
  value = false
214
233
  end
215
234
 
216
- return option, value
235
+ [option, value]
236
+ end
237
+
238
+ # Parse all possible aliases
239
+ #
240
+ # @param [String] option Option definition
241
+ # @param [Consoler::Tracker] tracker Optional tracker
242
+ # @raise [RuntimeError] On all kinds of occasions
243
+ # @return [(String, Array)] Remaining option definition, and, aliases if available
244
+ def _aliases(option, alias_defs, tracker)
245
+ return option, [] if alias_defs.empty?
246
+
247
+ raise 'Argument can\'t have aliases' if is_argument
248
+ raise 'Aliases are not allowed for multiple short options' if is_short && option.size > 1
249
+
250
+ aliases_ = []
251
+ alias_names = []
252
+
253
+ while (alias_def = alias_defs.shift)
254
+ Consoler::Option.create alias_def, tracker do |alias_|
255
+ raise "Duplicate alias name: #{alias_.name}" if alias_names.include? alias_.name
256
+ raise "Alias must have a value: #{alias_.name}" if is_value && !alias_.is_value
257
+ raise "Alias can't have a value: #{alias_.name}" if !is_value && alias_.is_value
258
+
259
+ aliases_.push alias_
260
+ alias_names.push alias_.name
261
+ end
262
+ end
263
+
264
+ [option, aliases_]
217
265
  end
218
266
  end
219
267
  end
@@ -3,7 +3,6 @@
3
3
  require_relative 'option'
4
4
 
5
5
  module Consoler
6
-
7
6
  # List of options
8
7
  #
9
8
  # @attr_reader [String] description Description of the options
@@ -21,7 +20,7 @@ module Consoler
21
20
  return if options_def.nil?
22
21
 
23
22
  # strip the description
24
- if match = /(^|\s+)-- (?<description>.*)$/.match(options_def) then
23
+ if (match = /(^|\s+)-- (?<description>.*)$/.match(options_def))
25
24
  @description = match[:description]
26
25
  options_def = options_def[0...-match[0].size]
27
26
  end
@@ -31,28 +30,50 @@ module Consoler
31
30
 
32
31
  option_names = []
33
32
 
34
- while option_def = options.shift do
33
+ while (option_def = options.shift)
35
34
  Consoler::Option.create option_def, tracker do |option|
36
35
  raise "Duplicate option name: #{option.name}" if option_names.include? option.name
37
36
 
38
- @options.push option
39
37
  option_names.push option.name
38
+
39
+ option.aliases.each do |alias_|
40
+ raise "Duplicate alias name: #{alias_.name}" if option_names.include? alias_.name
41
+
42
+ option_names.push alias_.name
43
+ end
44
+
45
+ @options.push option
40
46
  end
41
47
  end
42
48
  end
43
49
 
44
- # Get a options by its name
50
+ # Get a option by its name
51
+ #
52
+ # May be matched by one of its aliases
45
53
  #
46
54
  # @param name [String] Name of the option
47
55
  # @return [Consoler::Option, nil]
48
56
  def get(name)
57
+ option, = get_with_alias name
58
+ option
59
+ end
60
+
61
+ # Get a option by its name, with matched alias
62
+ #
63
+ # @param name [String] Name of the option
64
+ # @return [[(Consoler::Option, nil), (Consoler::Option, nil)]]
65
+ def get_with_alias(name)
49
66
  each do |option, _|
50
- if option.name == name then
51
- return option
67
+ if option.name == name
68
+ return option, option
69
+ end
70
+
71
+ option.aliases.each do |alias_|
72
+ return option, alias_ if alias_.name == name
52
73
  end
53
74
  end
54
75
 
55
- return nil
76
+ [nil, nil]
56
77
  end
57
78
 
58
79
  # Loop through all options
@@ -84,19 +105,17 @@ module Consoler
84
105
  each do |option, i|
85
106
  definition += ' '
86
107
 
87
- if optional.nil? and option.is_optional then
108
+ if optional.nil? && option.is_optional
88
109
  definition += '['
89
110
  optional = option.is_optional
90
111
  end
91
112
 
92
113
  definition += option.to_definition
93
114
 
94
- if option.is_optional then
95
- # only close when the next option is not optional, or another optional group
96
- if @options[i + 1].nil? or optional != @options[i + 1].is_optional then
97
- definition += ']'
98
- optional = nil
99
- end
115
+ # only close when the next option is not optional, or another optional group
116
+ if option.is_optional && (@options[i + 1].nil? || optional != @options[i + 1].is_optional)
117
+ definition += ']'
118
+ optional = nil
100
119
  end
101
120
  end
102
121
 
@@ -104,8 +123,6 @@ module Consoler
104
123
  end
105
124
  end
106
125
 
107
- private
108
-
109
126
  # Optionals tracker
110
127
  #
111
128
  # @attr [Boolean] is_tracking Is inside optional options
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Consoler
4
-
5
4
  # Current version number
6
- VERSION = '1.0.3'
5
+ VERSION = '1.1.0'.freeze
7
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: consoler
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-17 00:00:00.000000000 Z
11
+ date: 2019-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -25,61 +25,61 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.15'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 12.3.0
33
+ version: 5.11.3
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 12.3.0
40
+ version: 5.11.3
41
41
  - !ruby/object:Gem::Dependency
42
- name: minitest
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 5.11.3
47
+ version: 12.3.0
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 5.11.3
54
+ version: 12.3.0
55
55
  - !ruby/object:Gem::Dependency
56
- name: yard
56
+ name: simplecov
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.9.12
61
+ version: 0.15.1
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.9.12
68
+ version: 0.15.1
69
69
  - !ruby/object:Gem::Dependency
70
- name: simplecov
70
+ name: yard
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.15.1
75
+ version: 0.9.12
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.15.1
82
+ version: 0.9.12
83
83
  description: Sinatra-like application builder for the console
84
84
  email: me@justim.net
85
85
  executables: []
@@ -88,6 +88,7 @@ extra_rdoc_files: []
88
88
  files:
89
89
  - ".editorconfig"
90
90
  - ".gitignore"
91
+ - ".rubocop.yml"
91
92
  - ".travis.yml"
92
93
  - ".yardopts"
93
94
  - Gemfile
@@ -116,7 +117,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
116
117
  requirements:
117
118
  - - ">="
118
119
  - !ruby/object:Gem::Version
119
- version: '0'
120
+ version: '2.2'
120
121
  required_rubygems_version: !ruby/object:Gem::Requirement
121
122
  requirements:
122
123
  - - ">="
@@ -124,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
125
  version: '0'
125
126
  requirements: []
126
127
  rubyforge_project:
127
- rubygems_version: 2.6.11
128
+ rubygems_version: 2.7.6
128
129
  signing_key:
129
130
  specification_version: 4
130
131
  summary: Consoler