consoler 1.0.3 → 1.3.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: 4ce3b8eca4b35ef9a82bb1ac0886aae0f49614dca29721c06e20bb94fdfc3d3b
4
+ data.tar.gz: addc213497a379b6d68fe844df9374bb93c48bb38ac40e7174343a50463e6a5a
5
5
  SHA512:
6
- metadata.gz: b42c0a91ed2df03ef7d76796f1da5e986782d2d0415a14167159f34b10b76eb939e97f378109328bdd768c7f234607907871bcd03e2e5ad097180c7edaf385c6
7
- data.tar.gz: df6627a850424d6053c4525bea35014c1c8ec42bbe9cbe43f981119b535a747af53c4130dec92242309d36891c9e7368450c49838c37c55ea2662a682582fb26
6
+ metadata.gz: 15b94900e561a891e02f88d57f43b0614abb76b6ed75e59810d5a1ded0945874e71e1ceafacc320fd725be1ad470895bfc912f7f853601c45ce0ffc374ffd8a7
7
+ data.tar.gz: 0305b071d1b40ebd593d0cd81795787898d9081e2cbebee5636b406136ecbb325facade699426202e18bb6e4a39fc20c6760d3b18a90af33cb308e3d1141b4e2
@@ -0,0 +1,34 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+
9
+ jobs:
10
+ tests:
11
+ runs-on: ubuntu-latest
12
+
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ ruby: ['2.4', '2.5', '2.6', '2.7', '3.0']
17
+
18
+ name: Ruby ${{ matrix.ruby }}
19
+
20
+ steps:
21
+ - uses: actions/checkout@v2
22
+
23
+ - name: Set up Ruby
24
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
25
+ uses: ruby/setup-ruby@v1
26
+ with:
27
+ ruby-version: ${{ matrix.ruby }}
28
+ bundler-cache: true
29
+
30
+ - name: Report Ruby version
31
+ run: ruby -v
32
+
33
+ - name: Run tests
34
+ run: bundle exec rake
data/.rubocop.yml ADDED
@@ -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
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Consoler [![Build Status](https://api.travis-ci.org/justim/consoler-rb.svg?branch=master)](https://travis-ci.org/justim/consoler-rb) [![Gem Version](https://badge.fury.io/rb/consoler.svg)](https://badge.fury.io/rb/consoler)
1
+ # Consoler [![Gem Version](https://badge.fury.io/rb/consoler.svg)](https://badge.fury.io/rb/consoler)
2
2
 
3
3
  > Sinatra-like application builder for the console
4
4
 
@@ -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,15 +32,17 @@ 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
 
36
39
  Tests are run against multiple ruby versions (latest supported):
37
40
 
38
- - `2.2.x`
39
- - `2.3.x`
40
41
  - `2.4.x`
41
42
  - `2.5.x`
43
+ - `2.6.x`
44
+ - `2.7.x`
45
+ - `3.0.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.3.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.3.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/gems/consoler/1.3.0
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,40 @@ _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
145
+
146
+ # or, you can use command shortcuts. this works by prefix matching and only if
147
+ # there is one match, exact matches always have priority
148
+ ruby app.rb b --env production dist
149
+ ruby app.rb d --verbose
150
+
141
151
  ```
142
152
 
143
153
  #### Options definition
144
154
 
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]` |
155
+ | Option | Meaning | Example |
156
+ | --------------- | ------------------------------- | -------------------------------------- |
157
+ | `-f` | Short option with name `f` | `ruby app.rb build -f` |
158
+ | `--clean` | Long option with name `clean` | `ruby app.rb build --clean` |
159
+ | `-v\|--verbose` | Alias option for `v` | `ruby app.rb build --verbose` |
160
+ | `<output_dir>` | Argument with name `output_dir` | `ruby app.rb build dist/` |
161
+ | `--env=` | Long option with value | `ruby app.rb build --env production` |
162
+ | `-e=` | Short option with value | `ruby app.rb build -e production` |
163
+ | `[ .. ]` | Optional options/arguments | `ruby app.rb build` would match `[-v]` |
153
164
 
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.
165
+ Some notes about these options:
155
166
 
156
- Options and/or arguments are mandatory unless specified otherwise.
167
+ - The `<`, `>` around the argument name a optional, but are always shown in de usage message for better legibility
168
+ - 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.
169
+ - Options and/or arguments are mandatory unless specified otherwise.
170
+ - Aliases should be the same "type", meaning that if the original option expands an value, the alias must do as well
171
+ - Aliases are only allowed for short and long options, with or without value and can be optional, just like regular options
157
172
 
158
173
  Grouping of optionals allows you do things like this:
159
174
 
160
175
  ```ruby
161
176
  app = Consoler::Application.new
162
- app.shout '[first_name last_name] [name]' do |first_name, last_name, name|
177
+ app.shout '[<first_name> <last_name>] [<name>]' do |first_name, last_name, name|
163
178
  # by definition, `last_name` is also filled
164
179
  unless first_name.nil? then
165
180
  puts "Hello #{first_name} #{last_name}!"
@@ -172,11 +187,11 @@ end
172
187
 
173
188
  # calling with two arguments can fill the first group
174
189
  # prints "Hello John Doe!"
175
- app.run(['shout', 'John', 'Doe'])
190
+ app.run ['shout', 'John', 'Doe']
176
191
 
177
192
  # calling with one argument it is not possible to fill the first group
178
193
  # prints "Hello Mr. White!"
179
- app.run(['shout', 'Mr. White!'])
194
+ app.run ['shout', 'Mr. White!']
180
195
  ```
181
196
 
182
197
  #### Return types in action block
@@ -188,6 +203,8 @@ app.run(['shout', 'Mr. White!'])
188
203
  | Value | `String` (`production`) | `nil` |
189
204
  | Argument | `String` (`dist/`) | `nil` |
190
205
 
206
+ 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).
207
+
191
208
  #### Subapplications
192
209
 
193
210
  To make application nesting possible you can provide a complete application to a command, instead of an action block.
@@ -200,7 +217,7 @@ android = Consoler::Application.new
200
217
  android.build do; end
201
218
 
202
219
  # options are supported just like regular apps
203
- android.clean '--force' do; end
220
+ android.clean '--force|-f' do; end
204
221
 
205
222
  # create an application
206
223
  app = Consoler::Application.new
@@ -245,7 +262,7 @@ app = Consoler::Application.new
245
262
  app.db db
246
263
  app.cache cache
247
264
 
248
- app.build '[--clean] [--env=] [-v] output_dir -- build the project' do |clean, env, v, output_dir|
265
+ app.build '[--clean] [--env=] [-v] <output_dir> -- build the project' do |clean, env, v, output_dir|
249
266
  puts 'Starting build...' if v > 0
250
267
 
251
268
  if clean
@@ -274,10 +291,6 @@ Make the file executable with `chmod a+x app.rb`, you can now call it with `./ap
274
291
  ./app.rb
275
292
  ```
276
293
 
277
- ### Current wish-list
278
-
279
- - Aliases, mostly to "document" short options, `-v|--verbose`
280
-
281
294
  ## Final notes
282
295
 
283
296
  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.
data/consoler.gemspec CHANGED
@@ -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.add_development_dependency 'bundler', '~> 1.15'
26
- spec.add_development_dependency 'rake', '~> 12.3.0'
25
+ spec.required_ruby_version = '>= 2.4'
26
+ spec.add_development_dependency 'bundler', '~> 2.0'
27
27
  spec.add_development_dependency 'minitest', '~> 5.11.3'
28
+ spec.add_development_dependency 'rake', '~> 12.3.0'
29
+ spec.add_development_dependency 'simplecov', '~> 0.16.1'
28
30
  spec.add_development_dependency 'yard', '~> 0.9.12'
29
- spec.add_development_dependency 'simplecov', '~> 0.15.1'
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,31 @@ 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
+ # @option options [bool] :rescue_errors Should the application catch errors (optional)
31
+ def initialize(options = {})
33
32
  @description = options[:description]
33
+ @rescue_errors = if !options[:rescue_errors].nil? then
34
+ options[:rescue_errors]
35
+ else
36
+ true
37
+ end
34
38
  @commands = []
35
39
  end
36
40
 
41
+ # Accept all method_missing call
42
+ #
43
+ # We use the name as a command name, thus we accept all names
44
+ #
45
+ # @param _method_name [String] Name of the method
46
+ # @param _include_private [bool] Name of the method
47
+ def respond_to_missing?(_method_name, _include_private = false)
48
+ true
49
+ end
50
+
37
51
  # Register a command for this app
38
52
  #
39
53
  # @param command_name [Symbol] Name of the command
@@ -44,21 +58,21 @@ module Consoler
44
58
  action = nil
45
59
  options_def = ''
46
60
 
47
- unless block.nil? then
61
+ unless block.nil?
48
62
  action = block
49
63
  options_def = input
50
64
 
51
- if not options_def.nil? and not options_def.instance_of? String then
65
+ if !options_def.nil? && !options_def.instance_of?(String)
52
66
  raise 'Invalid options'
53
67
  end
54
68
  end
55
69
 
56
- if input.instance_of? Consoler::Application then
70
+ if input.instance_of? Consoler::Application
57
71
  action = input
58
72
  options_def = ''
59
73
  end
60
74
 
61
- if action.nil? then
75
+ if action.nil?
62
76
  raise 'Invalid subapp/block'
63
77
  end
64
78
 
@@ -66,7 +80,7 @@ module Consoler
66
80
 
67
81
  _add_command(command, options_def, action)
68
82
 
69
- return nil
83
+ nil
70
84
  end
71
85
 
72
86
  # Run the application with a list of arguments
@@ -75,15 +89,22 @@ module Consoler
75
89
  # @param disable_usage_message [Boolean] Disable the usage message when nothing it matched
76
90
  # @return [mixed] Result of your matched command, <tt>nil</tt> otherwise
77
91
  def run(args = ARGV, disable_usage_message = false)
78
- # TODO signal handling of some kind?
92
+ # TODO: signal handling of some kind?
79
93
 
80
94
  result, matched = _run(args.dup)
81
95
 
82
- if not matched and not disable_usage_message
96
+ if !matched && !disable_usage_message
83
97
  usage
84
98
  end
85
99
 
86
- return result
100
+ result
101
+ rescue RuntimeError => e
102
+ if @rescue_errors
103
+ $stderr.puts "A runtime error occured: #{e.message.strip}"
104
+ nil
105
+ else
106
+ raise e
107
+ end
87
108
  end
88
109
 
89
110
  # Show the usage message
@@ -93,7 +114,7 @@ module Consoler
93
114
  puts "#{@description}\n\n" unless @description.nil?
94
115
  puts 'Usage:'
95
116
 
96
- _commands_usage $0
117
+ _commands_usage $PROGRAM_NAME
97
118
  end
98
119
 
99
120
  protected
@@ -104,16 +125,37 @@ module Consoler
104
125
  # @return [(mixed, Boolean)] Result of the command, and, did the args match a command at all
105
126
  def _run(args)
106
127
  arg = args.shift
128
+
129
+ return [nil, false] if arg.nil?
130
+
107
131
  arguments = Consoler::Arguments.new args
132
+ exact_matches = []
133
+ partial_matches = []
108
134
 
109
135
  @commands.each do |command|
110
- if command.command == arg then
136
+ if command.command == arg
137
+ exact_matches.push command
138
+ elsif command.command.start_with? arg
139
+ partial_matches.push command
140
+ end
141
+ end
142
+
143
+ # we only allow a single partial match to prevent ambiguity
144
+ partial_match = if partial_matches.size == 1
145
+ partial_matches[0]
146
+ end
147
+
148
+ unless exact_matches.empty? && partial_match.nil?
149
+ matches = exact_matches
150
+ matches.push partial_match unless partial_match.nil?
151
+
152
+ matches.each do |command|
111
153
  # the matched command contains a subapp, run subapp with the same
112
154
  # arguments (excluding the arg that matched this command)
113
- if command.action.instance_of? Consoler::Application then
155
+ if command.action.instance_of?(Consoler::Application)
114
156
  result, matched = command.action._run(args)
115
157
 
116
- if matched then
158
+ if matched
117
159
  return result, true
118
160
  end
119
161
  else
@@ -126,26 +168,26 @@ module Consoler
126
168
  end
127
169
  end
128
170
 
129
- return nil, false
171
+ [nil, false]
130
172
  end
131
173
 
132
174
  # Print the usage message for this command
133
175
  #
134
176
  # @param [String] prefix A prefix for the command from a parent app
135
177
  # @return [Consoler::Application]
136
- def _commands_usage(prefix='')
178
+ def _commands_usage(prefix = '')
137
179
  @commands.each do |command|
138
180
  # print the usage message of a subapp with a prefix from the current command
139
- if command.action.instance_of? Consoler::Application then
181
+ if command.action.instance_of?(Consoler::Application)
140
182
  command.action._commands_usage "#{prefix} #{command.command}"
141
183
  else
142
184
  print " #{prefix} #{command.command}"
143
185
 
144
- if command.options.size then
186
+ if command.options.size
145
187
  print " #{command.options.to_definition}"
146
188
  end
147
189
 
148
- unless command.options.description.nil? then
190
+ unless command.options.description.nil?
149
191
  print " -- #{command.options.description}"
150
192
  end
151
193
 
@@ -165,11 +207,13 @@ module Consoler
165
207
  # @param [Proc, Consoler::Application] action Action or subapp
166
208
  # @return [Consoler::Application]
167
209
  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
- ))
210
+ @commands.push(
211
+ Consoler::Command.new(
212
+ command: command,
213
+ options: Consoler::Options.new(options_def),
214
+ action: action,
215
+ )
216
+ )
173
217
 
174
218
  self
175
219
  end
@@ -183,7 +227,7 @@ module Consoler
183
227
  arguments = action.parameters.map do |parameter|
184
228
  parameter_name = parameter[1].to_s
185
229
 
186
- if match.has_key? parameter_name then
230
+ if match.key? parameter_name
187
231
  match[parameter_name]
188
232
  else
189
233
  # check for the normalized name of every match to see
@@ -191,8 +235,8 @@ module Consoler
191
235
  match.each do |name, value|
192
236
  normalized_name = _normalize name
193
237
 
194
- if parameter_name == normalized_name then
195
- return value
238
+ if parameter_name == normalized_name
239
+ break value
196
240
  end
197
241
  end
198
242
  end
@@ -212,7 +256,7 @@ module Consoler
212
256
  # also be an syntax error, like starting with a number.
213
257
  # this normalization is more of a comvenience than anything
214
258
  # else
215
- name.gsub('-', '_')
259
+ name.tr('-', '_')
216
260
  end
217
261
  end
218
262
  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