optitron 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ optitron (0.0.9)
5
+ callsite (= 0.0.4)
6
+ ruby2ruby (= 1.2.4)
7
+ ruby_parser (>= 2.0)
8
+ sexp_processor (= 3.0.4)
9
+
10
+ GEM
11
+ remote: http://rubygems.org/
12
+ specs:
13
+ callsite (0.0.4)
14
+ rake (0.8.7)
15
+ rspec (1.3.0)
16
+ ruby2ruby (1.2.4)
17
+ ruby_parser (~> 2.0)
18
+ sexp_processor (~> 3.0)
19
+ ruby_parser (2.0.4)
20
+ sexp_processor (~> 3.0)
21
+ sexp_processor (3.0.4)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ bundler (>= 1.0.0.rc.3)
28
+ callsite (= 0.0.4)
29
+ optitron!
30
+ rake
31
+ rspec
32
+ ruby2ruby (= 1.2.4)
33
+ ruby_parser (>= 2.0)
34
+ sexp_processor (= 3.0.4)
@@ -112,16 +112,13 @@ Now, try running it.
112
112
 
113
113
  require 'optitron'
114
114
 
115
- class Runner
116
- include Optitron::ClassDsl
115
+ class Runner < Optitron::CLI
117
116
 
118
117
  class_opt 'verbose'
119
118
  use_help
120
119
 
121
120
  desc "Install stuff"
122
121
  opt 'force'
123
- arg 'file'
124
- arg 'source', :required => false
125
122
  def install(file, source)
126
123
  puts "install some things #{file} from #{source.inspect} #{params.inspect}"
127
124
  end
@@ -6,6 +6,7 @@ class Optitron
6
6
  autoload :Response, 'optitron/response'
7
7
  autoload :Option, 'optitron/option'
8
8
  autoload :Help, 'optitron/help'
9
+ autoload :CLI, 'optitron/cli'
9
10
 
10
11
  InvalidParser = Class.new(RuntimeError)
11
12
 
@@ -1,10 +1,66 @@
1
+ require 'callsite'
2
+ require 'ruby2ruby'
3
+ require 'ruby_parser'
4
+ require 'sexp_processor'
5
+
1
6
  class Optitron
7
+
8
+ class MethodArgs < SexpProcessor
9
+ attr_reader :method_map
10
+ def initialize(cls)
11
+ @cls = cls
12
+ @method_map = {}
13
+ super()
14
+ end
15
+
16
+ def process_class(exp)
17
+ exp.shift
18
+ @current_class = exp.first.to_sym
19
+ process(exp)
20
+ exp.clear
21
+ exp
22
+ end
23
+
24
+ def process_defn(exp)
25
+ exp.shift
26
+ @current_method = exp.shift
27
+ @ruby2ruby = Ruby2Ruby.new
28
+ process_args(exp.shift)
29
+ scope = exp.shift
30
+ exp
31
+ end
32
+
33
+ def process_args(exp)
34
+ exp.shift
35
+ arg_list = []
36
+ while !exp.empty?
37
+ t = exp.shift
38
+ case t
39
+ when Symbol
40
+ arg_list << if t.to_s[0] == ?*
41
+ [t.to_s[1, t.to_s.size].to_sym, :greedy]
42
+ else
43
+ [t, :required]
44
+ end
45
+ when Sexp
46
+ case t.shift
47
+ when :block
48
+ lasgn = t.shift
49
+ lasgn.shift
50
+ arg_list[-1].shift
51
+ arg_list[-1] << :optional
52
+ arg_list[-1] << @ruby2ruby.process(lasgn.last)
53
+ end
54
+ end
55
+ end
56
+ @method_map[@current_method] = arg_list if @cls.name.to_sym == @current_class
57
+ end
58
+ end
59
+
2
60
  module ClassDsl
3
61
 
4
62
  def self.included(o)
5
- o.module_eval "
6
- @@optitron_parser = Optitron::Parser.new
7
- @@optitron_dsl = Optitron::Dsl.new(@@optitron_parser)
63
+ o.class_eval "
8
64
  attr_accessor :params
9
65
  class << self;
10
66
  include ClassMethods
@@ -14,58 +70,86 @@ class Optitron
14
70
 
15
71
  module ClassMethods
16
72
  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
73
+ if @last_desc
74
+ last_opts = @opts
75
+ @cmds ||= []
76
+ @cmds << [m.to_s, @last_desc, @opts ? @opts.dup : []]
77
+ @opts.clear if @opts
78
+ @args.clear if @args
79
+ end
22
80
  end
