clint 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/clint.rb +166 -0
  2. data/man/clint.7.gz +0 -0
  3. metadata +106 -0
@@ -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
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
+