boson 0.4.0 → 1.0.0

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 (64) hide show
  1. data/.gemspec +6 -7
  2. data/.rspec +2 -0
  3. data/.travis.yml +7 -0
  4. data/CHANGELOG.rdoc +1 -1
  5. data/README.md +144 -0
  6. data/README.rdoc +2 -2
  7. data/Upgrading.md +23 -0
  8. data/bin/boson +2 -2
  9. data/lib/boson.rb +44 -52
  10. data/lib/boson/bare_runner.rb +83 -0
  11. data/lib/boson/bin_runner.rb +114 -0
  12. data/lib/boson/command.rb +92 -132
  13. data/lib/boson/inspector.rb +49 -48
  14. data/lib/boson/library.rb +71 -120
  15. data/lib/boson/loader.rb +73 -84
  16. data/lib/boson/manager.rb +131 -135
  17. data/lib/boson/method_inspector.rb +112 -0
  18. data/lib/boson/option_command.rb +71 -154
  19. data/lib/boson/option_parser.rb +178 -173
  20. data/lib/boson/options.rb +46 -32
  21. data/lib/boson/runner.rb +58 -66
  22. data/lib/boson/runner_library.rb +31 -0
  23. data/lib/boson/scientist.rb +48 -81
  24. data/lib/boson/util.rb +46 -61
  25. data/lib/boson/version.rb +1 -1
  26. data/test/bin_runner_test.rb +53 -191
  27. data/test/command_test.rb +5 -9
  28. data/test/deps.rip +2 -2
  29. data/test/loader_test.rb +18 -216
  30. data/test/manager_test.rb +69 -79
  31. data/test/method_inspector_test.rb +12 -36
  32. data/test/option_parser_test.rb +45 -32
  33. data/test/runner_library_test.rb +10 -0
  34. data/test/runner_test.rb +158 -28
  35. data/test/scientist_test.rb +9 -147
  36. data/test/test_helper.rb +87 -52
  37. metadata +30 -72
  38. data/deps.rip +0 -2
  39. data/lib/boson/commands.rb +0 -7
  40. data/lib/boson/commands/core.rb +0 -77
  41. data/lib/boson/commands/web_core.rb +0 -153
  42. data/lib/boson/index.rb +0 -48
  43. data/lib/boson/inspectors/argument_inspector.rb +0 -97
  44. data/lib/boson/inspectors/comment_inspector.rb +0 -100
  45. data/lib/boson/inspectors/method_inspector.rb +0 -98
  46. data/lib/boson/libraries/file_library.rb +0 -144
  47. data/lib/boson/libraries/gem_library.rb +0 -30
  48. data/lib/boson/libraries/local_file_library.rb +0 -30
  49. data/lib/boson/libraries/module_library.rb +0 -37
  50. data/lib/boson/libraries/require_library.rb +0 -23
  51. data/lib/boson/namespace.rb +0 -31
  52. data/lib/boson/pipe.rb +0 -147
  53. data/lib/boson/pipes.rb +0 -75
  54. data/lib/boson/repo.rb +0 -107
  55. data/lib/boson/runners/bin_runner.rb +0 -208
  56. data/lib/boson/runners/console_runner.rb +0 -58
  57. data/lib/boson/view.rb +0 -95
  58. data/test/argument_inspector_test.rb +0 -62
  59. data/test/commands_test.rb +0 -22
  60. data/test/comment_inspector_test.rb +0 -126
  61. data/test/file_library_test.rb +0 -42
  62. data/test/pipes_test.rb +0 -65
  63. data/test/repo_index_test.rb +0 -122
  64. data/test/repo_test.rb +0 -23
data/lib/boson/util.rb CHANGED
@@ -2,7 +2,7 @@ module Boson
2
2
  # Collection of utility methods used throughout Boson.
3
3
  module Util
4
4
  extend self
5
- # From Rails ActiveSupport, converts a camelcased string to an underscored string:
5
+ # From ActiveSupport, converts a camelcased string to an underscored string:
6
6
  # 'Boson::MethodInspector' -> 'boson/method_inspector'
7
7
  def underscore(camel_cased_word)
8
8
  camel_cased_word.to_s.gsub(/::/, '/').
@@ -12,10 +12,11 @@ module Boson
12
12
  downcase
13
13
  end
14
14
 
15
- # From Rails ActiveSupport, does the reverse of underscore:
15
+ # From ActiveSupport, does the reverse of underscore:
16
16
  # 'boson/method_inspector' -> 'Boson::MethodInspector'
17
17
  def camelize(string)
18
- Hirb::Util.camelize(string)
18
+ string.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.
19
+ gsub(/(?:^|_)(.)/) { $1.upcase }
19
20
  end
