boson 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|