subcommand 1.0.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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +108 -0
- data/Rakefile +53 -0
- data/TODO +1 -0
- data/VERSION +1 -0
- data/lib/subcommand.rb +209 -0
- data/test/helper.rb +10 -0
- data/test/test_subcommand.rb +7 -0
- metadata +85 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2009 Rahul Kumar
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
= subcommand
|
|
2
|
+
|
|
3
|
+
A tiny wrapper over ruby's awesome OptionParser (standard) which gives easy facility of subcommands.
|
|
4
|
+
It has a similar interface to git and prints subcommands summary as well.
|
|
5
|
+
|
|
6
|
+
== Features:
|
|
7
|
+
|
|
8
|
+
1. subcommands using all of OptionParser's features
|
|
9
|
+
2. aliases for subcommands
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
e.g
|
|
13
|
+
Assuming a program "prog" with subcommands "del" and "add"
|
|
14
|
+
|
|
15
|
+
prog help
|
|
16
|
+
prog --help
|
|
17
|
+
|
|
18
|
+
prog help del
|
|
19
|
+
prog del --help
|
|
20
|
+
|
|
21
|
+
prog del --force file.a
|
|
22
|
+
prog --verbose del --force file.a
|
|
23
|
+
|
|
24
|
+
== Examples
|
|
25
|
+
|
|
26
|
+
if a program has subcommands foo and baz
|
|
27
|
+
|
|
28
|
+
ruby subcommand.rb help
|
|
29
|
+
ruby subcommand.rb --help
|
|
30
|
+
ruby subcommand.rb help foo
|
|
31
|
+
ruby subcommand.rb foo --help
|
|
32
|
+
ruby subcommand.rb baz --quiet "some text"
|
|
33
|
+
ruby subcommand.rb --verbose foo --force file.zzz
|
|
34
|
+
|
|
35
|
+
== STEPS
|
|
36
|
+
|
|
37
|
+
1. define global_options (optional)
|
|
38
|
+
|
|
39
|
+
global_options do |opts|
|
|
40
|
+
opts.banner = "Usage: subcommand.rb [options] [subcommand [options]]"
|
|
41
|
+
opts.description = "Stupid program that does something"
|
|
42
|
+
opts.separator ""
|
|
43
|
+
opts.separator "Global options are:"
|
|
44
|
+
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
|
45
|
+
options[:verbose] = v
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
2. define commands using command(). Send multiple names for aliases.
|
|
50
|
+
|
|
51
|
+
command :foo do |opts|
|
|
52
|
+
opts.banner = "Usage: foo [options]"
|
|
53
|
+
opts.description = "desc for foo"
|
|
54
|
+
opts.on("-f", "--[no-]force", "force action") do |v|
|
|
55
|
+
options[:force] = v
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# aliases init and create
|
|
60
|
+
command :init, :create do |opts| ...
|
|
61
|
+
|
|
62
|
+
3. call opt_parse()
|
|
63
|
+
|
|
64
|
+
== Sample Output
|
|
65
|
+
|
|
66
|
+
$ ruby subcommand.rb help
|
|
67
|
+
|
|
68
|
+
Usage: subcommand.rb [options] [subcommand [options]]
|
|
69
|
+
Stupid program that does something
|
|
70
|
+
|
|
71
|
+
Global options are:
|
|
72
|
+
-v, --[no-]verbose Run verbosely
|
|
73
|
+
|
|
74
|
+
Commands are:
|
|
75
|
+
foo : desc for foo
|
|
76
|
+
baz : desc for baz
|
|
77
|
+
|
|
78
|
+
Aliases:
|
|
79
|
+
goo - foo
|
|
80
|
+
|
|
81
|
+
See 'subcommand.rb help COMMAND' for more information on a specific command.
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
$ ruby subcommand.rb help foo
|
|
85
|
+
|
|
86
|
+
Usage: foo [options]
|
|
87
|
+
desc for foo
|
|
88
|
+
-f, --[no-]force force action
|
|
89
|
+
|
|
90
|
+
== Install
|
|
91
|
+
|
|
92
|
+
sudo gem install subcommand
|
|
93
|
+
|
|
94
|
+
Or, copy into your lib directory and require (see source for sample usage)
|
|
95
|
+
|
|
96
|
+
== Note on Patches/Pull Requests
|
|
97
|
+
|
|
98
|
+
* Fork the project.
|
|
99
|
+
* Make your feature addition or bug fix.
|
|
100
|
+
* Add tests for it. This is important so I don't break it in a
|
|
101
|
+
future version unintentionally.
|
|
102
|
+
* Commit, do not mess with rakefile, version, or history.
|
|
103
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
|
104
|
+
* Send me a pull request. Bonus points for topic branches.
|
|
105
|
+
|
|
106
|
+
== Copyright
|
|
107
|
+
|
|
108
|
+
Copyright (c) 2010 Rahul Kumar. See LICENSE for details.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'jeweler'
|
|
6
|
+
Jeweler::Tasks.new do |gem|
|
|
7
|
+
gem.name = "subcommand"
|
|
8
|
+
gem.summary = %Q{A tiny wrapper over OptionParser giving simple, elegant subcommand facility}
|
|
9
|
+
gem.description = %Q{Subcommand and alias facility for command line programs with elegant help printing}
|
|
10
|
+
gem.email = "sentinel.1879@gmail.com"
|
|
11
|
+
gem.homepage = "http://github.com/rkumar/subcommand"
|
|
12
|
+
gem.authors = ["Rahul Kumar"]
|
|
13
|
+
#gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
|
14
|
+
gem.add_development_dependency "yard", ">= 0"
|
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
|
16
|
+
end
|
|
17
|
+
Jeweler::GemcutterTasks.new
|
|
18
|
+
rescue LoadError
|
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
require 'rake/testtask'
|
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
|
24
|
+
test.libs << 'lib' << 'test'
|
|
25
|
+
test.pattern = 'test/**/test_*.rb'
|
|
26
|
+
test.verbose = true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
begin
|
|
30
|
+
require 'rcov/rcovtask'
|
|
31
|
+
Rcov::RcovTask.new do |test|
|
|
32
|
+
test.libs << 'test'
|
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
|
34
|
+
test.verbose = true
|
|
35
|
+
end
|
|
36
|
+
rescue LoadError
|
|
37
|
+
task :rcov do
|
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
task :test => :check_dependencies
|
|
43
|
+
|
|
44
|
+
task :default => :test
|
|
45
|
+
|
|
46
|
+
begin
|
|
47
|
+
require 'yard'
|
|
48
|
+
YARD::Rake::YardocTask.new
|
|
49
|
+
rescue LoadError
|
|
50
|
+
task :yardoc do
|
|
51
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
|
52
|
+
end
|
|
53
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.0.0
|
data/lib/subcommand.rb
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
######################################
|
|
3
|
+
# A tiny wrapper over optparse that gives easy subcommand facility.
|
|
4
|
+
# It also neatly prints help for global and subcommands
|
|
5
|
+
# as well as summarizes subcommands in global help.
|
|
6
|
+
#
|
|
7
|
+
# Thanks to Robert Klemme for his idea on lazy loading the subcommand option parsers.
|
|
8
|
+
#
|
|
9
|
+
# @author Rahul Kumar, Jun 2010
|
|
10
|
+
# @date 2010-06-20 22:33
|
|
11
|
+
#
|
|
12
|
+
# @examples
|
|
13
|
+
# if a program has subcommands foo and baz
|
|
14
|
+
#
|
|
15
|
+
# ruby opt.rb help
|
|
16
|
+
# ruby opt.rb --help
|
|
17
|
+
# ruby opt.rb help foo
|
|
18
|
+
# ruby opt.rb foo --help
|
|
19
|
+
# ruby opt.rb baz --quiet "some text"
|
|
20
|
+
# ruby opt.rb --verbose foo --force file.zzz
|
|
21
|
+
#
|
|
22
|
+
# == STEPS
|
|
23
|
+
# 1. define global_options (optional)
|
|
24
|
+
#
|
|
25
|
+
# global_options do |opts|
|
|
26
|
+
# opts.banner = "Usage: #{$0} [options] [subcommand [options]]"
|
|
27
|
+
# opts.description = "Stupid program that does something"
|
|
28
|
+
# opts.separator ""
|
|
29
|
+
# opts.separator "Global options are:"
|
|
30
|
+
# opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
|
31
|
+
# options[:verbose] = v
|
|
32
|
+
# end
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# 2. define commands using command().
|
|
36
|
+
# command :foo do |opts|
|
|
37
|
+
# opts.banner = "Usage: foo [options]"
|
|
38
|
+
# opts.description = "desc for foo"
|
|
39
|
+
# opts.on("-f", "--[no-]force", "force verbosely") do |v|
|
|
40
|
+
# options[:force] = v
|
|
41
|
+
# end
|
|
42
|
+
# end
|
|
43
|
+
#
|
|
44
|
+
# 3. call opt_parse()
|
|
45
|
+
#
|
|
46
|
+
# 4. As before, handle ARGS and options hash.
|
|
47
|
+
#
|
|
48
|
+
# TODO: add aliases for commands
|
|
49
|
+
######################################
|
|
50
|
+
require 'optparse'
|
|
51
|
+
|
|
52
|
+
# Allow command to have a description to generate help
|
|
53
|
+
class OptionParser
|
|
54
|
+
attr_accessor :description
|
|
55
|
+
#attr_accessor :action
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
module Subcommands
|
|
59
|
+
##
|
|
60
|
+
# specify a single command and all its options
|
|
61
|
+
# If multiple names are given, they are treated as aliases.
|
|
62
|
+
# Do repeatedly for each command
|
|
63
|
+
# Yields the optionparser
|
|
64
|
+
def command *names
|
|
65
|
+
#puts "inside command with #{names} "
|
|
66
|
+
name = names.shift
|
|
67
|
+
@commands ||= {}
|
|
68
|
+
@aliases ||= {}
|
|
69
|
+
if names.length > 0
|
|
70
|
+
names.each do |n|
|
|
71
|
+
#puts "aliases #{n} => #{name} "
|
|
72
|
+
@aliases[n.to_s] = name.to_s
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
# Thanks to Robert Klemme for the lazy loading idea.
|
|
76
|
+
opt = lambda { OptionParser.new do |opts|
|
|
77
|
+
yield opts
|
|
78
|
+
# append desc to banner in next line
|
|
79
|
+
opts.banner << "\n#{opts.description}\n" if opts.description
|
|
80
|
+
end }
|
|
81
|
+
@commands[name.to_s] = opt
|
|
82
|
+
end
|
|
83
|
+
# specify global options and banner and description
|
|
84
|
+
# Yields the optionparser
|
|
85
|
+
def global_options
|
|
86
|
+
if !defined? @global
|
|
87
|
+
@global = OptionParser.new do |opts|
|
|
88
|
+
yield opts
|
|
89
|
+
end
|
|
90
|
+
else
|
|
91
|
+
yield @global
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
# first parse global optinos
|
|
95
|
+
# then parse subcommand options if valid subcommand
|
|
96
|
+
# special case of "help command" so we print help of command - git style (3)
|
|
97
|
+
# in all invalid cases print global help
|
|
98
|
+
def opt_parse
|
|
99
|
+
# if user has not defined global, we need to create it
|
|
100
|
+
if !defined? @global
|
|
101
|
+
global_options do |opts|
|
|
102
|
+
opts.banner = "Usage: #{$0} [options] [subcommand [options]]"
|
|
103
|
+
opts.separator ""
|
|
104
|
+
opts.separator "Global options are:"
|
|
105
|
+
opts.on("-h", "--help", "Print this help") do |v|
|
|
106
|
+
puts @global
|
|
107
|
+
exit
|
|
108
|
+
end
|
|
109
|
+
opts.separator ""
|
|
110
|
+
opts.separator subtext
|
|
111
|
+
end
|
|
112
|
+
else
|
|
113
|
+
# user has defined some, but lets add subcommand information
|
|
114
|
+
cmdtext = "Commands are:"
|
|
115
|
+
@commands.each_pair do |c, opt|
|
|
116
|
+
desc = opt.call.description
|
|
117
|
+
cmdtext << "\n #{c} : #{desc}"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# print aliases
|
|
121
|
+
unless @aliases.empty?
|
|
122
|
+
cmdtext << "\n\nAliases: \n"
|
|
123
|
+
@aliases.each_pair { |name, val| cmdtext << " #{name} - #{val}" }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
cmdtext << "\n\nSee '#{$0} help COMMAND' for more information on a specific command."
|
|
127
|
+
|
|
128
|
+
global_options do |opts|
|
|
129
|
+
# lets add the description user gave into banner
|
|
130
|
+
opts.banner << "\n#{opts.description}\n" if opts.description
|
|
131
|
+
opts.separator ""
|
|
132
|
+
opts.separator cmdtext
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
@global.order!
|
|
136
|
+
cmd = ARGV.shift
|
|
137
|
+
if cmd
|
|
138
|
+
#puts "Command: #{cmd} "
|
|
139
|
+
sc = @commands[cmd]
|
|
140
|
+
unless sc
|
|
141
|
+
# see if an alias exists
|
|
142
|
+
#puts "sc nil #{@aliases} "
|
|
143
|
+
alas = @aliases[cmd]
|
|
144
|
+
#puts "sc nil #{alas} "
|
|
145
|
+
sc = @commands[alas] if alas
|
|
146
|
+
#puts "sc nil #{sc} "
|
|
147
|
+
end
|
|
148
|
+
# if valid command parse the args
|
|
149
|
+
if sc
|
|
150
|
+
sc.call.order!
|
|
151
|
+
else
|
|
152
|
+
# else if help <command> then print its help GIT style (3)
|
|
153
|
+
if !ARGV.empty? and cmd == "help"
|
|
154
|
+
cmd = ARGV.shift
|
|
155
|
+
sc = @commands[cmd]
|
|
156
|
+
# if valid command print help, else print global help
|
|
157
|
+
if sc
|
|
158
|
+
#puts " 111 help foo/baz"
|
|
159
|
+
puts sc.call
|
|
160
|
+
else puts @global
|
|
161
|
+
end
|
|
162
|
+
else
|
|
163
|
+
# invalid command
|
|
164
|
+
puts @global
|
|
165
|
+
end
|
|
166
|
+
exit 0
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
if __FILE__ == $PROGRAM_NAME
|
|
173
|
+
include Subcommands
|
|
174
|
+
options = {}
|
|
175
|
+
appname = File.basename($0)
|
|
176
|
+
# global is optional
|
|
177
|
+
global_options do |opts|
|
|
178
|
+
opts.banner = "Usage: #{appname} [options] [subcommand [options]]"
|
|
179
|
+
opts.description = "Stupid program that does something"
|
|
180
|
+
opts.separator ""
|
|
181
|
+
opts.separator "Global options are:"
|
|
182
|
+
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
|
183
|
+
options[:verbose] = v
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
# define a command
|
|
187
|
+
command :foo, :goo do |opts|
|
|
188
|
+
opts.banner = "Usage: foo [options]"
|
|
189
|
+
opts.description = "desc for foo"
|
|
190
|
+
opts.on("-f", "--[no-]force", "force verbosely") do |v|
|
|
191
|
+
options[:force] = v
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
command :baz do |opts|
|
|
195
|
+
opts.banner = "Usage: baz [options]"
|
|
196
|
+
opts.description = "desc for baz"
|
|
197
|
+
opts.on("-q", "--[no-]quiet", "quietly run ") do |v|
|
|
198
|
+
options[:quiet] = v
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# do the parsing.
|
|
203
|
+
opt_parse()
|
|
204
|
+
|
|
205
|
+
puts "options ......"
|
|
206
|
+
p options
|
|
207
|
+
puts "ARGV:"
|
|
208
|
+
p ARGV
|
|
209
|
+
end
|
data/test/helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: subcommand
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease: false
|
|
5
|
+
segments:
|
|
6
|
+
- 1
|
|
7
|
+
- 0
|
|
8
|
+
- 0
|
|
9
|
+
version: 1.0.0
|
|
10
|
+
platform: ruby
|
|
11
|
+
authors:
|
|
12
|
+
- Rahul Kumar
|
|
13
|
+
autorequire:
|
|
14
|
+
bindir: bin
|
|
15
|
+
cert_chain: []
|
|
16
|
+
|
|
17
|
+
date: 2010-06-21 00:00:00 +05:30
|
|
18
|
+
default_executable:
|
|
19
|
+
dependencies:
|
|
20
|
+
- !ruby/object:Gem::Dependency
|
|
21
|
+
name: yard
|
|
22
|
+
prerelease: false
|
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
24
|
+
requirements:
|
|
25
|
+
- - ">="
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
segments:
|
|
28
|
+
- 0
|
|
29
|
+
version: "0"
|
|
30
|
+
type: :development
|
|
31
|
+
version_requirements: *id001
|
|
32
|
+
description: Subcommand and alias facility for command line programs with elegant help printing
|
|
33
|
+
email: sentinel.1879@gmail.com
|
|
34
|
+
executables: []
|
|
35
|
+
|
|
36
|
+
extensions: []
|
|
37
|
+
|
|
38
|
+
extra_rdoc_files:
|
|
39
|
+
- LICENSE
|
|
40
|
+
- README.rdoc
|
|
41
|
+
- TODO
|
|
42
|
+
files:
|
|
43
|
+
- .document
|
|
44
|
+
- .gitignore
|
|
45
|
+
- LICENSE
|
|
46
|
+
- README.rdoc
|
|
47
|
+
- Rakefile
|
|
48
|
+
- VERSION
|
|
49
|
+
- lib/subcommand.rb
|
|
50
|
+
- test/helper.rb
|
|
51
|
+
- test/test_subcommand.rb
|
|
52
|
+
- TODO
|
|
53
|
+
has_rdoc: true
|
|
54
|
+
homepage: http://github.com/rkumar/subcommand
|
|
55
|
+
licenses: []
|
|
56
|
+
|
|
57
|
+
post_install_message:
|
|
58
|
+
rdoc_options:
|
|
59
|
+
- --charset=UTF-8
|
|
60
|
+
require_paths:
|
|
61
|
+
- lib
|
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
segments:
|
|
67
|
+
- 0
|
|
68
|
+
version: "0"
|
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
segments:
|
|
74
|
+
- 0
|
|
75
|
+
version: "0"
|
|
76
|
+
requirements: []
|
|
77
|
+
|
|
78
|
+
rubyforge_project:
|
|
79
|
+
rubygems_version: 1.3.6
|
|
80
|
+
signing_key:
|
|
81
|
+
specification_version: 3
|
|
82
|
+
summary: A tiny wrapper over OptionParser giving simple, elegant subcommand facility
|
|
83
|
+
test_files:
|
|
84
|
+
- test/helper.rb
|
|
85
|
+
- test/test_subcommand.rb
|