optitron 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -63,3 +63,76 @@ If you try parsing invalid parameters, get back friendly error messages
63
63
  => ["File is required"]
64
64
  @parser.parse(%w(kill --pid=something)).error_messages
65
65
  => ["Pid is invalid"]
66
+
67
+ == Usage in a binary
68
+
69
+ To use this in a file, create a parser, and tell it to dispatch to your favourite object. For instance, save this down to <tt>test.rb</tt>
70
+
71
+ class Runner
72
+ def install(file, opts)
73
+ puts "installing #{file} with #{opts}!"
74
+ end
75
+ end
76
+
77
+ Optitron.dispatch(Runner.new) {
78
+ opt 'verbose', "Be very loud"
79
+ cmd "install", "This installs things" do
80
+ arg "file", "The file to install"
81
+ end
82
+ cmd "show", "This shows things" do
83
+ arg "first", "The first thing to show"
84
+ arg "second", "The second optional thing to show", :required => false
85
+ end
86
+ cmd "kill", "This kills things" do
87
+ opt "pids", "A list of pids to kill", :type => :array
88
+ opt "pid", "A pid to kill", :type => :numeric
89
+ opt "names", "Some sort of hash", :type => :hash
90
+ end
91
+ cmd "join", "This joins things" do
92
+ arg "thing", "Stuff to join", :type => :greedy
93
+ end
94
+ }
95
+
96
+ Now, try running it.
97
+
98
+ crapbook-pro:optitron joshua$ ruby test.rb
99
+ Commands
100
+
101
+ show [first] <second> # This shows things
102
+ install [file] # This installs things
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
108
+
109
+ Global options
110
+
111
+ -v/--verbose # Be very loud
112
+
113
+ Errors:
114
+ Unknown command
115
+
116
+ crapbook-pro:optitron joshua$ ruby test.rb install
117
+ Commands
118
+
119
+ show [first] <second> # This shows things
120
+ install [file] # This installs things
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
126
+
127
+ Global options
128
+
129
+ -v/--verbose # Be very loud
130
+
131
+ Errors:
132
+ File is required
133
+
134
+ crapbook-pro:optitron joshua$ ruby test.rb install file
135
+ installing file with {"verbose"=>false}!
136
+
137
+ crapbook-pro:optitron joshua$ ruby test.rb install file --verbose true
138
+ installing file with {"verbose"=>true}!
data/lib/optitron.rb CHANGED
@@ -4,9 +4,12 @@ class Optitron
4
4
  autoload :Parser, 'optitron/parser'
5
5
  autoload :Response, 'optitron/response'
6
6
  autoload :Option, 'optitron/option'
7
+ autoload :Help, 'optitron/help'
7
8
 
8
9
  InvalidParser = Class.new(RuntimeError)
9
10
 
11
+ attr_reader :parser
12
+
10
13
  def initialize(&blk)
11
14
  @parser = Parser.new
12
15
  Dsl.new(@parser, &blk) if blk
@@ -20,6 +23,12 @@ class Optitron
20
23
  Optitron.new(&blk).parse(args)
21
24
  end
22
25
 
26
+ def self.dispatch(target = nil, args = ARGV, &blk)
27
+ optitron = Optitron.new(&blk)
28
+ optitron.parser.target = target
29
+ optitron.parser.parse(args).dispatch
30
+ end
31
+
23
32
  def help
24
33
  @parser.help
25
34
  end
