optitron 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +45 -28
- data/lib/optitron.rb +1 -0
- data/lib/optitron/class_dsl.rb +82 -0
- data/lib/optitron/dsl.rb +28 -10
- data/lib/optitron/help.rb +1 -1
- data/lib/optitron/option.rb +34 -3
- data/lib/optitron/parser.rb +1 -1
- data/lib/optitron/response.rb +1 -2
- data/lib/optitron/tokenizer.rb +5 -5
- data/lib/optitron/version.rb +1 -1
- data/spec/option_spec.rb +2 -2
- metadata +5 -4
data/README.rdoc
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
You can specify lots of different commands with options
|
6
6
|
|
7
7
|
@parser = Optitron.new {
|
8
|
+
help
|
8
9
|
opt 'verbose', "Be very loud"
|
9
10
|
cmd "install", "This installs things" do
|
10
11
|
arg "file", "The file to install"
|
@@ -96,43 +97,59 @@ To use this in a file, create a parser, and tell it to dispatch to your favourit
|
|
96
97
|
Now, try running it.
|
97
98
|
|
98
99
|
crapbook-pro:optitron joshua$ ruby test.rb
|
99
|
-
|
100
|
+
Unknown command
|
100
101
|
|
101
|
-
|
102
|
-
|
103
|
-
kill # This kills things
|
104
|
-
-p/--pids=[ARRAY] # A list of pids to kill
|
105
|
-
-P/--pid=[NUMERIC] # A pid to kill
|
106
|
-
-n/--names=[HASH] # Some sort of hash
|
107
|
-
join [thing1 thing2 ...] # This joins things
|
102
|
+
crapbook-pro:optitron joshua$ ruby test.rb install
|
103
|
+
File is required
|
108
104
|
|
109
|
-
|
105
|
+
crapbook-pro:optitron joshua$ ruby test.rb install file
|
106
|
+
installing file with {"verbose"=>false}!
|
107
|
+
|
108
|
+
crapbook-pro:optitron joshua$ ruby test.rb install file --verbose
|
109
|
+
installing file with {"verbose"=>true}!
|
110
|
+
|
111
|
+
== Usage in a class
|
112
|
+
|
113
|
+
require 'optitron'
|
110
114
|
|
111
|
-
|
115
|
+
class Runner
|
116
|
+
include Optitron::ClassDsl
|
112
117
|
|
113
|
-
|
114
|
-
|
118
|
+
class_opt 'verbose'
|
119
|
+
use_help
|
115
120
|
|
116
|
-
|
121
|
+
desc "Install stuff"
|
122
|
+
opt 'force'
|
123
|
+
arg 'file'
|
124
|
+
arg 'source', :required => false
|
125
|
+
def install(file, source)
|
126
|
+
puts "install some things #{file} from #{source.inspect} #{params.inspect}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
Runner.dispatch
|
131
|
+
|
132
|
+
Running this gives you
|
133
|
+
|
134
|
+
crapbook-pro:optitron joshua$ ruby ideal.rb --help
|
117
135
|
Commands
|
118
136
|
|
119
|
-
|
120
|
-
|
121
|
-
kill # This kills things
|
122
|
-
-p/--pids=[ARRAY] # A list of pids to kill
|
123
|
-
-P/--pid=[NUMERIC] # A pid to kill
|
124
|
-
-n/--names=[HASH] # Some sort of hash
|
125
|
-
join [thing1 thing2 ...] # This joins things
|
137
|
+
install [file] <source> # Install stuff
|
138
|
+
-f/--force
|
126
139
|
|
127
140
|
Global options
|
128
141
|
|
129
|
-
-v/--verbose
|
130
|
-
|
131
|
-
|
142
|
+
-v/--verbose
|
143
|
+
-?/--help # Print help message
|
144
|
+
|
145
|
+
crapbook-pro:optitron joshua$ ruby ideal.rb install
|
132
146
|
File is required
|
133
|
-
|
134
|
-
crapbook-pro:optitron joshua$ ruby test.rb install file
|
135
|
-
installing file with {"verbose"=>false}!
|
136
147
|
|
137
|
-
crapbook-pro:optitron joshua$ ruby
|
138
|
-
installing file with {"verbose"=>
|
148
|
+
crapbook-pro:optitron joshua$ ruby ideal.rb install file
|
149
|
+
installing file from yourmom with params: {"help"=>false, "force"=>false, "verbose"=>false}
|
150
|
+
|
151
|
+
crapbook-pro:optitron joshua$ ruby ideal.rb install file yourdad
|
152
|
+
installing file from yourdad with params: {"help"=>false, "force"=>false, "verbose"=>false}
|
153
|
+
|
154
|
+
crapbook-pro:optitron joshua$ ruby ideal.rb install file yourdad -v
|
155
|
+
installing file from yourdad with params: {"help"=>false, "force"=>false, "verbose"=>true}
|
data/lib/optitron.rb
CHANGED
@@ -0,0 +1,82 @@
|
|
1
|
+
class Optitron
|
2
|
+
module ClassDsl
|
3
|
+
|
4
|
+
def self.included(o)
|
5
|
+
o.module_eval "
|
6
|
+
@@optitron_parser = Optitron::Parser.new
|
7
|
+
@@optitron_dsl = Optitron::Dsl.new(@@optitron_parser)
|
8
|
+
attr_accessor :params
|
9
|
+
class << self;
|
10
|
+
include ClassMethods
|
11
|
+
end"
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def method_added(m)
|
17
|
+
last_opts = @opts
|
18
|
+
@cmds ||= []
|
19
|
+
@cmds << [m.to_s, @last_desc, @args.dup || [], @opts.dup || []]
|
20
|
+
@opts.clear if @opts
|
21
|
+
@args.clear if @args
|
22
|
+
end
|
23
|
+
|
24
|
+
def optitron_dsl
|
25
|
+
self.send(:class_variable_get, :@@optitron_dsl)
|
26
|
+
end
|
27
|
+
|
28
|
+
def optitron_parser
|
29
|
+
self.send(:class_variable_get, :@@optitron_parser)
|
30
|
+
end
|
31
|
+
|
32
|
+
def class_opt(name, desc = nil, opts = nil)
|
33
|
+
optitron_dsl.root.opt(name, desc, opts)
|
34
|
+
end
|
35
|
+
|
36
|
+
def use_help
|
37
|
+
optitron_dsl.root.help
|
38
|
+
end
|
39
|
+
|
40
|
+
def desc(desc)
|
41
|
+
@last_desc = desc
|
42
|
+
end
|
43
|
+
|
44
|
+
def arg(name, desc = nil, opts = nil)
|
45
|
+
@args ||= []
|
46
|
+
@args << [name, desc, opts]
|
47
|
+
end
|
48
|
+
|
49
|
+
def opt(name, desc = nil, opts = nil)
|
50
|
+
@opts ||= []
|
51
|
+
@opts << [name, desc, opts]
|
52
|
+
end
|
53
|
+
|
54
|
+
def build
|
55
|
+
@cmds.each do |(cmd_name, cmd_desc, args, opts)|
|
56
|
+
arity = instance_method(cmd_name).arity
|
57
|
+
optitron_dsl.root.cmd(cmd_name, cmd_desc) do
|
58
|
+
opts.each { |o| opt *o }
|
59
|
+
args.each { |a| arg *a }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
optitron_dsl.configure_options
|
63
|
+
end
|
64
|
+
|
65
|
+
def dispatch
|
66
|
+
build
|
67
|
+
optitron_parser.target = new
|
68
|
+
response = optitron_parser.parse(ARGV)
|
69
|
+
if response.valid?
|
70
|
+
optitron_parser.target.params = response.params
|
71
|
+
args = response.args
|
72
|
+
while (args.size < optitron_parser.commands[response.command].args.size)
|
73
|
+
args << optitron_parser.commands[response.command].args[args.size].default
|
74
|
+
end
|
75
|
+
optitron_parser.target.send(response.command.to_sym, *response.args)
|
76
|
+
else
|
77
|
+
puts response.error_messages.join("\n")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/optitron/dsl.rb
CHANGED
@@ -1,23 +1,30 @@
|
|
1
1
|
class Optitron
|
2
2
|
class Dsl
|
3
|
+
attr_reader :root
|
3
4
|
|
4
5
|
def initialize(parser, &blk)
|
5
|
-
root = RootParserDsl.new(parser)
|
6
|
-
|
7
|
-
|
6
|
+
@root = RootParserDsl.new(parser)
|
7
|
+
if blk
|
8
|
+
@root.configure_with(&blk)
|
9
|
+
configure_options
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def configure_options
|
14
|
+
@root.unclaimed_opts.each do |opt_option|
|
8
15
|
name = opt_option.name
|
9
|
-
if
|
16
|
+
if !@root.short_opts.key?(name[0].chr)
|
10
17
|
opt_option.short_name = name[0].chr
|
11
|
-
root.short_opts[name[0].chr] = opt_option
|
12
|
-
elsif
|
18
|
+
@root.short_opts[name[0].chr] = opt_option
|
19
|
+
elsif !@root.short_opts.key?(name.upcase[0].chr)
|
13
20
|
opt_option.short_name = name.upcase[0].chr
|
14
|
-
root.short_opts[name.upcase[0].chr] = opt_option
|
21
|
+
@root.short_opts[name.upcase[0].chr] = opt_option
|
15
22
|
end
|
16
23
|
end
|
17
24
|
end
|
18
|
-
|
25
|
+
|
19
26
|
class AbstractDsl
|
20
|
-
def configure_with(block)
|
27
|
+
def configure_with(&block)
|
21
28
|
instance_eval(&block)
|
22
29
|
end
|
23
30
|
|
@@ -63,13 +70,24 @@ class Optitron
|
|
63
70
|
@unclaimed_opts = []
|
64
71
|
end
|
65
72
|
|
73
|
+
def help(desc = "Print help message")
|
74
|
+
configure_with {
|
75
|
+
opt 'help', desc, :short_name => '?', :run => proc{ |value, response|
|
76
|
+
if value
|
77
|
+
puts @target.help
|
78
|
+
exit(0)
|
79
|
+
end
|
80
|
+
}
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
66
84
|
def short_opts
|
67
85
|
@target.short_opts
|
68
86
|
end
|
69
87
|
|
70
88
|
def cmd(name, description = nil, opts = nil, &blk)
|
71
89
|
command_option = Option::Cmd.new(name, description, opts)
|
72
|
-
CmdParserDsl.new(self, command_option).configure_with(blk) if blk
|
90
|
+
CmdParserDsl.new(self, command_option).configure_with(&blk) if blk
|
73
91
|
@target.commands[name] = command_option
|
74
92
|
end
|
75
93
|
end
|
data/lib/optitron/help.rb
CHANGED
@@ -7,7 +7,7 @@ class Optitron
|
|
7
7
|
def help_line_for_opt(opt)
|
8
8
|
opt_line = ''
|
9
9
|
opt_line << [opt.short_name ? "-#{opt.short_name}" : nil, "--#{opt.name}"].compact.join('/')
|
10
|
-
opt_line << "=[#{opt.type.to_s.upcase}]"
|
10
|
+
opt_line << "=[#{opt.type.to_s.upcase}]" unless opt.boolean?
|
11
11
|
[opt_line, opt.desc]
|
12
12
|
end
|
13
13
|
|
data/lib/optitron/option.rb
CHANGED
@@ -35,6 +35,34 @@ class Optitron
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
def boolean?
|
39
|
+
@type == :boolean
|
40
|
+
end
|
41
|
+
|
42
|
+
def numeric?
|
43
|
+
@type == :numeric
|
44
|
+
end
|
45
|
+
|
46
|
+
def array?
|
47
|
+
@type == :array
|
48
|
+
end
|
49
|
+
|
50
|
+
def string?
|
51
|
+
@type == :string
|
52
|
+
end
|
53
|
+
|
54
|
+
def hash?
|
55
|
+
@type == :hash
|
56
|
+
end
|
57
|
+
|
58
|
+
def greedy?
|
59
|
+
@type == :greedy
|
60
|
+
end
|
61
|
+
|
62
|
+
def any?
|
63
|
+
@type.nil?
|
64
|
+
end
|
65
|
+
|
38
66
|
def validate(val)
|
39
67
|
validated_type = case @type
|
40
68
|
when :boolean
|
@@ -61,7 +89,7 @@ class Optitron
|
|
61
89
|
end
|
62
90
|
|
63
91
|
class Opt < Option
|
64
|
-
attr_accessor :short_name
|
92
|
+
attr_accessor :short_name, :run
|
65
93
|
def initialize(name, desc = nil, opts = nil)
|
66
94
|
if desc.is_a?(Hash)
|
67
95
|
desc, opts = nil, desc
|
@@ -69,6 +97,7 @@ class Optitron
|
|
69
97
|
@name, @desc = name, desc
|
70
98
|
@type = opts && opts[:type] || :boolean
|
71
99
|
self.short_name = opts[:short_name] if opts && opts[:short_name]
|
100
|
+
self.run = opts[:run] if opts && opts[:run]
|
72
101
|
self.inclusion_test = opts[:in] if opts && opts[:in]
|
73
102
|
self.default = opts && opts.key?(:default) ? opts[:default] : (@type == :boolean ? false : nil)
|
74
103
|
end
|
@@ -137,12 +166,13 @@ class Optitron
|
|
137
166
|
end
|
138
167
|
|
139
168
|
class Cmd < Option
|
140
|
-
attr_reader :options, :args
|
169
|
+
attr_reader :options, :args, :run
|
141
170
|
def initialize(name, desc = nil, opts = nil)
|
142
171
|
if desc.is_a?(Hash)
|
143
172
|
desc, opts = nil, desc
|
144
173
|
end
|
145
174
|
@name, @desc = name, desc
|
175
|
+
@run = opts[:run] if opts && opts[:run]
|
146
176
|
@options = []
|
147
177
|
@args = []
|
148
178
|
end
|
@@ -157,7 +187,8 @@ class Optitron
|
|
157
187
|
end
|
158
188
|
@name, @desc = name, desc
|
159
189
|
self.inclusion_test = opts[:in] if opts && opts[:in]
|
160
|
-
@
|
190
|
+
@default = opts && opts[:default]
|
191
|
+
@required = opts && opts.key?(:required) ? opts[:required] : @default.nil?
|
161
192
|
@type = opts && opts[:type]
|
162
193
|
end
|
163
194
|
|
data/lib/optitron/parser.rb
CHANGED
@@ -20,7 +20,7 @@ class Optitron
|
|
20
20
|
response = Response.new(self, tokens)
|
21
21
|
options = @options
|
22
22
|
args = @args
|
23
|
-
|
23
|
+
unless @commands.empty?
|
24
24
|
potential_cmd_toks = tokens.select { |t| t.respond_to?(:lit) }
|
25
25
|
if cmd_tok = potential_cmd_toks.find { |t| @commands[t.lit] }
|
26
26
|
tokens.delete(cmd_tok)
|
data/lib/optitron/response.rb
CHANGED
@@ -33,6 +33,7 @@ class Optitron
|
|
33
33
|
|
34
34
|
def validate
|
35
35
|
compile_params
|
36
|
+
@params_array.each { |(key, value)| key.run.call(params[key.name], self) if key.run }
|
36
37
|
@args = @args_with_tokens.map { |(arg, tok)|
|
37
38
|
begin
|
38
39
|
tok.is_a?(Array) ? tok.map{ |t| arg.validate(t.lit) } : arg.validate(tok.lit)
|
@@ -62,8 +63,6 @@ class Optitron
|
|
62
63
|
dispatch_args = params.empty? ? args : args + [params]
|
63
64
|
@parser.target.send(command.to_sym, *dispatch_args)
|
64
65
|
else
|
65
|
-
puts @parser.help
|
66
|
-
puts "\nErrors:"
|
67
66
|
puts error_messages.join("\n")
|
68
67
|
end
|
69
68
|
end
|
data/lib/optitron/tokenizer.rb
CHANGED
@@ -14,10 +14,10 @@ class Optitron
|
|
14
14
|
unless @tokens
|
15
15
|
@tokens = @opts.map {|t|
|
16
16
|
case t
|
17
|
-
when /^--([^=]+)=([^=]+)$/ then NamedWithValue.new($1, $2)
|
18
|
-
when /^--([^=]+)$/ then NamedWithValue.new($1, nil)
|
19
|
-
when /^-(.*)/
|
20
|
-
else
|
17
|
+
when /^--([^-][^=]+)=([^=]+)$/ then NamedWithValue.new($1, $2)
|
18
|
+
when /^--([^-][^=]+)$/ then NamedWithValue.new($1, nil)
|
19
|
+
when /^-(.*)/ then find_names_values($1)
|
20
|
+
else Value.new(t)
|
21
21
|
end
|
22
22
|
}
|
23
23
|
@tokens.flatten!
|
@@ -30,7 +30,7 @@ class Optitron
|
|
30
30
|
while !letters.empty?
|
31
31
|
let = letters.shift
|
32
32
|
toks << Named.new(let)
|
33
|
-
if @parser.short_opts[let] &&
|
33
|
+
if @parser.short_opts[let] && !@parser.short_opts[let].boolean? && !letters.empty?
|
34
34
|
toks << Value.new(letters.join)
|
35
35
|
letters.clear
|
36
36
|
end
|
data/lib/optitron/version.rb
CHANGED
data/spec/option_spec.rb
CHANGED
@@ -79,8 +79,8 @@ describe "Optitron::Parser options" do
|
|
79
79
|
}
|
80
80
|
end
|
81
81
|
|
82
|
-
it "should parse '-
|
83
|
-
@parser.parse(%w(-
|
82
|
+
it "should parse '-123otest'" do
|
83
|
+
@parser.parse(%w(-123otest)).params.should == {'option' => 'test', '1' => true, '2' => true, '3' => true}
|
84
84
|
end
|
85
85
|
|
86
86
|
it "should parse '-123o test'" do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: optitron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 13
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 9
|
10
|
+
version: 0.0.9
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Joshua Hull
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-08-
|
18
|
+
date: 2010-08-18 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -51,6 +51,7 @@ files:
|
|
51
51
|
- README.rdoc
|
52
52
|
- Rakefile
|
53
53
|
- lib/optitron.rb
|
54
|
+
- lib/optitron/class_dsl.rb
|
54
55
|
- lib/optitron/dsl.rb
|
55
56
|
- lib/optitron/help.rb
|
56
57
|
- lib/optitron/option.rb
|