docopt 0.0.4 → 0.5.0
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.
- data/Gemfile +3 -0
- data/{LICENSE-MIT → LICENSE} +4 -1
- data/README.md +117 -128
- data/Rakefile +150 -0
- data/docopt.gemspec +83 -0
- data/examples/any_options_example.rb +24 -0
- data/examples/calculator.rb +16 -0
- data/examples/counted_example.rb +22 -0
- data/examples/example_options.rb +44 -0
- data/examples/git_example.rb +44 -0
- data/examples/naval_fate.rb +30 -0
- data/examples/odd_even_example.rb +19 -0
- data/examples/quick_example.rb +16 -0
- data/lib/docopt.rb +632 -81
- data/test/test_docopt.rb +49 -0
- data/test/testee.rb +12 -0
- metadata +48 -18
- data/example.rb +0 -30
data/Gemfile
ADDED
data/{LICENSE-MIT → LICENSE}
RENAMED
@@ -1,4 +1,7 @@
|
|
1
|
-
Copyright (c) 2012 Vladimir Keleshev <vladimir@keleshev.com
|
1
|
+
Copyright (c) 2012 Vladimir Keleshev <vladimir@keleshev.com>
|
2
|
+
Blake Williams <code@shabbyrobe.org>
|
3
|
+
Alex Speller <alex@alexspeller.com>
|
4
|
+
Nima Johari
|
2
5
|
|
3
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
7
|
this software and associated documentation files (the "Software"), to deal in
|
data/README.md
CHANGED
@@ -1,72 +1,79 @@
|
|
1
|
-
`docopt` – command line option parser, that will make you smile
|
1
|
+
`docopt.rb` – command line option parser, that will make you smile
|
2
2
|
===============================================================================
|
3
3
|
|
4
|
-
[
|
4
|
+
This is the ruby port of [`docopt`](https://github.com/docopt/docopt),
|
5
|
+
the awesome option parser written originally in python.
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
> New in version 0.5.0:
|
8
|
+
>
|
9
|
+
> Repeatable flags and commands are counted if repeated (a-la ssh `-vvv`).
|
10
|
+
> Repeatable options with arguments are accumulated into list.
|
8
11
|
|
9
|
-
|
10
|
-
based on
|
12
|
+
Isn't it awesome how `optparse` and `argparse` generate help messages
|
13
|
+
based on your code?!
|
14
|
+
|
15
|
+
*Hell no!* You know what's awesome? It's when the option parser *is* generated
|
16
|
+
based on the beautiful help message that you write yourself! This way
|
11
17
|
you don't need to write this stupid repeatable parser-code, and instead can
|
12
|
-
write
|
13
|
-
to your code.
|
18
|
+
write only the help message--*the way you want it*.
|
14
19
|
|
15
|
-
|
20
|
+
`docopt` helps you create most beautiful command-line interfaces *easily*:
|
16
21
|
|
17
22
|
```ruby
|
18
|
-
|
23
|
+
require "docopt"
|
24
|
+
doc = <<DOCOPT
|
25
|
+
Naval Fate.
|
26
|
+
|
27
|
+
Usage:
|
28
|
+
#{__FILE__} ship new <name>...
|
29
|
+
#{__FILE__} ship <name> move <x> <y> [--speed=<kn>]
|
30
|
+
#{__FILE__} ship shoot <x> <y>
|
31
|
+
#{__FILE__} mine (set|remove) <x> <y> [--moored|--drifting]
|
32
|
+
#{__FILE__} -h | --help
|
33
|
+
#{__FILE__} --version
|
19
34
|
|
20
35
|
Options:
|
21
|
-
-h --help
|
22
|
-
--version
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
--count print total number of errors and warnings to standard
|
35
|
-
error and set exit code to 1 if total is not null
|
36
|
-
--benchmark measure processing speed
|
37
|
-
--testsuite=dir run regression tests from dir
|
38
|
-
--doctest run doctest on myself"
|
39
|
-
|
40
|
-
require 'docopt'
|
41
|
-
|
42
|
-
|
43
|
-
if __FILE__ == $0
|
44
|
-
options = Docopt(doc, '1.0.0') # parse options based on doc above
|
45
|
-
puts options.inspect
|
46
|
-
puts ARGV.inspect
|
36
|
+
-h --help Show this screen.
|
37
|
+
--version Show version.
|
38
|
+
--speed=<kn> Speed in knots [default: 10].
|
39
|
+
--moored Moored (anchored) mine.
|
40
|
+
--drifting Drifting mine.
|
41
|
+
|
42
|
+
DOCOPT
|
43
|
+
|
44
|
+
begin
|
45
|
+
require "pp"
|
46
|
+
pp Docopt::docopt(doc)
|
47
|
+
rescue Docopt::Exit => e
|
48
|
+
puts e.message
|
47
49
|
end
|
48
50
|
```
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
+
Beat that! The option parser is generated based on the docstring above that is
|
53
|
+
passed to `docopt` function. `docopt` parses the usage pattern
|
54
|
+
(`Usage: ...`) and option descriptions (lines starting with dash "`-`") and
|
55
|
+
ensures that the program invocation matches the usage pattern; it parses
|
56
|
+
options, arguments and commands based on that. The basic idea is that
|
57
|
+
*a good help message has all necessary information in it to make a parser*.
|
52
58
|
|
53
|
-
|
54
|
-
|
55
|
-
doc = "Usage: your_program.rb [options]
|
59
|
+
Installation
|
60
|
+
===============================================================================
|
56
61
|
|
57
|
-
|
58
|
-
-v --verbose Print more text.
|
59
|
-
--quiet Print less text.
|
60
|
-
-o FILE Specify output file [default: ./test.txt]"
|
62
|
+
Docopt is available via rubygems:
|
61
63
|
|
62
|
-
|
64
|
+
gem install docopt
|
63
65
|
|
64
|
-
|
66
|
+
Alternatively, you can just drop `lib/docopt.rb` file into your project--it is
|
67
|
+
self-contained. [Get source on github](http://github.com/docopt/docopt.rb).
|
65
68
|
|
66
|
-
|
69
|
+
`docopt` has been confirmed to work with 1.8.7p370 and 1.9.3p194. If you have
|
70
|
+
noticed it working (or not working) with an earlier version, please raise an
|
71
|
+
issue and we will investigate support.
|
67
72
|
|
73
|
+
API
|
74
|
+
===============================================================================
|
68
75
|
|
69
|
-
`Docopt` takes 1 required and
|
76
|
+
`Docopt` takes 1 required and 1 optional argument:
|
70
77
|
|
71
78
|
- `doc` should be a string that
|
72
79
|
describes **options** in a human-readable format, that will be parsed to create
|
@@ -81,6 +88,10 @@ section. Here is a quick example of such a string:
|
|
81
88
|
--quiet Print less text.
|
82
89
|
-o FILE Specify output file [default: ./test.txt].
|
83
90
|
|
91
|
+
|
92
|
+
The optional second argument contains a hash of additional data to influence
|
93
|
+
docopt. The following keys are supported:
|
94
|
+
|
84
95
|
- `help`, by default `true`, specifies whether the parser should automatically
|
85
96
|
print the usage-message (supplied as `doc`) in case `-h` or `--help` options
|
86
97
|
are encountered. After showing the usage-message, the program will terminate.
|
@@ -97,101 +108,79 @@ Note, when `docopt` is set to automatically handle `-h`, `--help` and
|
|
97
108
|
`--version` options, you still need to mention them in the options description
|
98
109
|
(`doc`) for your users to know about them.
|
99
110
|
|
100
|
-
The **return** value is
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
--verbose
|
105
|
-
-o FILE Output file [default: out.txt]"
|
106
|
-
|
107
|
-
options = Docopt(doc)
|
108
|
-
|
109
|
-
puts options.inspect
|
110
|
-
# --verbose=nil
|
111
|
-
# -o="out.txt"
|
112
|
-
```
|
113
|
-
|
114
|
-
You can access the values of options like a hash:
|
115
|
-
|
116
|
-
```
|
117
|
-
doc = "Options:
|
118
|
-
-v, --verbose Verbose output [default: true]
|
119
|
-
-o FILE Output file [default: out.txt]"
|
120
|
-
|
121
|
-
options = Docopt(doc)
|
111
|
+
The **return** value is just a dictionary with options, arguments and commands,
|
112
|
+
with keys spelled exactly like in a help message
|
113
|
+
(long versions of options are given priority). For example, if you invoke
|
114
|
+
the top example as::
|
122
115
|
|
123
|
-
|
124
|
-
|
125
|
-
puts options['-v']
|
126
|
-
puts options['--verbose']
|
127
|
-
puts options[:v]
|
128
|
-
puts options[:verbose]
|
116
|
+
naval_fate.rb ship Guardian move 100 150 --speed=15
|
129
117
|
|
118
|
+
the return dictionary will be::
|
130
119
|
|
120
|
+
```ruby
|
121
|
+
{"ship"=>true,
|
122
|
+
"new"=>false,
|
123
|
+
"<name>"=>["Guardian"],
|
124
|
+
"move"=>true,
|
125
|
+
"<x>"=>"100",
|
126
|
+
"<y>"=>"150",
|
127
|
+
"--speed"=>"15",
|
128
|
+
"shoot"=>false,
|
129
|
+
"mine"=>false,
|
130
|
+
"set"=>false,
|
131
|
+
"remove"=>false,
|
132
|
+
"--moored"=>false,
|
133
|
+
"--drifting"=>false,
|
134
|
+
"--help"=>false,
|
135
|
+
"--version"=>false}
|
131
136
|
```
|
132
137
|
|
133
|
-
|
134
|
-
|
135
|
-
`doc` string format for your usage-message
|
138
|
+
Help message format
|
136
139
|
===============================================================================
|
137
140
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
Here are the simple rules (that you probably already follow) for your
|
142
|
-
usage-message to be parsable:
|
143
|
-
|
144
|
-
- Every line that starts with `-` or `--` (not counting spaces) is treated
|
145
|
-
as an option description, e.g.:
|
146
|
-
|
147
|
-
Options:
|
148
|
-
--verbose # GOOD
|
149
|
-
-o FILE # GOOD
|
150
|
-
Other: --bad # BAD, line does not start with dash "-"
|
141
|
+
docopt.rb follows the docopt help message format.
|
142
|
+
You can find more details at
|
143
|
+
[official docopt git repo](https://github.com/docopt/docopt#help-message-format)
|
151
144
|
|
152
|
-
- To specify that an option has an argument, put a word describing that
|
153
|
-
argument after space (or equals `=` sign) as shown below.
|
154
|
-
You can use comma if you want to separate options. In the example below both
|
155
|
-
lines are valid, however you are recommended to stick to a single style.
|
156
145
|
|
157
|
-
|
158
|
-
|
146
|
+
Examples
|
147
|
+
-------------------------------------------------------------------------------
|
159
148
|
|
160
|
-
|
149
|
+
We have an extensive list of
|
150
|
+
[examples](https://github.com/docopt/docopt.rb/tree/master/examples)
|
151
|
+
which cover every aspect of functionality of `docopt`. Try them out,
|
152
|
+
read the source if in doubt.
|
161
153
|
|
162
|
-
|
163
|
-
|
164
|
-
-q Quit. # GOOD
|
165
|
-
-o FILE Output file. # GOOD
|
166
|
-
--stdout Use stdout. # GOOD, 2 spaces
|
154
|
+
Data validation
|
155
|
+
-------------------------------------------------------------------------------
|
167
156
|
|
168
|
-
|
169
|
-
|
157
|
+
`docopt` does one thing and does it well: it implements your command-line
|
158
|
+
interface. However it does not validate the input data. We are looking
|
159
|
+
for ruby validation libraries to make your option parsing experiene
|
160
|
+
even more awesome!
|
161
|
+
If you've got any suggestions or think your awesome schema validation gem
|
162
|
+
fits well with `docopt.rb`, open an issue on github and enjoy the eternal glory!
|
170
163
|
|
171
|
-
|
172
|
-
--coefficient=K The K coefficient [default: 2.95]
|
173
|
-
--output=FILE Output file [default: test.txt]
|
174
|
-
--directory=DIR Some directory [default: ./]
|
175
|
-
|
176
|
-
Something missing? Help porting [docopt](http://docopt.org/) to Ruby!
|
177
|
-
===============================================================================
|
178
|
-
|
179
|
-
Compatibility notice:
|
164
|
+
Contribution
|
180
165
|
===============================================================================
|
181
166
|
|
182
|
-
|
183
|
-
|
184
|
-
|
167
|
+
We would *love* to hear what you think about `docopt.rb`.
|
168
|
+
Contribute, make pull requrests, report bugs, suggest ideas and discuss
|
169
|
+
`docopt.rb` on
|
170
|
+
[issues page](http://github.com/docopt/docopt.rb/issues).
|
185
171
|
|
186
|
-
|
172
|
+
If you want to discuss the original `docopt` reference,
|
173
|
+
point to [it's home](http://github.com/docopt/docopt) or
|
174
|
+
drop a line directly to vladimir@keleshev.com!
|
187
175
|
|
188
|
-
|
189
|
-
|
190
|
-
Usage: my_program.rb [options] <argument>
|
191
|
-
|
192
|
-
or
|
176
|
+
Porting `docopt` to other languages
|
177
|
+
===============================================================================
|
193
178
|
|
194
|
-
|
179
|
+
Docopt is an interlinguistic (?) effort,
|
180
|
+
and this is the ruby port of `docopt`.
|
181
|
+
We coordinate our efforts with docopt community and try our best to
|
182
|
+
keep in sync with the python reference.
|
195
183
|
|
196
|
-
|
197
|
-
and
|
184
|
+
Docopt community *loves* to hear what you think about `docopt`, `docopt.rb`
|
185
|
+
and other sister projects on docopt's
|
186
|
+
[issues page](http://github.com/docopt/docopt/issues).
|
data/Rakefile
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
#############################################################################
|
6
|
+
#
|
7
|
+
# Helper functions
|
8
|
+
#
|
9
|
+
#############################################################################
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name ||= Dir['*.gemspec'].first.split('.').first
|
13
|
+
end
|
14
|
+
|
15
|
+
def version
|
16
|
+
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
17
|
+
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
18
|
+
end
|
19
|
+
|
20
|
+
def date
|
21
|
+
Date.today.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def rubyforge_project
|
25
|
+
name
|
26
|
+
end
|
27
|
+
|
28
|
+
def gemspec_file
|
29
|
+
"#{name}.gemspec"
|
30
|
+
end
|
31
|
+
|
32
|
+
def gem_file
|
33
|
+
"#{name}-#{version}.gem"
|
34
|
+
end
|
35
|
+
|
36
|
+
def replace_header(head, header_name)
|
37
|
+
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
38
|
+
end
|
39
|
+
|
40
|
+
#############################################################################
|
41
|
+
#
|
42
|
+
# Standard tasks
|
43
|
+
#
|
44
|
+
#############################################################################
|
45
|
+
|
46
|
+
task :default => :test
|
47
|
+
|
48
|
+
require 'rake/testtask'
|
49
|
+
Rake::TestTask.new(:test) do |test|
|
50
|
+
test.libs << 'lib' << 'test'
|
51
|
+
test.pattern = 'test/**/test_*.rb'
|
52
|
+
test.verbose = true
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Generate RCov test coverage and open in your browser"
|
56
|
+
task :coverage do
|
57
|
+
require 'rcov'
|
58
|
+
sh "rm -fr coverage"
|
59
|
+
sh "rcov test/test_*.rb"
|
60
|
+
sh "open coverage/index.html"
|
61
|
+
end
|
62
|
+
|
63
|
+
require 'rdoc/task'
|
64
|
+
Rake::RDocTask.new do |rdoc|
|
65
|
+
rdoc.rdoc_dir = 'rdoc'
|
66
|
+
rdoc.title = "#{name} #{version}"
|
67
|
+
rdoc.rdoc_files.include('README*')
|
68
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "Open an irb session preloaded with this library"
|
72
|
+
task :console do
|
73
|
+
sh "irb -rubygems -r ./lib/#{name}.rb"
|
74
|
+
end
|
75
|
+
|
76
|
+
#############################################################################
|
77
|
+
#
|
78
|
+
# Custom tasks (add your own tasks here)
|
79
|
+
#
|
80
|
+
#############################################################################
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
#############################################################################
|
85
|
+
#
|
86
|
+
# Packaging tasks
|
87
|
+
#
|
88
|
+
#############################################################################
|
89
|
+
|
90
|
+
desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
|
91
|
+
task :release => :build do
|
92
|
+
unless `git branch` =~ /^\* master$/
|
93
|
+
puts "You must be on the master branch to release!"
|
94
|
+
exit!
|
95
|
+
end
|
96
|
+
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
97
|
+
sh "git tag v#{version}"
|
98
|
+
sh "git push origin master"
|
99
|
+
sh "git push origin v#{version}"
|
100
|
+
sh "gem push pkg/#{name}-#{version}.gem"
|
101
|
+
end
|
102
|
+
|
103
|
+
desc "Build #{gem_file} into the pkg directory"
|
104
|
+
task :build => :gemspec do
|
105
|
+
sh "mkdir -p pkg"
|
106
|
+
sh "gem build #{gemspec_file}"
|
107
|
+
sh "mv #{gem_file} pkg"
|
108
|
+
end
|
109
|
+
|
110
|
+
desc "Generate #{gemspec_file}"
|
111
|
+
task :gemspec => :validate do
|
112
|
+
# read spec file and split out manifest section
|
113
|
+
spec = File.read(gemspec_file)
|
114
|
+
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
115
|
+
|
116
|
+
# replace name version and date
|
117
|
+
replace_header(head, :name)
|
118
|
+
replace_header(head, :version)
|
119
|
+
replace_header(head, :date)
|
120
|
+
#comment this out if your rubyforge_project has a different name
|
121
|
+
replace_header(head, :rubyforge_project)
|
122
|
+
|
123
|
+
# determine file list from git ls-files
|
124
|
+
files = `git ls-files`.
|
125
|
+
split("\n").
|
126
|
+
sort.
|
127
|
+
reject { |file| file =~ /^\./ }.
|
128
|
+
reject { |file| file =~ /^(rdoc|pkg)/ }.
|
129
|
+
map { |file| " #{file}" }.
|
130
|
+
join("\n")
|
131
|
+
|
132
|
+
# piece file back together and write
|
133
|
+
manifest = " s.files = %w[\n#{files}\n ]\n"
|
134
|
+
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
135
|
+
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
136
|
+
puts "Updated #{gemspec_file}"
|
137
|
+
end
|
138
|
+
|
139
|
+
desc "Validate #{gemspec_file}"
|
140
|
+
task :validate do
|
141
|
+
libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
|
142
|
+
unless libfiles.empty?
|
143
|
+
puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
|
144
|
+
exit!
|
145
|
+
end
|
146
|
+
unless Dir['VERSION*'].empty?
|
147
|
+
puts "A `VERSION` file at root level violates Gem best practices."
|
148
|
+
exit!
|
149
|
+
end
|
150
|
+
end
|