@@ -0,0 +1,82 @@
1
+ class Optitron
2
+ class Help
3
+ def initialize(parser)
4
+ @parser = parser
5
+ end
6
+
7
+ def help_line_for_opt(opt)
8
+ opt_line = ''
9
+ opt_line << [opt.short_name ? "-#{opt.short_name}" : nil, "--#{opt.name}"].compact.join('/')
10
+ opt_line << "=[#{opt.type.to_s.upcase}]" if opt.type != :boolean
11
+ [opt_line, opt.desc]
12
+ end
13
+
14
+ def help_line_for_arg(arg)
15
+ arg_line = ''
16
+ arg_line << (arg.required? ? '[' : '<')
17
+ if arg.type == :greedy
18
+ arg_line << arg.name << '1 ' << arg.name << '2 ...'
19
+ else
20
+ arg_line << arg.name
21
+ end
22
+ arg_line << (arg.required? ? ']' : '>')
23
+ arg_line
24
+ end
25
+
26
+ def generate
27
+ cmds = {}
28
+ @parser.commands.each do |cmd_name, cmd|
29
+ cmd_line = "#{cmd_name}"
30
+ cmd.args.each do |arg|
31
+ cmd_line << " " << help_line_for_arg(arg)
32
+ end
33
+ cmds[cmd_line] = [cmd.desc]
34
+ cmd.options.each do |opt|
35
+ cmds[cmd_line] << help_line_for_opt(opt)
36
+ end
37
+ end
38
+ opts_lines = @parser.options.map do |opt|
39
+ help_line_for_opt(opt)
40
+ end
41
+
42
+ args_lines = @parser.args.empty? ? nil : [@parser.args.map{|arg| help_line_for_arg(arg)}.join(' '), @parser.args.map{|arg| arg.desc}.join(', ')]
43
+
44
+ longest_line = 0
45
+ longest_line = [longest_line, cmds.keys.map{|k| k.size}.max].max unless cmds.empty?
46
+ opt_lines = cmds.map{|k,v| k.size + 2}.flatten
47
+ longest_line = [longest_line, args_lines.first.size].max if args_lines
48
+ longest_line = [longest_line, opt_lines.max].max unless opt_lines.empty?
49
+ longest_line = [opts_lines.map{|o| o.first.size}.max, longest_line].max unless opts_lines.empty?
50
+ help_output = []
51
+
52
+ unless cmds.empty?
53
+ help_output << "Commands\n\n" + cmds.map do |cmd, opts|
54
+ cmd_text = ""
55
+ cmd_text << "%-#{longest_line}s " % cmd
56
+ cmd_desc = opts.shift
57
+ cmd_text << "# #{cmd_desc}" if cmd_desc
58
+ opts.each do |opt|
59
+ cmd_text << "\n %-#{longest_line}s " % opt.first
60
+ cmd_text << "# #{opt.last}" if opt.last
61
+ end
62
+ cmd_text
63
+ end.join("\n")
64
+ end
65
+ if args_lines
66
+ arg_help = "Arguments\n\n"
67
+ arg_help << "%-#{longest_line}s " % args_lines.first
68
+ arg_help << "# #{args_lines.last}" if args_lines.first
69
+ help_output << arg_help
70
+ end
71
+ unless opts_lines.empty?
72
+ help_output << "Global options\n\n" + opts_lines.map do |opt|
73
+ opt_text = ''
74
+ opt_text << "%-#{longest_line}s " % opt.first
75
+ opt_text << "# #{opt.last}" if opt.last
76
+ opt_text
77
+ end.join("\n")
78
+ end
79
+ help_output.join("\n\n")
80
+ end
81
+ end
82
+ end
@@ -1,16 +1,23 @@
1
1
  class Optitron
2
2
  class Parser
3
- attr_reader :commands, :options
3
+ attr_accessor :target
4
+ attr_reader :commands, :options, :args
4
5
  def initialize
5
6
  @options = []
6
7
  @commands = {}
8
+ @args = []
9
+ @help = Help.new(self)
10
+ end
11
+
12
+ def help
13
+ @help.generate
7
14
  end
8
15
 
9
16
  def parse(args = ARGV)
10
17
  tokens = Tokenizer.new(args).tokens
11
- response = Response.new(tokens)
18
+ response = Response.new(self, tokens)
12
19
  options = @options
13
- args = nil
20
+ args = @args
14
21
  if !@commands.empty?
15
22
  potential_cmd_toks = tokens.select { |t| t.respond_to?(:val) }
16
23
  if cmd_tok = potential_cmd_toks.find { |t| @commands[t.val] }
@@ -43,60 +50,5 @@ class Optitron
43
50
  def parse_args(tokens, args, response)
44
51
  args.each { |arg| arg.consume(response, tokens) } if args
