boson 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemspec +1 -1
- data/CHANGELOG.rdoc +9 -0
- data/README.md +78 -9
- data/lib/boson/bare_runner.rb +12 -6
- data/lib/boson/option_parser.rb +5 -4
- data/lib/boson/runner.rb +38 -22
- data/lib/boson/version.rb +1 -1
- data/test/bin_runner_test.rb +9 -15
- data/test/deps.rip +1 -1
- data/test/option_parser_test.rb +2 -3
- data/test/runner_library_test.rb +5 -0
- data/test/runner_test.rb +81 -15
- data/test/scientist_test.rb +1 -2
- data/test/test_helper.rb +21 -0
- metadata +13 -14
- data/README.rdoc +0 -181
data/.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.add_development_dependency 'bacon', '>= 1.1.0'
|
17
17
|
s.add_development_dependency 'mocha-on-bacon'
|
18
18
|
s.add_development_dependency 'bacon-bits'
|
19
|
-
s.add_development_dependency 'bahia', '>= 0.
|
19
|
+
s.add_development_dependency 'bahia', '>= 0.5.0'
|
20
20
|
s.files = Dir.glob(%w[{lib,test}/**/*.rb bin/* [A-Z]*.{txt,rdoc,md} ext/**/*.{rb,c} **/deps.rip]) + %w{Rakefile .gemspec .travis.yml}
|
21
21
|
s.files += ['.rspec']
|
22
22
|
s.extra_rdoc_files = ["README.md", "LICENSE.txt", "Upgrading.md"]
|
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
== 1.2.0
|
2
|
+
* Add help subcommand for executables
|
3
|
+
* Allow Runner help methods to be extended
|
4
|
+
* Fix arg and no method error handling on rbx
|
5
|
+
* Fix OptionParser#delete_invalid_opts bug
|
6
|
+
* Prefix Runner executable errors with executable name
|
7
|
+
* Fix pending tests
|
8
|
+
* Rename Runner help methods to Runner.display_help and Runner.display_command_help
|
9
|
+
|
1
10
|
== 1.1.1
|
2
11
|
* Fix bug for command with one argument containing quoted arguments
|
3
12
|
|
data/README.md
CHANGED
@@ -85,9 +85,21 @@ First, what I consider pros boson has over thor. Boson
|
|
85
85
|
manually define your usage with desc: `desc "SOME USAGE", "SOME DESCRIPTION"`
|
86
86
|
* is lenient about descriptions. Describe commands at your leisure. With thor
|
87
87
|
you must define a desc.
|
88
|
-
* has
|
89
|
-
|
90
|
-
|
88
|
+
* has no blacklist for command names while thor has a
|
89
|
+
[blacklist](https://github.com/wycats/thor/blob/a24b6697a37d9bc0c0ea94ef9bf2cdbb33b8abb9/lib/thor/base.rb#L18-19)
|
90
|
+
due to its design. With boson you can even name commands after Kernel method
|
91
|
+
names but tread with caution in your own Runner class.
|
92
|
+
* allows for user-defined default global options (i.e. --help) and commands
|
93
|
+
(i.e. help). This means that with a plugin you could have your own additional
|
94
|
+
default options and commands shared across executables. See the extending
|
95
|
+
section below.
|
96
|
+
* allows default help and command help to be overridden/extended by
|
97
|
+
subclassing Runner.display_help and Runner.display_command_help respectively.
|
98
|
+
* provides an optional custom rc file for your executable. Simply set
|
99
|
+
ENV['BOSONRC'] to a path i.e. ~/.myprogramrc. This rc file loads before any
|
100
|
+
command processing is done, allowing for users to extend your executable
|
101
|
+
easily i.e. to add more subcommands. For an example, see
|
102
|
+
[vimdb](http://github.com/cldwalker/vimdb).
|
91
103
|
|
92
104
|
Now for pros thor has over boson. Thor
|
93
105
|
|
@@ -96,24 +108,37 @@ Now for pros thor has over boson. Thor
|
|
96
108
|
* is more stable as its feature set is mostly frozen.
|
97
109
|
* is used by rails and thus is guaranteed support for some time.
|
98
110
|
* supports ruby 1.8.7.
|
111
|
+
* can conveniently define an option across commands using class_option.
|
112
|
+
boson may add this later.
|
99
113
|
* TODO: I'm sure there's more
|
100
114
|
|
115
|
+
## Converting From Thor
|
116
|
+
|
117
|
+
* Change your requires and subclass from Boson::Runner instead of Thor.
|
118
|
+
* Delete the first argument from `desc`. Usage is automatically created in boson.
|
119
|
+
* Rename `method_option` to `option`
|
120
|
+
* `class_option` doesn't exist yet but you can emulate it for now by defining
|
121
|
+
your class option in a class method and then calling your class method before
|
122
|
+
every command. See [vimdb](http://github.com/cldwalker/vimdb) for an example.
|
123
|
+
|
101
124
|
## Writing Plugins
|
102
125
|
|
103
126
|
The most common way to write a plugin is to extend one of the many method hooks
|
104
127
|
available. Any methods that are defined in an API or APIClassMethods module
|
105
128
|
are extendable. For example, if you want to extend what any boson-based
|
106
|
-
executable does first, extend BareRunner.start:
|
129
|
+
executable does first, extend Boson::BareRunner.start:
|
107
130
|
|
108
131
|
```ruby
|
109
|
-
module
|
110
|
-
|
111
|
-
|
112
|
-
|
132
|
+
module Boson
|
133
|
+
module CustomStartUp
|
134
|
+
def start(*)
|
135
|
+
super
|
136
|
+
# additional startup
|
137
|
+
end
|
113
138
|
end
|
114
139
|
end
|
115
140
|
|
116
|
-
BareRunner.extend CustomStartUp
|
141
|
+
Boson::BareRunner.extend Boson::CustomStartUp
|
117
142
|
```
|
118
143
|
|
119
144
|
Notice that `extend` was used to extend a class method. To extend an instance
|
@@ -122,9 +147,53 @@ overridden method to call original functionality. If you don't, you're
|
|
122
147
|
possibly overridden existing functionality, which is fine as long as you know
|
123
148
|
what you are overriding.
|
124
149
|
|
150
|
+
If you want to gemify your plugin, name it boson-plugin_name and put it under
|
151
|
+
lib/boson/plugin_name. The previous example would go in
|
152
|
+
lib/boson/custom_startup.rb. To use your plugin, a user can simply require your
|
153
|
+
plugin in their executable.
|
154
|
+
|
125
155
|
For many plugin examples, see
|
126
156
|
[boson-more](http://github.com/cldwalker/boson-more).
|
127
157
|
|
158
|
+
## Using a Plugin
|
159
|
+
|
160
|
+
To use a plugin, just require it. For an executable:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
require 'boson/runner'
|
164
|
+
require 'boson/my_plugin'
|
165
|
+
|
166
|
+
MyRunner.start
|
167
|
+
```
|
168
|
+
|
169
|
+
For the boson executable, just require the plugins in ~/.bosonrc.
|
170
|
+
|
171
|
+
## Extending Your Executables
|
172
|
+
|
173
|
+
Boson allows for custom default options and commands. This means you can
|
174
|
+
add your own defaults in a plugin and use them across your executables.
|
175
|
+
|
176
|
+
To add a custom default command, simply reopen Boson::DefaultCommandsRunner:
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
class Boson::DefaultCommandsRunner
|
180
|
+
desc "whoomp"
|
181
|
+
def whoomp
|
182
|
+
puts "WHOOMP there it is!"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
To add a custom global option, add to Boson::Runner::GLOBAL_OPTIONS:
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
Boson::Runner::GLOBAL_OPTIONS.update(
|
191
|
+
verbose: {type: :boolean, desc: "Verbose description of loading libraries"}
|
192
|
+
)
|
193
|
+
```
|
194
|
+
|
195
|
+
Custom global options are defined in the same format as options for a command.
|
196
|
+
|
128
197
|
## Bugs/Issues
|
129
198
|
|
130
199
|
Please report them [on github](http://github.com/cldwalker/boson/issues).
|
data/lib/boson/bare_runner.rb
CHANGED
@@ -44,7 +44,8 @@ module Boson
|
|
44
44
|
raise if !allowed_argument_error?($!, cmd, args)
|
45
45
|
abort_with "'#{cmd}' was called incorrectly.\nUsage: " + Command.usage(cmd)
|
46
46
|
rescue NoMethodError => err
|
47
|
-
|
47
|
+
index = RUBY_ENGINE == 'rbx' ? 1 : 0
|
48
|
+
raise if !err.backtrace[index].include?('`full_invoke')
|
48
49
|
no_command_error cmd
|
49
50
|
end
|
50
51
|
|
@@ -55,15 +56,20 @@ module Boson
|
|
55
56
|
|
56
57
|
# Determines if a user command argument error or an internal Boson one
|
57
58
|
def allowed_argument_error?(err, cmd, args)
|
58
|
-
|
59
|
-
|
59
|
+
msg = RUBY_ENGINE == 'rbx' && err.class == ArgumentError ?
|
60
|
+
/given \d+, expected \d+/ : /wrong number of arguments/
|
61
|
+
(err.message[msg] && (cmd_obj = Command.find(cmd)) &&
|
62
|
+
cmd_obj.arg_size != args.size)
|
63
|
+
end
|
64
|
+
|
65
|
+
def option_parser
|
66
|
+
@option_parser ||= OptionParser.new(self::GLOBAL_OPTIONS)
|
60
67
|
end
|
61
68
|
|
62
69
|
private
|
63
70
|
def parse_args(args)
|
64
|
-
|
65
|
-
|
66
|
-
new_args = @option_parser.non_opts
|
71
|
+
options = option_parser.parse(args.dup, :opts_before_args=>true)
|
72
|
+
new_args = option_parser.non_opts
|
67
73
|
[new_args[0], options, new_args[1..-1]]
|
68
74
|
end
|
69
75
|
|
data/lib/boson/option_parser.rb
CHANGED
@@ -418,12 +418,13 @@ module Boson
|
|
418
418
|
end
|
419
419
|
|
420
420
|
def delete_invalid_opts
|
421
|
-
|
422
|
-
|
423
|
-
|
421
|
+
stop = nil
|
422
|
+
@trailing_non_opts.delete_if do |e|
|
423
|
+
stop ||= STOP_STRINGS.include?(e)
|
424
|
+
invalid = e.start_with?('-') && !stop
|
424
425
|
warn "Deleted invalid option '#{e}'" if invalid
|
425
426
|
invalid
|
426
|
-
|
427
|
+
end
|
427
428
|
end
|
428
429
|
|
429
430
|
def peek
|
data/lib/boson/runner.rb
CHANGED
@@ -4,16 +4,20 @@ module Boson
|
|
4
4
|
# Defines a RunnerLibrary for use by executables as a simple way to map
|
5
5
|
# methods to subcommands
|
6
6
|
class Runner < BareRunner
|
7
|
+
# Stores currently started Runner subclass
|
8
|
+
class <<self; attr_accessor :current; end
|
9
|
+
|
7
10
|
def self.inherited(mod)
|
8
11
|
@help_added ||= add_command_help
|
9
12
|
Inspector.enable all_classes: true, module: mod.singleton_class
|
10
13
|
end
|
11
14
|
|
12
15
|
def self.default_libraries
|
13
|
-
[self]
|
16
|
+
[self, DefaultCommandsRunner]
|
14
17
|
end
|
15
18
|
|
16
19
|
def self.start(args=ARGV)
|
20
|
+
Runner.current = self
|
17
21
|
Boson.in_shell = true
|
18
22
|
ENV['BOSONRC'] ||= ''
|
19
23
|
super
|
@@ -23,18 +27,15 @@ module Boson
|
|
23
27
|
end
|
24
28
|
|
25
29
|
def self.execute(command, args, options)
|
26
|
-
|
27
|
-
display_default_usage
|
28
|
-
else
|
30
|
+
options[:help] || command.nil? ? display_help :
|
29
31
|
execute_command(command, args, options)
|
30
|
-
end
|
31
32
|
end
|
32
33
|
|
33
34
|
def self.execute_command(cmd, args, options)
|
34
35
|
Command.find(cmd) ? super(cmd, args) : no_command_error(cmd)
|
35
36
|
end
|
36
37
|
|
37
|
-
def self.
|
38
|
+
def self.display_command_help(cmd)
|
38
39
|
puts "Usage: #{app_name} #{cmd.name} #{cmd.basic_usage}".rstrip, ""
|
39
40
|
if cmd.options
|
40
41
|
puts "Options:"
|
@@ -44,38 +45,53 @@ module Boson
|
|
44
45
|
puts "Description:\n #{cmd.desc || 'TODO'}"
|
45
46
|
end
|
46
47
|
|
47
|
-
def self.
|
48
|
+
def self.display_help
|
48
49
|
commands = Boson.commands.sort_by(&:name).map {|c| [c.name, c.desc.to_s] }
|
49
|
-
puts "Usage: #{app_name} COMMAND [ARGS]", "", "Available commands:",
|
50
|
-
Util.format_table(commands), "",
|
51
|
-
|
50
|
+
puts "Usage: #{app_name} [OPTIONS] COMMAND [ARGS]", "", "Available commands:",
|
51
|
+
Util.format_table(commands), "", "Options:"
|
52
|
+
option_parser.print_usage_table(no_headers: true)
|
52
53
|
end
|
53
54
|
|
54
55
|
def self.app_name
|
55
56
|
File.basename($0).split(' ').first
|
56
57
|
end
|
57
58
|
|
59
|
+
def self.abort_with(msg)
|
60
|
+
super "#{app_name}: #{msg}"
|
61
|
+
end
|
62
|
+
|
58
63
|
private
|
59
64
|
def self.load_options
|
60
65
|
{force: true}
|
61
66
|
end
|
62
67
|
|
63
68
|
def self.add_command_help
|
69
|
+
Scientist.extend(ScientistExtension)
|
70
|
+
Command.extend(CommandExtension)
|
71
|
+
true # Ensure this method is only called once
|
72
|
+
end
|
73
|
+
|
74
|
+
module ScientistExtension
|
64
75
|
# Overrides Scientist' default help
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end)
|
76
|
+
def run_help_option(cmd)
|
77
|
+
Boson::Runner.current.display_command_help(cmd)
|
78
|
+
end
|
79
|
+
end
|
70
80
|
|
81
|
+
module CommandExtension
|
71
82
|
# Ensure all commands have -h
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
83
|
+
def new_attributes(name, library)
|
84
|
+
super.update(option_command: true)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Defines default commands that are available to executables i.e. Runner.start
|
90
|
+
class DefaultCommandsRunner < Runner
|
91
|
+
desc "Displays help for a command"
|
92
|
+
def help(cmd)
|
93
|
+
(cmd_obj = Command.find(cmd)) ? Runner.current.display_command_help(cmd_obj) :
|
94
|
+
self.class.no_command_error(cmd)
|
79
95
|
end
|
80
96
|
end
|
81
97
|
end
|
data/lib/boson/version.rb
CHANGED
data/test/bin_runner_test.rb
CHANGED
@@ -8,11 +8,7 @@ describe "BinRunner" do
|
|
8
8
|
yield
|
9
9
|
end
|
10
10
|
|
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/]
|
15
|
-
|
11
|
+
unless ENV['FAST']
|
16
12
|
it "prints usage with no arguments" do
|
17
13
|
boson
|
18
14
|
stdout.should =~ /^boson/
|
@@ -44,7 +40,6 @@ describe "BinRunner" do
|
|
44
40
|
end
|
45
41
|
end
|
46
42
|
|
47
|
-
# TODO: test actual uses of Runner.debug
|
48
43
|
it "sets Boson.debug with --debug" do
|
49
44
|
boson "--debug -e 'print Boson.debug'"
|
50
45
|
stdout.should == 'true'
|
@@ -60,20 +55,19 @@ describe "BinRunner" do
|
|
60
55
|
it "prints error for unexpected error" do
|
61
56
|
boson %[-e 'raise "blarg"']
|
62
57
|
stderr.chomp.should == "Error: blarg"
|
63
|
-
process.success?.should == false
|
64
58
|
end
|
65
59
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
60
|
+
it "prints error for too many arguments" do
|
61
|
+
with_command('dude') do
|
62
|
+
boson "dude 1 2 3"
|
63
|
+
stderr.should =~ /^'dude' was called incorrectly/
|
64
|
+
process.success?.should == false
|
65
|
+
end
|
71
66
|
end
|
72
67
|
|
73
|
-
|
74
|
-
xit "prints error for invalid command" do
|
68
|
+
it "prints error for invalid command" do
|
75
69
|
boson 'blarg'
|
76
|
-
stderr.chomp.should ==
|
70
|
+
stderr.chomp.should == %[Could not find command "blarg"]
|
77
71
|
process.success?.should == false
|
78
72
|
end
|
79
73
|
end
|
data/test/deps.rip
CHANGED
data/test/option_parser_test.rb
CHANGED
@@ -180,14 +180,13 @@ describe "OptionParser" do
|
|
180
180
|
opt.non_opts.should == ['ok']
|
181
181
|
end
|
182
182
|
|
183
|
-
|
184
|
-
xit ":delete_invalid_opts deletes until - or --" do
|
183
|
+
it ":delete_invalid_opts deletes until - or --" do
|
185
184
|
create(:foo=>:boolean, :bar=>:boolean)
|
186
185
|
%w{- --}.each do |stop_char|
|
187
186
|
capture_stderr {
|
188
187
|
opt.parse(%w{ok -b -d} << stop_char << '-f', :delete_invalid_opts=>true)
|
189
188
|
}.should =~ /'-d'/
|
190
|
-
opt.non_opts.should == %w{ok
|
189
|
+
opt.non_opts.should == %w{ok} << stop_char << '-f'
|
191
190
|
end
|
192
191
|
end
|
193
192
|
|
data/test/runner_library_test.rb
CHANGED
@@ -7,4 +7,9 @@ describe "RunnerLibrary" do
|
|
7
7
|
Manager.load create_runner(:blah)
|
8
8
|
library('blarg').commands.should == ['blah']
|
9
9
|
end
|
10
|
+
|
11
|
+
it "can coexist with another runner library" do
|
12
|
+
Manager.load create_runner(:blah)
|
13
|
+
should_not_raise { Manager.load create_runner(:blih, library: :Blih) }
|
14
|
+
end
|
10
15
|
end
|
data/test/runner_test.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/test_helper'
|
2
2
|
require 'shellwords'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
# hack required to re-add default_commands_runner methods
|
5
|
+
$".delete_if {|e| e[%r{boson/runner.rb$}] }
|
6
|
+
Boson.send(:remove_const, :Runner)
|
7
|
+
Boson.send(:remove_const, :DefaultCommandsRunner)
|
8
|
+
require 'boson/runner'
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
end
|
10
|
+
# remove side effects from other tests
|
11
|
+
Boson::Runner::GLOBAL_OPTIONS.delete_if {|k,v| k != :help }
|
12
12
|
|
13
|
+
class MyRunner < Boson::Runner
|
13
14
|
desc "This is a small"
|
14
15
|
def small(*args)
|
15
16
|
p args
|
@@ -52,22 +53,42 @@ class MyRunner < Boson::Runner
|
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
56
|
+
class ExtendedRunner < Boson::Runner
|
57
|
+
def self.execute(command, args, options)
|
58
|
+
options[:version] ? puts("Version 1000.0") : super
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.display_command_help(cmd)
|
62
|
+
super
|
63
|
+
puts "And don't forget to eat BAACCCONN"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
55
67
|
describe "Runner" do
|
56
|
-
before_all {
|
68
|
+
before_all { reset }
|
57
69
|
|
58
70
|
def my_command(cmd='')
|
71
|
+
$0 = 'my_command'
|
59
72
|
capture_stdout do
|
60
73
|
MyRunner.start Shellwords.split(cmd)
|
61
74
|
end
|
62
75
|
end
|
63
76
|
|
77
|
+
def extended_command(cmd='')
|
78
|
+
$0 = 'extended_command'
|
79
|
+
capture_stdout do
|
80
|
+
ExtendedRunner.start Shellwords.split(cmd)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
64
84
|
def default_usage
|
65
85
|
<<-STR
|
66
|
-
Usage: my_command COMMAND [ARGS]
|
86
|
+
Usage: my_command [OPTIONS] COMMAND [ARGS]
|
67
87
|
|
68
88
|
Available commands:
|
69
89
|
boom
|
70
90
|
broken
|
91
|
+
help Displays help for a command
|
71
92
|
medium This is a medium
|
72
93
|
mini This is a mini
|
73
94
|
quiet
|
@@ -75,7 +96,8 @@ Available commands:
|
|
75
96
|
splot This is splot
|
76
97
|
test
|
77
98
|
|
78
|
-
|
99
|
+
Options:
|
100
|
+
-h, --help Displays this help message
|
79
101
|
STR
|
80
102
|
end
|
81
103
|
|
@@ -88,6 +110,23 @@ STR
|
|
88
110
|
my_command('--help').should == default_usage
|
89
111
|
end
|
90
112
|
|
113
|
+
describe "for help COMMAND" do
|
114
|
+
it 'prints help for valid command' do
|
115
|
+
my_command('help quiet').should ==<<-STR
|
116
|
+
Usage: my_command quiet
|
117
|
+
|
118
|
+
Description:
|
119
|
+
TODO
|
120
|
+
STR
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'prints error for invalid command' do
|
124
|
+
Boson::DefaultCommandsRunner.expects(:abort).
|
125
|
+
with("my_command: Could not find command \"invalid\"")
|
126
|
+
my_command('help invalid')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
91
130
|
describe "for COMMAND -h" do
|
92
131
|
it "prints help for descriptionless command" do
|
93
132
|
my_command('quiet -h').should == <<-STR
|
@@ -147,7 +186,7 @@ STR
|
|
147
186
|
|
148
187
|
it "calls command with too many args" do
|
149
188
|
MyRunner.expects(:abort).with <<-STR.chomp
|
150
|
-
'medium' was called incorrectly.
|
189
|
+
my_command: 'medium' was called incorrectly.
|
151
190
|
Usage: medium [ARG]
|
152
191
|
STR
|
153
192
|
my_command('medium 1 2 3')
|
@@ -161,7 +200,12 @@ STR
|
|
161
200
|
end
|
162
201
|
|
163
202
|
it "executes custom global option" do
|
164
|
-
|
203
|
+
# setup goes here to avoid coupling to other runner
|
204
|
+
ExtendedRunner::GLOBAL_OPTIONS[:version] = {
|
205
|
+
type: :boolean, :desc => 'Print version'
|
206
|
+
}
|
207
|
+
|
208
|
+
extended_command('-v').chomp.should == 'Version 1000.0'
|
165
209
|
end
|
166
210
|
|
167
211
|
it "allows Kernel-method command names" do
|
@@ -169,12 +213,12 @@ STR
|
|
169
213
|
end
|
170
214
|
|
171
215
|
it "prints error message for internal public method" do
|
172
|
-
MyRunner.expects(:abort).with %[Could not find command "to_s"]
|
216
|
+
MyRunner.expects(:abort).with %[my_command: Could not find command "to_s"]
|
173
217
|
my_command('to_s').should == ''
|
174
218
|
end
|
175
219
|
|
176
220
|
it "prints error message for nonexistant command" do
|
177
|
-
MyRunner.expects(:abort).with %[Could not find command "blarg"]
|
221
|
+
MyRunner.expects(:abort).with %[my_command: Could not find command "blarg"]
|
178
222
|
my_command('blarg').should == ''
|
179
223
|
end
|
180
224
|
|
@@ -187,7 +231,7 @@ STR
|
|
187
231
|
end
|
188
232
|
|
189
233
|
it "prints error message for private method" do
|
190
|
-
MyRunner.expects(:abort).with %[Could not find command "no_run"]
|
234
|
+
MyRunner.expects(:abort).with %[my_command: Could not find command "no_run"]
|
191
235
|
my_command('no_run').should == ''
|
192
236
|
end
|
193
237
|
|
@@ -208,4 +252,26 @@ STR
|
|
208
252
|
|
209
253
|
after_all { ENV['BOSONRC'] = File.dirname(__FILE__) + '/.bosonrc' }
|
210
254
|
end
|
255
|
+
|
256
|
+
describe "extend Runner" do
|
257
|
+
it "can extend help" do
|
258
|
+
extended_command('help help').should == <<-STR
|
259
|
+
Usage: extended_command help CMD
|
260
|
+
|
261
|
+
Description:
|
262
|
+
Displays help for a command
|
263
|
+
And don't forget to eat BAACCCONN
|
264
|
+
STR
|
265
|
+
end
|
266
|
+
|
267
|
+
it "can extend a command's --help" do
|
268
|
+
extended_command('help -h').should == <<-STR
|
269
|
+
Usage: extended_command help CMD
|
270
|
+
|
271
|
+
Description:
|
272
|
+
Displays help for a command
|
273
|
+
And don't forget to eat BAACCCONN
|
274
|
+
STR
|
275
|
+
end
|
276
|
+
end
|
211
277
|
end
|
data/test/scientist_test.rb
CHANGED
@@ -152,8 +152,7 @@ describe "Scientist" do
|
|
152
152
|
end
|
153
153
|
|
154
154
|
it "with too many args raises CommandArgumentError" do
|
155
|
-
args3 =
|
156
|
-
[ArgumentError, 'given 3, expected 2'] :
|
155
|
+
args3 = RUBY_ENGINE == 'rbx' ? [ArgumentError, 'given 3, expected 2'] :
|
157
156
|
[ArgumentError, '3 for 2']
|
158
157
|
args4 = [OptionCommand::CommandArgumentError, '4 for 2']
|
159
158
|
assert_error(*args3) { command_with_args 1,2,3 }
|
data/test/test_helper.rb
CHANGED
@@ -37,6 +37,27 @@ module TestHelpers
|
|
37
37
|
Boson.config = old_config
|
38
38
|
end
|
39
39
|
|
40
|
+
# for use with executables
|
41
|
+
def with_command(cmd)
|
42
|
+
old = ENV['BOSONRC']
|
43
|
+
ENV['BOSONRC'] = File.dirname(__FILE__) + '/.bosonrc.temp'
|
44
|
+
File.open(ENV['BOSONRC'], 'w') {|f|
|
45
|
+
f.puts <<-STR
|
46
|
+
require 'boson/runner'
|
47
|
+
class SomeRunner < Boson::Runner
|
48
|
+
def #{cmd}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
Boson::Manager.load SomeRunner
|
52
|
+
STR
|
53
|
+
}
|
54
|
+
|
55
|
+
yield
|
56
|
+
|
57
|
+
FileUtils.rm_f ENV['BOSONRC']
|
58
|
+
ENV['BOSONRC'] = old
|
59
|
+
end
|
60
|
+
|
40
61
|
def manager_load(lib, options={})
|
41
62
|
@stderr = capture_stderr { Manager.load(lib, options) }
|
42
63
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: boson
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03-
|
12
|
+
date: 2012-03-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mocha
|
16
|
-
requirement: &
|
16
|
+
requirement: &70095971880980 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.10.4
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70095971880980
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: bacon
|
27
|
-
requirement: &
|
27
|
+
requirement: &70095971880140 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 1.1.0
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70095971880140
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: mocha-on-bacon
|
38
|
-
requirement: &
|
38
|
+
requirement: &70095971878980 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70095971878980
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: bacon-bits
|
49
|
-
requirement: &
|
49
|
+
requirement: &70095971878240 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,18 +54,18 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70095971878240
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: bahia
|
60
|
-
requirement: &
|
60
|
+
requirement: &70095971877400 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
64
64
|
- !ruby/object:Gem::Version
|
65
|
-
version: 0.
|
65
|
+
version: 0.5.0
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70095971877400
|
69
69
|
description: Boson is a modular command/task framework. Thanks to its rich set of
|
70
70
|
plugins, it differentiates itself from rake and thor by being usable from irb and
|
71
71
|
the commandline, having optional automated views generated by hirb and allowing
|
@@ -112,7 +112,6 @@ files:
|
|
112
112
|
- bin/boson
|
113
113
|
- LICENSE.txt
|
114
114
|
- CHANGELOG.rdoc
|
115
|
-
- README.rdoc
|
116
115
|
- README.md
|
117
116
|
- Upgrading.md
|
118
117
|
- test/deps.rip
|
data/README.rdoc
DELETED
@@ -1,181 +0,0 @@
|
|
1
|
-
To read a linkable version of this README, {see here}[http://tagaholic.me/boson/doc/].
|
2
|
-
|
3
|
-
== Description
|
4
|
-
Boson is a command/task framework with the power to turn any ruby method into a full-fledged
|
5
|
-
executable with options. Some unique features that differentiate it from rake and thor include
|
6
|
-
being usable from irb and the commandline, optional automated views generated by hirb and allowing
|
7
|
-
libraries to be written as plain ruby. For my libraries that use this, see
|
8
|
-
{irbfiles}[http://github.com/cldwalker/irbfiles]. Works with all major ruby versions.
|
9
|
-
|
10
|
-
== UPDATE
|
11
|
-
Boson 0.4.x will be the last 1.8 compatible version.
|
12
|
-
Boson 1.0 will be a rewrite compatible with only 1.9. Work is going on in {boson2
|
13
|
-
branch}[https://github.com/cldwalker/boson/tree/boson2]. The goal of boson2 is to have a slimmer
|
14
|
-
core, move everything else {to plugins}[https://github.com/cldwalker/boson-more]
|
15
|
-
and to allow gems to use boson to make executables like thor.
|
16
|
-
|
17
|
-
== Features
|
18
|
-
* Simple organization: Commands are just methods on an object (default is main) and command libraries are just modules.
|
19
|
-
* Commands are accessible from the commandline (Boson::BinRunner) or irb (Boson::ConsoleRunner).
|
20
|
-
* Libraries
|
21
|
-
* can be written in plain ruby which allows for easy testing and use independent of boson (Boson::FileLibrary).
|
22
|
-
* can exist locally as a Bosonfile (Boson::LocalFileLibrary) and under lib/boson/commands or .boson/commands.
|
23
|
-
* can be made from gems (Boson::GemLibrary) or any require-able file (Boson::RequireLibrary).
|
24
|
-
* are encouraged to be shared. Libraries can be installed with a given url. Users can customize any aspect of a third-party
|
25
|
-
library without modifying it (Boson::Library).
|
26
|
-
* Commands
|
27
|
-
* can have any number of local and global options (Boson::OptionCommand). Options are defined with Boson::OptionParser.
|
28
|
-
* can have any view associated to it (via Hirb) without adding view code to the command's method.
|
29
|
-
These views can be toggled on and manipulated via global render options (Boson::View and Boson::OptionCommand).
|
30
|
-
* can pipe their return value into custom pipe options (Boson::Pipe).
|
31
|
-
* has default pipe options to search and sort an array of any objects (Boson::Pipes).
|
32
|
-
* Option parser (Boson::OptionParser)
|
33
|
-
* provides option types that map to objects i.e. :array type creates Array objects.
|
34
|
-
* come with 5 default option types: boolean, array, string, hash and numeric.
|
35
|
-
* can have have custom option types defined by users (Boson::Options).
|
36
|
-
* Comes with default commands to load, search, list and install commands and libraries (Boson::Commands::Core).
|
37
|
-
* Namespaces are optional and when used are methods which allow for method_missing magic.
|
38
|
-
|
39
|
-
== Creating Command Libraries
|
40
|
-
See Boson::FileLibrary or here[http://tagaholic.me/boson/doc/classes/Boson/FileLibrary.html].
|
41
|
-
|
42
|
-
== Irb Example
|
43
|
-
|
44
|
-
To use in irb, drop this in your ~/.irbrc:
|
45
|
-
require 'boson'
|
46
|
-
Boson.start
|
47
|
-
|
48
|
-
Having done that, let's start up irb:
|
49
|
-
|
50
|
-
bash> irb
|
51
|
-
Loaded library core
|
52
|
-
Loaded library web_core
|
53
|
-
|
54
|
-
# List default libraries
|
55
|
-
>> libraries
|
56
|
-
+----------+----------+------+--------------+
|
57
|
-
| name | commands | gems | library_type |
|
58
|
-
+----------+----------+------+--------------+
|
59
|
-
| core | 6 | | module |
|
60
|
-
| web_core | 3 | | module |
|
61
|
-
+----------+----------+------+--------------+
|
62
|
-
2 rows in set
|
63
|
-
|
64
|
-
# List default commands
|
65
|
-
>> commands
|
66
|
-
+--------------+----------+-------+--------------------------------------------+-----------------------------------------------------------------------------+
|
67
|
-
| full_name | lib | alias | usage | description |
|
68
|
-
+--------------+----------+-------+--------------------------------------------+-----------------------------------------------------------------------------+
|
69
|
-
| usage | core | | [name][--verbose] | Print a command's usage |
|
70
|
-
| libraries | core | | [query=''][--index] [--query_fields=A,B,C] | List or search libraries |
|
71
|
-
| render | core | | [object] [options={}] | Render any object using Hirb |
|
72
|
-
| load_library | core | | [library][--verbose] [--reload] | Load/reload a library |
|
73
|
-
| commands | core | | [query=''][--index] [--query_fields=A,B,C] | List or search commands |
|
74
|
-
| menu | core | | [output] [options={}] [&block] | Provide a menu to multi-select elements from a given array |
|
75
|
-
| get | web_core | | [url] | Gets the body of a url |
|
76
|
-
| install | web_core | | [url][--force] [--name=NAME] | Installs a library by url. Library should then be loaded with load_library. |
|
77
|
-
| browser | web_core | | [*urls] | Opens urls in a browser |
|
78
|
-
+--------------+----------+-------+--------------------------------------------+-----------------------------------------------------------------------------+
|
79
|
-
9 rows in set
|
80
|
-
|
81
|
-
# Boson commands can behave like shell commands:
|
82
|
-
|
83
|
-
# Basic help
|
84
|
-
>> commands '-h'
|
85
|
-
commands [query=''][--index] [--query_fields=A,B,C]
|
86
|
-
|
87
|
-
# Search the lib column for web
|
88
|
-
>> commands 'web -q=lib' # or 'web --query_fields=lib'
|
89
|
-
+-----------+----------+-------+------------------------------+-----------------------------------------------------------------------------+
|
90
|
-
| full_name | lib | alias | usage | description |
|
91
|
-
+-----------+----------+-------+------------------------------+-----------------------------------------------------------------------------+
|
92
|
-
| get | web_core | | [url] | Gets the body of a url |
|
93
|
-
| install | web_core | | [url][--force] [--name=NAME] | Installs a library by url. Library should then be loaded with load_library. |
|
94
|
-
| browser | web_core | | [*urls] | Opens urls in a browser |
|
95
|
-
+-----------+----------+-------+------------------------------+-----------------------------------------------------------------------------+
|
96
|
-
3 rows in set
|
97
|
-
|
98
|
-
== Commandline Example
|
99
|
-
|
100
|
-
# Just like in irb
|
101
|
-
bash> boson libraries
|
102
|
-
+----------+----------+------+--------------+
|
103
|
-
| name | commands | gems | library_type |
|
104
|
-
+----------+----------+------+--------------+
|
105
|
-
| core | 6 | | module |
|
106
|
-
| web_core | 3 | | module |
|
107
|
-
+----------+----------+------+--------------+
|
108
|
-
2 rows in set
|
109
|
-
|
110
|
-
# Let's install another library
|
111
|
-
bash> boson install https://github.com/cldwalker/irbfiles/raw/master/boson/commands/public/irb_core.rb
|
112
|
-
Saved to /Users/bozo/.boson/commands/irb_core.rb
|
113
|
-
|
114
|
-
# Let's start irb ...
|
115
|
-
bash> irb
|
116
|
-
|
117
|
-
>> commands
|
118
|
-
+-------------------------------+----------+------------+--------------------------------------------+-----------------------------------------------------------------------------+
|
119
|
-
| full_name | lib | alias | usage | description |
|
120
|
-
+-------------------------------+----------+------------+--------------------------------------------+-----------------------------------------------------------------------------+
|
121
|
-
| usage | core | | [name][--verbose] | Print a command's usage |
|
122
|
-
| libraries | core | | [query=''][--index] [--query_fields=name] | List or search libraries |
|
123
|
-
| render | core | | [object] [options={}] | Render any object using Hirb |
|
124
|
-
| load_library | core | | [library][--verbose] [--reload] | Load/reload a library |
|
125
|
-
| commands | core | | [query=''][--index] [--query_fields=A,B,C] | List or search commands |
|
126
|
-
| menu | core | | [output] [options={}] [&block] | Provide a menu to multi-select elements from a given array |
|
127
|
-
| get | web_core | | [url] | Gets the body of a url |
|
128
|
-
| install | web_core | | [url][--force] [--name=NAME] | Installs a library by url. Library should then be loaded with load_library. |
|
129
|
-
| browser | web_core | | [*urls] | Opens urls in a browser |
|
130
|
-
| irb_pop_workspace | irb_core | popws | | Pops current workspace and changes to next workspace in context |
|
131
|
-
| irb_require | irb_core | | | Evals file like require line by line |
|
132
|
-
| public | irb_core | | | Works same as module#public |
|
133
|
-
| private | irb_core | | | Works same as module#private |
|
134
|
-
| irb | irb_core | | | Starts a new workspace/subsession |
|
135
|
-
| irb_push_workspace | irb_core | pushws | | Creates a workspace for given object and pushes it into the current context |
|
136
|
-
| irb_load | irb_core | | | Evals file like load line by line |
|
137
|
-
| irb_change_workspace | irb_core | cws | | Changes current workspace to given object |
|
138
|
-
| irb_source | irb_core | source | | Evals full path file line by line |
|
139
|
-
| irb_jobs | irb_core | jobs | | List workspaces/subsessions |
|
140
|
-
| irb_fg | irb_core | fg | | Switch to a workspace/subsession |
|
141
|
-
| irb_help | irb_core | help | | Ri based help |
|
142
|
-
| irb_kill | irb_core | kill | | Kills a given workspace/subsession |
|
143
|
-
| include | irb_core | | | Works same as module#include |
|
144
|
-
| irb_exit | irb_core | exit | | Kills the current workspace/subsession |
|
145
|
-
| irb_workspaces | irb_core | workspaces | | Array of workspaces for current context |
|
146
|
-
| irb_context | irb_core | conf | | Displays configuration for current workspace/subsession |
|
147
|
-
| install_alias_method | irb_core | | | Aliases given method, allows lazy loading of dependent file |
|
148
|
-
| irb_current_working_workspace | irb_core | cwws | | Prints current workspace |
|
149
|
-
+-------------------------------+----------+------------+--------------------------------------------+-----------------------------------------------------------------------------+
|
150
|
-
28 rows in set
|
151
|
-
|
152
|
-
# Sweet! Now we have a list and description of commands that come with irb.
|
153
|
-
|
154
|
-
== Todo
|
155
|
-
* More tests
|
156
|
-
* Making commands out of existing gems easier and more powerful
|
157
|
-
* Features based on commands and their argument types i.e. completing and piping
|
158
|
-
* Consider dropping alias gem dependency if not using its full potential
|
159
|
-
|
160
|
-
== Bugs/Issues
|
161
|
-
Please report them {on github}[http://github.com/cldwalker/boson/issues].
|
162
|
-
|
163
|
-
== Contributing
|
164
|
-
{See here}[http://tagaholic.me/contributing.html]
|
165
|
-
|
166
|
-
== Motivation
|
167
|
-
My {tagging obsession}[http://github.com/cldwalker/tag-tree] from the ruby console.
|
168
|
-
|
169
|
-
== Links
|
170
|
-
* http://tagaholic.me/2009/10/14/boson-command-your-ruby-universe.html
|
171
|
-
* http://tagaholic.me/2009/10/15/boson-and-hirb-interactions.html
|
172
|
-
* http://tagaholic.me/2009/10/19/how-boson-enhances-your-irb-experience.html
|
173
|
-
|
174
|
-
== Credits
|
175
|
-
Boson stands on the shoulders of these people and their ideas:
|
176
|
-
* Yehuda Katz for inspiring me with Thor and its awesome option parser (Boson::OptionParser).
|
177
|
-
* Daniel Berger for his original work on thor's awesome option parser.
|
178
|
-
* Dave Thomas for scraping a method's comments (Boson::CommentInspector)
|
179
|
-
* Mauricio Fernandez for scraping a method's arguments (Boson::ArgumentInspector)
|
180
|
-
* Chris Wanstrath for inspiring Boson's libraries with Rip's packages.
|
181
|
-
* And its contributors: @mirell, @martinos
|