clint 0.1.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/lib/clint.rb +166 -0
- data/man/clint.7.gz +0 -0
- metadata +106 -0
data/lib/clint.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
class Clint
|
2
|
+
|
3
|
+
def initialize(options={})
|
4
|
+
reset
|
5
|
+
@strict = !options[:strict].nil?
|
6
|
+
end
|
7
|
+
|
8
|
+
def usage
|
9
|
+
if block_given?
|
10
|
+
@usage = Proc.new
|
11
|
+
else
|
12
|
+
@usage.call if @usage.respond_to? :call
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def help
|
17
|
+
if block_given?
|
18
|
+
@help = Proc.new
|
19
|
+
else
|
20
|
+
usage
|
21
|
+
@help.call if @help.respond_to? :call
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Reset the list of valid options and aliases.
|
26
|
+
def reset
|
27
|
+
@options, @aliases = {}, {}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Add new valid options and aliases with either classes to be constructed
|
31
|
+
# or default values (from which classes are inferred). This returns
|
32
|
+
# @options and thus works as an attr_reader with no arguments.
|
33
|
+
def options(options={})
|
34
|
+
options.each do |option, default|
|
35
|
+
option = option.to_sym
|
36
|
+
if Symbol == default.class
|
37
|
+
@aliases[option] = default
|
38
|
+
else
|
39
|
+
if Class == default.class
|
40
|
+
if default.respond_to? :new
|
41
|
+
begin
|
42
|
+
@options[option] = default.new
|
43
|
+
rescue ArgumentError
|
44
|
+
@options[option] = default.new(nil)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
begin
|
48
|
+
@options[option] = default()
|
49
|
+
rescue ArgumentError
|
50
|
+
@options[option] = default(nil)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
else
|
54
|
+
@options[option] = default
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
@options
|
59
|
+
end
|
60
|
+
|
61
|
+
attr_reader :aliases
|
62
|
+
|
63
|
+
# Parse arguments, saving options in @options and leaving everything else
|
64
|
+
# in @args.
|
65
|
+
def parse(args=nil)
|
66
|
+
args = @args if args.nil?
|
67
|
+
i = 0
|
68
|
+
while args.length > i do
|
69
|
+
|
70
|
+
# Skip anything not structured like an option.
|
71
|
+
case args[i]
|
72
|
+
when /^-([^-=\s])$/
|
73
|
+
when /^-([^-=\s])\s*(.+)$/
|
74
|
+
when /^--([^=\s]+)$/
|
75
|
+
when /^--([^=\s]+)(?:=|\s+)(.+)?$/
|
76
|
+
else
|
77
|
+
i += 1
|
78
|
+
next
|
79
|
+
end
|
80
|
+
option, value = $1.to_sym, $2
|
81
|
+
|
82
|
+
# Follow aliases through to a real option.
|
83
|
+
option = @aliases[option] while @aliases[option]
|
84
|
+
|
85
|
+
# Skip unknown options unless we're in strict mode.
|
86
|
+
if @options[option].nil?
|
87
|
+
if @strict
|
88
|
+
usage
|
89
|
+
exit 1
|
90
|
+
end
|
91
|
+
i += 1
|
92
|
+
next
|
93
|
+
end
|
94
|
+
|
95
|
+
# Handle boolean options.
|
96
|
+
if [TrueClass, FalseClass].include? @options[option].class
|
97
|
+
unless value.nil?
|
98
|
+
usage
|
99
|
+
exit 1
|
100
|
+
end
|
101
|
+
args.delete_at i
|
102
|
+
@options[option] = !@options[option]
|
103
|
+
|
104
|
+
# Handle options with values. The call to new below may raise
|
105
|
+
# NoMethodError but this is allowed to surface so it's noticed
|
106
|
+
# during development.
|
107
|
+
else
|
108
|
+
args.delete_at i
|
109
|
+
value = args.delete_at(i) if value.nil?
|
110
|
+
if value.nil?
|
111
|
+
usage
|
112
|
+
exit 1
|
113
|
+
end
|
114
|
+
@options[option] = @options[option].class.new(value)
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
118
|
+
@args = args
|
119
|
+
end
|
120
|
+
|
121
|
+
# Treat the first non-option argument as a subcommand in the given class.
|
122
|
+
# If a suitable class method is found, it is called with all remaining
|
123
|
+
# arguments, including @options if we can get away with it. Otherwise,
|
124
|
+
# an instance is constructed with the next non-option argument and the
|
125
|
+
# subcommand is sent to the instance with the remaining non-option
|
126
|
+
# arguments.
|
127
|
+
def subcommand(klass)
|
128
|
+
if 1 > @args.length
|
129
|
+
usage
|
130
|
+
exit 1
|
131
|
+
end
|
132
|
+
subcommand = @args.shift.to_sym
|
133
|
+
if klass.singleton_methods(false).include? subcommand.to_s
|
134
|
+
arity = klass.method(subcommand).arity
|
135
|
+
if @args.length != arity && -@args.length - 1 != arity
|
136
|
+
usage
|
137
|
+
exit 1
|
138
|
+
end
|
139
|
+
begin
|
140
|
+
klass.send subcommand, *(@args + [@options])
|
141
|
+
rescue ArgumentError
|
142
|
+
klass.send subcommand, *@args
|
143
|
+
end
|
144
|
+
exit 0
|
145
|
+
end
|
146
|
+
if 1 > @args.length
|
147
|
+
usage
|
148
|
+
exit 1
|
149
|
+
end
|
150
|
+
instance = klass.new(@args.shift)
|
151
|
+
if instance.public_methods(false).include? subcommand.to_s
|
152
|
+
arity = instance.method(subcommand).arity
|
153
|
+
if @args.length != arity && -@args.length - 1 != arity
|
154
|
+
usage
|
155
|
+
exit 1
|
156
|
+
end
|
157
|
+
begin
|
158
|
+
instance.send subcommand, *(@args + [@options])
|
159
|
+
rescue ArgumentError
|
160
|
+
instance.send subcommand, *@args
|
161
|
+
end
|
162
|
+
exit 0
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
data/man/clint.7.gz
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: clint
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Richard Crowley
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-02-22 00:00:00 +00:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: |
|
22
|
+
clint(7) -- Ruby command line argument parser
|
23
|
+
=============================================
|
24
|
+
|
25
|
+
## SYNOPSIS
|
26
|
+
|
27
|
+
require 'clint'
|
28
|
+
c = Clint.new
|
29
|
+
c.usage do
|
30
|
+
$stderr.puts "Usage: #{File.basename(__FILE__)} [-h|--help]"
|
31
|
+
end
|
32
|
+
c.help do
|
33
|
+
$stderr.puts " -h, --help\tshow this help message"
|
34
|
+
end
|
35
|
+
c.options :help => false, :h => :help
|
36
|
+
c.parse ARGV
|
37
|
+
if c.options[:help]
|
38
|
+
c.help
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
c.subcommand Klass
|
42
|
+
|
43
|
+
## DESCRIPTION
|
44
|
+
|
45
|
+
Clint is an alternative Ruby command line argument parser that's very good for programs using the subcommand pattern familiar from `git`(1), `svn`(1), `apt-get`(8), and many others. In addition, it separates option declarations from usage and help messages becuase the author feels like that's a better idea.
|
46
|
+
|
47
|
+
Clint options are declared by passing hash arguments to `Clint#options`. The hash keys should be `Symbol`s. If the value is also a `Symbol`, an alias is defined from the key to the value. If the value is a `Class`, Clint attempts to find a default value for that class. Otherwise, the value is treated as the default and the value's class will be used to construct type-accurate values from command line arguments.
|
48
|
+
|
49
|
+
`Clint#options` may be called repeatedly to declare extra options and aliases. `Clint#reset` can be used at any time to clear all declared options and aliases.
|
50
|
+
|
51
|
+
`Clint#parse` may likewise be called repeatedly. At the end of each invocation, it stores the remaining non-option arguments, meaning that arguments (for example, `ARGV`) must only be passed as a parameter to the first invocation.
|
52
|
+
|
53
|
+
`Clint#subcommand` may be called after `Clint#parse` to automatically handle the subcommand pattern as follows. The first non-option argument is taken to be the subcommand, which must exist as a singleton or instance method of the class object passed to `Clint#subcommand`. If a suitable class method is found, it is called with all remaining arguments, including a hash of the parsed options if we can get away with it. Otherwise, an instance is constructed with the next non-option argument and the instance method is called with all remaining arguments, again including a hash of the parsed options if we can get away with it.
|
54
|
+
|
55
|
+
Due to limitations in the Ruby 1.8 grammar, all methods that could act as subcommands must not declare default argument values except `options={}` if desired.
|
56
|
+
|
57
|
+
## AUTHOR
|
58
|
+
|
59
|
+
Richard Crowley <r@rcrowley.org>
|
60
|
+
|
61
|
+
## SEE ALSO
|
62
|
+
|
63
|
+
The standard Ruby `OptionParser` class <http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html>.
|
64
|
+
|
65
|
+
email: r@rcrowley.org
|
66
|
+
executables: []
|
67
|
+
|
68
|
+
extensions: []
|
69
|
+
|
70
|
+
extra_rdoc_files: []
|
71
|
+
|
72
|
+
files:
|
73
|
+
- lib/clint.rb
|
74
|
+
- man/clint.7.gz
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: http://github.com/rcrowley/clint.git
|
77
|
+
licenses: []
|
78
|
+
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
version: "0"
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 1.3.6
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: command line argument parser
|
105
|
+
test_files: []
|
106
|
+
|