clint 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|