bosonson 0.304.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/CHANGELOG.rdoc +108 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.rdoc +181 -0
  4. data/bin/bss +6 -0
  5. data/bosonson.gemspec +24 -0
  6. data/deps.rip +2 -0
  7. data/lib/boson.rb +96 -0
  8. data/lib/boson/command.rb +196 -0
  9. data/lib/boson/commands.rb +7 -0
  10. data/lib/boson/commands/core.rb +77 -0
  11. data/lib/boson/commands/web_core.rb +153 -0
  12. data/lib/boson/index.rb +48 -0
  13. data/lib/boson/inspector.rb +120 -0
  14. data/lib/boson/inspectors/argument_inspector.rb +97 -0
  15. data/lib/boson/inspectors/comment_inspector.rb +100 -0
  16. data/lib/boson/inspectors/method_inspector.rb +98 -0
  17. data/lib/boson/libraries/file_library.rb +144 -0
  18. data/lib/boson/libraries/gem_library.rb +30 -0
  19. data/lib/boson/libraries/local_file_library.rb +30 -0
  20. data/lib/boson/libraries/module_library.rb +37 -0
  21. data/lib/boson/libraries/require_library.rb +23 -0
  22. data/lib/boson/library.rb +179 -0
  23. data/lib/boson/loader.rb +118 -0
  24. data/lib/boson/manager.rb +169 -0
  25. data/lib/boson/namespace.rb +31 -0
  26. data/lib/boson/option_command.rb +222 -0
  27. data/lib/boson/option_parser.rb +475 -0
  28. data/lib/boson/options.rb +146 -0
  29. data/lib/boson/pipe.rb +147 -0
  30. data/lib/boson/pipes.rb +75 -0
  31. data/lib/boson/repo.rb +107 -0
  32. data/lib/boson/repo_index.rb +124 -0
  33. data/lib/boson/runner.rb +81 -0
  34. data/lib/boson/runners/bin_runner.rb +208 -0
  35. data/lib/boson/runners/console_runner.rb +58 -0
  36. data/lib/boson/scientist.rb +182 -0
  37. data/lib/boson/util.rb +129 -0
  38. data/lib/boson/version.rb +3 -0
  39. data/lib/boson/view.rb +95 -0
  40. data/test/argument_inspector_test.rb +62 -0
  41. data/test/bin_runner_test.rb +223 -0
  42. data/test/command_test.rb +22 -0
  43. data/test/commands_test.rb +22 -0
  44. data/test/comment_inspector_test.rb +126 -0
  45. data/test/deps.rip +4 -0
  46. data/test/file_library_test.rb +42 -0
  47. data/test/loader_test.rb +235 -0
  48. data/test/manager_test.rb +114 -0
  49. data/test/method_inspector_test.rb +90 -0
  50. data/test/option_parser_test.rb +367 -0
  51. data/test/options_test.rb +189 -0
  52. data/test/pipes_test.rb +65 -0
  53. data/test/repo_index_test.rb +122 -0
  54. data/test/repo_test.rb +23 -0
  55. data/test/runner_test.rb +40 -0
  56. data/test/scientist_test.rb +341 -0
  57. data/test/test_helper.rb +130 -0
  58. data/test/util_test.rb +56 -0
  59. data/vendor/bundle/gems/bacon-bits-0.1.0/deps.rip +1 -0
  60. data/vendor/bundle/gems/hirb-0.6.0/test/deps.rip +4 -0
  61. 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
@@ -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
@@ -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
@@ -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