miniparse 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.yardopts +2 -0
- data/FAQ.md +17 -1
- data/README.md +29 -5
- data/Rakefile +11 -3
- data/lib/miniparse.rb +3 -38
- data/lib/miniparse/app.rb +45 -4
- data/lib/miniparse/command.rb +4 -1
- data/lib/miniparse/commander.rb +34 -21
- data/lib/miniparse/constants.rb +49 -0
- data/lib/miniparse/control.rb +14 -47
- data/lib/miniparse/internals.rb +22 -0
- data/lib/miniparse/option_broker.rb +1 -1
- data/lib/miniparse/parser.rb +49 -29
- data/lib/miniparse/version.rb +2 -2
- data/lib/miniparse/word_wrap.rb +12 -8
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bd0805b6bb2f315ae9df2c2ccea6c314f8cc7d69
|
|
4
|
+
data.tar.gz: 925c5bbe738870df5cceba609983d82a32ab8500
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 40a6e76211a8a40bdd394878fd91efd47026a517516ac75c602ccba55cf04065a7804279207500d6d84626bcb172ce1fe7dc7f8a008a5f8566b1e95daa1bb3fe
|
|
7
|
+
data.tar.gz: 6cba205c6f3ec89acf6e71f96c98d03fde95fad7e81d20edd64ead076ff079da3711f98d5b47484c80d9295a21134a0a459146f68233cc76638530a099645141
|
data/.gitignore
CHANGED
data/.yardopts
ADDED
data/FAQ.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
### What's the meaning of
|
|
6
|
+
### What's the meaning of _x.y.z_ in your version numbers?
|
|
7
7
|
|
|
8
8
|
We use a [Semantic Versioning](http://semver.org/) like scheme of MAJOR.MINOR.PATCH:
|
|
9
9
|
|
|
@@ -17,6 +17,22 @@ Additionally a `b` will be added for beta (i.e. pre-release) versions.
|
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
+
### What do you mean with _public interface_?
|
|
21
|
+
|
|
22
|
+
At the moment the complete public interface is restricted to the class Parser, the control Miniparse class methods and the optional module App
|
|
23
|
+
|
|
24
|
+
You can find the complete specification in the public methods and constants on these files:
|
|
25
|
+
|
|
26
|
+
* [miniparse.rb](https://github.com/jmrod4/miniparse/blob/master/lib/miniparse/constants.rb)
|
|
27
|
+
|
|
28
|
+
* [miniparse/parser.rb](https://github.com/jmrod4/miniparse/blob/master/lib/miniparse/parser.rb)
|
|
29
|
+
|
|
30
|
+
* [miniparse/control.rb](https://github.com/jmrod4/miniparse/blob/master/lib/miniparse/control.rb)
|
|
31
|
+
|
|
32
|
+
* [miniparse/app.rb](https://github.com/jmrod4/miniparse/blob/master/lib/miniparse/app.rb)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
20
36
|
### Why have you `protected` methods in your classes?
|
|
21
37
|
|
|
22
38
|
It is the tersest way we found of defining a public interface. Please note that the non-public interface can be changed at any time, even when just releasing minor patches.
|
data/README.md
CHANGED
|
@@ -3,14 +3,34 @@
|
|
|
3
3
|
|
|
4
4
|
**Miniparse is an easy to use yet flexible and powerful ruby library for parsing command-line options.**
|
|
5
5
|
|
|
6
|
-
The main objetive of this implementation is minimun boiler plate
|
|
6
|
+
The main objetive of this implementation is to get minimun boiler plate but keep ease of use and a self documenting specification.
|
|
7
7
|
|
|
8
8
|
Additionally the library is quite flexible and allows a lot of customization but always with sane defaults so **you don't need to learn nothing to start using it**.
|
|
9
9
|
|
|
10
|
-
## How to
|
|
10
|
+
## How to Use
|
|
11
11
|
|
|
12
12
|
Please find below a short but meaningful example, then you can **[get more examples](https://github.com/jmrod4/miniparse/tree/master/examples)** at [Github miniparse repository](https://github.com/jmrod4/miniparse).
|
|
13
13
|
|
|
14
|
+
You can find also a nice autogenerated documentation from the current gem at http://www.rubydoc.info/gems/miniparse/
|
|
15
|
+
|
|
16
|
+
And finally, for the source files for the complete public interface you can see the class Parser in:
|
|
17
|
+
|
|
18
|
+
* [miniparse/parser.rb](https://github.com/jmrod4/miniparse/blob/master/lib/miniparse/parser.rb)
|
|
19
|
+
|
|
20
|
+
The control class methods in:
|
|
21
|
+
|
|
22
|
+
* [miniparse/control.rb](https://github.com/jmrod4/miniparse/blob/master/lib/miniparse/control.rb)
|
|
23
|
+
|
|
24
|
+
And the constants in:
|
|
25
|
+
|
|
26
|
+
* [miniparse.rb](https://github.com/jmrod4/miniparse/blob/master/lib/miniparse/constants.rb)
|
|
27
|
+
|
|
28
|
+
If you use the optional App module you can find the source here:
|
|
29
|
+
|
|
30
|
+
* [miniparse/app.rb](https://github.com/jmrod4/miniparse/blob/master/lib/miniparse/app.rb)
|
|
31
|
+
|
|
32
|
+
## Simple Example
|
|
33
|
+
|
|
14
34
|
Let's try putting the following code in `myprogram.rb`
|
|
15
35
|
|
|
16
36
|
require 'miniparse'
|
|
@@ -41,7 +61,7 @@ or even get the auto generated help
|
|
|
41
61
|
|
|
42
62
|
$ ruby yourprogram.rb --help
|
|
43
63
|
|
|
44
|
-
usage:
|
|
64
|
+
usage: ex01_simple.rb [--help] [--debug] <args>
|
|
45
65
|
|
|
46
66
|
Options:
|
|
47
67
|
--debug activate debugging
|
|
@@ -55,10 +75,14 @@ You can install it as an standard ruby gem with
|
|
|
55
75
|
then to use you can require it adding the following to the top of your ruby source file
|
|
56
76
|
|
|
57
77
|
require 'miniparse'
|
|
78
|
+
|
|
79
|
+
## F.A.Q.
|
|
80
|
+
|
|
81
|
+
There is a FAQ available at https://github.com/jmrod4/miniparse/blob/master/FAQ.md
|
|
58
82
|
|
|
59
83
|
## Contributing
|
|
60
84
|
|
|
61
|
-
Bug reports and pull requests are welcome at https://github.com/jmrod4/miniparse.
|
|
85
|
+
Bug reports and pull requests are most welcome at https://github.com/jmrod4/miniparse.
|
|
62
86
|
|
|
63
87
|
After checking out the repo you can:
|
|
64
88
|
|
|
@@ -70,6 +94,6 @@ After checking out the repo you can:
|
|
|
70
94
|
|
|
71
95
|
## License
|
|
72
96
|
|
|
73
|
-
This library
|
|
97
|
+
This library has Copyright 2016 Juanma Rodriguez and it's available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
|
74
98
|
|
|
75
99
|
You can find the source code at https://github.com/jmrod4/miniparse.
|
data/Rakefile
CHANGED
|
@@ -2,17 +2,25 @@ require "bundler/gem_tasks"
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
require "rake/testtask"
|
|
5
|
-
|
|
6
5
|
Rake::TestTask.new(:test) do |t|
|
|
7
6
|
t.libs << "test"
|
|
8
7
|
t.libs << "lib"
|
|
9
8
|
t.test_files = FileList['test/**/*_test.rb']
|
|
10
9
|
end
|
|
11
|
-
|
|
12
10
|
#task :default => :test
|
|
13
11
|
|
|
14
|
-
require "rspec/core/rake_task"
|
|
15
12
|
|
|
13
|
+
|
|
14
|
+
require "rspec/core/rake_task"
|
|
16
15
|
RSpec::Core::RakeTask.new(:spec)
|
|
17
16
|
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
desc "Generate yard documentation"
|
|
20
|
+
task :doc do
|
|
21
|
+
sh "yard doc"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
18
26
|
task :default => :spec
|
data/lib/miniparse.rb
CHANGED
|
@@ -1,51 +1,16 @@
|
|
|
1
1
|
# TODO consider inserting here instructions about how to use or how to find help
|
|
2
2
|
|
|
3
|
+
|
|
3
4
|
# other modules
|
|
4
5
|
require "miniparse/word_wrap"
|
|
5
6
|
|
|
6
|
-
module Miniparse
|
|
7
|
-
# error exit codes
|
|
8
|
-
# module ErrorCodes
|
|
9
|
-
ERR_HELP_REQ = 1
|
|
10
|
-
ERR_ARGUMENT = 2
|
|
11
|
-
# end
|
|
12
|
-
# include ErrorCodes
|
|
13
|
-
end
|
|
14
|
-
|
|
15
7
|
# module miniparse
|
|
8
|
+
require "miniparse/constants"
|
|
16
9
|
require "miniparse/command"
|
|
17
10
|
require "miniparse/commander"
|
|
18
11
|
require "miniparse/control"
|
|
12
|
+
require "miniparse/internals"
|
|
19
13
|
require "miniparse/option_broker"
|
|
20
14
|
require "miniparse/parser"
|
|
21
15
|
require "miniparse/version"
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
module Miniparse
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def self.help_usage_format(right_text)
|
|
30
|
-
left_text = "usage: #{File.basename($PROGRAM_NAME)}"
|
|
31
|
-
if Miniparse.control(:formatted_help)
|
|
32
|
-
width_display = Miniparse.control(:width_display)
|
|
33
|
-
width_left = left_text.size
|
|
34
|
-
WordWrap.two_cols_word_wrap(left_text, ' ', right_text,
|
|
35
|
-
width_left, width_display - 1 - width_left)
|
|
36
|
-
else
|
|
37
|
-
left_text + " " + right_text
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def self.debug(msg)
|
|
43
|
-
puts "\nDEBUG #{caller[0]}: #{msg}"
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
data/lib/miniparse/app.rb
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
require 'miniparse'
|
|
2
2
|
|
|
3
|
+
# this optional module allows an easy integration of parsing and trivial output,
|
|
4
|
+
# in your application
|
|
5
|
+
#
|
|
6
|
+
# simple example of use:
|
|
7
|
+
#
|
|
8
|
+
# require "miniparse/app"
|
|
9
|
+
#
|
|
10
|
+
# App.configure_parser do |parser|
|
|
11
|
+
# Miniparse.set_control(
|
|
12
|
+
# autoshortable: true,
|
|
13
|
+
# )
|
|
14
|
+
# parser.add_program_description "my program help introduction\n"
|
|
15
|
+
# parser.add_option("--list", "list something")
|
|
16
|
+
# parser.parse ARGV
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# # to see App.debug you have to specify the --debug option
|
|
20
|
+
# # for example: 'ruby ex08_app_simple.rb --debug'
|
|
21
|
+
# App.debug App.options # to stdout
|
|
22
|
+
# App.info App.options # to stdout
|
|
23
|
+
# App.warn App.options # to stderr
|
|
24
|
+
# App.error App.options # to stderr (note: it doesn't exit)
|
|
25
|
+
#
|
|
26
|
+
# # you have direct access to the parser object
|
|
27
|
+
# puts "parser object: #{App.parser}"
|
|
28
|
+
#
|
|
29
|
+
# puts 'Done.'
|
|
30
|
+
#
|
|
3
31
|
module App
|
|
4
32
|
|
|
5
33
|
# error exit codes
|
|
@@ -9,33 +37,45 @@ module App
|
|
|
9
37
|
@parser = nil
|
|
10
38
|
@do_configure_parser = lambda { |parser| parser.parse ARGV }
|
|
11
39
|
|
|
40
|
+
# @param msg [string] is the message to output
|
|
41
|
+
# @return [void]
|
|
12
42
|
def self.error(msg)
|
|
13
43
|
$stderr.puts "error: #{msg}"
|
|
14
44
|
end
|
|
15
45
|
|
|
46
|
+
# @param msg [string] is the message to output
|
|
47
|
+
# @return [void]
|
|
16
48
|
def self.warn(msg)
|
|
17
49
|
$stderr.puts "warning: #{msg}"
|
|
18
50
|
end
|
|
19
51
|
|
|
52
|
+
# @param msg [string] is the message to output
|
|
53
|
+
# @return [void]
|
|
20
54
|
def self.info(msg)
|
|
21
55
|
$stdout.puts "info: #{msg}"
|
|
22
56
|
end
|
|
23
57
|
|
|
58
|
+
# @param msg [string] is the message to output
|
|
59
|
+
# @return [void]
|
|
24
60
|
def self.debug(msg)
|
|
25
61
|
$stdout.puts "debug: #{msg}" if App.options[:debug]
|
|
26
62
|
end
|
|
27
63
|
|
|
64
|
+
# @return [hash] parsed options, i.e. specified in argv
|
|
28
65
|
def self.options
|
|
29
66
|
parser.options
|
|
30
67
|
end
|
|
31
68
|
|
|
69
|
+
# defines a block that takes parser as parameter and configures and parses it
|
|
70
|
+
# @return [void] specified block
|
|
32
71
|
def self.configure_parser(&block)
|
|
33
72
|
reset_parser
|
|
34
|
-
@do_configure_parser = block
|
|
35
|
-
nil
|
|
73
|
+
@do_configure_parser = block
|
|
36
74
|
end
|
|
37
75
|
|
|
38
|
-
# lazy
|
|
76
|
+
# (the first time it is called after ##configure_parser, it does a lazy
|
|
77
|
+
# parser creation using the block defined in ##configure_parser)
|
|
78
|
+
# @return [Parser] parser
|
|
39
79
|
def self.parser
|
|
40
80
|
return @parser if @parser
|
|
41
81
|
@parser = Miniparse::Parser.new
|
|
@@ -44,7 +84,8 @@ module App
|
|
|
44
84
|
parser
|
|
45
85
|
end
|
|
46
86
|
|
|
47
|
-
# re-initialize the parser (
|
|
87
|
+
# re-initialize the parser (it doesn't change the configuration)
|
|
88
|
+
# @return [void]
|
|
48
89
|
def self.reset_parser
|
|
49
90
|
@parser = nil
|
|
50
91
|
end
|
data/lib/miniparse/command.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Miniparse
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
# @private
|
|
5
5
|
class Command
|
|
6
6
|
|
|
7
7
|
def self.spec_to_name(spec)
|
|
@@ -86,6 +86,7 @@ end
|
|
|
86
86
|
|
|
87
87
|
# TODO FEATURE consider doing unambiguous matches for shortened options
|
|
88
88
|
# TODO FEATURE consider the option default value setting the type
|
|
89
|
+
# @private
|
|
89
90
|
class Option < Command
|
|
90
91
|
|
|
91
92
|
attr_reader :value, :shortable
|
|
@@ -129,6 +130,7 @@ end
|
|
|
129
130
|
|
|
130
131
|
|
|
131
132
|
|
|
133
|
+
# @private
|
|
132
134
|
class SwitchOption < Option
|
|
133
135
|
|
|
134
136
|
def self.spec_to_name(spec)
|
|
@@ -170,6 +172,7 @@ end
|
|
|
170
172
|
|
|
171
173
|
|
|
172
174
|
|
|
175
|
+
# @private
|
|
173
176
|
class FlagOption < Option
|
|
174
177
|
|
|
175
178
|
def self.spec_to_name(spec)
|
data/lib/miniparse/commander.rb
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
module Miniparse
|
|
2
2
|
|
|
3
|
-
# TODO create external documentation, maybe auto
|
|
4
|
-
|
|
5
3
|
# TODO this class maybe does too much, separate a command broker or something similar
|
|
6
4
|
|
|
5
|
+
|
|
6
|
+
# @private
|
|
7
7
|
class Commander
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
# command ready to add options to
|
|
10
|
+
attr_reader :current_command_name
|
|
11
|
+
# command found while parsing arguments
|
|
12
|
+
attr_reader :parsed_command_name
|
|
13
|
+
# remaining command args after parsing the options for the parsed command
|
|
14
|
+
attr_reader :parsed_args
|
|
10
15
|
|
|
16
|
+
# @return [hash] parsed option values for the parsed command
|
|
11
17
|
def parsed_values
|
|
12
|
-
brokers[
|
|
18
|
+
brokers[parsed_command_name].parsed_values if parsed_command_name
|
|
13
19
|
end
|
|
14
20
|
|
|
15
|
-
# @return current command broker or nil
|
|
21
|
+
# @return [Broker] current command broker or nil if there is not current command
|
|
16
22
|
def current_broker
|
|
17
|
-
brokers[
|
|
23
|
+
brokers[current_command_name]
|
|
18
24
|
end
|
|
19
25
|
|
|
20
26
|
def initialize
|
|
@@ -22,12 +28,12 @@ class Commander
|
|
|
22
28
|
@brokers = {}
|
|
23
29
|
end
|
|
24
30
|
|
|
25
|
-
# @param
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
+
# @param args [hash] can have keys:
|
|
32
|
+
# * :name is the command name (ex. either "kill" or :kill)
|
|
33
|
+
# * :desc is a short description of the command
|
|
34
|
+
# * :opts are the options to apply to the command, can have keys:
|
|
35
|
+
# * :no_options indicates the command has no command line options
|
|
36
|
+
# @return [Command] the command added
|
|
31
37
|
def add_command(args, &block)
|
|
32
38
|
spec = args[:spec]
|
|
33
39
|
unless name = Command.spec_to_name(spec)
|
|
@@ -43,12 +49,12 @@ class Commander
|
|
|
43
49
|
puts help_command_text(name)
|
|
44
50
|
exit ERR_HELP_REQ
|
|
45
51
|
end
|
|
46
|
-
@
|
|
52
|
+
@current_command_name = name unless args[:no_options]
|
|
47
53
|
cmd
|
|
48
54
|
end
|
|
49
55
|
|
|
50
|
-
# @param argv is ARGV like
|
|
51
|
-
# @return
|
|
56
|
+
# @param argv [array] is ARGV like
|
|
57
|
+
# @return [array] of argv parts: [global_argv, command_name, command_argv]
|
|
52
58
|
def split_argv(argv)
|
|
53
59
|
index = index_command(argv)
|
|
54
60
|
if index
|
|
@@ -60,18 +66,21 @@ class Commander
|
|
|
60
66
|
end
|
|
61
67
|
end
|
|
62
68
|
|
|
69
|
+
# @param name [symbol] command name
|
|
70
|
+
# @param argv [array] is ARGV like to parse
|
|
71
|
+
# @return [array] #parsed_args
|
|
63
72
|
def parse_argv(name, argv)
|
|
64
73
|
cmd = commands.fetch(name)
|
|
65
|
-
@
|
|
74
|
+
@parsed_command_name = cmd.name
|
|
66
75
|
@parsed_args = brokers[cmd.name].parse_argv(argv)
|
|
67
76
|
commands[cmd.name].run(parsed_args)
|
|
68
77
|
parsed_args
|
|
69
78
|
end
|
|
70
79
|
|
|
71
|
-
# @return the command general help for the commands in the commander
|
|
80
|
+
# @return [string] the command general help for the commands in the commander
|
|
72
81
|
def help_desc
|
|
73
82
|
text = ""
|
|
74
|
-
if
|
|
83
|
+
if current_command_name
|
|
75
84
|
names_wo_desc = []
|
|
76
85
|
desc_texts = commands.sort.collect do |name, cmd|
|
|
77
86
|
if cmd.desc
|
|
@@ -97,7 +106,9 @@ class Commander
|
|
|
97
106
|
protected
|
|
98
107
|
|
|
99
108
|
attr_reader :commands, :brokers
|
|
100
|
-
|
|
109
|
+
|
|
110
|
+
# @param name [symbol] command name
|
|
111
|
+
# @return [string] text help for the specified command
|
|
101
112
|
def help_command_text(name)
|
|
102
113
|
header = "Command #{name}: #{commands[name].desc}"
|
|
103
114
|
text = "\n"
|
|
@@ -113,6 +124,8 @@ protected
|
|
|
113
124
|
text
|
|
114
125
|
end
|
|
115
126
|
|
|
127
|
+
# adds a default help command
|
|
128
|
+
# @return [void]
|
|
116
129
|
def add_help_command
|
|
117
130
|
add_command(spec: :help, desc: nil, no_options: true) do |args|
|
|
118
131
|
index = index_command(args)
|
|
@@ -126,8 +139,8 @@ protected
|
|
|
126
139
|
end
|
|
127
140
|
end
|
|
128
141
|
|
|
129
|
-
# @param argv is like ARGV
|
|
130
|
-
# @return index number of the first found command, nil if not found
|
|
142
|
+
# @param argv [array] is like ARGV
|
|
143
|
+
# @return [int|nil] index number of the first found command, nil if not found
|
|
131
144
|
def index_command(argv)
|
|
132
145
|
commands.values.each do |cmd|
|
|
133
146
|
index = argv.find_index { |arg| cmd.check(arg) }
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
|
|
2
|
+
module Miniparse
|
|
3
|
+
|
|
4
|
+
ERR_HELP_REQ = 1
|
|
5
|
+
ERR_ARGUMENT = 2
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
DEFAULT_CONTROLS = {
|
|
9
|
+
# gives an error if there is an unrecognized option either short or long
|
|
10
|
+
# (if not then passes them as arguments)
|
|
11
|
+
raise_on_unrecognized: true,
|
|
12
|
+
|
|
13
|
+
# intercepts .parse ArgumentError (i.e. the commandline user introduced
|
|
14
|
+
# wrong or invalid options) and exits with a helpful msg
|
|
15
|
+
rescue_argument_error: true,
|
|
16
|
+
|
|
17
|
+
# gives usage help and exits if commandline is empty
|
|
18
|
+
# useful if your app always needs args or options to work
|
|
19
|
+
help_cmdline_empty: false,
|
|
20
|
+
|
|
21
|
+
# raises an ArgumentError if there are global args
|
|
22
|
+
# (after parsing options and commands)
|
|
23
|
+
# useful if you don't expect any args
|
|
24
|
+
raise_global_args: false,
|
|
25
|
+
|
|
26
|
+
# formats help output with the width_... controls
|
|
27
|
+
formatted_help: true,
|
|
28
|
+
|
|
29
|
+
width_display: 79,
|
|
30
|
+
width_indent: 3,
|
|
31
|
+
width_left: 18,
|
|
32
|
+
|
|
33
|
+
# use a detailed options help usage msg or a generic one
|
|
34
|
+
detailed_usage: true,
|
|
35
|
+
|
|
36
|
+
# uses --no-... options for all options
|
|
37
|
+
# useful if you want all your options to be negatable by default
|
|
38
|
+
autonegatable: false,
|
|
39
|
+
|
|
40
|
+
# uses short options (besides long ones) for all options
|
|
41
|
+
# useful if you want all your options to be shortable by default
|
|
42
|
+
autoshortable: false,
|
|
43
|
+
|
|
44
|
+
# TODO: consider adding following control
|
|
45
|
+
# admits -h besides --help in help predefined option
|
|
46
|
+
# shortable_help: false,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
end
|
data/lib/miniparse/control.rb
CHANGED
|
@@ -1,65 +1,32 @@
|
|
|
1
1
|
module Miniparse
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
# gives an error if there is an unrecognized option either short or long
|
|
6
|
-
# (if not then passes them as arguments)
|
|
7
|
-
raise_on_unrecognized: true,
|
|
8
|
-
|
|
9
|
-
# intercepts .parse ArgumentError (i.e. the commandline user introduced
|
|
10
|
-
# wrong or invalid options) and exits with a helpful msg
|
|
11
|
-
rescue_argument_error: true,
|
|
12
|
-
|
|
13
|
-
# gives usage help and exits if commandline is empty
|
|
14
|
-
# useful if your app always needs args or options to work
|
|
15
|
-
help_cmdline_empty: false,
|
|
16
|
-
|
|
17
|
-
# raises an ArgumentError if there are global args
|
|
18
|
-
# (after parsing options and commands)
|
|
19
|
-
# useful if you don't expect any args
|
|
20
|
-
raise_global_args: false,
|
|
21
|
-
|
|
22
|
-
# formats help output with the width_... controls
|
|
23
|
-
formatted_help: true,
|
|
24
|
-
|
|
25
|
-
width_display: 79,
|
|
26
|
-
width_indent: 3,
|
|
27
|
-
width_left: 18,
|
|
28
|
-
|
|
29
|
-
# use a detailed options help usage msg or a generic one
|
|
30
|
-
detailed_usage: true,
|
|
31
|
-
|
|
32
|
-
# uses --no-... options for all options
|
|
33
|
-
# useful if you want all your options to be negatable by default
|
|
34
|
-
autonegatable: false,
|
|
35
|
-
|
|
36
|
-
# uses short options (besides long ones) for all options
|
|
37
|
-
# useful if you want all your options to be shortable by default
|
|
38
|
-
autoshortable: false,
|
|
39
|
-
|
|
40
|
-
# TODO: consider adding this option
|
|
41
|
-
# admits -h besides --help in help predefined option
|
|
42
|
-
# shortable_help: false,
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
def self.reset_controls
|
|
3
|
+
# @return [void]
|
|
4
|
+
def self.reset_default_controls
|
|
46
5
|
@behaviour_controls = {}
|
|
47
6
|
@behaviour_controls.merge! DEFAULT_CONTROLS
|
|
48
7
|
end
|
|
49
8
|
|
|
50
|
-
self.
|
|
9
|
+
self.reset_default_controls
|
|
51
10
|
|
|
52
|
-
# raises a KeyError if key is not a recognized control
|
|
53
11
|
# TODO consider raising SyntaxError with a custom msg instead of KeyError
|
|
12
|
+
|
|
13
|
+
# @param key [symbol] must be a known control, if not it raises a KeyError
|
|
14
|
+
# @return [boolean] the current value of specified control
|
|
54
15
|
def self.control(key)
|
|
55
16
|
@behaviour_controls.fetch(key)
|
|
56
17
|
end
|
|
57
18
|
|
|
58
|
-
#
|
|
19
|
+
# sets the control values specified in the hash, modern abbreviated notation can
|
|
20
|
+
# be used like e.g.
|
|
21
|
+
#
|
|
22
|
+
# set_control(autoshortable: true)
|
|
23
|
+
# set_control(autoshortable: true, autonegatable: true)
|
|
24
|
+
#
|
|
25
|
+
# @param opts [hash] must contain only known controls, if not it raises a KeyError
|
|
26
|
+
# @return [void]
|
|
59
27
|
def self.set_control(opts = {})
|
|
60
28
|
opts.keys.each { |key| @behaviour_controls.fetch key }
|
|
61
29
|
@behaviour_controls.merge! opts
|
|
62
|
-
nil
|
|
63
30
|
end
|
|
64
31
|
|
|
65
32
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Miniparse
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# @private
|
|
6
|
+
# @param right_text [string] is a usage text to be displayed to the right
|
|
7
|
+
# @return [string] the formatted text to the right with an usage header to the left
|
|
8
|
+
def self.help_usage_format(right_text)
|
|
9
|
+
left_text = "usage: #{File.basename($PROGRAM_NAME)}"
|
|
10
|
+
if Miniparse.control(:formatted_help)
|
|
11
|
+
width_display = Miniparse.control(:width_display)
|
|
12
|
+
width_left = left_text.size
|
|
13
|
+
WordWrap.two_cols_word_wrap(left_text, ' ', right_text,
|
|
14
|
+
width_left, width_display - 1 - width_left)
|
|
15
|
+
else
|
|
16
|
+
left_text + " " + right_text
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
end
|
data/lib/miniparse/parser.rb
CHANGED
|
@@ -3,23 +3,41 @@ module Miniparse
|
|
|
3
3
|
# TODO create external documentation, maybe auto
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
# this is the key class to the miniparse library,
|
|
7
|
+
# please find below an example of use:
|
|
8
|
+
#
|
|
9
|
+
# require 'miniparse'
|
|
10
|
+
#
|
|
11
|
+
# parser = Miniparse::Parser.new
|
|
12
|
+
# parser.add_option "--debug", "activate debugging"
|
|
13
|
+
# parser.parse ARGV
|
|
14
|
+
#
|
|
15
|
+
# if parser.options[:debug]
|
|
16
|
+
# puts "DEBUG ACTIVATED!"
|
|
17
|
+
# else
|
|
18
|
+
# puts "run silently"
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
6
21
|
class Parser
|
|
7
22
|
|
|
8
|
-
# @return
|
|
23
|
+
# @return [array] rest of arguments after parsing
|
|
9
24
|
attr_reader :args
|
|
10
25
|
|
|
11
|
-
# @return parsed
|
|
26
|
+
# @return [hash] parsed, i.e. specified, global options
|
|
12
27
|
def options; global_broker.parsed_values; end
|
|
13
28
|
|
|
14
|
-
# @return
|
|
15
|
-
def
|
|
29
|
+
# @return [symbol|nil] name of command parsed , i.e. specified, (or nil if none)
|
|
30
|
+
def command_name; commander.parsed_command_name; end
|
|
31
|
+
|
|
32
|
+
# @return [hash] parsed option values for the parsed command
|
|
16
33
|
def command_options; commander.parsed_values; end
|
|
34
|
+
|
|
35
|
+
# @return [array] remaining command args after parsing the options for the parsed command
|
|
17
36
|
def command_args; commander.parsed_args; end
|
|
18
37
|
|
|
19
|
-
# @return the command the next add_option will apply to
|
|
20
|
-
def
|
|
38
|
+
# @return [symbol] the name of the command the next #add_option will apply to
|
|
39
|
+
def current_command_name; commander.current_command_name; end
|
|
21
40
|
|
|
22
|
-
|
|
23
41
|
def initialize
|
|
24
42
|
@commander = Commander.new
|
|
25
43
|
@program_desc = nil
|
|
@@ -30,40 +48,42 @@ class Parser
|
|
|
30
48
|
exit ERR_HELP_REQ
|
|
31
49
|
end
|
|
32
50
|
end
|
|
33
|
-
|
|
34
|
-
|
|
51
|
+
|
|
52
|
+
# @param desc [string] is the program description to display on help msgs
|
|
53
|
+
# @return [void] same argument
|
|
35
54
|
def add_program_description(desc)
|
|
36
55
|
@program_desc = desc
|
|
37
56
|
end
|
|
38
57
|
|
|
39
|
-
|
|
40
|
-
#
|
|
41
|
-
#
|
|
58
|
+
# @param spec [string] is the option specification, similar to the option
|
|
59
|
+
# invocation in the command line (e.g. `--debug` or `--verbose LEVEL`)
|
|
60
|
+
# @param desc [string|nil] is a short description of the option
|
|
61
|
+
# @param opts [hash] are the options to apply to the option, keys can include:
|
|
42
62
|
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
# :shortable
|
|
63
|
+
# * :default
|
|
64
|
+
# * :negatable (used only for switches)
|
|
65
|
+
# * :shortable
|
|
66
|
+
#
|
|
67
|
+
# @return [void] added Option
|
|
49
68
|
def add_option(spec, desc, opts={}, &block)
|
|
50
69
|
args = opts.merge(spec: spec, desc: desc)
|
|
51
70
|
current_broker.add_option(args, &block)
|
|
52
71
|
end
|
|
53
72
|
|
|
54
|
-
# @param name is the command name
|
|
73
|
+
# @param name [symbol|string] is the command name
|
|
74
|
+
# @param desc [string|nil] is a short description of the command
|
|
75
|
+
# @param opts [hash] are the options to apply to the command, keys can be:
|
|
76
|
+
#
|
|
77
|
+
# * :no_options indicates the command has no command line options
|
|
55
78
|
#
|
|
56
|
-
# @
|
|
57
|
-
#
|
|
58
|
-
# @param opts are the options to apply to the command
|
|
59
|
-
# :no_options indicates the command has no command line options
|
|
79
|
+
# @return [void] added Command
|
|
60
80
|
def add_command(name, desc, opts={}, &block)
|
|
61
81
|
args = opts.merge(spec: name, desc: desc)
|
|
62
82
|
commander.add_command(args, &block)
|
|
63
83
|
end
|
|
64
84
|
|
|
65
85
|
# @param argv is like ARGV but just for this parser
|
|
66
|
-
# @return unprocessed arguments
|
|
86
|
+
# @return [array] unprocessed arguments
|
|
67
87
|
def parse(argv)
|
|
68
88
|
if Miniparse.control(:help_cmdline_empty) && argv.empty?
|
|
69
89
|
puts help_usage
|
|
@@ -77,14 +97,14 @@ class Parser
|
|
|
77
97
|
end
|
|
78
98
|
if Miniparse.control(:raise_global_args) && (! args.empty?)
|
|
79
99
|
# FIXME review this logic later
|
|
80
|
-
error =
|
|
100
|
+
error = current_command_name ? "unrecognized command" : "extra arguments"
|
|
81
101
|
raise ArgumentError, "#{error} '#{args[0]}'"
|
|
82
102
|
end
|
|
83
103
|
args
|
|
84
104
|
end
|
|
85
105
|
end
|
|
86
106
|
|
|
87
|
-
# @return a help message with the short descriptions
|
|
107
|
+
# @return [string] a help message with the short descriptions
|
|
88
108
|
def help_desc
|
|
89
109
|
#FIXME
|
|
90
110
|
text = ""
|
|
@@ -96,17 +116,17 @@ class Parser
|
|
|
96
116
|
text
|
|
97
117
|
end
|
|
98
118
|
|
|
99
|
-
# @return a usage message
|
|
119
|
+
# @return [string] a usage message
|
|
100
120
|
def help_usage
|
|
101
121
|
#FIXME
|
|
102
122
|
if Miniparse.control(:detailed_usage)
|
|
103
123
|
right_text = @global_broker.help_usage
|
|
104
|
-
elsif
|
|
124
|
+
elsif current_command_name
|
|
105
125
|
right_text = "[global_options]"
|
|
106
126
|
else
|
|
107
127
|
right_text = "[options]"
|
|
108
128
|
end
|
|
109
|
-
if
|
|
129
|
+
if current_command_name
|
|
110
130
|
right_text += " <command> [command_options]"
|
|
111
131
|
end
|
|
112
132
|
right_text += " <args>"
|
data/lib/miniparse/version.rb
CHANGED
data/lib/miniparse/word_wrap.rb
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
# @private
|
|
1
2
|
module WordWrap
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
# wrap a text at word boundaries
|
|
6
|
+
# wrap a text at word boundaries with a given line width
|
|
6
7
|
#
|
|
7
|
-
# @param text
|
|
8
|
-
# @param width
|
|
9
|
-
# @param reformat
|
|
10
|
-
# @return text with line breaks inserted as
|
|
8
|
+
# @param text [string] is the text to wrap
|
|
9
|
+
# @param width [numeric] is the maximum width allowed for one line before inserting line breaks
|
|
10
|
+
# @param reformat [boolean] if true then the existing line breaks are removed from the text before wrapping it
|
|
11
|
+
# @return text with line breaks inserted as necessary
|
|
11
12
|
def self.word_wrap(text, width, reformat:false)
|
|
12
13
|
text = text.gsub(/\s*\n/, ' ') if reformat
|
|
13
14
|
|
|
@@ -25,8 +26,12 @@ def self.word_wrap_lines(*args)
|
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
#
|
|
29
|
+
# wrap two texts in two separate columns
|
|
29
30
|
#
|
|
31
|
+
# @param text_left [string] is the text in the left column
|
|
32
|
+
# @param text_right [string] is the text in the right column
|
|
33
|
+
# @param separator [string] is inserted at every line between the two columns, typical are ' ' or ' | '
|
|
34
|
+
# @param reformat [boolean] like in word_wrap(...)
|
|
30
35
|
# @return an array of lines containing the merged and rearranged texts
|
|
31
36
|
def self.two_cols_word_wrap_lines(text_left, separator, text_right,
|
|
32
37
|
width_left, width_right, reformat:false)
|
|
@@ -46,9 +51,8 @@ def self.two_cols_word_wrap_lines(text_left, separator, text_right,
|
|
|
46
51
|
end
|
|
47
52
|
|
|
48
53
|
|
|
49
|
-
#
|
|
54
|
+
# same as two_cols_word_wrap_lines(...) but returns a text with \n inserted
|
|
50
55
|
#
|
|
51
|
-
# @param separator: a string of characters inserted at every line between the two collums, for example ' ' or ' | '
|
|
52
56
|
# @return text with line breaks inserted as necessary
|
|
53
57
|
def self.two_cols_word_wrap(*args)
|
|
54
58
|
two_cols_word_wrap_lines(*args).join("\n")
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: miniparse
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Juanma Rodriguez
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-03-
|
|
11
|
+
date: 2016-03-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -76,6 +76,7 @@ files:
|
|
|
76
76
|
- ".gitignore"
|
|
77
77
|
- ".rspec"
|
|
78
78
|
- ".travis.yml"
|
|
79
|
+
- ".yardopts"
|
|
79
80
|
- FAQ.md
|
|
80
81
|
- Gemfile
|
|
81
82
|
- LICENSE.txt
|
|
@@ -95,7 +96,9 @@ files:
|
|
|
95
96
|
- lib/miniparse/app.rb
|
|
96
97
|
- lib/miniparse/command.rb
|
|
97
98
|
- lib/miniparse/commander.rb
|
|
99
|
+
- lib/miniparse/constants.rb
|
|
98
100
|
- lib/miniparse/control.rb
|
|
101
|
+
- lib/miniparse/internals.rb
|
|
99
102
|
- lib/miniparse/option_broker.rb
|
|
100
103
|
- lib/miniparse/parser.rb
|
|
101
104
|
- lib/miniparse/version.rb
|