20
21
 
21
22
  # Converts a module/class string to the actual constant.
@@ -24,41 +25,43 @@ module Boson
24
25
  any_const_get(camelize(string))
25
26
  end
26
27
 
27
- # Returns a constant like const_get() no matter what namespace it's nested in.
28
- # Returns nil if the constant is not found.
28
+ # Returns a constant like const_get() no matter what namespace it's nested
29
+ # in. Returns nil if the constant is not found.
29
30
  def any_const_get(name)
30
- Hirb::Util.any_const_get(name)
31
+ return name if name.is_a?(Module)
32
+ klass = Object
33
+ name.split('::').each {|e|
34
+ klass = klass.const_get(e)
35
+ }
36
+ klass
37
+ rescue
38
+ nil
31
39
  end
32
40
 
33
- # Detects new object/kernel methods, gems and modules created within a block.
34
- # Returns a hash of what's detected.
35
- # Valid options and possible returned keys are :methods, :object_methods, :modules, :gems.
41
+ # Detects new object/kernel methods, gems and modules created within a
42
+ # block. Returns a hash of what's detected. Valid options and possible
43
+ # returned keys are :methods, :object_methods, :modules, :gems.
36
44
  def detect(options={}, &block)
37
- options = {:methods=>true, :object_methods=>true}.merge!(options)
38
- original_gems = Object.const_defined?(:Gem) ? Gem.loaded_specs.keys : []
45
+ options = {methods: true}.merge!(options)
46
+ original_gems = defined?(Gem) ? Gem.loaded_specs.keys : []
39
47
  original_object_methods = Object.instance_methods
40
- original_instance_methods = class << Boson.main_object; instance_methods end
48
+ original_instance_methods = Boson.main_object.singleton_class.instance_methods
41
49
  original_modules = modules if options[:modules]
50
+
42
51
  block.call
52
+
43
53
  detected = {}
44
- detected[:methods] = options[:methods] ? (class << Boson.main_object; instance_methods end -
45
- original_instance_methods) : []
46
- detected[:methods] -= (Object.instance_methods - original_object_methods) unless options[:object_methods]
47
- detected[:gems] = Gem.loaded_specs.keys - original_gems if Object.const_defined? :Gem
54
+ detected[:methods] = options[:methods] ?
55
+ (Boson.main_object.singleton_class.instance_methods -
56
+ original_instance_methods) : []
57
+ unless options[:object_methods]
58
+ detected[:methods] -= (Object.instance_methods - original_object_methods)
59
+ end
60
+ detected[:gems] = Gem.loaded_specs.keys - original_gems if defined? Gem
48
61
  detected[:modules] = modules - original_modules if options[:modules]
49
62
  detected
50
63
  end
51
64
 
52
- # Safely calls require, returning false if LoadError occurs.
53
- def safe_require(lib)
54
- begin
55
- require lib
56
- true
57
- rescue LoadError
58
- false
59
- end
60
- end
61
-
62
65
  # Returns all modules that currently exist.
63
66
  def modules
64
67
  all_modules = []
@@ -66,51 +69,23 @@ module Boson
66
69
  all_modules
67
70
  end
68
71
 
69
- # Creates a module under a given base module and possible name. If the module already exists or conflicts
70
- # per top_level_class_conflict, it attempts to create one with a number appended to the name.
72
+ # Creates a module under a given base module and possible name. If the
73
+ # module already exists, it attempts to create one with a number appended to
74
+ # the name.
71
75
  def create_module(base_module, name)
72
76
  desired_class = camelize(name)
73
77
  possible_suffixes = [''] + %w{1 2 3 4 5 6 7 8 9 10}
74
- if (suffix = possible_suffixes.find {|e| !base_module.const_defined?(desired_class+e) &&
75
- !top_level_class_conflict(base_module, "#{base_module}::#{desired_class}#{e}") })
76
- base_module.const_set(desired_class+suffix, Module.new)
78
+ if suffix = possible_suffixes.find {|e|
79
+ !base_module.const_defined?(desired_class+e) }
80
+ base_module.const_set(desired_class+suffix, Module.new)
77
81
  end
78
82
  end
79
83
 
80
- # Behaves just like the unix which command, returning the full path to an executable based on ENV['PATH'].
81
- def which(command)
82
- ENV['PATH'].split(File::PATH_SEPARATOR).map {|e| File.join(e, command) }.find {|e| File.exists?(e) }
83
- end
84
-
85
- # Deep copies any object if it can be marshaled. Useful for deep hashes.
86
- def deep_copy(obj)
87
- Marshal::load(Marshal::dump(obj))
88
- end
89
-
90
84
  # Recursively merge hash1 with hash2.
