bosonson 0.304.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +108 -0
- data/LICENSE.txt +22 -0
- data/README.rdoc +181 -0
- data/bin/bss +6 -0
- data/bosonson.gemspec +24 -0
- data/deps.rip +2 -0
- data/lib/boson.rb +96 -0
- data/lib/boson/command.rb +196 -0
- data/lib/boson/commands.rb +7 -0
- data/lib/boson/commands/core.rb +77 -0
- data/lib/boson/commands/web_core.rb +153 -0
- data/lib/boson/index.rb +48 -0
- data/lib/boson/inspector.rb +120 -0
- data/lib/boson/inspectors/argument_inspector.rb +97 -0
- data/lib/boson/inspectors/comment_inspector.rb +100 -0
- data/lib/boson/inspectors/method_inspector.rb +98 -0
- data/lib/boson/libraries/file_library.rb +144 -0
- data/lib/boson/libraries/gem_library.rb +30 -0
- data/lib/boson/libraries/local_file_library.rb +30 -0
- data/lib/boson/libraries/module_library.rb +37 -0
- data/lib/boson/libraries/require_library.rb +23 -0
- data/lib/boson/library.rb +179 -0
- data/lib/boson/loader.rb +118 -0
- data/lib/boson/manager.rb +169 -0
- data/lib/boson/namespace.rb +31 -0
- data/lib/boson/option_command.rb +222 -0
- data/lib/boson/option_parser.rb +475 -0
- data/lib/boson/options.rb +146 -0
- data/lib/boson/pipe.rb +147 -0
- data/lib/boson/pipes.rb +75 -0
- data/lib/boson/repo.rb +107 -0
- data/lib/boson/repo_index.rb +124 -0
- data/lib/boson/runner.rb +81 -0
- data/lib/boson/runners/bin_runner.rb +208 -0
- data/lib/boson/runners/console_runner.rb +58 -0
- data/lib/boson/scientist.rb +182 -0
- data/lib/boson/util.rb +129 -0
- data/lib/boson/version.rb +3 -0
- data/lib/boson/view.rb +95 -0
- data/test/argument_inspector_test.rb +62 -0
- data/test/bin_runner_test.rb +223 -0
- data/test/command_test.rb +22 -0
- data/test/commands_test.rb +22 -0
- data/test/comment_inspector_test.rb +126 -0
- data/test/deps.rip +4 -0
- data/test/file_library_test.rb +42 -0
- data/test/loader_test.rb +235 -0
- data/test/manager_test.rb +114 -0
- data/test/method_inspector_test.rb +90 -0
- data/test/option_parser_test.rb +367 -0
- data/test/options_test.rb +189 -0
- data/test/pipes_test.rb +65 -0
- data/test/repo_index_test.rb +122 -0
- data/test/repo_test.rb +23 -0
- data/test/runner_test.rb +40 -0
- data/test/scientist_test.rb +341 -0
- data/test/test_helper.rb +130 -0
- data/test/util_test.rb +56 -0
- data/vendor/bundle/gems/bacon-bits-0.1.0/deps.rip +1 -0
- data/vendor/bundle/gems/hirb-0.6.0/test/deps.rip +4 -0
- metadata +217 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
describe "Options" do
|
4
|
+
def create(opts)
|
5
|
+
@opt = OptionParser.new(opts)
|
6
|
+
end
|
7
|
+
|
8
|
+
def parse(*args)
|
9
|
+
@non_opts = []
|
10
|
+
@opt.parse(args.flatten)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe ":string type" do
|
14
|
+
before {
|
15
|
+
create "--foo" => :string, "--bar" => :string, :blah=>{:type=>:string, :default=>:huh}
|
16
|
+
}
|
17
|
+
|
18
|
+
it "doesn't set nonexistant options" do
|
19
|
+
parse("--bling")[:bar].should == nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it "sets values correctly" do
|
23
|
+
parse("--foo", "12")[:foo].should == "12"
|
24
|
+
parse("--bar", "12")[:bar].should == "12"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "raises error if passed another valid option" do
|
28
|
+
assert_error(OptionParser::Error, "cannot pass.*'foo'") { parse("--foo", "--bar") }
|
29
|
+
end
|
30
|
+
|
31
|
+
it "raises error if not passed a value" do
|
32
|
+
assert_error(OptionParser::Error, "no value.*'foo'") { parse("--foo") }
|
33
|
+
end
|
34
|
+
|
35
|
+
it "overwrites earlier values with later values" do
|
36
|
+
parse("--foo", "12", "--foo", "13")[:foo].should == "13"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "can have symbolic default value" do
|
40
|
+
parse('--blah','ok')[:blah].should == 'ok'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ":string type with :values attribute" do
|
45
|
+
before_all { create :foo=>{:type=>:string, :values=>%w{angola abu abib}} }
|
46
|
+
it "auto aliases if a match exists" do
|
47
|
+
parse("-f", "an")[:foo].should == 'angola'
|
48
|
+
end
|
49
|
+
|
50
|
+
it "auto aliases first sorted match" do
|
51
|
+
parse("-f", "a")[:foo].should == 'abib'
|
52
|
+
end
|
53
|
+
|
54
|
+
it "raises error if option doesn't auto alias or match given values" do
|
55
|
+
assert_error(OptionParser::Error, "invalid.*'z'") { parse("-f", "z") }
|
56
|
+
end
|
57
|
+
|
58
|
+
it "doesn't raise error for a nonmatch if enum is false" do
|
59
|
+
create :foo=>{:type=>:string, :values=>%w{angola abu abib}, :enum=>false}
|
60
|
+
parse("-f", "z")[:foo].should == 'z'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe ":string type with default value" do
|
65
|
+
before { create "--branch" => "master" }
|
66
|
+
|
67
|
+
it "should get the specified value" do
|
68
|
+
parse("--branch", "bugfix").should == { :branch => "bugfix" }
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should get the default value when not specified" do
|
72
|
+
parse.should == { :branch => "master" }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe ":numeric type" do
|
77
|
+
before { create "n" => :numeric, "m" => 5 }
|
78
|
+
|
79
|
+
it "supports numeric defaults" do
|
80
|
+
parse["m"].should == 5
|
81
|
+
end
|
82
|
+
|
83
|
+
it "converts values to numeric types" do
|
84
|
+
parse("-n", "3", "-m", ".5").should == {:n => 3, :m => 0.5}
|
85
|
+
end
|
86
|
+
|
87
|
+
it "raises error when value isn't numeric" do
|
88
|
+
assert_error(OptionParser::Error, "expected numeric value for.*'n'") { parse("-n", "foo") }
|
89
|
+
end
|
90
|
+
|
91
|
+
it "raises error when opt is present without value" do
|
92
|
+
assert_error(OptionParser::Error, "no value.*'n'") { parse("-n") }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe ":array type" do
|
97
|
+
before_all {
|
98
|
+
create :a=>:array, :b=>[1,2,3], :c=>{:type=>:array, :values=>%w{foo fa bar zebra}, :enum=>false},
|
99
|
+
:d=>{:type=>:array, :split=>" ", :values=>[:ab, :bc, :cd], :enum=>false},
|
100
|
+
:e=>{:type=>:array, :values=>%w{some so silly}, :regexp=>true}
|
101
|
+
}
|
102
|
+
|
103
|
+
it "supports array defaults" do
|
104
|
+
parse[:b].should == [1,2,3]
|
105
|
+
end
|
106
|
+
|
107
|
+
it "converts comma delimited values to an array" do
|
108
|
+
parse("-a","1,2,5")[:a].should == %w{1 2 5}
|
109
|
+
end
|
110
|
+
|
111
|
+
it "raises error when option has no value" do
|
112
|
+
assert_error(OptionParser::Error, "no value.*'a'") { parse("-a") }
|
113
|
+
end
|
114
|
+
|
115
|
+
it "auto aliases :values attribute" do
|
116
|
+
parse("-c","f,b")[:c].should == %w{fa bar}
|
117
|
+
end
|
118
|
+
|
119
|
+
it "auto aliases symbolic :values" do
|
120
|
+
parse("-d","a c")[:d].should == [:ab,:cd]
|
121
|
+
end
|
122
|
+
|
123
|
+
it "supports a configurable splitter" do
|
124
|
+
parse("-d", "yogi berra")[:d].should == %w{yogi berra}
|
125
|
+
end
|
126
|
+
|
127
|
+
it "aliases * to all values" do
|
128
|
+
parse("-c", '*')[:c].sort.should == %w{bar fa foo zebra}
|
129
|
+
parse("-c", '*,ok')[:c].sort.should == %w{bar fa foo ok zebra}
|
130
|
+
end
|
131
|
+
|
132
|
+
it "aliases correctly with :regexp on" do
|
133
|
+
parse("-e", 'so')[:e].sort.should == %w{so some}
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe ":hash type" do
|
138
|
+
before_all {
|
139
|
+
create :a=>:hash, :b=>{:default=>{:a=>'b'}}, :c=>{:type=>:hash, :keys=>%w{one two three}},
|
140
|
+
:e=>{:type=>:hash, :keys=>[:one, :two, :three], :default_keys=>:three},
|
141
|
+
:d=>{:type=>:hash, :split=>" "}
|
142
|
+
}
|
143
|
+
|
144
|
+
it "converts comma delimited pairs to hash" do
|
145
|
+
parse("-a", "f:3,g:4")[:a].should == {'f'=>'3', 'g'=>'4'}
|
146
|
+
end
|
147
|
+
|
148
|
+
it "supports hash defaults" do
|
149
|
+
parse[:b].should == {:a=>'b'}
|
150
|
+
end
|
151
|
+
|
152
|
+
it "raises error when option has no value" do
|
153
|
+
assert_error(OptionParser::Error, "no value.*'a'") { parse("-a") }
|
154
|
+
end
|
155
|
+
|
156
|
+
it "raises error if invalid key-value pair given for unknown keys" do
|
157
|
+
assert_error(OptionParser::Error, "invalid.*pair.*'a'") { parse("-a", 'b') }
|
158
|
+
end
|
159
|
+
|
160
|
+
it "auto aliases :keys attribute" do
|
161
|
+
parse("-c","t:3,o:1")[:c].should == {'three'=>'3', 'one'=>'1'}
|
162
|
+
end
|
163
|
+
|
164
|
+
it "adds in explicit default keys with value only argument" do
|
165
|
+
parse('-e', 'whoop')[:e].should == {:three=>'whoop'}
|
166
|
+
end
|
167
|
+
|
168
|
+
it "adds in default keys from known :keys with value only argument" do
|
169
|
+
parse("-c","okay")[:c].should == {'one'=>'okay'}
|
170
|
+
end
|
171
|
+
|
172
|
+
it "auto aliases symbolic :keys" do
|
173
|
+
parse("-e","t:3,o:1")[:e].should == {:three=>'3', :one=>'1'}
|
174
|
+
end
|
175
|
+
|
176
|
+
it "supports a configurable splitter" do
|
177
|
+
parse("-d","a:ab b:bc")[:d].should == {'a'=>'ab', 'b'=>'bc'}
|
178
|
+
end
|
179
|
+
|
180
|
+
it "supports grouping keys" do
|
181
|
+
parse("-c", "t,tw:foo,o:bar")[:c].should == {'three'=>'foo','two'=>'foo', 'one'=>'bar'}
|
182
|
+
end
|
183
|
+
|
184
|
+
it "aliases * to all keys" do
|
185
|
+
parse("-c", "*:foo")[:c].should == {'three'=>'foo', 'two'=>'foo', 'one'=>'foo'}
|
186
|
+
parse('-a', '*:foo')[:a].should == {'*'=>'foo'}
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
data/test/pipes_test.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
describe "Pipes" do
|
4
|
+
before_all { ::Ab = Struct.new(:a, :b) }
|
5
|
+
describe "query_pipe" do
|
6
|
+
before_all {
|
7
|
+
@hashes = [{:a=>'some', :b=>'thing'}, {:a=>:more, :b=>:yep}]
|
8
|
+
@objects = [Ab.new('some', 'thing'), Ab.new(:more, :yep)]
|
9
|
+
}
|
10
|
+
|
11
|
+
it "searches one query" do
|
12
|
+
[@hashes, @objects].each {|e|
|
13
|
+
Pipes.query_pipe(e, :a=>'some').should == e[0,1]
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
it "searches non-string values" do
|
18
|
+
[@hashes, @objects].each {|e|
|
19
|
+
Pipes.query_pipe(e, :a=>'more').should == e[1,1]
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
it "searches multiple search terms" do
|
24
|
+
[@hashes, @objects].each {|e|
|
25
|
+
Pipes.query_pipe(e, :a=>'some', :b=>'yep').size.should == 2
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
it "prints error for invalid search field" do
|
30
|
+
capture_stderr { Pipes.query_pipe(@objects, :blah=>'blah') }.should =~ /failed.*'blah'/
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "sort_pipe" do
|
35
|
+
before_all {
|
36
|
+
@hashes = [{:a=>'some', :b=>'thing'}, {:a=>:more, :b=>:yep}]
|
37
|
+
@objects = [Ab.new('some', 'thing'), Ab.new(:more, :yep)]
|
38
|
+
}
|
39
|
+
it "sorts objects with values of different types" do
|
40
|
+
Pipes.sort_pipe(@objects, :a).should == @objects.reverse
|
41
|
+
end
|
42
|
+
|
43
|
+
it "sorts hashes with values of different types" do
|
44
|
+
Pipes.sort_pipe(@hashes, :a).should == @hashes.reverse
|
45
|
+
end
|
46
|
+
|
47
|
+
it "sorts numeric values" do
|
48
|
+
hashes = [{:a=>10, :b=>4}, {:a=>5, :b=>3}]
|
49
|
+
Pipes.sort_pipe(hashes, :a).should == hashes.reverse
|
50
|
+
end
|
51
|
+
|
52
|
+
it "sorts numeric hash keys by string" do
|
53
|
+
hashes = [{2=>'thing'}, {2=>'some'}]
|
54
|
+
Pipes.sort_pipe(hashes, '2').should == hashes.reverse
|
55
|
+
end
|
56
|
+
|
57
|
+
it "sorts numeric hash keys by string" do
|
58
|
+
Pipes.sort_pipe(@hashes, 'a').should == @hashes.reverse
|
59
|
+
end
|
60
|
+
|
61
|
+
it "prints error for invalid sort field" do
|
62
|
+
capture_stderr { Pipes.sort_pipe(@objects, :blah)}.should =~ /failed.*'blah'/
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
describe "RepoIndex" do
|
4
|
+
# since we're defining our own @commands, @libraries, @lib_hashes
|
5
|
+
def index
|
6
|
+
@index ||= begin
|
7
|
+
ind = RepoIndex.new(Boson.repo)
|
8
|
+
ind.instance_variable_set "@read", true
|
9
|
+
ind
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "read_and_transfer" do
|
14
|
+
before { reset_boson; index.instance_eval "@libraries = @commands = nil" }
|
15
|
+
|
16
|
+
def transfers(options={})
|
17
|
+
index.instance_variable_set "@libraries", [Library.new(:name=>'blah', :commands=>['blurb']),
|
18
|
+
Library.new(:name=>'bling')]
|
19
|
+
index.instance_variable_set "@commands", [Command.new(:name=>'blurb', :lib=>'blah')]
|
20
|
+
index.read_and_transfer options[:ignored] || []
|
21
|
+
Boson.libraries.map {|e| e.name}.should == options[:libraries]
|
22
|
+
Boson.commands.map {|e| e.name}.should == options[:commands]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "transfers libraries with no libraries to ignore" do
|
26
|
+
transfers :libraries=>%w{blah bling}, :commands=>%w{blurb}, :ignored=>[]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "transfers libraries and commands except for ignored libraries and its commands" do
|
30
|
+
transfers :libraries=>%w{bling}, :commands=>[], :ignored=>%w{blah}
|
31
|
+
end
|
32
|
+
|
33
|
+
it "doesn't replace existing libraries" do
|
34
|
+
lib = Library.new(:name=>'blah')
|
35
|
+
cmd = Command.new(:name=>'blurb', :lib=>'blah')
|
36
|
+
Boson.libraries << lib
|
37
|
+
Boson.commands << cmd
|
38
|
+
transfers :libraries=>%w{blah bling}, :commands=>%w{blurb}
|
39
|
+
Boson.libraries.include?(lib).should == true
|
40
|
+
Boson.commands.include?(cmd).should == true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "find_library" do
|
45
|
+
before_all {
|
46
|
+
reset_boson
|
47
|
+
commands = [Command.new(:name=>'blurb', :lib=>'blah', :alias=>'bb'),
|
48
|
+
Command.new(:name=>'sub', :lib=>'bling', :alias=>'s')
|
49
|
+
]
|
50
|
+
index.instance_variable_set "@commands", commands
|
51
|
+
index.instance_variable_set "@libraries", [Library.new(:name=>'blah'), Library.new(:name=>'bling', :namespace=>'bling')]
|
52
|
+
}
|
53
|
+
|
54
|
+
it "finds command aliased or not" do
|
55
|
+
index.find_library('blurb').should == 'blah'
|
56
|
+
index.find_library('bb').should == 'blah'
|
57
|
+
end
|
58
|
+
|
59
|
+
it "doesn't find command" do
|
60
|
+
index.find_library('blah').should == nil
|
61
|
+
end
|
62
|
+
|
63
|
+
it "finds a subcommand aliased or not" do
|
64
|
+
index.find_library('bling.sub').should == 'bling'
|
65
|
+
# @index.find_library('bl.s').should == 'bling'
|
66
|
+
end
|
67
|
+
|
68
|
+
it "finds namespace command aliased or not without a subcommand" do
|
69
|
+
index.find_library('bling').should == 'bling'
|
70
|
+
# @index.find_library('bl').should == 'bling'
|
71
|
+
end
|
72
|
+
|
73
|
+
it "doesn't find a subcommand" do
|
74
|
+
index.find_library('d.d').should == nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "changed_libraries" do
|
79
|
+
before_all { index.instance_eval "@lib_hashes = nil" }
|
80
|
+
|
81
|
+
def changed(string, all_libs=['file1'])
|
82
|
+
index.repo.expects(:all_libraries).returns(all_libs)
|
83
|
+
index.instance_variable_set "@lib_hashes", {"file1"=>Digest::MD5.hexdigest("state1")}
|
84
|
+
File.stubs(:exists?).returns(true)
|
85
|
+
File.expects(:read).returns(string)
|
86
|
+
index.changed_libraries
|
87
|
+
end
|
88
|
+
|
89
|
+
it "detects changed libraries" do
|
90
|
+
changed("state2").should == %w{file1}
|
91
|
+
end
|
92
|
+
|
93
|
+
it "detects new libraries" do
|
94
|
+
changed("state1", ['file2']).should == %w{file2}
|
95
|
+
end
|
96
|
+
|
97
|
+
it "detects no changed libraries" do
|
98
|
+
changed("state1").should == []
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "write" do
|
103
|
+
before_all {
|
104
|
+
reset_boson
|
105
|
+
Boson.commands << Command.new(:name=>'blah', :lib=>'blah', :args=>[['arg1', {}], ['arg2', self.class]])
|
106
|
+
Boson.libraries << Library.new(:name=>'blah', :module=>self.class)
|
107
|
+
index.expects(:latest_hashes)
|
108
|
+
libraries = commands = []
|
109
|
+
index.expects(:save_marshal_index).with {|str| libraries, commands, hashes = Marshal.load(str) ; true}
|
110
|
+
index.write
|
111
|
+
@index_hash = {:libraries=>libraries, :commands=>commands}
|
112
|
+
}
|
113
|
+
|
114
|
+
it "saves library module constants as strings" do
|
115
|
+
@index_hash[:libraries][0].module.class.should == String
|
116
|
+
end
|
117
|
+
|
118
|
+
it "save commands with arg values as strings" do
|
119
|
+
@index_hash[:commands][0].args.each {|e| e[1].class.should == String}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/test/repo_test.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
describe "config" do
|
4
|
+
before_all { reset }
|
5
|
+
before { @repo = Repo.new(File.dirname(__FILE__)) }
|
6
|
+
|
7
|
+
it "reloads config when passed true" do
|
8
|
+
@repo.config.object_id.should.not == @repo.config(true).object_id
|
9
|
+
end
|
10
|
+
|
11
|
+
it "reads existing config correctly" do
|
12
|
+
expected_hash = {:commands=>{'c1'=>{}}, :libraries=>{}}
|
13
|
+
File.expects(:exists?).returns(true)
|
14
|
+
YAML.expects(:load_file).returns(expected_hash)
|
15
|
+
@repo.config[:commands]['c1'].should == {}
|
16
|
+
end
|
17
|
+
|
18
|
+
it "ignores nonexistent file and sets config defaults" do
|
19
|
+
@repo.config[:command_aliases].class.should == Hash
|
20
|
+
@repo.config[:libraries].class.should == Hash
|
21
|
+
end
|
22
|
+
after_all { FileUtils.rm_r File.dirname(__FILE__)+'/config', :force=>true }
|
23
|
+
end
|
data/test/runner_test.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
describe "repl_runner" do
|
4
|
+
def start(hash={})
|
5
|
+
Hirb.stubs(:enable)
|
6
|
+
Boson.start(hash.merge(:verbose=>false))
|
7
|
+
end
|
8
|
+
|
9
|
+
before_all { reset }
|
10
|
+
before { ConsoleRunner.instance_eval("@initialized = false") }
|
11
|
+
|
12
|
+
it "loads default libraries and libraries in :console_defaults config" do
|
13
|
+
defaults = Runner.default_libraries + ['yo']
|
14
|
+
with_config(:console_defaults=>['yo']) do
|
15
|
+
Manager.expects(:load).with {|*args| args[0] == defaults }
|
16
|
+
start
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "doesn't call init twice" do
|
21
|
+
capture_stderr { start }
|
22
|
+
ConsoleRunner.expects(:init).never
|
23
|
+
start
|
24
|
+
end
|
25
|
+
|
26
|
+
it "loads multiple libraries with :libraries option" do
|
27
|
+
ConsoleRunner.expects(:init)
|
28
|
+
Manager.expects(:load).with([:lib1,:lib2], anything)
|
29
|
+
start(:libraries=>[:lib1, :lib2])
|
30
|
+
end
|
31
|
+
|
32
|
+
it "autoloader autoloads libraries" do
|
33
|
+
start(:autoload_libraries=>true)
|
34
|
+
Index.expects(:read)
|
35
|
+
Index.expects(:find_library).with('blah').returns('blah')
|
36
|
+
Manager.expects(:load).with('blah', anything)
|
37
|
+
Boson.main_object.blah
|
38
|
+
end
|
39
|
+
after_all { FileUtils.rm_r File.dirname(__FILE__)+'/config', :force=>true }
|
40
|
+
end
|
@@ -0,0 +1,341 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
describe "Scientist" do
|
4
|
+
before_all {
|
5
|
+
Runner.in_shell = nil
|
6
|
+
eval <<-EOF
|
7
|
+
module Blah
|
8
|
+
def blah(arg1, options={})
|
9
|
+
[arg1, options]
|
10
|
+
end
|
11
|
+
def splat_blah(*args)
|
12
|
+
args
|
13
|
+
end
|
14
|
+
def default_blah(arg1, arg2=default, options={})
|
15
|
+
[arg1, arg2, options]
|
16
|
+
end
|
17
|
+
def default; 'some default'; end
|
18
|
+
def default_option(options={})
|
19
|
+
options
|
20
|
+
end
|
21
|
+
end
|
22
|
+
EOF
|
23
|
+
@opt_cmd = Object.new.extend Blah
|
24
|
+
}
|
25
|
+
|
26
|
+
def command(hash, args)
|
27
|
+
hash = {:name=>'blah', :lib=>'bling', :options=>{:force=>:boolean, :level=>2}}.merge(hash)
|
28
|
+
@cmd = Command.new hash
|
29
|
+
@cmd.instance_variable_set("@file_parsed_args", true) if hash[:file_parsed_args]
|
30
|
+
Scientist.redefine_command(@opt_cmd, @cmd)
|
31
|
+
@opt_cmd.send(hash[:name], *args)
|
32
|
+
end
|
33
|
+
|
34
|
+
def command_with_arg_size(*args)
|
35
|
+
command({:args=>2}, args)
|
36
|
+
end
|
37
|
+
|
38
|
+
def command_with_args(*args)
|
39
|
+
command({:args=>[['arg1'],['options', {}]]}, args)
|
40
|
+
end
|
41
|
+
|
42
|
+
def basic_command(hash, args)
|
43
|
+
command({:name=>'splat_blah', :args=>'*'}.merge(hash), args)
|
44
|
+
end
|
45
|
+
|
46
|
+
def command_with_splat_args(*args)
|
47
|
+
command({:name=>'splat_blah', :args=>'*'}, args)
|
48
|
+
end
|
49
|
+
|
50
|
+
def command_with_arg_defaults(*args)
|
51
|
+
arg_defaults = [%w{arg1}, %w{arg2 default}, %w{options {}}]
|
52
|
+
command({:name=>'default_blah', :file_parsed_args=>true, :args=>arg_defaults}, args)
|
53
|
+
end
|
54
|
+
|
55
|
+
def args_are_equal(args, array)
|
56
|
+
command_with_args(*args).should == array
|
57
|
+
command_with_arg_size(*args).should == array
|
58
|
+
command_with_splat_args(*args).should == array
|
59
|
+
end
|
60
|
+
|
61
|
+
def all_commands
|
62
|
+
[:command_with_args, :command_with_arg_size, :command_with_splat_args]
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "all commands" do
|
66
|
+
it "translate arg and options as one string" do
|
67
|
+
args_are_equal ['a1 -f'], ['a1', {:force=>true, :level=>2}]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "translate arg and stringified options" do
|
71
|
+
args_are_equal [:cool, '-l3'], [:cool, {:level=>3}]
|
72
|
+
end
|
73
|
+
|
74
|
+
it "translate arg and normal hash options" do
|
75
|
+
args_are_equal [:cool, {:ok=>true}], [:cool, {:ok=>true, :level=>2}]
|
76
|
+
end
|
77
|
+
|
78
|
+
it "translate stringified arg without options sets default options" do
|
79
|
+
args_are_equal ['cool'], ['cool', {:level=>2}]
|
80
|
+
end
|
81
|
+
|
82
|
+
it "translate arg without options sets default options" do
|
83
|
+
args_are_equal [:cool], [:cool, {:level=>2}]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "with invalid options print errors and delete them" do
|
87
|
+
all_commands.each do |cmd|
|
88
|
+
capture_stderr {
|
89
|
+
send(cmd, 'cool -f -z').should == ['cool', {:force=>true, :level=>2}]
|
90
|
+
}.should =~/invalid.*z/
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it "print help with help option" do
|
95
|
+
all_commands.each do |cmd|
|
96
|
+
Boson.expects(:invoke).with(:usage, anything, anything)
|
97
|
+
send(cmd, '-h')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "command" do
|
103
|
+
describe "with arg defaults" do
|
104
|
+
it "sets defaults with stringified args" do
|
105
|
+
command_with_arg_defaults('1').should == ["1", "some default", {:level=>2}]
|
106
|
+
end
|
107
|
+
|
108
|
+
it "sets defaults with normal args" do
|
109
|
+
command_with_arg_defaults(1).should == [1, "some default", {:level=>2}]
|
110
|
+
end
|
111
|
+
|
112
|
+
it "sets default if optional arg is a valid option" do
|
113
|
+
command_with_arg_defaults("cool -f").should == ["cool", "some default", {:level=>2, :force=>true}]
|
114
|
+
end
|
115
|
+
|
116
|
+
it "doesn't set defaults if not needed" do
|
117
|
+
command_with_arg_defaults(1, 'nada').should == [1, "nada", {:level=>2}]
|
118
|
+
end
|
119
|
+
|
120
|
+
it "prints error for invalid defaults" do
|
121
|
+
arg_defaults = [%w{arg1}, %w{arg2 invalidzzz}, %w{options {}}]
|
122
|
+
capture_stderr {
|
123
|
+
command({:name=>'default_blah', :file_parsed_args=>true, :args=>arg_defaults}, [1])
|
124
|
+
}.should =~ /Error.*position 2/
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "prints error" do
|
129
|
+
it "with option error" do
|
130
|
+
capture_stderr { command_with_args('a1 -l') }.should =~ /Error.*level/
|
131
|
+
end
|
132
|
+
|
133
|
+
it "with unexpected error in render" do
|
134
|
+
Scientist.expects(:can_render?).raises("unexpected")
|
135
|
+
capture_stderr { command_with_args('a1') }.should =~ /Error.*unexpected/
|
136
|
+
end
|
137
|
+
|
138
|
+
it "with no argument defined for options" do
|
139
|
+
assert_error(OptionCommand::CommandArgumentError, '2 for 1') { command({:args=>1}, 'ok') }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
it "translates stringfied args + options starting at second arg" do
|
144
|
+
command_with_arg_defaults(1, 'nada -l3').should == [1, "nada", {:level=>3}]
|
145
|
+
end
|
146
|
+
|
147
|
+
it "with leading option-like args are translated as arguments" do
|
148
|
+
command_with_args('-z -f').should == ["-z", {:force=>true, :level=>2}]
|
149
|
+
command_with_args('--blah -f').should == ['--blah', {:force=>true, :level=>2}]
|
150
|
+
end
|
151
|
+
|
152
|
+
it "with splat args does not raise error for too few or many args" do
|
153
|
+
[[], [''], [1,2,3], ['1 2 3']].each do |args|
|
154
|
+
should.not.raise { command_with_splat_args *args }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
it "with debug option prints debug" do
|
159
|
+
capture_stdout { command_with_args("-v ok") }.should =~ /Arguments.*ok/
|
160
|
+
end
|
161
|
+
|
162
|
+
it "with pretend option prints arguments and returns early" do
|
163
|
+
Scientist.expects(:render_or_raw).never
|
164
|
+
capture_stdout { command_with_args("-p ok") }.should =~ /Arguments.*ok/
|
165
|
+
end
|
166
|
+
|
167
|
+
it "with not enough args raises CommandArgumentError" do
|
168
|
+
args = [OptionCommand::CommandArgumentError, '0 for 1']
|
169
|
+
assert_error(*args) { command_with_args }
|
170
|
+
assert_error(*args) { command_with_args '' }
|
171
|
+
assert_error(*args) { command_with_arg_size }
|
172
|
+
assert_error(*args) { command_with_arg_size '' }
|
173
|
+
end
|
174
|
+
|
175
|
+
it "with too many args raises CommandArgumentError" do
|
176
|
+
args3 = [ArgumentError, '3 for 2']
|
177
|
+
args4 = [OptionCommand::CommandArgumentError, '4 for 2']
|
178
|
+
assert_error(*args3) { command_with_args 1,2,3 }
|
179
|
+
assert_error(*args4) { command_with_args '1 2 3' }
|
180
|
+
assert_error(*args3) { command_with_arg_size 1,2,3 }
|
181
|
+
assert_error(*args4) { command_with_arg_size '1 2 3' }
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def command_with_render(*args)
|
186
|
+
basic_command({:render_options=>{:fields=>{:values=>['f1', 'f2']}} }, args)
|
187
|
+
end
|
188
|
+
|
189
|
+
def render_expected(options=nil)
|
190
|
+
View.expects(:render).with(anything, options || anything, false)
|
191
|
+
end
|
192
|
+
|
193
|
+
describe "render" do
|
194
|
+
it "called for command with render_options" do
|
195
|
+
render_expected
|
196
|
+
command_with_render('1')
|
197
|
+
end
|
198
|
+
|
199
|
+
it "called for command without render_options and --render" do
|
200
|
+
render_expected
|
201
|
+
command_with_args('--render 1')
|
202
|
+
end
|
203
|
+
|
204
|
+
it "not called for command with render_options and --render" do
|
205
|
+
Boson.expects(:invoke).never
|
206
|
+
command_with_render('--render 1')
|
207
|
+
end
|
208
|
+
|
209
|
+
it "not called for command without render_options" do
|
210
|
+
Boson.expects(:invoke).never
|
211
|
+
command_with_args('1')
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe "command renders" do
|
216
|
+
it "with basic render options" do
|
217
|
+
render_expected :fields => ['f1', 'f2']
|
218
|
+
command_with_render("--fields f1,f2 ab")
|
219
|
+
end
|
220
|
+
|
221
|
+
it "without non-render options" do
|
222
|
+
render_expected :fields=>['f1']
|
223
|
+
Scientist.expects(:can_render?).returns(true)
|
224
|
+
args = ["--render --fields f1 ab"]
|
225
|
+
basic_command({:render_options=>{:fields=>{:values=>['f1', 'f2']}} }, args)
|
226
|
+
end
|
227
|
+
|
228
|
+
it "with user-defined render options" do
|
229
|
+
render_expected :fields=>['f1'], :foo=>true
|
230
|
+
args = ["--foo --fields f1 ab"]
|
231
|
+
basic_command({:render_options=>{:foo=>:boolean, :fields=>{:values=>['f1', 'f2']}} }, args)
|
232
|
+
end
|
233
|
+
|
234
|
+
it "with non-hash user-defined render options" do
|
235
|
+
render_expected :fields=>['f1'], :foo=>true
|
236
|
+
args = ["--foo --fields f1 ab"]
|
237
|
+
basic_command({:render_options=>{:foo=>:boolean, :fields=>%w{f1 f2 f3}} }, args)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
describe "command with default option" do
|
242
|
+
before_all { @cmd_attributes = {:name=>'default_option', :default_option=>'level', :args=>1} }
|
243
|
+
|
244
|
+
it "parses normally from irb" do
|
245
|
+
command(@cmd_attributes, '-f --level=3').should == {:level=>3, :force=>true}
|
246
|
+
end
|
247
|
+
|
248
|
+
it "parses normally from cmdline" do
|
249
|
+
Runner.expects(:in_shell?).times(2).returns true
|
250
|
+
command(@cmd_attributes, ['--force', '--level=3']).should == {:level=>3, :force=>true}
|
251
|
+
end
|
252
|
+
|
253
|
+
it "parses no arguments normally" do
|
254
|
+
command(@cmd_attributes, '').should == {:level=>2}
|
255
|
+
end
|
256
|
+
|
257
|
+
it "parses ruby arguments normally" do
|
258
|
+
command(@cmd_attributes, [{:force=>true, :level=>10}]).should == {:level=>10, :force=>true}
|
259
|
+
end
|
260
|
+
|
261
|
+
it "prepends correctly from irb" do
|
262
|
+
command(@cmd_attributes, '3 -f').should == {:level=>3, :force=>true}
|
263
|
+
end
|
264
|
+
|
265
|
+
it "prepends correctly from cmdline" do
|
266
|
+
Runner.expects(:in_shell?).times(2).returns true
|
267
|
+
command(@cmd_attributes, ['3','-f']).should == {:level=>3, :force=>true}
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
it "optionless command renders" do
|
272
|
+
render_expected :fields=>['f1']
|
273
|
+
command({:args=>2, :options=>nil, :render_options=>{:fields=>:array}}, ["--fields f1 ab ok"])
|
274
|
+
end
|
275
|
+
|
276
|
+
it "redefine_command prints error for command with nonexistant method" do
|
277
|
+
capture_stderr {
|
278
|
+
Scientist.redefine_command Object.new, Command.new(:name=>'blah', :lib=>'blah')
|
279
|
+
}.should =~ /Error: No method.*'blah'/
|
280
|
+
end
|
281
|
+
|
282
|
+
describe "global options:" do
|
283
|
+
def local_and_global(*args)
|
284
|
+
Scientist.stubs(:can_render?).returns(false) # turn off rendering caused by :render_options
|
285
|
+
@non_opts = basic_command(@command_options, args)
|
286
|
+
@non_opts.slice!(-1,1) << Scientist.global_options
|
287
|
+
end
|
288
|
+
|
289
|
+
before_all {
|
290
|
+
@command_options = {:options=>{:do=>:boolean, :foo=>:boolean},
|
291
|
+
:render_options=>{:dude=>:boolean}}
|
292
|
+
@expected_non_opts = [[], ['doh'], ['doh'], [:doh]]
|
293
|
+
}
|
294
|
+
|
295
|
+
it "local option overrides global one" do
|
296
|
+
['-d', 'doh -d','-d doh', [:doh, '-d']].each_with_index do |args, i|
|
297
|
+
local_and_global(*args).should == [{:do=>true}, {}]
|
298
|
+
@non_opts.should == @expected_non_opts[i]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
it "global option before local one is valid" do
|
303
|
+
args_arr = ['--dude -f', '--dude doh -f', '--dude -f doh', [:doh, '--dude -f']]
|
304
|
+
args_arr.each_with_index do |args, i|
|
305
|
+
local_and_global(*args).should == [{:foo=>true}, {:dude=>true}]
|
306
|
+
@non_opts.should == @expected_non_opts[i]
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
it "delete_options deletes global options" do
|
311
|
+
local_and_global('--delete_options=r,p -rp -f').should ==
|
312
|
+
[{:foo=>true}, {:delete_options=>["r", "p"]}]
|
313
|
+
end
|
314
|
+
|
315
|
+
it "global option after local one is invalid" do
|
316
|
+
args_arr = ['-f --dude', '-f doh --dude', '-f --dude doh', [:doh, '-f --dude'] ]
|
317
|
+
args_arr.each_with_index do |args, i|
|
318
|
+
capture_stderr {
|
319
|
+
local_and_global(*args).should == [{:foo=>true}, {}]
|
320
|
+
@non_opts.should == @expected_non_opts[i]
|
321
|
+
}.should =~ /invalid.*dude/
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
it "global option after local one and -" do
|
326
|
+
local_and_global("doh -r -f - --dude").should == [{:foo=>true}, {:dude=>true, :render=>true}]
|
327
|
+
end
|
328
|
+
|
329
|
+
it "conflicting global option after -" do
|
330
|
+
local_and_global('doh - -f=1,2').should == [{}, {:fields=>["1", "2"]}]
|
331
|
+
end
|
332
|
+
|
333
|
+
it "no options parsed after --" do
|
334
|
+
local_and_global('doh -f -- -r').should == [{:foo=>true}, {}]
|
335
|
+
local_and_global('doh -- -r -f').should == [{}, {}]
|
336
|
+
local_and_global('-- -r -f').should == [{}, {}]
|
337
|
+
local_and_global('doh -r -- -f').should == [{}, {:render=>true}]
|
338
|
+
end
|
339
|
+
end
|
340
|
+
after_all { Runner.in_shell = false }
|
341
|
+
end
|