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
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