bosonson 0.304.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.
- 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
|