boson 1.1.1 → 1.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/.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
|