45
52
  end
46
-
47
- def help
48
- cmds = {}
49
- @commands.each do |cmd_name, cmd|
50
- cmd_line = "#{cmd_name}"
51
- cmd.args.each do |arg|
52
- cmd_line << " "
53
- cmd_line << (arg.required? ? '[' : '<')
54
- if arg.type == :greedy
55
- cmd_line << arg.name << '1 ' << arg.name << '2 ...'
56
- else
57
- cmd_line << arg.name
58
- end
59
- cmd_line << (arg.required? ? ']' : '>')
60
- end
61
- cmds[cmd_line] = [cmd.desc]
62
- cmd.options.each do |opt|
63
- opt_line = ''
64
- opt_line << [opt.short_name ? "-#{opt.short_name}" : nil, "--#{opt.name}"].compact.join('/')
65
- opt_line << "=[#{opt.type.to_s.upcase}]" if opt.type != :boolean
66
- cmds[cmd_line] << [opt_line, opt.desc]
67
- end
68
- end
69
- opts_lines = @options.map do |opt|
70
- opt_line = ''
71
- opt_line << [opt.short_name ? "-#{opt.short_name}" : nil, "--#{opt.name}"].compact.join('/')
72
- opt_line << "=[#{opt.type.to_s.upcase}]" if opt.type != :boolean
73
- [opt_line, opt.desc]
74
- end
75
-
76
- longest_line = cmds.keys.map{|k| k.size}.max
77
- opt_lines = cmds.map{|k,v| k.size + 2}.flatten
78
- longest_line = [longest_line, opt_lines.max].max unless opt_lines.empty?
79
- longest_line = [opts_lines.map{|o| o.first.size}.max, longest_line].max unless opts_lines.empty?
80
- help_output = "Commands\n\n" + cmds.map do |cmd, opts|
81
- cmd_text = ""
82
- cmd_text << "%-#{longest_line}s " % cmd
83
- cmd_desc = opts.shift
84
- cmd_text << "# #{cmd_desc}" if cmd_desc
85
- opts.each do |opt|
86
- cmd_text << "\n %-#{longest_line}s " % opt.first
87
- cmd_text << "# #{opt.last}" if opt.last
88
- end
89
- cmd_text
90
- end.join("\n")
91
- unless opts_lines.empty?
92
- help_output << "\n\nGlobal options\n\n"
93
- help_output << opts_lines.map do |opt|
94
- opt_text = ''
95
- opt_text << "%-#{longest_line}s " % opt.first
96
- opt_text << "# #{opt.last}" if opt.last
97
- opt_text
98
- end.join("\n")
99
- end
100
- end
101
53
  end
102
54
  end
@@ -2,8 +2,8 @@ class Optitron
2
2
  class Response
3
3
  attr_reader :params_array, :args, :params, :args_with_tokens, :errors
4
4
  attr_accessor :command
5
- def initialize(tokens)
6
- @tokens = tokens
5
+ def initialize(parser, tokens)
6
+ @parser, @tokens = parser, tokens
7
7
  @params_array = []
8
8
  @args_with_tokens = []
9
9
  @args = []
@@ -56,15 +56,18 @@ class Optitron
56
56
  end
57
57
  end
58
58
 
59
- def dispatch(obj)
59
+ def dispatch
60
+ raise unless @parser.target
60
61
  if valid?
61
62
  dispatch_args = params.empty? ? args : args + [params]
62
- obj.send(command.to_sym, *dispatch_args)
63
+ @parser.target.send(command.to_sym, *dispatch_args)
63
64
  else
64
- raise
65
+ puts @parser.help
66
+ puts "\nErrors:"
67
+ puts error_messages.join("\n")
65
68
  end
66
69
  end
67
-
70
+
68
71
  def valid?
69
72
  @errors.empty?
70
73
  end
@@ -1,3 +1,3 @@
1
1
  class Optitron
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
data/spec/arg_spec.rb CHANGED
@@ -143,4 +143,19 @@ describe "Optitron::Parser arg spec" do
143
143
  }.should raise_error(Optitron::InvalidParser)
