boson 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/LICENSE.txt +22 -0
  2. data/README.rdoc +133 -0
  3. data/Rakefile +52 -0
  4. data/VERSION.yml +4 -0
  5. data/bin/boson +6 -0
  6. data/lib/boson.rb +72 -0
  7. data/lib/boson/command.rb +117 -0
  8. data/lib/boson/commands.rb +7 -0
  9. data/lib/boson/commands/core.rb +66 -0
  10. data/lib/boson/commands/web_core.rb +36 -0
  11. data/lib/boson/index.rb +95 -0
  12. data/lib/boson/inspector.rb +80 -0
  13. data/lib/boson/inspectors/argument_inspector.rb +92 -0
  14. data/lib/boson/inspectors/comment_inspector.rb +79 -0
  15. data/lib/boson/inspectors/method_inspector.rb +94 -0
  16. data/lib/boson/libraries/file_library.rb +76 -0
  17. data/lib/boson/libraries/gem_library.rb +21 -0
  18. data/lib/boson/libraries/module_library.rb +17 -0
  19. data/lib/boson/libraries/require_library.rb +11 -0
  20. data/lib/boson/library.rb +108 -0
  21. data/lib/boson/loader.rb +103 -0
  22. data/lib/boson/manager.rb +184 -0
  23. data/lib/boson/namespace.rb +45 -0
  24. data/lib/boson/option_parser.rb +318 -0
  25. data/lib/boson/repo.rb +38 -0
  26. data/lib/boson/runner.rb +51 -0
  27. data/lib/boson/runners/bin_runner.rb +100 -0
  28. data/lib/boson/runners/repl_runner.rb +40 -0
  29. data/lib/boson/scientist.rb +168 -0
  30. data/lib/boson/util.rb +93 -0
  31. data/lib/boson/view.rb +31 -0
  32. data/test/argument_inspector_test.rb +62 -0
  33. data/test/bin_runner_test.rb +136 -0
  34. data/test/commands_test.rb +51 -0
  35. data/test/comment_inspector_test.rb +99 -0
  36. data/test/config/index.marshal +0 -0
  37. data/test/file_library_test.rb +50 -0
  38. data/test/index_test.rb +117 -0
  39. data/test/loader_test.rb +181 -0
  40. data/test/manager_test.rb +110 -0
  41. data/test/method_inspector_test.rb +64 -0
  42. data/test/option_parser_test.rb +365 -0
  43. data/test/repo_test.rb +22 -0
  44. data/test/runner_test.rb +43 -0
  45. data/test/scientist_test.rb +291 -0
  46. data/test/test_helper.rb +119 -0
  47. metadata +133 -0