23
-
81
+ def optitron_parser
82
+ send(:class_variable_set, :@@optitron_parser, Optitron::Parser.new) unless send(:class_variable_defined?, :@@optitron_parser)
83
+ send(:class_variable_get, :@@optitron_parser)
84
+ end
85
+
24
86
  def optitron_dsl
25
- self.send(:class_variable_get, :@@optitron_dsl)
87
+ send(:class_variable_set, :@@optitron_dsl, Optitron::Dsl.new(optitron_parser)) unless send(:class_variable_defined?, :@@optitron_dsl)
88
+ send(:class_variable_get, :@@optitron_dsl)
26
89
  end
27
90
 
28
- def optitron_parser
29
- self.send(:class_variable_get, :@@optitron_parser)
91
+ def method_args
92
+ send(:class_variable_get, :@@method_args)
93
+ end
94
+
95
+ def build_method_args(file)
96
+ unless self.send(:class_variable_defined?, :@@method_args)
97
+ parser = RubyParser.new
98
+ sexp = parser.process(File.read(file))
99
+ method_args = MethodArgs.new(self)
100
+ method_args.process(sexp)
101
+ self.send(:class_variable_set, :@@method_args, method_args.method_map)
102
+ end
103
+ send(:class_variable_get, :@@method_args)
30
104
  end
31
105
 
32
106
  def class_opt(name, desc = nil, opts = nil)
33
107
  optitron_dsl.root.opt(name, desc, opts)
34
108
  end
35
109
 
36
- def use_help
110
+ def dont_use_help
37
111
  optitron_dsl.root.help
38
112
  end
39
113
 
40
114
  def desc(desc)
115
+ build_method_args(Callsite.parse(caller.first).filename)
41
116
  @last_desc = desc
42
117
  end
43
118
 
44
- def arg(name, desc = nil, opts = nil)
45
- @args ||= []
46
- @args << [name, desc, opts]
47
- end
48
-
49
119
  def opt(name, desc = nil, opts = nil)
50
120
  @opts ||= []
51
121
  @opts << [name, desc, opts]
52
122
  end
53
123
 
54
124
  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 }
125
+ unless @built
126
+ optitron_dsl.root.help
127
+ @cmds.each do |(cmd_name, cmd_desc, opts)|
128
+ args = method_args[cmd_name.to_sym]
129
+ arity = instance_method(cmd_name).arity
130
+ optitron_dsl.root.cmd(cmd_name, cmd_desc) do
131
+ opts.each { |o| opt *o }
132
+ args.each do |(arg_name, arg_type, arg_default)|
133
+ case arg_type
134
+ when :required
135
+ arg arg_name.to_s, :default => arg_default && eval(arg_default)
136
+ when :optional
137
+ arg arg_name.to_s, :default => arg_default && eval(arg_default), :required => false
138
+ when :greedy
139
+ arg arg_name.to_s, :default => arg_default && eval(arg_default), :type => :greedy
140
+ end
141
+ end
142
+ end
60
143
  end
144
+ optitron_dsl.configure_options
145
+ @built = true
61
146
  end
62
- optitron_dsl.configure_options
63
147
  end
64
148
 
65
- def dispatch
149
+ def dispatch(args = ARGV, &blk)
66
150
  build
67
- optitron_parser.target = new
68
- response = optitron_parser.parse(ARGV)
151
+ optitron_parser.target = blk ? blk.call : new
152
+ response = optitron_parser.parse(args)
69
153
  if response.valid?
70
154
  optitron_parser.target.params = response.params
71
155
  args = response.args
@@ -0,0 +1,6 @@
1
+ class Optitron
2
+ class CLI
3
+ include ClassDsl
4
+ end
5
+ end
6
+
@@ -35,8 +35,8 @@ class Optitron
35
35
  else
36
36
  unclaimed_opts << opt_option
37
37
  end
38
-
39
38
  @target.options << opt_option
39
+ opt_option
40
40
  end
41
41
 
42
42
  def arg(name, description = nil, opts = nil)
@@ -61,6 +61,18 @@ class Optitron
61
61
  @root_dsl = root_dsl
62
62
  @target = command
63
63
  end
64
+
65
+ def opt(name, description = nil, opts = nil)
66
+ o = super
67
+ o.parent_cmd = @target
68
+ o
69
+ end
70
+
71
+ def arg(name, description = nil, opts = nil)
72
+ a = super
73
+ a.parent_cmd = @target
74
+ a
75
+ end
64
76
  end
65
77
 
66
78
  class RootParserDsl < AbstractDsl
@@ -19,6 +19,9 @@ class Optitron
19
19
  else
20
20
  arg_line << arg.name
21
21
  end