144
144
  end
145
145
  end
146
+
147
+ context "root level args" do
148
+ before(:each) {
149
+ @parser = Optitron.new {
150
+ arg "file"
151
+ }
152
+ }
153
+
154
+ it "should parse" do
155
+ response = @parser.parse(%w(life.rb))
156
+ response.command.should be_nil
157
+ response.args.should == ['life.rb']
158
+ response.valid?.should be_true
159
+ end
160
+ end
146
161
  end
@@ -4,32 +4,29 @@ describe "Optitron::Parser dispatching" do
4
4
  it "should dispatch 'install'" do
5
5
  m = mock('mock')
6
6
  m.should_receive('install').with()
7
- @parser = Optitron.new {
7
+ Optitron.dispatch(m, %w(install)) {
8
8
  cmd "install"
9
9
  }
10
- @parser.parse(%w(install)).dispatch(m)
11
10
  end
12
11
 
13
12
  it "should dispatch 'install file'" do
14
13
  m = mock('mock')
15
14
  m.should_receive('install').with('file.rb')
16
- @parser = Optitron.new {
15
+ Optitron.dispatch(m, %w(install file.rb)) {
17
16
  cmd "install" do
18
17
  arg "file"
19
18
  end
20
19
  }
21
- @parser.parse(%w(install file.rb)).dispatch(m)
22
20
  end
23
21
 
24
22
  it "should dispatch 'install file --noop'" do
25
23
  m = mock('mock')
26
24
  m.should_receive('install').with('file.rb', {'noop' => true})
27
- @parser = Optitron.new {
25
+ Optitron.dispatch(m, %w(install file.rb --noop)) {
28
26
  cmd "install" do
29
27
  opt 'noop'
30
28
  arg "file"
31
29
  end
32
30
  }
33
- @parser.parse(%w(install file.rb --noop)).dispatch(m)
34
31
  end
35
32
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "Optitron::Parser help" do
4
- it "generate help" do
4
+ it "generate help for command parsers" do
5
5
  @parser = Optitron.new {
6
6
  opt 'verbose', "Be very loud"
7
7
  cmd "install", "This installs things" do
@@ -22,4 +22,13 @@ describe "Optitron::Parser help" do
22
22
  }
23
23
  @parser.help.should == "Commands\n\nshow [first] <second> # This shows things\ninstall [file] # This installs things\nkill # This kills things\n -p/--pids=[ARRAY] # A list of pids to kill\n -P/--pid=[NUMERIC] # A pid to kill\n -n/--names=[HASH] # Some sort of hash\njoin [thing1 thing2 ...] # This joins things\n\nGlobal options\n\n-v/--verbose # Be very loud"
24
24
  end
25
+
26
+ it "generate help for non-command parsers" do
27
+ @parser = Optitron.new {
28
+ opt 'verbose', "Be very loud"
29
+ arg "src", "Source"
30
+ arg "dest", "Destination"
31
+ }
32
+ @parser.help.should == "Arguments\n\n[src] [dest] # Source, Destination\n\nGlobal options\n\n-v/--verbose # Be very loud"
33
+ end
25
34
  end
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: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 4
10
- version: 0.0.4
9
+ - 5
10
+ version: 0.0.5
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-15 00:00:00 -07:00
18
+ date: 2010-08-16 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -52,6 +52,7 @@ files:
52
52
  - Rakefile
53
53
  - lib/optitron.rb
54
54
  - lib/optitron/dsl.rb
55
+ - lib/optitron/help.rb
55
56
  - lib/optitron/option.rb
56
57
  - lib/optitron/parser.rb
57
58
  - lib/optitron/response.rb
@@ -62,8 +63,8 @@ files:
62
63
  - spec/default_spec.rb
63
64
  - spec/dispatch_spec.rb
64
65
  - spec/errors_spec.rb
66
+ - spec/help_spec.rb
65
67
  - spec/option_spec.rb
66
- - spec/other_spec.rb
67
68
  - spec/short_name_spec.rb
68
69
  - spec/simple_spec.rb
69
70
  - spec/spec.opts