91
85
  def recursive_hash_merge(hash1, hash2)
92
86
  hash1.merge(hash2) {|k,o,n| (o.is_a?(Hash)) ? recursive_hash_merge(o,n) : n}
93
87
  end
94
88
 
95
- # From Rubygems, determine a user's home.
96
- def find_home
97
- Hirb::Util.find_home
98
- end
99
-
100
- # Returns name of top level class that conflicts if it exists. For example, for base module Boson::Commands,
101
- # Boson::Commands::Alias conflicts with Alias if Alias exists.
102
- def top_level_class_conflict(base_module, conflicting_module)
103
- (conflicting_module =~ /^#{base_module}.*::([^:]+)/) && Object.const_defined?($1) && $1
104
- end
105
-
106
- # Splits array into array of arrays with given element
107
- def split_array_by(arr, divider)
108
- arr.inject([[]]) {|results, element|
109
- (divider == element) ? (results << []) : (results.last << element)
110
- results
111
- }
112
- end
113
-
114
89
  # Regular expression search of a list with underscore anchoring of words.
115
90
  # For example 'some_dang_long_word' can be specified as 's_d_l_w'.
116
91
  def underscore_search(input, list, first_match=false)
@@ -118,12 +93,22 @@ module Boson
118
93
  return (first_match ? input : [input]) if list.include?(input)
119
94
  input = input.to_s
120
95
  if input.include?("_")
121
- underscore_regex = input.split('_').map {|e| Regexp.escape(e) }.join("([^_]+)?_")
96
+ underscore_regex = input.split('_').map {|e|
97
+ Regexp.escape(e) }.join("([^_]+)?_")
122
98
  list.send(meth) {|e| e.to_s =~ /^#{underscore_regex}/ }
123
99
  else
124
100
  escaped_input = Regexp.escape(input)
125
101
  list.send(meth) {|e| e.to_s =~ /^#{escaped_input}/ }
126
102
  end
127
103
  end
104
+
105
+ def format_table(arr_of_arr)
106
+ name_max = arr_of_arr.map {|arr| arr[0].length }.max
107
+ desc_max = arr_of_arr.map {|arr| arr[1].length }.max
108
+
109
+ arr_of_arr.map do |name, desc|
110
+ (" %-*s %-*s" % [name_max, name, desc_max, desc]).rstrip
111
+ end
112
+ end
128
113
  end
129
114
  end
data/lib/boson/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Boson
2
- VERSION = '0.4.0'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -1,223 +1,85 @@
1
1
  require File.join(File.dirname(__FILE__), 'test_helper')
2
- require 'boson/runners/bin_runner'
2
+ require 'boson/bin_runner'
3
3
  BinRunner = Boson::BinRunner
4
4
 
5
5
  describe "BinRunner" do
6
- def start(*args)
7
- Hirb.stubs(:enable)
8
- BinRunner.start(args)
6
+ def aborts_with(regex)
7
+ BinRunner.expects(:abort).with {|e| e[regex] }
8
+ yield
9
9
  end
10
10
 
11
- before {|e|
12
- BinRunner.instance_variables.each {|e| BinRunner.instance_variable_set(e, nil)}
13
- }
14
- describe "at commandline" do
15
- before_all { reset }
11
+ unless ENV['FAST'] ||
12
+ # disable rubinius until Open3.spawn defined in Bahia
13
+ # disable jruby until Open3.spawn works with ENV in Bahia
14
+ RUBY_DESCRIPTION.include?('rubinius') || RUBY_PLATFORM[/java/]
16
15
 
17
- it "no arguments prints usage" do
18
- capture_stdout { start }.should =~ /^boson/
16
+ it "prints usage with no arguments" do
17
+ boson
18
+ stdout.should =~ /^boson/
19
19
  end
20
20
 
21
- it "invalid option value prints error" do
22
- aborts_with(/Error: no value/) { start("-l") }
23
- end
24
-
25
- it "help option but no arguments prints usage" do
26
- capture_stdout { start '-h' }.should =~ /^boson/
27
- end
28
-
29
- it "help option and command prints help" do
30
- capture_stdout { start('-h', 'commands') }.should =~ /^commands/
31
- end
32
-
33
- it "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
- it "console option starts irb" do
40
- ConsoleRunner.expects(:start)
41
- Util.expects(:which).returns("/usr/bin/irb")
42
- ConsoleRunner.expects(:load_repl).with("/usr/bin/irb")
43
- start("--console")
44
- end
45
-
46
- it "console option but no irb found prints error" do
47
- ConsoleRunner.expects(:start)
48
- Util.expects(:which).returns(nil)
49
- ConsoleRunner.expects(:abort).with {|arg| arg[/Console not found/] }
50
- start '--console'
51
- end
52
-
53
- it "execute option executes string" do
54
- BinRunner.expects(:define_autoloader)
55
- capture_stdout { start("-e", "p 1 + 1") }.should == "2\n"
56
- end
57
-
58
- it "global option takes value with whitespace" do
59
- View.expects(:render).with {|*args| args[1][:fields] = %w{f1 f2} }
60
- start('commands', '-f', 'f1, f2')
61
- end
62
-
63
- it "debug option sets Runner.debug" do
64
- View.expects(:render)
65
- start('-d', 'commands')
66
- Runner.debug.should == true
67
- Runner.debug = nil
68
- end
69
-
70
- it "ruby_debug option sets $DEBUG" do
71
- View.expects(:render)
72
- start('-D', 'commands')
73
- $DEBUG.should == true
74
- $DEBUG = nil
75
- end
76
-
77
- it "execute option errors are caught" do
78
- aborts_with(/^Error:/) { start("-e", "raise 'blah'") }
79
- end
80
-
81
- it "option command and too many arguments prints error" do
82
- capture_stdout {
83
- aborts_with(/'commands'.*incorrect/) { start('commands','1','2','3') }
84
- }
85
- end
86
-
87
- it "normal command and too many arguments prints error" do
88
- capture_stdout {
89
- aborts_with(/'render'.*incorrect/) { start('render') }
90
- }
91
- end
92
-
93
- it "failed subcommand prints error and not command not found" do
94
- BinRunner.expects(:execute_command).raises("bling")
95
- aborts_with(/Error: bling/) { start("commands.to_s") }
96
- end
97
-
98
- it "nonexistant subcommand prints command not found" do
99
- aborts_with(/'to_s.bling' not found/) { start("to_s.bling") }
100
- end
101
-
102
- it "undiscovered command prints error" do
103
- BinRunner.expects(:autoload_command).returns(false)
104
- aborts_with(/Error.*not found/) { start 'blah' }
105
- end
106
-
107
- it "with backtrace option prints backtrace" do
108
- BinRunner.expects(:autoload_command).returns(false)
109
- aborts_with(/not found\nOriginal.*runner\.rb:/m) { start("--backtrace", "blah") }
110
- end
111
-
112
- it "basic command executes" do
113
- BinRunner.expects(:init).returns(true)
114
- BinRunner.stubs(:render_output)
115
- Boson.main_object.expects(:send).with('kick','it')
116
- start 'kick','it'
117
- end
118
-
119
- it "sub command executes" do
120
- obj = Object.new
121
- Boson.main_object.extend Module.new { def phone; Struct.new(:home).new('done'); end }
122
- BinRunner.expects(:init).returns(true)
123
- BinRunner.expects(:render_output).with('done')
124
- start 'phone.home'
125
- end
126
-
127
- it "bin_defaults config loads by default" do
128
- defaults = Runner.default_libraries + ['yo']
129
- with_config(:bin_defaults=>['yo']) do
130
- Manager.expects(:load).with {|*args| args[0] == defaults }
131
- aborts_with(/blah/) { start 'blah' }
21
+ it "prints usage with --help" do
22
+ %w{-h --help}.each do |option|
23
+ boson option
24
+ stdout.should =~ /^boson/
132
25
  end
133
26
  end
134
- end
135
-
136
- describe "autoload_command" do
137
- def index(options={})
138
- Manager.expects(:load).with {|*args| args[0][0].is_a?(Module) ? true : args[0] == options[:load]
139
- }.at_least(1).returns(!options[:fails])
140
- Index.indexes[0].expects(:write)
141
- end
142
-
143
- it "with index option, no existing index and core command updates index and prints index message" do
144
- index :load=>Runner.all_libraries
145
- Index.indexes[0].stubs(:exists?).returns(false)
146
- capture_stdout { start("--index", "libraries") }.should =~ /Generating index/
147
- end
148
27
 
149
- it "with index option, existing index and core command updates incremental index" do
150
- index :load=>['changed']
151
- Index.indexes[0].stubs(:exists?).returns(true)
152
- capture_stdout { start("--index=changed", "libraries")}.should =~ /Indexing.*changed/
28
+ it 'prints version with --version' do
29
+ boson '--version'
30
+ stdout.chomp.should == "boson #{Boson::VERSION}"
153
31
  end
154
32
 
155
- it "with index option, failed indexing prints error" do
156
- index :load=>['changed'], :fails=>true
157
- Index.indexes[0].stubs(:exists?).returns(true)
158
- Manager.stubs(:failed_libraries).returns(['changed'])
159
- capture_stderr {
160
- capture_stdout { start("--index=changed", "libraries")}.should =~ /Indexing.*changed/
161
- }.should =~ /Error:.*failed.*changed/
162
- end
163
-
164
- it "with core command updates index and doesn't print index message" do
165
- Index.indexes[0].expects(:write)
166
- Boson.main_object.expects(:send).with('libraries')
167
- capture_stdout { start 'libraries'}.should.not =~ /index/i
168
- end
169
-
170
- it "with non-core command not finding library, does update index" do
171
- Index.expects(:find_library).returns(nil, 'sweet_lib')
172
- Manager.expects(:load).with {|*args| args[0].is_a?(String) ? args[0] == 'sweet_lib' : true}.at_least(1)
173
- Index.indexes[0].expects(:update).returns(true)
174
- aborts_with(/sweet/) { start 'sweet' }
33
+ it "executes string with --execute" do
34
+ %w{--execute -e}.each do |option|
35
+ boson "#{option} 'print 1 + 1'"
36
+ stdout.should == '2'
37
+ end
175
38
  end
176
- end
177
39
 
178
- describe "render_output" do
179
- before { Scientist.rendered = false; BinRunner.instance_eval "@options = {}" }
180
-
181
- it "doesn't render when nil, false or true" do
182
- View.expects(:render).never
183
- [nil, false, true].each do |e|
184
- BinRunner.render_output e
40
+ it "sets $DEBUG with --ruby-debug" do
41
+ %w{--ruby_debug -D}.each do |option|
42
+ boson "#{option} -e 'print $DEBUG'"
43
+ stdout.should == 'true'
185
44
  end
186
45
  end
187
46
 
188
- it "doesn't render when rendered with Scientist" do
189
- Scientist.rendered = true
190
- View.expects(:render).never
191
- BinRunner.render_output 'blah'
47
+ # TODO: test actual uses of Runner.debug
48
+ it "sets Boson.debug with --debug" do
49
+ boson "--debug -e 'print Boson.debug'"
50
+ stdout.should == 'true'
192
51
  end
193
52
 
194
- it "render with puts when non-string" do
195
- View.expects(:render).with('dude', {:method => 'puts'})
196
- BinRunner.render_output 'dude'
53
+ it "prepends to $: with --load_path" do
54
+ %w{--load_path -I}.each do |option|
55
+ boson "#{option}=lib -e 'print $:[0]'"
56
+ stdout.should == 'lib'
57
+ end
197
58
  end
198
59
 
199
- it "renders with inspect when non-array and non-string" do
200
- [{:a=>true}, :ok].each do |e|
201
- View.expects(:puts).with(e.inspect)
202
- BinRunner.render_output e
203
- end
60
+ it "prints error for unexpected error" do
61
+ boson %[-e 'raise "blarg"']
62
+ stderr.chomp.should == "Error: blarg"
63
+ process.success?.should == false
204
64
  end
205
65
 
206
- it "renders with inspect when Scientist rendering toggled off with :render" do
207
- Scientist.global_options = {:render=>true}
208
- View.expects(:puts).with([1,2].inspect)
209
- BinRunner.render_output [1,2]
210
- Scientist.global_options = nil
66
+ # TODO: enable once bin runner's fate is decided
67
+ xit "prints error for too many arguments" do
68
+ boson "commands 1 2 3"
69
+ stderr.should =~ /^'commands' was called incorrectly/
70
+ process.success?.should == false
211
71
  end
212
72
 
213
- it "renders with hirb when array" do
214
- View.expects(:render_object)
215
- BinRunner.render_output [1,2,3]
73
+ # TODO: possible error with persistance extraction
74
+ xit "prints error for invalid command" do
75
+ boson 'blarg'
76
+ stderr.chomp.should == "Error: Command 'blarg' not found"
77
+ process.success?.should == false
216
78
  end
217
79
  end
218
80
 
219
- it "parse_args only translates options before command" do
220
- BinRunner.parse_args(['-v', 'com', '-v']).should == ["com", {:verbose=>true}, ['-v']]
221
- BinRunner.parse_args(['com', '-v']).should == ["com", {}, ['-v']]
81
+ it ".parse_args only translates options before command" do
82
+ BinRunner.send(:parse_args, ['-d', 'com', '-v']).should == ["com", {debug: true}, ['-v']]
83
+ BinRunner.send(:parse_args, ['com', '-v']).should == ["com", {}, ['-v']]
222
84
  end
223
85
  end