22
+ if arg.default
23
+ arg_line << "=#{arg.default.inspect}"
24
+ end
22
25
  arg_line << (arg.required? ? ']' : '>')
23
26
  arg_line
24
27
  end
@@ -89,7 +89,7 @@ class Optitron
89
89
  end
90
90
 
91
91
  class Opt < Option
92
- attr_accessor :short_name, :run
92
+ attr_accessor :short_name, :run, :parent_cmd
93
93
  def initialize(name, desc = nil, opts = nil)
94
94
  if desc.is_a?(Hash)
95
95
  desc, opts = nil, desc
@@ -179,7 +179,7 @@ class Optitron
179
179
  end
180
180
 
181
181
  class Arg < Option
182
- attr_accessor :greedy, :inclusion_test
182
+ attr_accessor :greedy, :inclusion_test, :parent_cmd
183
183
  alias_method :greedy?, :greedy
184
184
  def initialize(name = nil, desc = nil, opts = nil)
185
185
  if desc.is_a?(Hash)
@@ -1,3 +1,3 @@
1
1
  class Optitron
2
- VERSION = "0.0.9"
2
+ VERSION = "0.0.10"
3
3
  end
@@ -15,7 +15,13 @@ Gem::Specification.new do |s|
15
15
  s.required_rubygems_version = ">= 1.3.6"
16
16
  s.rubyforge_project = "optitron"
17
17
 
18
+ s.add_dependency "ruby_parser", ">= 2.0"
19
+ s.add_dependency "callsite", "= 0.0.4"
20
+ s.add_dependency "ruby2ruby", "= 1.2.4"
21
+ s.add_dependency "sexp_processor", "= 3.0.4"
18
22
  s.add_development_dependency "bundler", ">= 1.0.0.rc.3"
23
+ s.add_development_dependency "rake"
24
+ s.add_development_dependency "rspec"
19
25
 
20
26
  s.files = `git ls-files`.split("\n")
21
27
  s.require_path = 'lib'
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ class CLIExample < Optitron::CLI
4
+
5
+ def a_method_we_didnt_describe
6
+ end
7
+
8
+ class_opt 'verbose'
9
+
10
+ desc "Use this"
11
+ opt 'use_opt'
12
+ def use
13
+ puts "using this"
14
+ end
15
+
16
+ desc "Use this too"
17
+ opt 'another_opt'
18
+ def use_too(one, two = 'three')
19
+ end
20
+
21
+ desc "Use this three"
22
+ opt 'another_opt_as_well', :default => 123
23
+ def use_greedy(one, *two)
24
+ end
25
+ end
26
+
27
+ class AnotherCLIExample < Optitron::CLI
28
+
29
+ def initialize(env)
30
+ @env = env
31
+ end
32
+
33
+ class_opt 'verbose'
34
+
35
+ desc "Use this too"
36
+ opt 'another_opt'
37
+ def use_too(one, two = 'three')
38
+ puts "using this too #{one} #{two} #{params['another_opt']} #{@env}"
39
+ end
40
+ end
41
+
42
+
43
+ describe "Optitron::Parser defaults" do
44
+ it "should generate the correct help" do
45
+ CLIExample.build
46
+ CLIExample.optitron_parser.help.strip.should == "Commands\n\nuse_greedy [one] [two1 two2 ...] # Use this three\n -A/--another_opt_as_well=[NUMERIC] \nuse_too [one] <required=\"three\"> # Use this too\n -a/--another_opt \nuse # Use this\n -u/--use_opt \n\nGlobal options\n\n-v/--verbose \n-?/--help # Print help message"
47
+ end
48
+
49
+ it "should dispatch" do
50
+ capture(:stdout) { CLIExample.dispatch(%w(use))}.should == "using this\n"
51
+ end
52
+
53
+ it "should generate the correct help" do
54
+ AnotherCLIExample.build
55
+ AnotherCLIExample.optitron_parser.help.strip.should == "Commands\n\nuse_too [one] <required=\"three\"> # Use this too\n -a/--another_opt \n\nGlobal options\n\n-v/--verbose \n-?/--help # Print help message"
56
+ end
57
+
58
+ it "should dispatch with a custom initer" do
59
+ capture(:stdout) { AnotherCLIExample.dispatch(%w(use_too three four --another_opt)) { AnotherCLIExample.new("test") } }.should == "using this too three four true test\n"
60
+ end
61
+ end
@@ -65,7 +65,32 @@ describe "Optitron::Parser options" do
65
65
  it "should parse '-Vv --vicious=what'" do
66
66
  response = @parser.parse(%w(-Vv --vicious=what))
67
67
  response.valid?.should be_true
