boson 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/LICENSE.txt +22 -0
  2. data/README.rdoc +133 -0
  3. data/Rakefile +52 -0
  4. data/VERSION.yml +4 -0
  5. data/bin/boson +6 -0
  6. data/lib/boson.rb +72 -0
  7. data/lib/boson/command.rb +117 -0
  8. data/lib/boson/commands.rb +7 -0
  9. data/lib/boson/commands/core.rb +66 -0
  10. data/lib/boson/commands/web_core.rb +36 -0
  11. data/lib/boson/index.rb +95 -0
  12. data/lib/boson/inspector.rb +80 -0
  13. data/lib/boson/inspectors/argument_inspector.rb +92 -0
  14. data/lib/boson/inspectors/comment_inspector.rb +79 -0
  15. data/lib/boson/inspectors/method_inspector.rb +94 -0
  16. data/lib/boson/libraries/file_library.rb +76 -0
  17. data/lib/boson/libraries/gem_library.rb +21 -0
  18. data/lib/boson/libraries/module_library.rb +17 -0
  19. data/lib/boson/libraries/require_library.rb +11 -0
  20. data/lib/boson/library.rb +108 -0
  21. data/lib/boson/loader.rb +103 -0
  22. data/lib/boson/manager.rb +184 -0
  23. data/lib/boson/namespace.rb +45 -0
  24. data/lib/boson/option_parser.rb +318 -0
  25. data/lib/boson/repo.rb +38 -0
  26. data/lib/boson/runner.rb +51 -0
  27. data/lib/boson/runners/bin_runner.rb +100 -0
  28. data/lib/boson/runners/repl_runner.rb +40 -0
  29. data/lib/boson/scientist.rb +168 -0
  30. data/lib/boson/util.rb +93 -0
  31. data/lib/boson/view.rb +31 -0
  32. data/test/argument_inspector_test.rb +62 -0
  33. data/test/bin_runner_test.rb +136 -0
  34. data/test/commands_test.rb +51 -0
  35. data/test/comment_inspector_test.rb +99 -0
  36. data/test/config/index.marshal +0 -0
  37. data/test/file_library_test.rb +50 -0
  38. data/test/index_test.rb +117 -0
  39. data/test/loader_test.rb +181 -0
  40. data/test/manager_test.rb +110 -0
  41. data/test/method_inspector_test.rb +64 -0
  42. data/test/option_parser_test.rb +365 -0
  43. data/test/repo_test.rb +22 -0
  44. data/test/runner_test.rb +43 -0
  45. data/test/scientist_test.rb +291 -0
  46. data/test/test_helper.rb +119 -0
  47. metadata +133 -0
