boson 0.1.0 → 0.2.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.
- data/README.rdoc +15 -3
- data/Rakefile +2 -2
- data/VERSION.yml +1 -1
- data/lib/boson.rb +22 -4
- data/lib/boson/command.rb +12 -4
- data/lib/boson/commands/core.rb +19 -19
- data/lib/boson/commands/web_core.rb +18 -5
- data/lib/boson/index.rb +31 -22
- data/lib/boson/inspector.rb +12 -10
- data/lib/boson/inspectors/method_inspector.rb +1 -1
- data/lib/boson/libraries/file_library.rb +31 -11
- data/lib/boson/libraries/gem_library.rb +6 -0
- data/lib/boson/libraries/module_library.rb +24 -4
- data/lib/boson/libraries/require_library.rb +8 -0
- data/lib/boson/library.rb +75 -35
- data/lib/boson/loader.rb +41 -12
- data/lib/boson/manager.rb +19 -14
- data/lib/boson/option_parser.rb +144 -107
- data/lib/boson/options.rb +127 -0
- data/lib/boson/repo.rb +25 -6
- data/lib/boson/runner.rb +2 -2
- data/lib/boson/runners/bin_runner.rb +25 -15
- data/lib/boson/scientist.rb +98 -28
- data/lib/boson/util.rb +20 -14
- data/lib/boson/view.rb +70 -15
- data/test/bin_runner_test.rb +27 -11
- data/test/file_library_test.rb +14 -1
- data/test/index_test.rb +2 -1
- data/test/loader_test.rb +84 -21
- data/test/manager_test.rb +1 -9
- data/test/method_inspector_test.rb +2 -2
- data/test/option_parser_test.rb +176 -20
- data/test/repo_test.rb +1 -0
- data/test/scientist_test.rb +38 -2
- data/test/test_helper.rb +6 -3
- data/test/view_test.rb +58 -0
- metadata +7 -6
- data/test/commands_test.rb +0 -51
data/lib/boson/util.rb
CHANGED
@@ -24,12 +24,6 @@ module Boson
|
|
24
24
|
any_const_get(camelize(string))
|
25
25
|
end
|
26
26
|
|
27
|
-
def symbolize_keys(hash)
|
28
|
-
hash.inject({}) {|options, (key, value)|
|
29
|
-
options[key.to_sym] = value; options
|
30
|
-
}
|
31
|
-
end
|
32
|
-
|
33
27
|
# Returns a constant like const_get() no matter what namespace it's nested in.
|
34
28
|
# Returns nil if the constant is not found.
|
35
29
|
def any_const_get(name)
|
@@ -80,16 +74,13 @@ module Boson
|
|
80
74
|
all_modules
|
81
75
|
end
|
82
76
|
|
83
|
-
#
|
84
|
-
|
85
|
-
(module1.instance_methods + module1.private_instance_methods) & (module2.instance_methods + module2.private_instance_methods)
|
86
|
-
end
|
87
|
-
|
88
|
-
# Creates a module under a given base module and possible name. If the module already exists, it attempts
|
89
|
-
# to create one with a number appended to the name.
|
77
|
+
# Creates a module under a given base module and possible name. If the module already exists or conflicts
|
78
|
+
# per top_level_class_conflict, it attempts to create one with a number appended to the name.
|
90
79
|
def create_module(base_module, name)
|
91
80
|
desired_class = camelize(name)
|
92
|
-
|
81
|
+
possible_suffixes = [''] + %w{1 2 3 4 5 6 7 8 9 10}
|
82
|
+
if (suffix = possible_suffixes.find {|e| !base_module.const_defined?(desired_class+e) &&
|
83
|
+
!top_level_class_conflict(base_module, "#{base_module}::#{desired_class}#{e}") })
|
93
84
|
base_module.const_set(desired_class+suffix, Module.new)
|
94
85
|
end
|
95
86
|
end
|
@@ -108,5 +99,20 @@ module Boson
|
|
108
99
|
def recursive_hash_merge(hash1, hash2)
|
109
100
|
hash1.merge(hash2) {|k,o,n| (o.is_a?(Hash)) ? recursive_hash_merge(o,n) : n}
|
110
101
|
end
|
102
|
+
|
103
|
+
# From Rubygems, determine a user's home.
|
104
|
+
def find_home
|
105
|
+
['HOME', 'USERPROFILE'].each {|e| return ENV[e] if ENV[e] }
|
106
|
+
return "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}" if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
|
107
|
+
File.expand_path("~")
|
108
|
+
rescue
|
109
|
+
File::ALT_SEPARATOR ? "C:/" : "/"
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns name of top level class that conflicts if it exists. For example, for base module Boson::Commands,
|
113
|
+
# Boson::Commands::Alias conflicts with Alias if Alias exists.
|
114
|
+
def top_level_class_conflict(base_module, conflicting_module)
|
115
|
+
(conflicting_module =~ /^#{base_module}.*::([^:]+)/) && Object.const_defined?($1) && $1
|
116
|
+
end
|
111
117
|
end
|
112
118
|
end
|
data/lib/boson/view.rb
CHANGED
@@ -11,33 +11,88 @@ module Boson
|
|
11
11
|
|
12
12
|
# Renders any object via Hirb. Options are passed directly to
|
13
13
|
# {Hirb::Console.render_output}[http://tagaholic.me/hirb/doc/classes/Hirb/Console.html#M000011].
|
14
|
-
def render(object, options={})
|
14
|
+
def render(object, options={}, return_obj=false)
|
15
15
|
if silent_object?(object)
|
16
16
|
puts(object.inspect) unless options[:silence_booleans]
|
17
17
|
else
|
18
|
-
render_object(object, options)
|
18
|
+
render_object(object, options, return_obj)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
# Searches and sorts an array of objects or hashes using options :query, :sort and :reverse_sort.
|
23
|
+
# The :query option is a hash of fields mapped to their search terms. Searches are OR-ed.
|
24
|
+
def search_and_sort(object, options)
|
25
|
+
if object.is_a?(Array)
|
26
|
+
object = search_object(object, options[:query]) if options[:query]
|
27
|
+
object = sort_object(object, options[:sort], options[:reverse_sort]) if object.size > 0 && options[:sort]
|
28
|
+
end
|
29
|
+
object
|
30
|
+
end
|
31
|
+
|
32
|
+
#:stopdoc:
|
33
|
+
def toggle_pager
|
34
|
+
Hirb::View.toggle_pager
|
35
|
+
end
|
36
|
+
|
22
37
|
def silent_object?(obj)
|
23
38
|
[nil,false,true].include?(obj)
|
24
39
|
end
|
25
40
|
|
26
|
-
def render_object(object, options={})
|
41
|
+
def render_object(object, options={}, return_obj=false)
|
27
42
|
options[:class] ||= :auto_table
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
43
|
+
render_result = Hirb::Console.render_output(object, options)
|
44
|
+
return_obj ? object : render_result
|
45
|
+
end
|
46
|
+
|
47
|
+
def search_object(object, query_hash)
|
48
|
+
if object[0].is_a?(Hash)
|
49
|
+
TableCallbacks.search_callback(object, :query=>query_hash)
|
50
|
+
else
|
51
|
+
query_hash.map {|field,query| object.select {|e| e.send(field).to_s =~ /#{query}/i } }.flatten.uniq
|
52
|
+
end
|
53
|
+
rescue NoMethodError
|
54
|
+
$stderr.puts "Query failed with nonexistant method '#{$!.message[/`(.*)'/,1]}'"
|
55
|
+
end
|
56
|
+
|
57
|
+
def sort_object(object, sort, reverse_sort=false)
|
58
|
+
if object[0].is_a?(Hash)
|
59
|
+
TableCallbacks.sort_callback(object, :sort=>sort, :reverse_sort=>reverse_sort)
|
60
|
+
else
|
61
|
+
sort_lambda = object.all? {|e| e.send(sort).respond_to?(:<=>) } ? lambda {|e| e.send(sort) || ''} :
|
62
|
+
lambda {|e| e.send(sort).to_s }
|
63
|
+
object = object.sort_by &sort_lambda
|
64
|
+
object = object.reverse if reverse_sort
|
65
|
+
object
|
66
|
+
end
|
67
|
+
rescue NoMethodError, ArgumentError
|
68
|
+
$stderr.puts "Sort failed with nonexistant method '#{sort}'"
|
69
|
+
end
|
70
|
+
#:startdoc:
|
71
|
+
|
72
|
+
# Callbacks used by Hirb::Helpers::Table to search and sort arrays of hashes.
|
73
|
+
module TableCallbacks
|
74
|
+
extend self
|
75
|
+
# Case-insensitive searches an array of hashes using the option :query. Numerical string keys
|
76
|
+
# in :query are converted to actual numbers to interface with Hirb. See View.search_and_sort for more
|
77
|
+
# about :query.
|
78
|
+
def search_callback(obj, options)
|
79
|
+
!options[:query] ? obj : begin
|
80
|
+
options[:query].map {|field,query|
|
81
|
+
field = field.to_i if field.to_s[/^\d+$/]
|
82
|
+
obj.select {|e| e[field].to_s =~ /#{query}/i }
|
83
|
+
}.flatten.uniq
|
38
84
|
end
|
39
85
|
end
|
40
|
-
|
86
|
+
|
87
|
+
# Sorts an array of hashes using :sort option and reverses the sort with :reverse_sort option.
|
88
|
+
def sort_callback(obj, options)
|
89
|
+
sort = options[:sort].to_s[/^\d+$/] ? options[:sort].to_i : options[:sort]
|
90
|
+
sort_lambda = (obj.all? {|e| e[sort].respond_to?(:<=>) } ? lambda {|e| e[sort] } : lambda {|e| e[sort].to_s })
|
91
|
+
obj = obj.sort_by &sort_lambda
|
92
|
+
obj = obj.reverse if options[:reverse_sort]
|
93
|
+
obj
|
94
|
+
end
|
41
95
|
end
|
42
96
|
end
|
43
|
-
end
|
97
|
+
end
|
98
|
+
Hirb::Helpers::Table.send :include, Boson::View::TableCallbacks
|
data/test/bin_runner_test.rb
CHANGED
@@ -55,8 +55,8 @@ module Boson
|
|
55
55
|
end
|
56
56
|
|
57
57
|
test "global option takes value with whitespace" do
|
58
|
-
View.expects(:render).with(anything, {:
|
59
|
-
start('commands', '-g', 'f=name,lib
|
58
|
+
View.expects(:render).with(anything, {:vertical=>true, :fields => [:name, :lib]}, anything)
|
59
|
+
start('commands', '-g', 'f=name,lib vertical')
|
60
60
|
end
|
61
61
|
|
62
62
|
test "execute option errors are caught" do
|
@@ -69,7 +69,11 @@ module Boson
|
|
69
69
|
|
70
70
|
test "failed subcommand prints error and not command not found" do
|
71
71
|
BinRunner.expects(:execute_command).raises("bling")
|
72
|
-
capture_stderr { start("commands.
|
72
|
+
capture_stderr { start("commands.to_s") }.should =~ /Error: bling/
|
73
|
+
end
|
74
|
+
|
75
|
+
test "nonexistant subcommand prints command not found" do
|
76
|
+
capture_stderr { start("to_s.bling") }.should =~ /'to_s.bling' not found/
|
73
77
|
end
|
74
78
|
|
75
79
|
test "undiscovered command prints error" do
|
@@ -102,19 +106,31 @@ module Boson
|
|
102
106
|
end
|
103
107
|
|
104
108
|
context "load_command_by_index" do
|
109
|
+
def index(options={})
|
110
|
+
Manager.expects(:load).with {|*args| args[0][0].is_a?(Module) ? true : args[0] == options[:load]
|
111
|
+
}.at_least(1).returns(!options[:fails])
|
112
|
+
Index.expects(:write)
|
113
|
+
end
|
114
|
+
|
105
115
|
test "with index option, no existing index and core command updates index and prints index message" do
|
106
|
-
|
107
|
-
Index.
|
108
|
-
Index.expects(:write)
|
116
|
+
index :load=>Runner.all_libraries
|
117
|
+
Index.stubs(:exists?).returns(false)
|
109
118
|
capture_stdout { start("--index", "libraries") }.should =~ /Generating index/
|
110
119
|
end
|
111
120
|
|
112
121
|
test "with index option, existing index and core command updates incremental index" do
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
122
|
+
index :load=>['changed']
|
123
|
+
Index.stubs(:exists?).returns(true)
|
124
|
+
capture_stdout { start("--index=changed", "libraries")}.should =~ /Indexing.*changed/
|
125
|
+
end
|
126
|
+
|
127
|
+
test "with index option, failed indexing prints error" do
|
128
|
+
index :load=>['changed'], :fails=>true
|
129
|
+
Index.stubs(:exists?).returns(true)
|
130
|
+
Manager.stubs(:failed_libraries).returns(['changed'])
|
131
|
+
capture_stderr {
|
132
|
+
capture_stdout { start("--index=changed", "libraries")}.should =~ /Indexing.*changed/
|
133
|
+
}.should =~ /Error:.*failed.*changed/
|
118
134
|
end
|
119
135
|
|
120
136
|
test "with core command updates index and doesn't print index message" do
|
data/test/file_library_test.rb
CHANGED
@@ -13,7 +13,20 @@ module Boson
|
|
13
13
|
|
14
14
|
test "in a subdirectory loads" do
|
15
15
|
load 'site/delicious', :file_string=>"module Delicious; def blah; end; end"
|
16
|
-
library_has_module('site/delicious', "Boson::Commands::Delicious")
|
16
|
+
library_has_module('site/delicious', "Boson::Commands::Site::Delicious")
|
17
|
+
command_exists?('blah')
|
18
|
+
end
|
19
|
+
|
20
|
+
test "in a sub subdirectory loads" do
|
21
|
+
load 'web/site/delicious', :file_string=>"module Delicious; def blah; end; end"
|
22
|
+
library_has_module('web/site/delicious', "Boson::Commands::Web::Site::Delicious")
|
23
|
+
command_exists?('blah')
|
24
|
+
end
|
25
|
+
|
26
|
+
test "loads by basename" do
|
27
|
+
Dir.stubs(:[]).returns(['./test/commands/site/github.rb'])
|
28
|
+
load 'github', :file_string=>"module Github; def blah; end; end", :exists=>false
|
29
|
+
library_has_module('site/github', "Boson::Commands::Site::Github")
|
17
30
|
command_exists?('blah')
|
18
31
|
end
|
19
32
|
|
data/test/index_test.rb
CHANGED
@@ -9,7 +9,8 @@ module Boson
|
|
9
9
|
before(:each) { reset_boson; Index.instance_eval "@libraries = @commands = nil" }
|
10
10
|
|
11
11
|
def transfers(options={})
|
12
|
-
Index.instance_variable_set "@libraries", [Library.new(:name=>'blah'
|
12
|
+
Index.instance_variable_set "@libraries", [Library.new(:name=>'blah', :commands=>['blurb']),
|
13
|
+
Library.new(:name=>'bling')]
|
13
14
|
Index.instance_variable_set "@commands", [Command.new(:name=>'blurb', :lib=>'blah')]
|
14
15
|
Index.read_and_transfer options[:ignored] || []
|
15
16
|
Boson.libraries.map {|e| e.name}.should == options[:libraries]
|
data/test/loader_test.rb
CHANGED
@@ -7,20 +7,62 @@ module Boson
|
|
7
7
|
Manager.load([Boson::Commands::Namespace])
|
8
8
|
end
|
9
9
|
|
10
|
+
context "config" do
|
11
|
+
before(:each) { reset }
|
12
|
+
test "from callback overridden by user's config" do
|
13
|
+
with_config(:libraries=>{'blih'=>{:namespace=>false}}) do
|
14
|
+
load :blih, :file_string=>"module Blah; def self.config; {:namespace=>'bling'}; end; end"
|
15
|
+
library('blih').namespace.should == false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# if this test fails, other exists? using methods fail
|
20
|
+
test "from callback recursively merges with user's config" do
|
21
|
+
with_config(:libraries=>{'blah'=>{:commands=>{'bling'=>{:description=>'bling', :options=>{:num=>3}}}}}) do
|
22
|
+
File.stubs(:exists?).returns(true)
|
23
|
+
load :blah, :file_string=> "module Blah; def self.config; {:commands=>{'blang'=>{:alias=>'ba'}, " +
|
24
|
+
"'bling'=>{:options=>{:verbose=>:boolean}}}}; end; end"
|
25
|
+
library('blah').command_object('bling').options.should == {:verbose=>:boolean, :num=>3}
|
26
|
+
library('blah').command_object('bling').description.should == 'bling'
|
27
|
+
library('blah').command_object('blang').alias.should == 'ba'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
test "non-hash from inspector overridden by user's config" do
|
32
|
+
with_config(:libraries=>{'blah'=>{:commands=>{'bling'=>{:description=>'already'}}}}) do
|
33
|
+
load :blah, :file_string=>"module Blah; #from file\ndef bling; end; end"
|
34
|
+
library('blah').command_object('bling').description.should == 'already'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
test "hash from inspector recursively merged with user's config" do
|
39
|
+
with_config(:libraries=>{'blah'=>{:commands=>{'blung'=>{:args=>[], :render_options=>{:sort=>'this'}}}}}) do
|
40
|
+
CommentInspector.expects(:scrape).returns({:render_options=>{:fields=>['this']}})
|
41
|
+
load :blah, :file_string=>"module Blah; def blung; end; end"
|
42
|
+
library('blah').command_object('blung').render_options.should == {:fields=>["this"], :sort=>"this"}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
10
47
|
context "load" do
|
11
48
|
before(:each) { reset }
|
12
|
-
test "calls included
|
49
|
+
test "calls included callback" do
|
13
50
|
capture_stdout {
|
14
51
|
load :blah, :file_string=>"module Blah; def self.included(mod); puts 'included blah'; end; def blah; end; end"
|
15
52
|
}.should =~ /included blah/
|
16
53
|
end
|
17
54
|
|
18
|
-
test "calls
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
55
|
+
test "calls after_included callback" do
|
56
|
+
capture_stdout {
|
57
|
+
load :blah, :file_string=>"module Blah; def self.after_included; puts 'yo'; end; end"
|
58
|
+
}.should == "yo\n"
|
59
|
+
end
|
60
|
+
|
61
|
+
test "prints error if library module conflicts with top level constant/module" do
|
62
|
+
capture_stderr {
|
63
|
+
load :blah, :file_string=>"module Object; def self.blah; end; end"
|
64
|
+
}.should =~ /conflict.*'Object'/
|
65
|
+
library_loaded?('blah')
|
24
66
|
end
|
25
67
|
|
26
68
|
test "prints error and returns false for existing library" do
|
@@ -37,6 +79,14 @@ module Boson
|
|
37
79
|
end
|
38
80
|
end
|
39
81
|
|
82
|
+
test "loads a library and creates its class commands" do
|
83
|
+
with_config(:libraries=>{"blah"=>{:class_commands=>{"bling"=>"Blah.bling", "Blah"=>['hmm']}}}) do
|
84
|
+
load :blah, :file_string=>"module Blah; def self.bling; end; def self.hmm; end; end"
|
85
|
+
command_exists? 'bling'
|
86
|
+
command_exists? 'hmm'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
40
90
|
test "loads a library with dependencies" do
|
41
91
|
File.stubs(:exists?).returns(true)
|
42
92
|
File.stubs(:read).returns("module Water; def water; end; end", "module Oaks; def oaks; end; end")
|
@@ -60,6 +110,14 @@ module Boson
|
|
60
110
|
end
|
61
111
|
end
|
62
112
|
|
113
|
+
test "prints error for method conflicts with main_object method" do
|
114
|
+
with_config(:error_method_conflicts=>true) do
|
115
|
+
capture_stderr {
|
116
|
+
load('blah', :file_string=>"module Blah; def require; end; end")
|
117
|
+
}.should =~ /Unable to load library blah.*conflict.*require/
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
63
121
|
test "prints error for method conflicts with config error_method_conflicts" do
|
64
122
|
with_config(:error_method_conflicts=>true) do
|
65
123
|
load('blah', :file_string=>"module Blah; def chwhat; end; end")
|
@@ -81,11 +139,25 @@ module Boson
|
|
81
139
|
context "module library" do
|
82
140
|
def mock_library(*args); end
|
83
141
|
|
84
|
-
test "loads a module library" do
|
85
|
-
eval %[module ::Harvey; def bird; end; end]
|
142
|
+
test "loads a module library and all its class methods by default" do
|
143
|
+
eval %[module ::Harvey; def self.bird; end; def self.eagle; end; end]
|
86
144
|
load ::Harvey, :no_mock=>true
|
87
|
-
|
88
|
-
|
145
|
+
library_has_command('harvey', 'bird')
|
146
|
+
library_has_command('harvey', 'eagle')
|
147
|
+
end
|
148
|
+
|
149
|
+
test "loads a module library with specified commands" do
|
150
|
+
eval %[module ::Peanut; def self.bird; end; def self.eagle; end; end]
|
151
|
+
load ::Peanut, :no_mock=>true, :commands=>%w{bird}
|
152
|
+
library('peanut').commands.size.should == 1
|
153
|
+
library_has_command('peanut', 'bird')
|
154
|
+
end
|
155
|
+
|
156
|
+
test "loads a module library as a class" do
|
157
|
+
eval %[class ::Mentok; def self.bird; end; def self.eagle; end; end]
|
158
|
+
load ::Mentok, :no_mock=>true, :commands=>%w{bird}
|
159
|
+
library('mentok').commands.size.should == 1
|
160
|
+
library_has_command('mentok', 'bird')
|
89
161
|
end
|
90
162
|
end
|
91
163
|
|
@@ -145,17 +217,8 @@ module Boson
|
|
145
217
|
end
|
146
218
|
end
|
147
219
|
|
148
|
-
test "loads with config except" do
|
149
|
-
with_config(:libraries=>{'blong'=>{:namespace=>true, :except=>['wrong']}}) do
|
150
|
-
load 'blong', :file_string=>"module Blong; def bling; end; def wrong; end; end"
|
151
|
-
library_has_command('blong', 'bling')
|
152
|
-
library_has_command('blong', 'wrong', false)
|
153
|
-
library('blong').commands.size.should == 1
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
220
|
test "prints error if namespace conflicts with existing commands" do
|
158
|
-
eval "module Conflict; def bleng; end; end"
|
221
|
+
eval "module Conflict; def self.bleng; end; end"
|
159
222
|
load Conflict, :no_mock=>true
|
160
223
|
with_config(:libraries=>{'bleng'=>{:namespace=>true}}) do
|
161
224
|
capture_stderr {
|
data/test/manager_test.rb
CHANGED
@@ -5,7 +5,7 @@ module Boson
|
|
5
5
|
context "after_load" do
|
6
6
|
def load_library(hash)
|
7
7
|
new_attributes = {:name=>hash.delete(:name), :commands=>[], :created_dependencies=>[], :loaded=>true}
|
8
|
-
[:module, :
|
8
|
+
[:module, :commands].each {|e| new_attributes[e] = hash.delete(e) if hash[e] }
|
9
9
|
Manager.expects(:load_once).returns(Library.new(new_attributes))
|
10
10
|
Manager.load([hash[:name]])
|
11
11
|
end
|
@@ -24,14 +24,6 @@ module Boson
|
|
24
24
|
command_exists?('meatwad')
|
25
25
|
end
|
26
26
|
|
27
|
-
test "loads library with commands and except option" do
|
28
|
-
Boson.main_object.instance_eval("class<<self;self;end").expects(:undef_method).with('frylock')
|
29
|
-
load_library :name=>'blah', :commands=>['frylock','meatwad'], :except=>['frylock']
|
30
|
-
library_loaded? 'blah'
|
31
|
-
command_exists?('frylock', false)
|
32
|
-
command_exists?('meatwad')
|
33
|
-
end
|
34
|
-
|
35
27
|
context "command aliases" do
|
36
28
|
before(:each) { eval %[module ::Aquateen; def frylock; end; end] }
|
37
29
|
after(:each) { Object.send(:remove_const, "Aquateen") }
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
2
|
|
3
3
|
module Boson
|
4
|
-
class
|
4
|
+
class MethodInspectorTest < Test::Unit::TestCase
|
5
5
|
test "non commands module can't set anything" do
|
6
6
|
eval "module Blah; end"
|
7
7
|
MethodInspector.current_module = Blah
|
@@ -21,7 +21,7 @@ module Boson
|
|
21
21
|
end
|
22
22
|
|
23
23
|
before(:all) { eval "module ::Boson::Commands::Zzz; end" }
|
24
|
-
before(:each) { MethodInspector.mod_store
|
24
|
+
before(:each) { MethodInspector.mod_store.delete(::Boson::Commands::Zzz) }
|
25
25
|
|
26
26
|
test "desc sets descriptions" do
|
27
27
|
parsed = parse "desc 'test'; def m1; end; desc 'one'; desc 'more'; def m2; end"
|
data/test/option_parser_test.rb
CHANGED
@@ -90,7 +90,7 @@ module Boson
|
|
90
90
|
end
|
91
91
|
|
92
92
|
it "allows humanized opt name" do
|
93
|
-
create 'foo' => :string, :bar => :
|
93
|
+
create 'foo' => :string, :bar => :string
|
94
94
|
parse("-f", "1", "-b", "2").should == {:foo => "1", :bar => "2"}
|
95
95
|
end
|
96
96
|
|
@@ -113,6 +113,7 @@ module Boson
|
|
113
113
|
it "accepts --[no-]opt variant for booleans, setting false for value" do
|
114
114
|
create "--foo" => :boolean
|
115
115
|
parse("--no-foo")["foo"].should == false
|
116
|
+
parse("--no-f")["foo"].should == false
|
116
117
|
parse("--foo")["foo"].should == true
|
117
118
|
end
|
118
119
|
|
@@ -125,7 +126,7 @@ module Boson
|
|
125
126
|
|
126
127
|
context "option values can be set with" do
|
127
128
|
it "a opt=<value> assignment" do
|
128
|
-
create
|
129
|
+
create :foo => :string
|
129
130
|
parse("--foo=12")["foo"].should == "12"
|
130
131
|
parse("-f=12")["foo"].should == "12"
|
131
132
|
parse("--foo=bar=baz")["foo"].should == "bar=baz"
|
@@ -133,8 +134,8 @@ module Boson
|
|
133
134
|
end
|
134
135
|
|
135
136
|
it "a -nXY assignment" do
|
136
|
-
create "--num" => :
|
137
|
-
parse("-n12")["num"].should ==
|
137
|
+
create "--num" => :numeric
|
138
|
+
parse("-n12")["num"].should == 12
|
138
139
|
end
|
139
140
|
|
140
141
|
it "conjoined short options" do
|
@@ -146,17 +147,17 @@ module Boson
|
|
146
147
|
end
|
147
148
|
|
148
149
|
it "conjoined short options with argument" do
|
149
|
-
create "--foo" => true, "--bar" => true, "--app" => :
|
150
|
+
create "--foo" => true, "--bar" => true, "--app" => :numeric
|
150
151
|
opts = parse "-fba", "12"
|
151
152
|
opts["foo"].should == true
|
152
153
|
opts["bar"].should == true
|
153
|
-
opts["app"].should ==
|
154
|
+
opts["app"].should == 12
|
154
155
|
end
|
155
156
|
end
|
156
157
|
|
157
158
|
context "parse" do
|
158
159
|
it "extracts non-option arguments" do
|
159
|
-
create "--foo" => :
|
160
|
+
create "--foo" => :string, "--bar" => true
|
160
161
|
parse("foo", "bar", "--baz", "--foo", "12", "--bar", "-T", "bang").should == {
|
161
162
|
:foo => "12", :bar => true
|
162
163
|
}
|
@@ -208,14 +209,55 @@ module Boson
|
|
208
209
|
end
|
209
210
|
end
|
210
211
|
|
211
|
-
|
212
|
-
|
213
|
-
|
212
|
+
context ":required option attribute" do
|
213
|
+
before(:all) {
|
214
|
+
create "--foo" => {:type=>:string, :required=>true}, :bar => {:type=>:hash, :required=>true}
|
215
|
+
}
|
216
|
+
|
217
|
+
it "raises an error if string option isn't given" do
|
218
|
+
assert_error(OptionParser::Error, 'no value.*required.*foo') { parse("--bar", "str:ok") }
|
219
|
+
end
|
220
|
+
|
221
|
+
it "raises an error if non-string option isn't given" do
|
222
|
+
assert_error(OptionParser::Error, 'no value.*required.*bar') { parse("--foo", "yup") }
|
223
|
+
end
|
224
|
+
|
225
|
+
it "raises no error when given arguments" do
|
226
|
+
parse("--foo", "yup", "--bar","ok:dude").should == {:foo=>'yup', :bar=>{'ok'=>'dude'}}
|
227
|
+
end
|
214
228
|
end
|
215
|
-
|
229
|
+
|
230
|
+
context ":bool_default option attribute" do
|
231
|
+
before(:all) {
|
232
|
+
create :foo=>{:type=>:string, :bool_default=>'whoop'}, :bar=>{:type=>:array, :bool_default=>'1'},
|
233
|
+
:verbose=>:boolean, :yep=>{:type=>:string, :bool_default=>true}
|
234
|
+
}
|
235
|
+
|
236
|
+
it "sets default boolean" do
|
237
|
+
parse('--foo', '--bar', '1')[:foo].should == 'whoop'
|
238
|
+
parse('--foo', 'ok', 'dokay')[:foo].should == 'whoop'
|
239
|
+
end
|
240
|
+
|
241
|
+
it "sets options normally" do
|
242
|
+
parse('--foo=boo', '--bar=har').should == {:foo=>'boo', :bar=>['har']}
|
243
|
+
end
|
244
|
+
|
245
|
+
it "sets default boolean for array" do
|
246
|
+
parse("--bar", '--foo', '2')[:bar].should == ['1']
|
247
|
+
end
|
248
|
+
|
249
|
+
it "sets default boolean for non-string value" do
|
250
|
+
parse('--yep', '--foo=2')[:yep].should == true
|
251
|
+
end
|
252
|
+
|
253
|
+
it "default booleans can be joined with boolean options" do
|
254
|
+
parse('-fbv').should == {:verbose=>true, :bar=>['1'], :foo=>'whoop'}
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
216
258
|
context ":string type" do
|
217
259
|
before :each do
|
218
|
-
create "--foo" => :string, "--bar" => :string
|
260
|
+
create "--foo" => :string, "--bar" => :string, :blah=>{:type=>:string, :default=>:huh}
|
219
261
|
end
|
220
262
|
|
221
263
|
it "doesn't set nonexistant options" do
|
@@ -238,6 +280,10 @@ module Boson
|
|
238
280
|
it "overwrites earlier values with later values" do
|
239
281
|
parse("--foo", "12", "--foo", "13")[:foo].should == "13"
|
240
282
|
end
|
283
|
+
|
284
|
+
it "can have symbolic default value" do
|
285
|
+
parse('--blah','ok')[:blah].should == 'ok'
|
286
|
+
end
|
241
287
|
end
|
242
288
|
|
243
289
|
context ":string type with :values attribute" do
|
@@ -298,8 +344,8 @@ module Boson
|
|
298
344
|
|
299
345
|
context ":array type" do
|
300
346
|
before(:all) {
|
301
|
-
create :a=>:array, :b=>[1,2,3], :c=>{:type=>:array, :values=>%w{foo fa bar zebra}},
|
302
|
-
:d=>{:type=>:array, :split=>" "}
|
347
|
+
create :a=>:array, :b=>[1,2,3], :c=>{:type=>:array, :values=>%w{foo fa bar zebra}, :enum=>false},
|
348
|
+
:d=>{:type=>:array, :split=>" ", :values=>[:ab, :bc, :cd], :enum=>false}
|
303
349
|
}
|
304
350
|
|
305
351
|
it "supports array defaults" do
|
@@ -318,9 +364,71 @@ module Boson
|
|
318
364
|
parse("-c","f,b")[:c].should == %w{fa bar}
|
319
365
|
end
|
320
366
|
|
321
|
-
it "
|
367
|
+
it "auto aliases symbolic :values" do
|
368
|
+
parse("-d","a c")[:d].should == [:ab,:cd]
|
369
|
+
end
|
370
|
+
|
371
|
+
it "supports a configurable splitter" do
|
322
372
|
parse("-d", "yogi berra")[:d].should == %w{yogi berra}
|
323
373
|
end
|
374
|
+
|
375
|
+
it "aliases * to all values" do
|
376
|
+
parse("-c", '*')[:c].sort.should == %w{bar fa foo zebra}
|
377
|
+
parse("-c", '*,ok')[:c].sort.should == %w{bar fa foo ok zebra}
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
context ":hash type" do
|
382
|
+
before(:all) {
|
383
|
+
create :a=>:hash, :b=>{:default=>{:a=>'b'}}, :c=>{:type=>:hash, :keys=>%w{one two three}},
|
384
|
+
:e=>{:type=>:hash, :keys=>[:one, :two, :three], :default_keys=>:three},
|
385
|
+
:d=>{:type=>:hash, :split=>" "}
|
386
|
+
}
|
387
|
+
|
388
|
+
it "converts comma delimited pairs to hash" do
|
389
|
+
parse("-a", "f:3,g:4")[:a].should == {'f'=>'3', 'g'=>'4'}
|
390
|
+
end
|
391
|
+
|
392
|
+
it "supports hash defaults" do
|
393
|
+
parse[:b].should == {:a=>'b'}
|
394
|
+
end
|
395
|
+
|
396
|
+
it "raises error when option has no value" do
|
397
|
+
assert_error(OptionParser::Error, "no value.*'a'") { parse("-a") }
|
398
|
+
end
|
399
|
+
|
400
|
+
it "raises error if invalid key-value pair given for unknown keys" do
|
401
|
+
assert_error(OptionParser::Error, "invalid.*pair.*'a'") { parse("-a", 'b') }
|
402
|
+
end
|
403
|
+
|
404
|
+
it "auto aliases :keys attribute" do
|
405
|
+
parse("-c","t:3,o:1")[:c].should == {'three'=>'3', 'one'=>'1'}
|
406
|
+
end
|
407
|
+
|
408
|
+
it "adds in explicit default keys with value only argument" do
|
409
|
+
parse('-e', 'whoop')[:e].should == {:three=>'whoop'}
|
410
|
+
end
|
411
|
+
|
412
|
+
it "adds in default keys from known :keys with value only argument" do
|
413
|
+
parse("-c","okay")[:c].should == {'one'=>'okay'}
|
414
|
+
end
|
415
|
+
|
416
|
+
it "auto aliases symbolic :keys" do
|
417
|
+
parse("-e","t:3,o:1")[:e].should == {:three=>'3', :one=>'1'}
|
418
|
+
end
|
419
|
+
|
420
|
+
it "supports a configurable splitter" do
|
421
|
+
parse("-d","a:ab b:bc")[:d].should == {'a'=>'ab', 'b'=>'bc'}
|
422
|
+
end
|
423
|
+
|
424
|
+
it "supports grouping keys" do
|
425
|
+
parse("-c", "t,tw:foo,o:bar")[:c].should == {'three'=>'foo','two'=>'foo', 'one'=>'bar'}
|
426
|
+
end
|
427
|
+
|
428
|
+
it "aliases * to all keys" do
|
429
|
+
parse("-c", "*:foo")[:c].should == {'three'=>'foo', 'two'=>'foo', 'one'=>'foo'}
|
430
|
+
parse('-a', '*:foo')[:a].should == {'*'=>'foo'}
|
431
|
+
end
|
324
432
|
end
|
325
433
|
|
326
434
|
context "option with attributes" do
|
@@ -341,11 +449,11 @@ module Boson
|
|
341
449
|
end
|
342
450
|
end
|
343
451
|
|
452
|
+
def usage
|
453
|
+
@opt.formatted_usage.split(" ").sort
|
454
|
+
end
|
455
|
+
|
344
456
|
context "#formatted_usage" do
|
345
|
-
def usage
|
346
|
-
@opt.formatted_usage.split(" ").sort
|
347
|
-
end
|
348
|
-
|
349
457
|
it "outputs string args with sample values" do
|
350
458
|
create "--repo" => :string, "--branch" => "bugfix", "-n" => 6
|
351
459
|
usage.should == %w([--branch=bugfix] [--repo=REPO] [-n=6])
|
@@ -360,6 +468,54 @@ module Boson
|
|
360
468
|
create "--libs" => :array
|
361
469
|
usage.should == ["[--libs=A,B,C]"]
|
362
470
|
end
|
471
|
+
|
472
|
+
it "outputs hash args with sample value" do
|
473
|
+
create '--paths' => :hash
|
474
|
+
usage.should == ["[--paths=A:B,C:D]"]
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
context "user defined option class" do
|
479
|
+
before(:all) {
|
480
|
+
::FooBoo = Struct.new(:name)
|
481
|
+
module ::Boson::Options::FooBoo
|
482
|
+
def create_foo_boo(value)
|
483
|
+
::FooBoo.new(value)
|
484
|
+
end
|
485
|
+
def validate_foo_boo(value); end
|
486
|
+
end
|
487
|
+
::Boson::OptionParser.send :include, ::Boson::Options::FooBoo
|
488
|
+
create :a=>:foo_boo, :b=>::FooBoo.new('blah'), :c=>:blah_blah,
|
489
|
+
:d=>{:type=>:foo_boo, :type=>::FooBoo.new('bling')}
|
490
|
+
}
|
491
|
+
|
492
|
+
test "created from symbol" do
|
493
|
+
(obj = parse('-a', 'whoop')[:a]).class.should == ::FooBoo
|
494
|
+
obj.name.should == 'whoop'
|
495
|
+
end
|
496
|
+
|
497
|
+
test "created from default" do
|
498
|
+
(obj = parse[:b]).class.should == ::FooBoo
|
499
|
+
obj.name.should == 'blah'
|
500
|
+
end
|
501
|
+
|
502
|
+
test "created from type attribute" do
|
503
|
+
(obj = parse('-d', 'whoop')[:d]).class.should == ::FooBoo
|
504
|
+
obj.name.should == 'whoop'
|
505
|
+
end
|
506
|
+
|
507
|
+
test "has its validation called" do
|
508
|
+
@opt.expects(:validate_foo_boo)
|
509
|
+
parse("-a", 'blah')
|
510
|
+
end
|
511
|
+
|
512
|
+
test "has default usage" do
|
513
|
+
usage[0].should == "[-a=:foo_boo]"
|
514
|
+
end
|
515
|
+
|
516
|
+
test "when nonexistant raises error" do
|
517
|
+
assert_error(OptionParser::Error, "invalid.*:blah_blah") { parse("-c", 'ok') }
|
518
|
+
end
|
363
519
|
end
|
364
520
|
end
|
365
|
-
end
|
521
|
+
end
|