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
data/test/repo_test.rb ADDED
@@ -0,0 +1,22 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class Boson::RepoTest < Test::Unit::TestCase
4
+ context "config" do
5
+ before(:all) { reset }
6
+ before(:each) { @repo = Boson::Repo.new(File.dirname(__FILE__)) }
7
+
8
+ test "reloads config when passed true" do
9
+ @repo.config.object_id.should_not == @repo.config(true).object_id
10
+ end
11
+
12
+ test "reads existing config correctly" do
13
+ expected_hash = {:commands=>{'c1'=>{}}, :libraries=>{}}
14
+ YAML.expects(:load_file).returns(expected_hash)
15
+ @repo.config[:commands]['c1'].should == {}
16
+ end
17
+
18
+ test "ignores nonexistent file and sets config defaults" do
19
+ assert @repo.config[:commands].is_a?(Hash) && @repo.config[:libraries].is_a?(Hash)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,43 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ module Boson
4
+ class RunnerTest < Test::Unit::TestCase
5
+ context "repl_runner" do
6
+ def start(*args)
7
+ Hirb.stubs(:enable)
8
+ Boson.start(*args)
9
+ end
10
+
11
+ before(:all) { reset }
12
+ before(:each) { Boson::ReplRunner.instance_eval("@initialized = false") }
13
+
14
+ test "loads default libraries and libraries in :defaults config" do
15
+ defaults = Boson::Runner.default_libraries + ['yo']
16
+ with_config(:defaults=>['yo']) do
17
+ Manager.expects(:load).with {|*args| args[0] == defaults }
18
+ start
19
+ end
20
+ end
21
+
22
+ test "doesn't call init twice" do
23
+ start
24
+ ReplRunner.expects(:init).never
25
+ start
26
+ end
27
+
28
+ test "loads multiple libraries with :libraries option" do
29
+ ReplRunner.expects(:init)
30
+ Manager.expects(:load).with([:lib1,:lib2], anything)
31
+ start(:libraries=>[:lib1, :lib2])
32
+ end
33
+
34
+ test "autoloader autoloads libraries" do
35
+ start(:autoload_libraries=>true)
36
+ Index.expects(:read)
37
+ Index.expects(:find_library).with('blah').returns('blah')
38
+ Manager.expects(:load).with('blah', :verbose=>true)
39
+ Boson.main_object.blah
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,291 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ module Boson
4
+ class ScientistTest < Test::Unit::TestCase
5
+ before(:all) {
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
+ end
19
+ EOF
20
+ @opt_cmd = Object.new.extend Blah
21
+ }
22
+
23
+ def command(hash, args)
24
+ hash = {:name=>'blah', :lib=>'bling', :options=>{:force=>:boolean, :level=>2}}.merge(hash)
25
+ @cmd = Command.new hash
26
+ @cmd.instance_variable_set("@file_parsed_args", true) if hash[:file_parsed_args]
27
+ Scientist.create_option_command(@opt_cmd, @cmd)
28
+ @opt_cmd.send(hash[:name], *args)
29
+ end
30
+
31
+ def command_with_arg_size(*args)
32
+ command({:args=>2}, args)
33
+ end
34
+
35
+ def command_with_args(*args)
36
+ command({:args=>[['arg1'],['options', {}]]}, args)
37
+ end
38
+
39
+ def basic_command(hash, args)
40
+ command({:name=>'splat_blah', :args=>'*'}.merge(hash), args)
41
+ end
42
+
43
+ def command_with_splat_args(*args)
44
+ command({:name=>'splat_blah', :args=>'*'}, args)
45
+ end
46
+
47
+ def command_with_arg_defaults(*args)
48
+ arg_defaults = [%w{arg1}, %w{arg2 default}, %w{options {}}]
49
+ command({:name=>'default_blah', :file_parsed_args=>true, :args=>arg_defaults}, args)
50
+ end
51
+
52
+ def args_are_equal(args, array)
53
+ command_with_args(*args).should == array
54
+ command_with_arg_size(*args).should == array
55
+ command_with_splat_args(*args).should == array
56
+ end
57
+
58
+ ALL_COMMANDS = [:command_with_args, :command_with_arg_size, :command_with_splat_args]
59
+
60
+ context "all commands" do
61
+ test "translate arg and options as one string" do
62
+ args_are_equal ['a1 -f'], ['a1', {:force=>true, :level=>2}]
63
+ end
64
+
65
+ test "translate arg and stringified options" do
66
+ args_are_equal [:cool, '-l3'], [:cool, {:level=>3}]
67
+ end
68
+
69
+ test "translate arg and normal hash options" do
70
+ args_are_equal [:cool, {:ok=>true}], [:cool, {:ok=>true}]
71
+ end
72
+
73
+ test "translate stringified arg without options sets default options" do
74
+ args_are_equal ['cool'], ['cool', {:level=>2}]
75
+ end
76
+
77
+ test "translate arg without options sets default options" do
78
+ args_are_equal [:cool], [:cool, {:level=>2}]
79
+ end
80
+
81
+ test "with invalid options print errors and delete them" do
82
+ ALL_COMMANDS.each do |cmd|
83
+ capture_stderr {
84
+ send(cmd, 'cool -f -z').should == ['cool', {:force=>true, :level=>2}]
85
+ }.should =~/invalid.*z/
86
+ end
87
+ end
88
+
89
+ test "print help with help option" do
90
+ ALL_COMMANDS.each do |cmd|
91
+ Boson.expects(:invoke).with(:usage, anything, anything)
92
+ send(cmd, '-h')
93
+ end
94
+ end
95
+ end
96
+
97
+ context "command" do
98
+ context "with arg defaults" do
99
+ test "sets defaults with stringified args" do
100
+ command_with_arg_defaults('1').should == ["1", "some default", {:level=>2}]
101
+ end
102
+
103
+ test "sets defaults with normal args" do
104
+ command_with_arg_defaults(1).should == [1, "some default", {:level=>2}]
105
+ end
106
+
107
+ test "sets default if optional arg is a valid option" do
108
+ command_with_arg_defaults("cool -f").should == ["cool", "some default", {:level=>2, :force=>true}]
109
+ end
110
+
111
+ test "doesn't set defaults if not needed" do
112
+ command_with_arg_defaults(1, 'nada').should == [1, "nada", {:level=>2}]
113
+ end
114
+
115
+ test "prints error for invalid defaults" do
116
+ arg_defaults = [%w{arg1}, %w{arg2 invalidzzz}, %w{options {}}]
117
+ capture_stderr {
118
+ command({:name=>'default_blah', :file_parsed_args=>true, :args=>arg_defaults}, [1])
119
+ }.should =~ /Error.*position 2/
120
+ end
121
+ end
122
+
123
+ context "prints error" do
124
+ test "with option error" do
125
+ capture_stderr { command_with_args('a1 -l') }.should =~ /Error.*level/
126
+ end
127
+
128
+ test "with unexpected error in translation" do
129
+ Scientist.expects(:command_options).raises("unexpected")
130
+ capture_stderr { command_with_args('a1') }.should =~ /Error.*unexpected/
131
+ end
132
+
133
+ test "with unexpected error in render" do
134
+ Scientist.expects(:render?).raises("unexpected")
135
+ capture_stderr { command_with_args('a1') }.should =~ /Error.*unexpected/
136
+ end
137
+
138
+ test "with no argument defined for options" do
139
+ capture_stderr { command({:args=>1}, 'ok') }.should =~ /misaligned/
140
+ end
141
+ end
142
+
143
+ test "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
+ test "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
+ test "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
+ assert_nothing_raised { command_with_splat_args *args }
155
+ end
156
+ end
157
+
158
+ test "with debug option prints debug" do
159
+ capture_stdout { command_with_args("-v ok") } =~ /Arguments.*ok/
160
+ end
161
+
162
+ test "with pretend option prints arguments and returns early" do
163
+ Scientist.expects(:render_or_raw).never
164
+ capture_stdout { command_with_args("-p ok") } =~ /Arguments.*ok/
165
+ end
166
+
167
+ test "with not enough args raises ArgumentError" do
168
+ args = [ArgumentError, '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
+ test "with too many args raises ArgumentError" do
176
+ args = [ArgumentError, '3 for 2']
177
+ assert_error(*args) { command_with_args 1,2,3 }
178
+ assert_error(*args) { command_with_args '1 2 3' }
179
+ assert_error(*args) { command_with_arg_size 1,2,3 }
180
+ assert_error(*args) { command_with_arg_size '1 2 3' }
181
+ end
182
+ end
183
+
184
+ def command_with_render(*args)
185
+ basic_command({:render_options=>{:fields=>{:values=>['f1', 'f2']}} }, args)
186
+ end
187
+
188
+ def render_expected(options=nil)
189
+ View.expects(:render).with(anything, options || anything)
190
+ end
191
+
192
+ context "render" do
193
+ test "called for command with render_options" do
194
+ render_expected
195
+ command_with_render('1')
196
+ end
197
+
198
+ test "called for command without render_options and --render" do
199
+ render_expected
200
+ command_with_args('--render 1')
201
+ end
202
+
203
+ test "not called for command with render_options and --render" do
204
+ Boson.expects(:invoke).never
205
+ command_with_render('--render 1')
206
+ end
207
+
208
+ test "not called for command without render_options" do
209
+ Boson.expects(:invoke).never
210
+ command_with_args('1')
211
+ end
212
+ end
213
+
214
+ context "command renders" do
215
+ test "with basic render options" do
216
+ render_expected :fields => ['f1', 'f2']
217
+ command_with_render("--fields f1,f2 ab")
218
+ end
219
+
220
+ test "without non-render options" do
221
+ render_expected :fields=>['f1']
222
+ Scientist.expects(:render?).returns(true)
223
+ args = ["--render --fields f1 ab"]
224
+ basic_command({:render_options=>{:fields=>{:values=>['f1', 'f2']}} }, args)
225
+ end
226
+
227
+ test "with user-defined render options" do
228
+ render_expected :fields=>['f1'], :foo=>true
229
+ args = ["--foo --fields f1 ab"]
230
+ basic_command({:render_options=>{:foo=>:boolean, :fields=>{:values=>['f1', 'f2']}} }, args)
231
+ end
232
+
233
+ test "with non-hash user-defined render options" do
234
+ render_expected :fields=>['f1'], :foo=>true
235
+ args = ["--foo --fields f1 ab"]
236
+ basic_command({:render_options=>{:foo=>:boolean, :fields=>%w{f1 f2 f3}} }, args)
237
+ end
238
+ end
239
+
240
+ test "optionless command renders" do
241
+ render_expected :fields=>['f1']
242
+ command({:args=>2, :options=>nil, :render_options=>{:fields=>:array}}, ["--fields f1 ab ok"])
243
+ end
244
+
245
+ context "global options:" do
246
+ def local_and_global(*args)
247
+ Scientist.stubs(:render?).returns(false) # turn off rendering caused by :render_options
248
+ @non_opts = basic_command(@command_options, args)
249
+ @non_opts.slice!(-1,1) << Scientist.global_options
250
+ end
251
+
252
+ before(:all) {
253
+ @command_options = {:options=>{:do=>:boolean, :foo=>:boolean},
254
+ :render_options=>{:dude=>:boolean}}
255
+ @expected_non_opts = [[], ['doh'], ['doh'], [:doh]]
256
+ }
257
+
258
+ test "local option overrides global one" do
259
+ ['-d', 'doh -d','-d doh', [:doh, '-d']].each_with_index do |args, i|
260
+ local_and_global(*args).should == [{:do=>true}, {}]
261
+ @non_opts.should == @expected_non_opts[i]
262
+ end
263
+ end
264
+
265
+ test "global option before local one is valid" do
266
+ args_arr = ['--dude -f', '--dude doh -f', '--dude -f doh', [:doh, '--dude -f']]
267
+ args_arr.each_with_index do |args, i|
268
+ local_and_global(*args).should == [{:foo=>true}, {:dude=>true}]
269
+ @non_opts.should == @expected_non_opts[i]
270
+ end
271
+ end
272
+
273
+ test "global option after local one is invalid" do
274
+ args_arr = ['-f --dude', '-f doh --dude', '-f --dude doh', [:doh, '-f --dude'] ]
275
+ args_arr.each_with_index do |args, i|
276
+ capture_stderr {
277
+ local_and_global(*args).should == [{:foo=>true}, {}]
278
+ @non_opts.should == @expected_non_opts[i]
279
+ }.should =~ /invalid.*dude/
280
+ end
281
+ end
282
+
283
+ test "--global option adds additional global options" do
284
+ local_and_global('-g=d -d').should == [{:do=>true}, {:dude=>true, :global=>'d'}]
285
+ local_and_global('-g "r dude" -d').should == [{:do=>true},
286
+ {:global=>"r dude", :dude=>true, :render=>true}]
287
+ end
288
+ end
289
+
290
+ end
291
+ end
@@ -0,0 +1,119 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'context' #gem install jeremymcanally-context --source http://gems.github.com
4
+ require 'matchy' #gem install jeremymcanally-matchy --source http://gems.github.com
5
+ require 'mocha'
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ require 'boson'
8
+ require 'test_benchmark' if ENV['BENCHMARK']
9
+
10
+ class Test::Unit::TestCase
11
+ # make local so it doesn't pick up my real boson dir
12
+ Boson.repo.dir = File.dirname(__FILE__)
13
+ Boson.instance_variable_set "@repos", [Boson.repo]
14
+
15
+ def assert_error(error, message=nil)
16
+ yield
17
+ rescue error=>e
18
+ e.class.should == error
19
+ e.message.should =~ Regexp.new(message) if message
20
+ else
21
+ nil.should == error
22
+ end
23
+
24
+ def reset
25
+ reset_main_object
26
+ reset_boson
27
+ end
28
+
29
+ def reset_main_object
30
+ Boson.send :remove_const, "Universe"
31
+ eval "module ::Boson::Universe; include ::Boson::Commands::Namespace; end"
32
+ Boson::Commands.send :remove_const, "Blah" if Boson::Commands.const_defined?("Blah")
33
+ Boson.main_object = Object.new
34
+ end
35
+
36
+ def reset_boson
37
+ reset_libraries
38
+ Boson.instance_eval("@commands = nil")
39
+ end
40
+
41
+ def reset_libraries
42
+ Boson.instance_eval("@libraries = nil")
43
+ end
44
+
45
+ def command_exists?(name, bool=true)
46
+ !!Boson::Command.find(name).should == bool
47
+ end
48
+
49
+ def library_loaded?(name, bool=true)
50
+ Boson::Manager.loaded?(name).should == bool
51
+ end
52
+
53
+ def library(name)
54
+ Boson.library(name)
55
+ end
56
+
57
+ def library_has_module(lib, lib_module)
58
+ Boson::Manager.loaded?(lib).should == true
59
+ test_lib = library(lib)
60
+ (test_lib.module.is_a?(Module) && (test_lib.module.to_s == lib_module)).should == true
61
+ end
62
+
63
+ def library_has_command(lib, command, bool=true)
64
+ (lib = library(lib)) && lib.commands.include?(command).should == bool
65
+ end
66
+
67
+ # mocks as a file library
68
+ def mock_library(lib, options={})
69
+ options[:file_string] ||= ''
70
+ File.expects(:exists?).with(Boson::FileLibrary.library_file(lib.to_s, Boson.repo.dir)).returns(true)
71
+ File.expects(:read).returns(options.delete(:file_string))
72
+ end
73
+
74
+ def load(lib, options={})
75
+ # prevent conflicts with existing File.read stubs
76
+ Boson::MethodInspector.stubs(:inspector_in_file?).returns(false)
77
+ mock_library(lib, options) unless options.delete(:no_mock)
78
+ result = Boson::Manager.load([lib], options)
79
+ Boson::FileLibrary.reset_file_cache
80
+ result
81
+ end
82
+
83
+ def capture_stdout(&block)
84
+ original_stdout = $stdout
85
+ $stdout = fake = StringIO.new
86
+ begin
87
+ yield
88
+ ensure
89
+ $stdout = original_stdout
90
+ end
91
+ fake.string
92
+ end
93
+
94
+ def with_config(options)
95
+ old_config = Boson.repo.config
96
+ Boson.repo.config = Boson.repo.config.merge(options)
97
+ yield
98
+ Boson.repo.config = old_config
99
+ end
100
+
101
+ def capture_stderr(&block)
102
+ original_stderr = $stderr
103
+ $stderr = fake = StringIO.new
104
+ begin
105
+ yield
106
+ ensure
107
+ $stderr = original_stderr
108
+ end
109
+ fake.string
110
+ end
111
+
112
+ def create_library(libraries, attributes={})
113
+ libraries = [libraries] unless libraries.is_a?(Array)
114
+ libraries.map {|e|
115
+ lib = Boson::Library.new({:name=>e}.update(attributes))
116
+ Boson::Manager.add_library(lib); lib
117
+ }
118
+ end
119
+ end