consoler 1.0.1 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/tests.yml +34 -0
- data/.rubocop.yml +45 -0
- data/.yardopts +2 -0
- data/README.md +264 -11
- data/consoler.gemspec +4 -3
- data/lib/consoler/application.rb +112 -27
- data/lib/consoler/arguments.rb +0 -1
- data/lib/consoler/command.rb +0 -1
- data/lib/consoler/matcher.rb +157 -63
- data/lib/consoler/option.rb +113 -27
- data/lib/consoler/options.rb +40 -17
- data/lib/consoler/version.rb +1 -2
- metadata +21 -20
- data/.travis.yml +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 924c498b6db12469aef2fb31bbe7cc0bbcaa0e10cccb2a18b91584484b3cb189
|
4
|
+
data.tar.gz: f99cdb84ac04f1ce5baa60ce29ccd7667a5239c0b959b1310ce8f8a9da4292a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20ff199169e12d2bff2be0d12b3bcb6e615eb5613166406c4e46963e6716fd9b70306f7b6f561525f0783866f6d8e0d4c7315b73f98150d2133bccf4a643bc48
|
7
|
+
data.tar.gz: 34fb00e04d57de3fbcfc0b8242d01f5a76010860cac2fc78a69622ee3c7c89eb656a3f6d0abe05a7c38bdf71b97a97871c6738e488d52be203c5a2cc8bb603d4
|
@@ -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/.yardopts
ADDED
data/README.md
CHANGED
@@ -1,27 +1,51 @@
|
|
1
|
-
# 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
|
|
5
|
-
##
|
5
|
+
## Quick usage
|
6
6
|
|
7
7
|
```ruby
|
8
|
-
# create
|
8
|
+
# create an application
|
9
9
|
app = Consoler::Application.new description: 'A simple app'
|
10
10
|
|
11
11
|
# define a command
|
12
|
-
app.build '
|
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
|
-
#
|
17
|
-
build_project
|
16
|
+
# output_dir contains a string
|
17
|
+
build_project output_dir
|
18
18
|
end
|
19
|
-
|
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
|
24
|
+
app.run ['deploy', 'production']
|
25
|
+
|
26
|
+
# defaults to ARGV
|
27
|
+
app.run
|
23
28
|
```
|
24
29
|
|
30
|
+
## Features
|
31
|
+
|
32
|
+
- No fiddling with `ARGV` and friends
|
33
|
+
- Arguments filled based on their name, for easy access
|
34
|
+
- Grouped optionals
|
35
|
+
- Easy option aliasing
|
36
|
+
|
37
|
+
## Requirements
|
38
|
+
|
39
|
+
Tests are run against multiple ruby versions (latest supported):
|
40
|
+
|
41
|
+
- `2.4.x`
|
42
|
+
- `2.5.x`
|
43
|
+
- `2.6.x`
|
44
|
+
- `2.7.x`
|
45
|
+
- `3.0.x`
|
46
|
+
|
47
|
+
No other requirements exist.
|
48
|
+
|
25
49
|
## Installation
|
26
50
|
|
27
51
|
```sh
|
@@ -31,13 +55,242 @@ gem install consoler
|
|
31
55
|
or add to your `Gemfile` for applications
|
32
56
|
|
33
57
|
```ruby
|
34
|
-
gem 'consoler', '~> 1.
|
58
|
+
gem 'consoler', '~> 1.2.1'
|
35
59
|
```
|
36
60
|
|
37
61
|
or to your `.gemspec` file for gems
|
38
62
|
|
39
63
|
```ruby
|
40
|
-
Gem::Specification.new do |
|
41
|
-
|
64
|
+
Gem::Specification.new do |spec|
|
65
|
+
spec.add_dependency 'consoler', '~> 1.2.1'
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
## Docs
|
70
|
+
|
71
|
+
Full API documentation can the found here: https://www.rubydoc.info/gems/consoler/1.2.1
|
72
|
+
|
73
|
+
### API
|
74
|
+
|
75
|
+
#### Creating an application
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
# create an application
|
79
|
+
app = Consoler::Application.new
|
80
|
+
|
81
|
+
# .. or with a description that will show up in the usage message
|
82
|
+
app = Consoler::Application.new description: 'A simple app'
|
83
|
+
```
|
84
|
+
|
85
|
+
#### Adding commands
|
86
|
+
|
87
|
+
All actions you want to create must have a command name, there is no top-level matching available at this point.
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
# create an application
|
91
|
+
app = Consoler::Application.new
|
92
|
+
|
93
|
+
# in the most simple way, you provide a name (the method name) and a block you
|
94
|
+
# want to execute if it matches
|
95
|
+
app.build do
|
96
|
+
# your code here
|
97
|
+
end
|
98
|
+
|
99
|
+
# to add options to your command, provide a options definition as an argument
|
100
|
+
app.build '[--clean] [--env=] [-v|--verbose] <output_dir>' do |clean, env, verbose, output_dir|
|
101
|
+
# parameters are matched based on name
|
102
|
+
# `clean` contains a boolean
|
103
|
+
# `env` contains a string or nil if not provided
|
104
|
+
# `verbose` contains a number, counting the times it occurred in the arguments
|
105
|
+
# `output_dir` contains a string, if needed to match this command
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
#### Running the application
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
# filename: app.rb
|
113
|
+
|
114
|
+
# create an application
|
115
|
+
app = Consoler::Application.new
|
116
|
+
|
117
|
+
# a build command
|
118
|
+
app.build '[--clean] [--env=] [-v|--verbose] <output_dir>' do |clean, env, verbose, output_dir|
|
119
|
+
puts 'Starting build...' if verbose > 0
|
120
|
+
|
121
|
+
do_clean_up if clean
|
122
|
+
|
123
|
+
do_build env || 'development', output_dir
|
124
|
+
|
125
|
+
puts 'Build complete' if verbose > 0
|
42
126
|
end
|
127
|
+
|
128
|
+
# a deploy command
|
129
|
+
app.build '[-v|--verbose] [--env=]' do |env|
|
130
|
+
puts 'Starting deploy...' if verbose > 0
|
131
|
+
|
132
|
+
do_deploy env || 'development'
|
133
|
+
|
134
|
+
puts 'Deploy complete' if verbose > 0
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
138
|
+
_Shell commands:_
|
139
|
+
```sh
|
140
|
+
# start a build
|
141
|
+
ruby app.rb build --env production dist
|
142
|
+
|
143
|
+
# 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
|
+
|
43
151
|
```
|
152
|
+
|
153
|
+
#### Options definition
|
154
|
+
|
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]` |
|
164
|
+
|
165
|
+
Some notes about these options:
|
166
|
+
|
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
|
172
|
+
|
173
|
+
Grouping of optionals allows you do things like this:
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
app = Consoler::Application.new
|
177
|
+
app.shout '[<first_name> <last_name>] [<name>]' do |first_name, last_name, name|
|
178
|
+
# by definition, `last_name` is also filled
|
179
|
+
unless first_name.nil? then
|
180
|
+
puts "Hello #{first_name} #{last_name}!"
|
181
|
+
end
|
182
|
+
|
183
|
+
unless name.nil? then
|
184
|
+
puts "Hello #{name}!"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# calling with two arguments can fill the first group
|
189
|
+
# prints "Hello John Doe!"
|
190
|
+
app.run ['shout', 'John', 'Doe']
|
191
|
+
|
192
|
+
# calling with one argument it is not possible to fill the first group
|
193
|
+
# prints "Hello Mr. White!"
|
194
|
+
app.run ['shout', 'Mr. White!']
|
195
|
+
```
|
196
|
+
|
197
|
+
#### Return types in action block
|
198
|
+
|
199
|
+
| Option type | Return type (ex.) | Default (if optional) |
|
200
|
+
| ----------- | ----------------------- | --------------------- |
|
201
|
+
| Short | `Integer` (`1`) | `0` |
|
202
|
+
| Long | `Boolean` (`true`) | `false` |
|
203
|
+
| Value | `String` (`production`) | `nil` |
|
204
|
+
| Argument | `String` (`dist/`) | `nil` |
|
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
|
+
|
208
|
+
#### Subapplications
|
209
|
+
|
210
|
+
To make application nesting possible you can provide a complete application to a command, instead of an action block.
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
# filename: app.rb
|
214
|
+
|
215
|
+
# create a subapplication
|
216
|
+
android = Consoler::Application.new
|
217
|
+
android.build do; end
|
218
|
+
|
219
|
+
# options are supported just like regular apps
|
220
|
+
android.clean '--force|-f' do; end
|
221
|
+
|
222
|
+
# create an application
|
223
|
+
app = Consoler::Application.new
|
224
|
+
|
225
|
+
# mount the android application on top of the android command
|
226
|
+
# note that the command does not support options
|
227
|
+
app.android android
|
228
|
+
```
|
229
|
+
|
230
|
+
You can now run the next bit to run the android build command:
|
231
|
+
|
232
|
+
```sh
|
233
|
+
ruby app.rb android build
|
234
|
+
```
|
235
|
+
|
236
|
+
You can build them as complicated as you like :)
|
237
|
+
|
238
|
+
### Complete example
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
#!/usr/bin/env ruby
|
242
|
+
|
243
|
+
db = Consoler::Application.new
|
244
|
+
db.migrate '-- run all pending migrations' do
|
245
|
+
run_migrate
|
246
|
+
end
|
247
|
+
|
248
|
+
db.rollback '[--migrations=] -- rollback a number of migrations' do |migrations|
|
249
|
+
run_rollback migrations || 1
|
250
|
+
end
|
251
|
+
|
252
|
+
cache = Consoler::Application.new
|
253
|
+
cache.clear '[--env=] -- clear the cache for a given environment' do |env|
|
254
|
+
run_cache_clear env || 'development'
|
255
|
+
end
|
256
|
+
|
257
|
+
cache.warmup '[--env=] -- warmup the cache for a given environment' do |env|
|
258
|
+
run_cache_warmup env || 'development'
|
259
|
+
end
|
260
|
+
|
261
|
+
app = Consoler::Application.new
|
262
|
+
app.db db
|
263
|
+
app.cache cache
|
264
|
+
|
265
|
+
app.build '[--clean] [--env=] [-v] <output_dir> -- build the project' do |clean, env, v, output_dir|
|
266
|
+
puts 'Starting build...' if v > 0
|
267
|
+
|
268
|
+
if clean
|
269
|
+
puts 'Starting clean...' if v > 1
|
270
|
+
do_clean_up if clean
|
271
|
+
end
|
272
|
+
|
273
|
+
do_build env || 'development', output_dir
|
274
|
+
|
275
|
+
puts 'Build complete' if v > 0
|
276
|
+
end
|
277
|
+
```
|
278
|
+
|
279
|
+
Make the file executable with `chmod a+x app.rb`, you can now call it with `./app.rb` without `ruby` in front of it, saves a couple keystrokes.
|
280
|
+
|
281
|
+
```sh
|
282
|
+
# run all migration
|
283
|
+
./app.rb db migrate
|
284
|
+
# rollback the last 4 migrations
|
285
|
+
./app.rb db rollback 4
|
286
|
+
# clean production cache
|
287
|
+
./app.rb cache clear --env production
|
288
|
+
# build the project, including cleaning and logging
|
289
|
+
./app.rb build --clean --env production -vv dist/
|
290
|
+
# print the usage message
|
291
|
+
./app.rb
|
292
|
+
```
|
293
|
+
|
294
|
+
## Final notes
|
295
|
+
|
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.
|
26
|
-
spec.add_development_dependency '
|
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
|
data/lib/consoler/application.rb
CHANGED
@@ -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?
|
55
|
+
unless block.nil?
|
48
56
|
action = block
|
49
57
|
options_def = input
|
50
58
|
|
51
|
-
if
|
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
|
64
|
+
if input.instance_of? Consoler::Application
|
57
65
|
action = input
|
58
66
|
options_def = ''
|
59
67
|
end
|
60
68
|
|
61
|
-
if action.nil?
|
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
|
-
|
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
|
-
result, matched = _run(args)
|
88
|
+
result, matched = _run(args.dup)
|
81
89
|
|
82
|
-
if
|
90
|
+
if !matched && !disable_usage_message
|
83
91
|
usage
|
84
92
|
end
|
85
93
|
|
86
|
-
|
94
|
+
result
|
87
95
|
end
|
88
96
|
|
89
97
|
# Show the usage message
|
@@ -93,21 +101,48 @@ module Consoler
|
|
93
101
|
puts "#{@description}\n\n" unless @description.nil?
|
94
102
|
puts 'Usage:'
|
95
103
|
|
96
|
-
_commands_usage $
|
104
|
+
_commands_usage $PROGRAM_NAME
|
97
105
|
end
|
98
106
|
|
99
107
|
protected
|
100
108
|
|
109
|
+
# Run the app
|
110
|
+
#
|
111
|
+
# @param [Array<String>] args Arguments
|
112
|
+
# @return [(mixed, Boolean)] Result of the command, and, did the args match a command at all
|
101
113
|
def _run(args)
|
102
114
|
arg = args.shift
|
115
|
+
|
116
|
+
return [nil, false] if arg.nil?
|
117
|
+
|
103
118
|
arguments = Consoler::Arguments.new args
|
119
|
+
exact_matches = []
|
120
|
+
partial_matches = []
|
104
121
|
|
105
122
|
@commands.each do |command|
|
106
|
-
if command.command == arg
|
107
|
-
|
123
|
+
if command.command == arg
|
124
|
+
exact_matches.push command
|
125
|
+
elsif command.command.start_with? arg
|
126
|
+
partial_matches.push command
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# we only allow a single partial match to prevent ambiguity
|
131
|
+
partial_match = if partial_matches.size == 1
|
132
|
+
partial_matches[0]
|
133
|
+
end
|
134
|
+
|
135
|
+
unless exact_matches.empty? && partial_match.nil?
|
136
|
+
matches = exact_matches
|
137
|
+
matches.push partial_match unless partial_match.nil?
|
138
|
+
|
139
|
+
matches.each do |command|
|
140
|
+
# the matched command contains a subapp, run subapp with the same
|
141
|
+
# arguments (excluding the arg that matched this command)
|
142
|
+
if command.action.instance_of?(Consoler::Application)
|
108
143
|
result, matched = command.action._run(args)
|
109
144
|
|
110
|
-
if matched
|
145
|
+
if matched
|
111
146
|
return result, true
|
112
147
|
end
|
113
148
|
else
|
@@ -120,45 +155,95 @@ module Consoler
|
|
120
155
|
end
|
121
156
|
end
|
122
157
|
|
123
|
-
|
158
|
+
[nil, false]
|
124
159
|
end
|
125
160
|
|
126
|
-
|
161
|
+
# Print the usage message for this command
|
162
|
+
#
|
163
|
+
# @param [String] prefix A prefix for the command from a parent app
|
164
|
+
# @return [Consoler::Application]
|
165
|
+
def _commands_usage(prefix = '')
|
127
166
|
@commands.each do |command|
|
128
|
-
|
167
|
+
# print the usage message of a subapp with a prefix from the current command
|
168
|
+
if command.action.instance_of?(Consoler::Application)
|
129
169
|
command.action._commands_usage "#{prefix} #{command.command}"
|
130
170
|
else
|
131
171
|
print " #{prefix} #{command.command}"
|
132
172
|
|
133
|
-
if command.options.size
|
173
|
+
if command.options.size
|
134
174
|
print " #{command.options.to_definition}"
|
135
175
|
end
|
136
176
|
|
137
|
-
unless command.options.description.nil?
|
177
|
+
unless command.options.description.nil?
|
138
178
|
print " -- #{command.options.description}"
|
139
179
|
end
|
140
180
|
|
141
181
|
print "\n"
|
142
182
|
end
|
143
183
|
end
|
184
|
+
|
185
|
+
self
|
144
186
|
end
|
145
187
|
|
146
188
|
private
|
147
189
|
|
190
|
+
# Add a command
|
191
|
+
#
|
192
|
+
# @param [String] command Command name
|
193
|
+
# @param [String] options_def Definition of options
|
194
|
+
# @param [Proc, Consoler::Application] action Action or subapp
|
195
|
+
# @return [Consoler::Application]
|
148
196
|
def _add_command(command, options_def, action)
|
149
|
-
@commands.push(
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
197
|
+
@commands.push(
|
198
|
+
Consoler::Command.new(
|
199
|
+
command: command,
|
200
|
+
options: Consoler::Options.new(options_def),
|
201
|
+
action: action,
|
202
|
+
)
|
203
|
+
)
|
204
|
+
|
205
|
+
self
|
154
206
|
end
|
155
207
|
|
208
|
+
# Execute an action with argument match info
|
209
|
+
#
|
210
|
+
# @param [Proc] action Action
|
211
|
+
# @param [Hash] match Argument match information
|
156
212
|
def _dispatch(action, match)
|
213
|
+
# match parameter names to indices of match information
|
157
214
|
arguments = action.parameters.map do |parameter|
|
158
|
-
|
215
|
+
parameter_name = parameter[1].to_s
|
216
|
+
|
217
|
+
if match.key? parameter_name
|
218
|
+
match[parameter_name]
|
219
|
+
else
|
220
|
+
# check for the normalized name of every match to see
|
221
|
+
# if it fits the parameter name
|
222
|
+
match.each do |name, value|
|
223
|
+
normalized_name = _normalize name
|
224
|
+
|
225
|
+
if parameter_name == normalized_name
|
226
|
+
break value
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
159
230
|
end
|
160
231
|
|
161
232
|
action.call(*arguments)
|
162
233
|
end
|
234
|
+
|
235
|
+
# Normalize a name to be used as a variable name
|
236
|
+
#
|
237
|
+
# @param [String] name Name
|
238
|
+
# @return [String] Normalized name
|
239
|
+
def _normalize(name)
|
240
|
+
# maybe do something more, maybe not.. ruby does allow for
|
241
|
+
# some weird stuff to be used as a variable name. the user
|
242
|
+
# should use some common sense. and, other things might
|
243
|
+
# also be an syntax error, like starting with a number.
|
244
|
+
# this normalization is more of a comvenience than anything
|
245
|
+
# else
|
246
|
+
name.tr('-', '_')
|
247
|
+
end
|
163
248
|
end
|
164
249
|
end
|