boson 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/LICENSE.txt +22 -0
  2. data/README.rdoc +133 -0
  3. data/Rakefile +52 -0
  4. data/VERSION.yml +4 -0
  5. data/bin/boson +6 -0
  6. data/lib/boson.rb +72 -0
  7. data/lib/boson/command.rb +117 -0
  8. data/lib/boson/commands.rb +7 -0
  9. data/lib/boson/commands/core.rb +66 -0
  10. data/lib/boson/commands/web_core.rb +36 -0
  11. data/lib/boson/index.rb +95 -0
  12. data/lib/boson/inspector.rb +80 -0
  13. data/lib/boson/inspectors/argument_inspector.rb +92 -0
  14. data/lib/boson/inspectors/comment_inspector.rb +79 -0
  15. data/lib/boson/inspectors/method_inspector.rb +94 -0
  16. data/lib/boson/libraries/file_library.rb +76 -0
  17. data/lib/boson/libraries/gem_library.rb +21 -0
  18. data/lib/boson/libraries/module_library.rb +17 -0
  19. data/lib/boson/libraries/require_library.rb +11 -0
  20. data/lib/boson/library.rb +108 -0
  21. data/lib/boson/loader.rb +103 -0
  22. data/lib/boson/manager.rb +184 -0
  23. data/lib/boson/namespace.rb +45 -0
  24. data/lib/boson/option_parser.rb +318 -0
  25. data/lib/boson/repo.rb +38 -0
  26. data/lib/boson/runner.rb +51 -0
  27. data/lib/boson/runners/bin_runner.rb +100 -0
  28. data/lib/boson/runners/repl_runner.rb +40 -0
  29. data/lib/boson/scientist.rb +168 -0
  30. data/lib/boson/util.rb +93 -0
  31. data/lib/boson/view.rb +31 -0
  32. data/test/argument_inspector_test.rb +62 -0
  33. data/test/bin_runner_test.rb +136 -0
  34. data/test/commands_test.rb +51 -0
  35. data/test/comment_inspector_test.rb +99 -0
  36. data/test/config/index.marshal +0 -0
  37. data/test/file_library_test.rb +50 -0
  38. data/test/index_test.rb +117 -0
  39. data/test/loader_test.rb +181 -0
  40. data/test/manager_test.rb +110 -0
  41. data/test/method_inspector_test.rb +64 -0
  42. data/test/option_parser_test.rb +365 -0
  43. data/test/repo_test.rb +22 -0
  44. data/test/runner_test.rb +43 -0
  45. data/test/scientist_test.rb +291 -0
  46. data/test/test_helper.rb +119 -0
  47. metadata +133 -0
@@ -0,0 +1,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