68
- response.params
68
+ response.params.should == {"vicious"=>"what", "vendetta"=>true, "verbose"=>true}
69
+ end
70
+ end
71
+
72
+ context "auto assingment of short names in cmd parsers" do
73
+ before(:each) do
74
+ @parser = Optitron.new {
75
+ cmd "one" do
76
+ opt "verbose"
77
+ end
78
+ cmd "two" do
79
+ opt "verbose"
80
+ end
81
+ }
82
+ end
83
+
84
+ it "should parse 'one -v'" do
85
+ response = @parser.parse(%w(one -v))
86
+ response.valid?.should be_true
87
+ response.params.should == {'verbose' => true}
88
+ end
89
+
90
+ it "should parse 'two -V'" do
91
+ response = @parser.parse(%w(two -V))
92
+ response.valid?.should be_true
93
+ response.params.should == {'verbose' => true}
69
94
  end
70
95
  end
71
96
 
@@ -1,2 +1,16 @@
1
1
  $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
2
- require 'optitron'
2
+ require 'optitron'
3
+
4
+ class Object
5
+ def capture(stream)
6
+ begin
7
+ stream = stream.to_s
8
+ eval "$#{stream} = StringIO.new"
9
+ yield
10
+ result = eval("$#{stream}").string
11
+ ensure
12
+ eval("$#{stream} = #{stream.upcase}")
13
+ end
14
+ result
15
+ end
16
+ 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: 13
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 9
10
- version: 0.0.9
9
+ - 10
10
+ version: 0.0.10
11
11
  platform: ruby
12
12
  authors:
13
13
  - Joshua Hull
@@ -15,13 +15,76 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-18 00:00:00 -07:00
18
+ date: 2010-08-19 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: bundler
22
+ name: ruby_parser
23
23
  prerelease: false
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 2
32
+ - 0
33
+ version: "2.0"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: callsite
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - "="
43
+ - !ruby/object:Gem::Version
44
+ hash: 23
45
+ segments:
46
+ - 0
47
+ - 0
48
+ - 4
49
+ version: 0.0.4
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: ruby2ruby
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - "="
59
+ - !ruby/object:Gem::Version
60
+ hash: 23
61
+ segments:
62
+ - 1
63
+ - 2
64
+ - 4
65
+ version: 1.2.4
66
+ type: :runtime
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: sexp_processor
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - "="
75
+ - !ruby/object:Gem::Version
76
+ hash: 15
77
+ segments:
78
+ - 3
79
+ - 0
80
+ - 4
81
+ version: 3.0.4
82
+ type: :runtime
83
+ version_requirements: *id004
84
+ - !ruby/object:Gem::Dependency
85
+ name: bundler
86
+ prerelease: false
87
+ requirement: &id005 !ruby/object:Gem::Requirement
25
88
  none: false
26
89
  requirements:
27
90
  - - ">="
@@ -35,7 +98,35 @@ dependencies:
35
98
  - 3
36
99
  version: 1.0.0.rc.3
37
100
  type: :development
38
- version_requirements: *id001
101
+ version_requirements: *id005
102
+ - !ruby/object:Gem::Dependency
103
+ name: rake
104
+ prerelease: false
105
+ requirement: &id006 !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ hash: 3
111
+ segments:
112
+ - 0
113
+ version: "0"
114
+ type: :development
115
+ version_requirements: *id006
116
+ - !ruby/object:Gem::Dependency
117
+ name: rspec
118
+ prerelease: false
119
+ requirement: &id007 !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ hash: 3
125
+ segments:
126
+ - 0
127
+ version: "0"
128
+ type: :development
129
+ version_requirements: *id007
39
130
  description: Sensible options parsing
40
131
  email:
41
132
  - joshbuddy@gmail.com
@@ -48,10 +139,12 @@ extra_rdoc_files: []
48
139
  files:
49
140
  - .gitignore
50
141
  - Gemfile
142
+ - Gemfile.lock
51
143
  - README.rdoc
52
144
  - Rakefile
53
145
  - lib/optitron.rb
54
146
  - lib/optitron/class_dsl.rb
147
+ - lib/optitron/cli.rb
55
148
  - lib/optitron/dsl.rb
56
149
  - lib/optitron/help.rb
57
150
  - lib/optitron/option.rb
@@ -61,6 +154,7 @@ files:
61
154
  - lib/optitron/version.rb
62
155
  - optitron.gemspec
63
156
  - spec/arg_spec.rb
157
+ - spec/cli_spec.rb
64
158
  - spec/default_spec.rb
65
159
  - spec/dispatch_spec.rb
66
160
  - spec/errors_spec.rb