@@ -0,0 +1,136 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+ require 'boson/runners/bin_runner'
3
+
4
+ module Boson
5
+ class BinRunnerTest < Test::Unit::TestCase
6
+ def start(*args)
7
+ Hirb.stubs(:enable)
8
+ BinRunner.start(args)
9
+ end
10
+
11
+ before(:each) {|e|
12
+ Boson::BinRunner.instance_variables.each {|e| Boson::BinRunner.instance_variable_set(e, nil)}
13
+ }
14
+ context "at commandline" do
15
+ before(:all) { reset }
16
+
17
+ test "no arguments prints usage" do
18
+ capture_stdout { start }.should =~ /^boson/
19
+ end
20
+
21
+ test "invalid option value prints error" do
22
+ capture_stderr { start("-l") }.should =~ /Error:/
23
+ end
24
+
25
+ test "help option but no arguments prints usage" do
26
+ capture_stdout { start '-h' }.should =~ /^boson/
27
+ end
28
+
29
+ test "help option and command prints help" do
30
+ capture_stdout { start('-h', 'commands') } =~ /^commands/
31
+ end
32
+
33
+ test "load option loads libraries" do
34
+ Manager.expects(:load).with {|*args| args[0][0].is_a?(Module) ? true : args[0][0] == 'blah'}.times(2)
35
+ BinRunner.stubs(:execute_command)
36
+ start('-l', 'blah', 'libraries')
37
+ end
38
+
39
+ test "repl option starts repl" do
40
+ ReplRunner.expects(:start)
41
+ Util.expects(:which).returns("/usr/bin/irb")
42
+ Kernel.expects(:load).with("/usr/bin/irb")
43
+ start("--repl")
44
+ end
45
+
46
+ test "repl option but no repl found prints error" do
47
+ ReplRunner.expects(:start)
48
+ Util.expects(:which).returns(nil)
49
+ capture_stderr { start("--repl") } =~ /Repl not found/
50
+ end
51
+
52
+ test "execute option executes string" do
53
+ BinRunner.expects(:define_autoloader)
54
+ capture_stdout { start("-e", "p 1 + 1") }.should == "2\n"
55
+ end
56
+
57
+ test "execute option errors are caught" do
58
+ capture_stderr { start("-e", "raise 'blah'") }.should =~ /^Error:/
59
+ end
60
+
61
+ test "command and too many arguments prints error" do
62
+ capture_stdout { start('commands','1','2','3') }.should =~ /Wrong number/
63
+ end
64
+
65
+ test "undiscovered command prints error" do
66
+ BinRunner.expects(:load_command_by_index).returns(false)
67
+ capture_stderr { start('blah') }.should =~ /Error.*blah/
68
+ end
69
+
70
+ test "basic command executes" do
71
+ BinRunner.expects(:init).returns(true)
72
+ BinRunner.stubs(:render_output)
73
+ Boson.main_object.expects(:send).with('kick','it')
74
+ start 'kick','it'
75
+ end
76
+
77
+ test "sub command executes" do
78
+ obj = Object.new
79
+ Boson.main_object.extend Module.new { def phone; Struct.new(:home).new('done'); end }
80
+ BinRunner.expects(:init).returns(true)
81
+ BinRunner.expects(:render_output).with('done')
82
+ start 'phone.home'
83
+ end
84
+
85
+ test "bin_defaults config loads by default" do
86
+ defaults = Boson::Runner.default_libraries + ['yo']
87
+ with_config(:bin_defaults=>['yo']) do
88
+ Manager.expects(:load).with {|*args| args[0] == defaults }
89
+ capture_stderr { start 'blah' }
90
+ end
91
+ end
92
+ end
93
+
94
+ context "load_command_by_index" do
95
+ test "with index option, no existing index and core command updates index and prints index message" do
96
+ Manager.expects(:load).with {|*args| args[0][0].is_a?(Module) ? true : args[0] == Runner.all_libraries }.at_least(1)
97
+ Index.expects(:exists?).returns(false)
98
+ Index.expects(:write)
99
+ capture_stdout { start("--index", "libraries") }.should =~ /Generating index/
100
+ end
101
+
102
+ test "with index option, existing index and core command updates incremental index" do
103
+ Index.expects(:changed_libraries).returns(['changed'])
104
+ Manager.expects(:load).with {|*args| args[0][0].is_a?(Module) ? true : args[0] == ['changed'] }.at_least(1)
105
+ Index.expects(:exists?).returns(true)
106
+ Index.expects(:write)
107
+ capture_stdout { start("--index", "libraries")}.should =~ /Indexing.*changed/
108
+ end
109
+
110
+ test "with core command updates index and doesn't print index message" do
111
+ Index.expects(:write)
112
+ Boson.main_object.expects(:send).with('libraries')
113
+ capture_stdout { start 'libraries'}.should_not =~ /index/i
114
+ end
115
+
116
+ test "with non-core command finding library doesn't update index" do
117
+ Index.expects(:find_library).returns('sweet_lib')
118
+ Manager.expects(:load).with {|*args| args[0].is_a?(String) ? args[0] == 'sweet_lib' : true}.at_least(1)
119
+ Index.expects(:update).never
120
+ capture_stderr { start("sweet") }.should =~ /sweet/
121
+ end
122
+
123
+ test "with non-core command not finding library, does update index" do
124
+ Index.expects(:find_library).returns(nil, 'sweet_lib').times(2)
125
+ Manager.expects(:load).with {|*args| args[0].is_a?(String) ? args[0] == 'sweet_lib' : true}.at_least(1)
126
+ Index.expects(:update).returns(true)
127
+ capture_stderr { start("sweet") }.should =~ /sweet/
128
+ end
129
+ end
130
+
131
+ test "parse_args only translates options before command" do
132
+ BinRunner.parse_args(['-v', 'com', '-v']).should == ["com", {:verbose=>true}, ['-v']]
133
+ BinRunner.parse_args(['com', '-v']).should == ["com", {}, ['-v']]
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,51 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ module Boson
4
+ class CommandsTest < Test::Unit::TestCase
5
+ before(:all) {
6
+ reset_boson
7
+ @higgs = Boson.main_object
8
+ ancestors = class <<Boson.main_object; self end.ancestors
9
+ # allows running just this test file
10
+ Manager.load Runner.default_libraries unless ancestors.include?(Boson::Commands::Core)
11
+ }
12
+
13
+ def render_expects(&block)
14
+ View.expects(:render).with(&block)
15
+ end
16
+
17
+ context "libraries" do
18
+ before(:all) {
19
+ Boson.libraries << Boson::Library.new(:name=>'blah')
20
+ Boson.libraries << Boson::Library.new(:name=>'another', :module=>"Cool")
21
+ }
22
+
23
+ test "lists all when given no argument" do
24
+ render_expects {|*args| args[0].size == 2}
25
+ @higgs.libraries
26
+ end
27
+
28
+ test "searches with a given search field" do
29
+ render_expects {|*args| args[0] == [Boson.library('another')]}
30
+ @higgs.libraries('Cool', :query_fields=>[:module])
31
+ end
32
+ end
33
+
34
+ context "commands" do
35
+ before(:all) {
36
+ Boson.commands << Command.create('some', Library.new(:name=>'thing'))
37
+ Boson.commands << Command.create('and', Library.new(:name=>'this'))
38
+ }
39
+
40
+ test "lists all when given no argument" do
41
+ render_expects {|*args| args[0].size == 2}
42
+ @higgs.commands
43
+ end
44
+
45
+ test "searches with a given search field" do
46
+ render_expects {|*args| args[0] == [Command.find('and')]}
47
+ @higgs.commands('this', :query_fields=>[:lib])
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,99 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ module Boson
4
+ class ScraperTest < Test::Unit::TestCase
5
+ before(:all) { eval "module Optional; def self.bling; {:a=>'bling'}; end; end" }
6
+ context "description" do
7
+ before(:each) {
8
+ @lines = ["module Foo", " # some comments yay", " def foo", " end", "end"]
9
+ }
10
+ def description(options={})
11
+ CommentInspector.scrape(@lines.join("\n"), options[:line] || 3, Optional)[:desc]
12
+ end
13
+
14
+ test "directly above method returns desc" do
15
+ description.should == "some comments yay"
16
+ end
17
+
18
+ test "with explicit @desc returns desc" do
19
+ @lines[1] = '#@desc some comments yay'
20
+ description.should == "some comments yay"
21
+ end
22
+
23
+ test "of multiple lines returns desc" do
24
+ @lines.delete_at(1)
25
+ @lines.insert(1, '#@options :b=>1', '#@desc here be', '# comments')
26
+ description(:line=>5).should == "here be comments"
27
+ end
28
+
29
+ test "that is empty returns nil" do
30
+ @lines[1] = ""
31
+ description.should == nil
32
+ end
33
+
34
+ test "that is empty with options keyword returns nil" do
35
+ @lines[1] = '#@options {:a=>1}'
36
+ description.should == nil
37
+ end
38
+
39
+ test "not directly above returns nil" do
40
+ @lines.insert(2, " ")
41
+ description(:line=>4).should == nil
42
+ end
43
+ end
44
+
45
+ context "options" do
46
+ before(:each) {
47
+ @lines = ["module Foo", ' #@options {:a=>true}', " def foo", " end", "end"]
48
+ }
49
+ def options(opts={})
50
+ @lines[1] = opts[:value] if opts[:value]
51
+ args = [@lines.join("\n"), 3, Optional]
52
+ CommentInspector.scrape(*args)[:options]
53
+ end
54
+
55
+ test "that are basic return options" do
56
+ options.should == {:a=>true}
57
+ end
58
+
59
+ test "that are hash-like returns hashified options" do
60
+ options(:value=>'#@options :a => 2').should == {:a=>2}
61
+ end
62
+
63
+ test "that are whitespaced return options" do
64
+ options(:value=>"\t"+ '# @options {:a=>1}').should == {:a=>1}
65
+ end
66
+
67
+ test "that have a local value return options" do
68
+ options(:value=>'#@options bling').should == {:a=>'bling'}
69
+ end
70
+
71
+ test "that are multi-line return options" do
72
+ @lines.delete_at(1)
73
+ @lines.insert(1, '#@options {:a =>', " # 1}", "# some comments")
74
+ CommentInspector.scrape(@lines.join("\n"), 5, Optional)[:options].should == {:a=>1}
75
+ end
76
+
77
+ test "with failed eval return nil" do
78
+ options(:value=>'#@options !--').should == nil
79
+ end
80
+
81
+ test "that are empty return nil" do
82
+ options(:value=>"# nada").should == nil
83
+ end
84
+ end
85
+
86
+ test "scrape all comment types with implicit desc" do
87
+ @lines = ["module Foo", '# @render_options :b=>1', ' # @options {:a=>true}', '#blah', " def foo", " end", "end"]
88
+ expected = {:desc=>"blah", :options=>{:a=>true}, :render_options=>{:b=>1}}
89
+ CommentInspector.scrape(@lines.join("\n"), 5, Optional).should == expected
90
+ end
91
+
92
+ test "scrape all comment types with explicit desc" do
93
+ @lines = ["module Foo", '#@desc blah', '# @render_options :b=>1,', '# :c=>2',
94
+ ' # @options {:a=>true}', " def foo", " end", "end"]
95
+ expected = {:desc=>"blah", :options=>{:a=>true}, :render_options=>{:b=>1, :c=>2}}
96
+ CommentInspector.scrape(@lines.join("\n"), 6, Optional).should == expected
97
+ end
98
+ end
99
+ end
Binary file
@@ -0,0 +1,50 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ module Boson
4
+ class FileLibraryTest < Test::Unit::TestCase
5
+ context "file library" do
6
+ before(:each) { reset; FileLibrary.reset_file_cache }
7
+
8
+ test "loads" do
9
+ load :blah, :file_string=>"module Blah; def blah; end; end"
10
+ library_has_module('blah', 'Boson::Commands::Blah')
11
+ command_exists?('blah')
12
+ end
13
+
14
+ test "in a subdirectory loads" do
15
+ load 'site/delicious', :file_string=>"module Delicious; def blah; end; end"
16
+ library_has_module('site/delicious', "Boson::Commands::Delicious")
17
+ command_exists?('blah')
18
+ end
19
+
20
+ test "prints error for file library with no module" do
21
+ capture_stderr { load(:blah, :file_string=>"def blah; end") }.should =~ /Can't.*at least/
22
+ end
23
+
24
+ test "prints error for file library with multiple modules" do
25
+ capture_stderr { load(:blah, :file_string=>"module Doo; end; module Daa; end") }.should =~ /Can't.*config/
26
+ end
27
+
28
+ test "with same module reloads" do
29
+ load(:blah, :file_string=>"module Blah; def blah; end; end")
30
+ File.stubs(:exists?).returns(true)
31
+ File.stubs(:read).returns("module Blah; def bling; end; end")
32
+ Manager.reload('blah').should == true
33
+ command_exists?('bling')
34
+ library('blah').commands.size.should == 2
35
+ end
36
+
37
+ test "with different module reloads" do
38
+ load(:blah, :file_string=>"module Blah; def blah; end; end")
39
+ File.stubs(:exists?).returns(true)
40
+ File.stubs(:read).returns("module Bling; def bling; end; end")
41
+ Manager.reload('blah').should == true
42
+ library_has_module('blah', "Boson::Commands::Bling")
43
+ command_exists?('bling')
44
+ command_exists?('blah', false)
45
+ library('blah').commands.size.should == 1
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,117 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ module Boson
4
+ class IndexTest < Test::Unit::TestCase
5
+ # since we're defining our own @commands, @libraries, @lib_hashes
6
+ before(:all) { Index.instance_variable_set "@read", true }
7
+
8
+ context "read_and_transfer" do
9
+ before(:each) { reset_boson; Index.instance_eval "@libraries = @commands = nil" }
10
+
11
+ def transfers(options={})
12
+ Index.instance_variable_set "@libraries", [Library.new(:name=>'blah'), Library.new(:name=>'bling')]
13
+ Index.instance_variable_set "@commands", [Command.new(:name=>'blurb', :lib=>'blah')]
14
+ Index.read_and_transfer options[:ignored] || []
15
+ Boson.libraries.map {|e| e.name}.should == options[:libraries]
16
+ Boson.commands.map {|e| e.name}.should == options[:commands]
17
+ end
18
+
19
+ test "transfers libraries with no libraries to ignore" do
20
+ transfers :libraries=>%w{blah bling}, :commands=>%w{blurb}, :ignored=>[]
21
+ end
22
+
23
+ test "transfers libraries and commands except for ignored libraries and its commands" do
24
+ transfers :libraries=>%w{bling}, :commands=>[], :ignored=>%w{blah}
25
+ end
26
+
27
+ test "doesn't replace existing libraries" do
28
+ lib = Library.new(:name=>'blah')
29
+ cmd = Command.new(:name=>'blurb', :lib=>'blah')
30
+ Boson.libraries << lib
31
+ Boson.commands << cmd
32
+ transfers :libraries=>%w{blah bling}, :commands=>%w{blurb}
33
+ Boson.libraries.include?(lib).should == true
34
+ Boson.commands.include?(cmd).should == true
35
+ end
36
+ end
37
+
38
+ context "find_library" do
39
+ before(:all) {
40
+ reset_boson
41
+ commands = [Command.new(:name=>'blurb', :lib=>'blah', :alias=>'bb'),
42
+ Command.new(:name=>'sub', :lib=>'bling', :alias=>'s')
43
+ ]
44
+ Index.instance_variable_set "@commands", commands
45
+ Index.instance_variable_set "@libraries", [Library.new(:name=>'blah'), Library.new(:name=>'bling', :namespace=>'bling')]
46
+ }
47
+
48
+ test "finds command aliased or not" do
49
+ Index.find_library('blurb').should == 'blah'
50
+ Index.find_library('bb').should == 'blah'
51
+ end
52
+
53
+ test "doesn't find command" do
54
+ Index.find_library('blah').should == nil
55
+ end
56
+
57
+ test "finds a subcommand aliased or not" do
58
+ Index.find_library('bling.sub').should == 'bling'
59
+ # Index.find_library('bl.s').should == 'bling'
60
+ end
61
+
62
+ test "finds namespace command aliased or not without a subcommand" do
63
+ Index.find_library('bling').should == 'bling'
64
+ # Index.find_library('bl').should == 'bling'
65
+ end
66
+
67
+ test "doesn't find a subcommand" do
68
+ Index.find_library('d.d').should == nil
69
+ end
70
+ end
71
+
72
+ context "changed_libraries" do
73
+ before(:all) { Index.instance_eval "@lib_hashes = nil" }
74
+
75
+ def changed(string, all_libs=['file1'])
76
+ Runner.expects(:all_libraries).returns(all_libs)
77
+ Index.instance_variable_set "@lib_hashes", {"file1"=>Digest::MD5.hexdigest("state1")}
78
+ File.stubs(:exists?).returns(true)
79
+ File.expects(:read).returns(string)
80
+ Index.changed_libraries
81
+ end
82
+
83
+ test "detects changed libraries" do
84
+ changed("state2").should == %w{file1}
85
+ end
86
+
87
+ test "detects new libraries" do
88
+ changed("state1", ['file2']).should == %w{file2}
89
+ end
90
+
91
+ test "detects no changed libraries" do
92
+ changed("state1").should == []
93
+ end
94
+ end
95
+
96
+ context "write" do
97
+ before(:all) {
98
+ reset_boson
99
+ Boson.commands << Command.new(:name=>'blah', :lib=>'blah', :args=>[['arg1', {}], ['arg2', self.class]])
100
+ Boson.libraries << Library.new(:name=>'blah', :module=>self.class)
101
+ Index.expects(:latest_hashes)
102
+ libraries = commands = []
103
+ Index.expects(:save_marshal_index).with {|str| libraries, commands, hashes = Marshal.load(str) ; true}
104
+ Index.write
105
+ @index = {:libraries=>libraries, :commands=>commands}
106
+ }
107
+
108
+ test "saves library module constants as strings" do
109
+ @index[:libraries][0].module.class.should == String
110
+ end
111
+
112
+ test "save commands with arg values as strings" do
113
+ @index[:commands][0].args.each {|e| e[1].class.should == String}
114
+ end
115
+ end
116
+ end
117
+ end