@@ -0,0 +1,181 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ module Boson
4
+ class LoaderTest < Test::Unit::TestCase
5
+
6
+ def load_namespace_library
7
+ Manager.load([Boson::Commands::Namespace])
8
+ end
9
+
10
+ context "load" do
11
+ before(:each) { reset }
12
+ test "calls included hook" do
13
+ capture_stdout {
14
+ load :blah, :file_string=>"module Blah; def self.included(mod); puts 'included blah'; end; def blah; end; end"
15
+ }.should =~ /included blah/
16
+ end
17
+
18
+ test "calls methods in config init_methods" do
19
+ with_config(:libraries=>{"blah"=>{:init_methods=>['blah']}}) do
20
+ capture_stdout {
21
+ load :blah, :file_string=>"module Blah; def blah; puts 'yo'; end; end"
22
+ }.should == "yo\n"
23
+ end
24
+ end
25
+
26
+ test "prints error and returns false for existing library" do
27
+ libs = create_library('blah', :loaded=>true)
28
+ Manager.stubs(:loader_create).returns(libs[0])
29
+ capture_stderr { load('blah', :no_mock=>true, :verbose=>true).should == false }.should =~ /already exists/
30
+ end
31
+
32
+ test "loads and strips aliases from a library's commands" do
33
+ with_config(:commands=>{"blah"=>{:alias=>'b'}}) do
34
+ load :blah, :file_string=>"module Blah; def blah; end; alias_method(:b, :blah); end"
35
+ library_loaded?('blah')
36
+ library('blah').commands.should == ['blah']
37
+ end
38
+ end
39
+
40
+ test "loads a library with dependencies" do
41
+ File.stubs(:exists?).returns(true)
42
+ File.stubs(:read).returns("module Water; def water; end; end", "module Oaks; def oaks; end; end")
43
+ with_config(:libraries=>{"water"=>{:dependencies=>"oaks"}}) do
44
+ load 'water', :no_mock=>true
45
+ library_has_module('water', "Boson::Commands::Water")
46
+ library_has_module('oaks', "Boson::Commands::Oaks")
47
+ command_exists?('water')
48
+ command_exists?('oaks')
49
+ end
50
+ end
51
+
52
+ test "prints error for library with invalid dependencies" do
53
+ GemLibrary.stubs(:is_a_gem?).returns(true) #mock all as gem libs
54
+ Util.stubs(:safe_require).returns(true)
55
+ with_config(:libraries=>{"water"=>{:dependencies=>"fire"}, "fire"=>{:dependencies=>"man"}}) do
56
+ capture_stderr {
57
+ load('water', :no_mock=>true)
58
+ }.should == "Unable to load library fire. Reason: Can't load dependency man\nUnable to load"+
59
+ " library water. Reason: Can't load dependency fire\n"
60
+ end
61
+ end
62
+
63
+ test "prints error for method conflicts with config error_method_conflicts" do
64
+ with_config(:error_method_conflicts=>true) do
65
+ load('blah', :file_string=>"module Blah; def chwhat; end; end")
66
+ capture_stderr {
67
+ load('chwhat', :file_string=>"module Chwhat; def chwhat; end; end")
68
+ }.should =~ /Unable to load library chwhat.*conflict.*chwhat/
69
+ end
70
+ end
71
+
72
+ test "namespaces a library that has a method conflict" do
73
+ load('blah', :file_string=>"module Blah; def chwhat; end; end")
74
+ capture_stderr {
75
+ load('chwhat2', :file_string=>"module Chwhat2; def chwhat; end; end")
76
+ }.should =~ /conflict.*chwhat.*chwhat2/
77
+ library_has_command('namespace', 'chwhat2')
78
+ library_has_command('chwhat2', 'chwhat')
79
+ end
80
+
81
+ context "module library" do
82
+ def mock_library(*args); end
83
+
84
+ test "loads a module library" do
85
+ eval %[module ::Harvey; def bird; end; end]
86
+ load ::Harvey, :no_mock=>true
87
+ library_has_module('harvey', "Harvey")
88
+ command_exists?('bird')
89
+ end
90
+ end
91
+
92
+ context "gem library" do
93
+ def mock_library(lib, options={})
94
+ options[:file_string] ||= ''
95
+ File.stubs(:exists?).returns(false)
96
+ GemLibrary.expects(:is_a_gem?).returns(true)
97
+ Util.expects(:safe_require).with { eval options.delete(:file_string) || ''; true}.returns(true)
98
+ end
99
+
100
+ test "loads" do
101
+ with_config(:libraries=>{"dude"=>{:module=>'Dude'}}) do
102
+ load "dude", :file_string=>"module ::Dude; def blah; end; end"
103
+ library_has_module('dude', "Dude")
104
+ command_exists?("blah")
105
+ end
106
+ end
107
+
108
+ test "with kernel methods loads" do
109
+ load "dude", :file_string=>"module ::Kernel; def dude; end; end"
110
+ library_loaded? 'dude'
111
+ library('dude').module.should == nil
112
+ command_exists?("dude")
113
+ end
114
+
115
+ test "prints error when nonexistent" do
116
+ capture_stderr { load('blah') }.should =~ /Unable.*load/
117
+ end
118
+
119
+ test "with invalid module prints error" do
120
+ with_config(:libraries=>{"coolio"=>{:module=>"Cool"}}) do
121
+ capture_stderr {
122
+ load('coolio', :file_string=>"module ::Coolio; def coolio; end; end")
123
+ }.should =~ /Unable.*coolio.*No module/
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ context "library with namespace" do
130
+ before(:all) { reset_main_object }
131
+ before(:each) { reset_boson }
132
+
133
+ test "loads and defaults to library name" do
134
+ with_config(:libraries=>{'blang'=>{:namespace=>true}}) do
135
+ load 'blang', :file_string=>"module Blang; def bling; end; end"
136
+ library_has_command('blang', 'bling')
137
+ end
138
+ end
139
+
140
+ test "loads with config namespace" do
141
+ with_config(:libraries=>{'blung'=>{:namespace=>'dope'}}) do
142
+ load 'blung', :file_string=>"module Blung; def bling; end; end"
143
+ library_has_command('blung', 'bling')
144
+ library('blung').commands.size.should == 1
145
+ end
146
+ end
147
+
148
+ test "loads with config except" do
149
+ with_config(:libraries=>{'blong'=>{:namespace=>true, :except=>['wrong']}}) do
150
+ load 'blong', :file_string=>"module Blong; def bling; end; def wrong; end; end"
151
+ library_has_command('blong', 'bling')
152
+ library_has_command('blong', 'wrong', false)
153
+ library('blong').commands.size.should == 1
154
+ end
155
+ end
156
+
157
+ test "prints error if namespace conflicts with existing commands" do
158
+ eval "module Conflict; def bleng; end; end"
159
+ load Conflict, :no_mock=>true
160
+ with_config(:libraries=>{'bleng'=>{:namespace=>true}}) do
161
+ capture_stderr {
162
+ load 'bleng', :file_string=>"module Bleng; def bling; end; end"
163
+ }.should =~ /conflict.*bleng/
164
+ end
165
+ end
166
+ end
167
+
168
+ context "reload" do
169
+ before(:each) { reset }
170
+ test "loads currently unloaded library" do
171
+ create_library('blah')
172
+ Manager.expects(:load).with('blah', anything)
173
+ Manager.reload('blah')
174
+ end
175
+
176
+ test "doesn't load nonexistent library" do
177
+ capture_stdout { Manager.reload('bling', :verbose=>true) }.should =~ /bling doesn't/
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,110 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ module Boson
4
+ class ManagerTest < Test::Unit::TestCase
5
+ context "after_load" do
6
+ def load_library(hash)
7
+ new_attributes = {:name=>hash.delete(:name), :commands=>[], :created_dependencies=>[], :loaded=>true}
8
+ [:module, :except, :commands].each {|e| new_attributes[e] = hash.delete(e) if hash[e] }
9
+ Manager.expects(:load_once).returns(Library.new(new_attributes))
10
+ Manager.load([hash[:name]])
11
+ end
12
+
13
+ before(:each) { reset_boson }
14
+
15
+ test "loads basic library" do
16
+ load_library :name=>'blah'
17
+ library_loaded? 'blah'
18
+ end
19
+
20
+ test "loads library with commands" do
21
+ load_library :name=>'blah', :commands=>['frylock','meatwad']
22
+ library_loaded? 'blah'
23
+ command_exists?('frylock')
24
+ command_exists?('meatwad')
25
+ end
26
+
27
+ test "loads library with commands and except option" do
28
+ Boson.main_object.instance_eval("class<<self;self;end").expects(:undef_method).with('frylock')
29
+ load_library :name=>'blah', :commands=>['frylock','meatwad'], :except=>['frylock']
30
+ library_loaded? 'blah'
31
+ command_exists?('frylock', false)
32
+ command_exists?('meatwad')
33
+ end
34
+
35
+ context "command aliases" do
36
+ before(:each) { eval %[module ::Aquateen; def frylock; end; end] }
37
+ after(:each) { Object.send(:remove_const, "Aquateen") }
38
+
39
+ test "created with command specific config" do
40
+ with_config(:commands=>{'frylock'=>{:alias=>'fr'}}) do
41
+ Manager.expects(:create_instance_aliases).with({"Aquateen"=>{"frylock"=>"fr"}})
42
+ load_library :name=>'aquateen', :commands=>['frylock'], :module=>Aquateen
43
+ library_loaded? 'aquateen'
44
+ end
45
+ end
46
+
47
+ test "created with config command_aliases" do
48
+ with_config(:command_aliases=>{"frylock"=>"fr"}) do
49
+ Manager.expects(:create_instance_aliases).with({"Aquateen"=>{"frylock"=>"fr"}})
50
+ load_library :name=>'aquateen', :commands=>['frylock'], :module=>Aquateen
51
+ library_loaded? 'aquateen'
52
+ end
53
+ end
54
+
55
+ test "not created and warns for commands with no module" do
56
+ with_config(:commands=>{'frylock'=>{:alias=>'fr'}}) do
57
+ capture_stderr {
58
+ load_library(:name=>'aquateen', :commands=>['frylock'])
59
+ }.should =~ /No aliases/
60
+ library_loaded? 'aquateen'
61
+ Aquateen.method_defined?(:fr).should == false
62
+ end
63
+ end
64
+ end
65
+
66
+ test "merges with existing created library" do
67
+ create_library('blah')
68
+ load_library :name=>'blah'
69
+ library_loaded? 'blah'
70
+ Boson.libraries.size.should == 1
71
+ end
72
+ end
73
+
74
+ context "option commands without args" do
75
+ before(:all) {
76
+ reset_boson
77
+ @library = Library.new(:name=>'blah', :commands=>['foo', 'bar'])
78
+ Boson.libraries << @library
79
+ @foo = Command.new(:name=>'foo', :lib=>'blah', :options=>{:fool=>:string}, :args=>'*')
80
+ Boson.commands << @foo
81
+ Boson.commands << Command.new(:name=>'bar', :lib=>'blah', :options=>{:bah=>:string})
82
+ }
83
+
84
+ test "are deleted" do
85
+ Scientist.expects(:create_option_command).with(anything, @foo)
86
+ Manager.create_option_commands(@library, @library.commands)
87
+ end
88
+
89
+ test "are deleted and printed when verbose" do
90
+ Scientist.expects(:create_option_command).with(anything, @foo)
91
+ @library.instance_eval("@options = {:verbose=>true}")
92
+ capture_stdout { Manager.create_option_commands(@library, @library.commands) } =~ /options.*blah/
93
+ end
94
+ end
95
+
96
+ context "loaded" do
97
+ before(:each) { reset_libraries }
98
+
99
+ test "returns false when library isn't loaded" do
100
+ create_library('blah')
101
+ Manager.loaded?('blah').should be(false)
102
+ end
103
+
104
+ test "returns true when library is loaded" do
105
+ create_library('blah', :loaded=>true)
106
+ Manager.loaded?('blah').should be(true)
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,64 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ module Boson
4
+ class InspectorTest < Test::Unit::TestCase
5
+ test "non commands module can't set anything" do
6
+ eval "module Blah; end"
7
+ MethodInspector.current_module = Blah
8
+ Inspector.enable
9
+ Blah.module_eval("desc 'test'; def test; end; options :a=>1; def test2; end")
10
+ Inspector.disable
11
+ MethodInspector.store[:desc].empty?.should == true
12
+ MethodInspector.store[:options].empty?.should == true
13
+ end
14
+
15
+ context "commands module with" do
16
+ def parse(string)
17
+ Inspector.enable
18
+ ::Boson::Commands::Zzz.module_eval(string)
19
+ Inspector.disable
20
+ MethodInspector.store
21
+ end
22
+
23
+ before(:all) { eval "module ::Boson::Commands::Zzz; end" }
24
+ before(:each) { MethodInspector.mod_store[::Boson::Commands::Zzz] = {} }
25
+
26
+ test "desc sets descriptions" do
27
+ parsed = parse "desc 'test'; def m1; end; desc 'one'; desc 'more'; def m2; end"
28
+ parsed[:desc].should == {"m1"=>"test", "m2"=>"more"}
29
+ end
30
+
31
+ test "options sets options" do
32
+ parse("options :z=>'b'; def zee; end")[:options].should == {"zee"=>{:z=>'b'}}
33
+ end
34
+
35
+ test "render_options sets render_options" do
36
+ parse("render_options :z=>true; def zee; end")[:render_options].should == {"zee"=>{:z=>true}}
37
+ end
38
+
39
+ test "neither options or desc set, sets method_locations" do
40
+ MethodInspector.stubs(:find_method_locations).returns(["/some/path", 10])
41
+ parsed = parse "desc 'yo'; def yo; end; options :yep=>1; def yep; end; render_options :a=>1; desc 'z'; options :a=>1; def az; end"
42
+ parsed[:method_locations].key?('yo').should == true
43
+ parsed[:method_locations].key?('yep').should == true
44
+ parsed[:method_locations].key?('az').should == false
45
+ end
46
+
47
+ test "no find_method_locations doesn't set method_locations" do
48
+ MethodInspector.stubs(:find_method_locations).returns(nil)
49
+ parse("def bluh; end")[:method_locations].key?('bluh').should == false
50
+ end
51
+
52
+ test "options calls scrape_with_eval" do
53
+ ArgumentInspector.expects(:scrape_with_eval).returns([['arg1']])
54
+ parse("desc 'desc'; options :some=>:opts; def doy(arg1); end")[:method_args]['doy'].should == [['arg1']]
55
+ end
56
+
57
+ test "options in file calls scrape_with_eval" do
58
+ MethodInspector.expects(:inspector_in_file?).returns(true)
59
+ ArgumentInspector.expects(:scrape_with_eval).returns([['arg1']])
60
+ parse("desc 'desc'; def doz(arg1); end")[:method_args]['doz'].should == [['arg1']]
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,365 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ module Boson
4
+ class OptionParserTest < Test::Unit::TestCase
5
+ def create(opts)
6
+ @opt = OptionParser.new(opts)
7
+ end
8
+
9
+ def parse(*args)
10
+ @non_opts = []
11
+ @opt.parse(args.flatten)
12
+ end
13
+
14
+ context "IndifferentAccessHash" do
15
+ before(:each) {
16
+ @hash = IndifferentAccessHash.new 'foo' => 'bar', 'baz' => 'bee'
17
+ }
18
+ it "can access values indifferently" do
19
+ @hash['foo'].should == 'bar'
20
+ @hash[:foo].should == 'bar'
21
+ @hash.values_at(:foo, :baz).should == ['bar', 'bee']
22
+ end
23
+
24
+ it "can be initialized with either strings or symbols and be equal" do
25
+ hash2 = IndifferentAccessHash.new :foo=>'bar', :baz=>'bee'
26
+ @hash.should == hash2
27
+ end
28
+
29
+ it "returns keys as symbols by default" do
30
+ @hash.should == {:foo=>'bar', :baz=>'bee'}
31
+ end
32
+
33
+ it "can set values indifferently" do
34
+ @hash['foo'] = 'duh'
35
+ @hash[:foo].should == 'duh'
36
+ @hash[:baz] = 'wasp'
37
+ @hash['baz'].should == 'wasp'
38
+ end
39
+ end
40
+
41
+ context "naming" do
42
+ it "automatically aliases long options with their first letter" do
43
+ create "--foo" => true
44
+ parse("-f")["foo"].should == true
45
+ end
46
+
47
+ it "automatically aliases two options with same first letters by aliasing alphabetical first with lowercase and second with uppercase" do
48
+ create :verbose=>:boolean, :vertical=>:string, :verz=>:boolean
49
+ parse('-v', '-V','2').should == {:verbose=>true, :vertical=>'2'}
50
+ end
51
+
52
+ it "doesn't auto-alias options that have multiple names given" do
53
+ create ["--foo", "--bar"] => :boolean
54
+ parse("-f")["foo"].should == nil
55
+ end
56
+
57
+ it "allows aliases to be symbols or strings" do
58
+ create [:foo, :bar, 'baz'] =>:string
59
+ parse("--foo", "12")[:foo].should == "12"
60
+ parse("--bar", "12")[:foo].should == "12"
61
+ parse("--baz", "12")[:foo].should == "12"
62
+ end
63
+
64
+ it "allows multiple aliases for a given opt" do
65
+ create ["--foo", "--bar", "--baz"] => :string
66
+ parse("--foo", "12")["foo"].should == "12"
67
+ parse("--bar", "12")["foo"].should == "12"
68
+ parse("--baz", "12")["foo"].should == "12"
69
+ end
70
+
71
+ it "allows custom short names" do
72
+ create "-f" => :string
73
+ parse("-f", "12").should == {:f => "12"}
74
+ end
75
+
76
+ it "allows capital short names" do
77
+ create :A => :boolean
78
+ parse("-A")[:A].should == true
79
+ end
80
+
81
+ it "allows capital short aliases" do
82
+ create [:awesome, :A] => :string
83
+ parse("--awesome", "bar")[:awesome].should == 'bar'
84
+ parse("-A", "bar")[:awesome].should == 'bar'
85
+ end
86
+
87
+ it "allows custom short aliases" do
88
+ create ["--bar", "-f"] => :string
89
+ parse("-f", "12").should == {:bar => "12"}
90
+ end
91
+
92
+ it "allows humanized opt name" do
93
+ create 'foo' => :string, :bar => :required
94
+ parse("-f", "1", "-b", "2").should == {:foo => "1", :bar => "2"}
95
+ end
96
+
97
+ it "allows humanized symbol opt name" do
98
+ create :foo=>:string
99
+ parse('-f','1').should == {:foo=>'1'}
100
+ end
101
+
102
+ it "doesn't allow alias to override another option" do
103
+ create :foo=>:string, [:bar, :foo]=>:boolean
104
+ parse("--foo", "boo")[:foo].should == 'boo'
105
+ end
106
+
107
+ it "doesn't recognize long opt format for a opt that is originally short" do
108
+ create 'f' => :string
109
+ parse("-f", "1").should == {:f => "1"}
110
+ parse("--f", "1").should == {}
111
+ end
112
+
113
+ it "accepts --[no-]opt variant for booleans, setting false for value" do
114
+ create "--foo" => :boolean
115
+ parse("--no-foo")["foo"].should == false
116
+ parse("--foo")["foo"].should == true
117
+ end
118
+
119
+ it "will prefer 'no-opt' variant over inverting 'opt' if explicitly set" do
120
+ create "--no-foo" => true
121
+ parse("--no-foo")["no-foo"].should == true
122
+ end
123
+
124
+ end
125
+
126
+ context "option values can be set with" do
127
+ it "a opt=<value> assignment" do
128
+ create "--foo" => :required
129
+ parse("--foo=12")["foo"].should == "12"
130
+ parse("-f=12")["foo"].should == "12"
131
+ parse("--foo=bar=baz")["foo"].should == "bar=baz"
132
+ parse("--foo=sentence with spaces")["foo"].should == "sentence with spaces"
133
+ end
134
+
135
+ it "a -nXY assignment" do
136
+ create "--num" => :required
137
+ parse("-n12")["num"].should == "12"
138
+ end
139
+
140
+ it "conjoined short options" do
141
+ create "--foo" => true, "--bar" => true, "--app" => true
142
+ opts = parse "-fba"
143
+ opts["foo"].should == true
144
+ opts["bar"].should == true
145
+ opts["app"].should == true
146
+ end
147
+
148
+ it "conjoined short options with argument" do
149
+ create "--foo" => true, "--bar" => true, "--app" => :required
150
+ opts = parse "-fba", "12"
151
+ opts["foo"].should == true
152
+ opts["bar"].should == true
153
+ opts["app"].should == "12"
154
+ end
155
+ end
156
+
157
+ context "parse" do
158
+ it "extracts non-option arguments" do
159
+ create "--foo" => :required, "--bar" => true
160
+ parse("foo", "bar", "--baz", "--foo", "12", "--bar", "-T", "bang").should == {
161
+ :foo => "12", :bar => true
162
+ }
163
+ @opt.leading_non_opts.should == ["foo", "bar", "--baz"]
164
+ @opt.trailing_non_opts.should == ["-T", "bang"]
165
+ @opt.non_opts.should == ["foo", "bar", "--baz", "-T", "bang"]
166
+ end
167
+
168
+ context "with parse flag" do
169
+ it ":delete_invalid_opts deletes and warns of invalid options" do
170
+ create(:foo=>:boolean)
171
+ capture_stderr {
172
+ @opt.parse(%w{-f -d ok}, :delete_invalid_opts=>true)
173
+ }.should =~ /Deleted invalid option '-d'/
174
+ @opt.non_opts.should == ['ok']
175
+ end
176
+
177
+ it ":opts_before_args only allows options before args" do
178
+ create(:foo=>:boolean)
179
+ @opt.parse(%w{ok -f}, :opts_before_args=>true).should == {}
180
+ @opt.parse(%w{-f ok}, :opts_before_args=>true).should == {:foo=>true}
181
+ end
182
+ end
183
+
184
+ context "with no arguments" do
185
+ it "and no options returns an empty hash" do
186
+ create({})
187
+ parse.should == {}
188
+ end
189
+
190
+ it "and several options returns an empty hash" do
191
+ create "--foo" => :boolean, "--bar" => :string
192
+ parse.should == {}
193
+ end
194
+ end
195
+ end
196
+
197
+ context "option hashes" do
198
+ it "make hash keys available as symbols as well" do
199
+ create "--foo" => :string
200
+ parse("--foo", "12")[:foo].should == "12"
201
+ end
202
+
203
+ it "don't set nonexistant options" do
204
+ create "--foo" => :boolean
205
+ parse("--foo")["bar"].should == nil
206
+ opts = parse
207
+ opts["foo"].should == nil
208
+ end
209
+ end
210
+
211
+ it ":required type raises an error if it isn't given" do
212
+ create "--foo" => :required, "--bar" => :string
213
+ assert_error(OptionParser::Error, 'no value.*required.*foo') { parse("--bar", "str") }
214
+ end
215
+
216
+ context ":string type" do
217
+ before :each do
218
+ create "--foo" => :string, "--bar" => :string
219
+ end
220
+
221
+ it "doesn't set nonexistant options" do
222
+ parse("--bling")[:bar].should == nil
223
+ end
224
+
225
+ it "sets values correctly" do
226
+ parse("--foo", "12")[:foo].should == "12"
227
+ parse("--bar", "12")[:bar].should == "12"
228
+ end
229
+
230
+ it "raises error if passed another valid option" do
231
+ assert_error(OptionParser::Error, "cannot pass.*'foo'") { parse("--foo", "--bar") }
232
+ end
233
+
234
+ it "raises error if not passed a value" do
235
+ assert_error(OptionParser::Error, "no value.*'foo'") { parse("--foo") }
236
+ end
237
+
238
+ it "overwrites earlier values with later values" do
239
+ parse("--foo", "12", "--foo", "13")[:foo].should == "13"
240
+ end
241
+ end
242
+
243
+ context ":string type with :values attribute" do
244
+ before(:all ) { create :foo=>{:type=>:string, :values=>%w{angola abu abib}} }
245
+ it "auto aliases if a match exists" do
246
+ parse("-f", "an")[:foo].should == 'angola'
247
+ end
248
+
249
+ it "auto aliases first sorted match" do
250
+ parse("-f", "a")[:foo].should == 'abib'
251
+ end
252
+
253
+ it "raises error if option doesn't auto alias or match given values" do
254
+ assert_error(OptionParser::Error, "invalid.*'z'") { parse("-f", "z") }
255
+ end
256
+
257
+ it "doesn't raise error for a nonmatch if enum is false" do
258
+ create :foo=>{:type=>:string, :values=>%w{angola abu abib}, :enum=>false}
259
+ parse("-f", "z")[:foo].should == 'z'
260
+ end
261
+ end
262
+
263
+ context ":string type with default value" do
264
+ before(:each) do
265
+ create "--branch" => "master"
266
+ end
267
+
268
+ it "should get the specified value" do
269
+ parse("--branch", "bugfix").should == { :branch => "bugfix" }
270
+ end
271
+
272
+ it "should get the default value when not specified" do
273
+ parse.should == { :branch => "master" }
274
+ end
275
+ end
276
+
277
+ context ":numeric type" do
278
+ before(:each) do
279
+ create "n" => :numeric, "m" => 5
280
+ end
281
+
282
+ it "supports numeric defaults" do
283
+ parse["m"].should == 5
284
+ end
285
+
286
+ it "converts values to numeric types" do
287
+ parse("-n", "3", "-m", ".5").should == {:n => 3, :m => 0.5}
288
+ end
289
+
290
+ it "raises error when value isn't numeric" do
291
+ assert_error(OptionParser::Error, "expected numeric value for.*'n'") { parse("-n", "foo") }
292
+ end
293
+
294
+ it "raises error when opt is present without value" do
295
+ assert_error(OptionParser::Error, "no value.*'n'") { parse("-n") }
296
+ end
297
+ end
298
+
299
+ context ":array type" do
300
+ before(:all) {
301
+ create :a=>:array, :b=>[1,2,3], :c=>{:type=>:array, :values=>%w{foo fa bar zebra}},
302
+ :d=>{:type=>:array, :split=>" "}
303
+ }
304
+
305
+ it "supports array defaults" do
306
+ parse[:b].should == [1,2,3]
307
+ end
308
+
309
+ it "converts comma delimited values to an array" do
310
+ parse("-a","1,2,5")[:a].should == %w{1 2 5}
311
+ end
312
+
313
+ it "raises error when option has no value" do
314
+ assert_error(OptionParser::Error, "no value.*'a'") { parse("-a") }
315
+ end
316
+
317
+ it "auto aliases :values attribute" do
318
+ parse("-c","f,b")[:c].should == %w{fa bar}
319
+ end
320
+
321
+ it "allows a configurable splitter" do
322
+ parse("-d", "yogi berra")[:d].should == %w{yogi berra}
323
+ end
324
+ end
325
+
326
+ context "option with attributes" do
327
+ it "can get type from :type" do
328
+ create :foo=>{:type=>:numeric}
329
+ parse("-f", '3')[:foo] == 3
330
+ end
331
+
332
+ it "can get type and default from :default" do
333
+ create :foo=>{:default=>[]}
334
+ parse("-f", "1")[:foo].should == ['1']
335
+ parse[:foo].should == []
336
+ end
337
+
338
+ it "assumes :boolean type if no type found" do
339
+ create :foo=>{:some=>'params'}
340
+ parse('-f')[:foo].should == true
341
+ end
342
+ end
343
+
344
+ context "#formatted_usage" do
345
+ def usage
346
+ @opt.formatted_usage.split(" ").sort
347
+ end
348
+
349
+ it "outputs string args with sample values" do
350
+ create "--repo" => :string, "--branch" => "bugfix", "-n" => 6
351
+ usage.should == %w([--branch=bugfix] [--repo=REPO] [-n=6])
352
+ end
353
+
354
+ it "outputs numeric args with 'N' as sample value" do
355
+ create "--iter" => :numeric
356
+ usage.should == ["[--iter=N]"]
357
+ end
358
+
359
+ it "outputs array args with sample value" do
360
+ create "--libs" => :array
361
+ usage.should == ["[--libs=A,B,C]"]
362
+ end
363
+ end
364
+ end
365
+ end