boson 0.4.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemspec +6 -7
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.rdoc +1 -1
- data/README.md +144 -0
- data/README.rdoc +2 -2
- data/Upgrading.md +23 -0
- data/bin/boson +2 -2
- data/lib/boson.rb +44 -52
- data/lib/boson/bare_runner.rb +83 -0
- data/lib/boson/bin_runner.rb +114 -0
- data/lib/boson/command.rb +92 -132
- data/lib/boson/inspector.rb +49 -48
- data/lib/boson/library.rb +71 -120
- data/lib/boson/loader.rb +73 -84
- data/lib/boson/manager.rb +131 -135
- data/lib/boson/method_inspector.rb +112 -0
- data/lib/boson/option_command.rb +71 -154
- data/lib/boson/option_parser.rb +178 -173
- data/lib/boson/options.rb +46 -32
- data/lib/boson/runner.rb +58 -66
- data/lib/boson/runner_library.rb +31 -0
- data/lib/boson/scientist.rb +48 -81
- data/lib/boson/util.rb +46 -61
- data/lib/boson/version.rb +1 -1
- data/test/bin_runner_test.rb +53 -191
- data/test/command_test.rb +5 -9
- data/test/deps.rip +2 -2
- data/test/loader_test.rb +18 -216
- data/test/manager_test.rb +69 -79
- data/test/method_inspector_test.rb +12 -36
- data/test/option_parser_test.rb +45 -32
- data/test/runner_library_test.rb +10 -0
- data/test/runner_test.rb +158 -28
- data/test/scientist_test.rb +9 -147
- data/test/test_helper.rb +87 -52
- metadata +30 -72
- data/deps.rip +0 -2
- data/lib/boson/commands.rb +0 -7
- data/lib/boson/commands/core.rb +0 -77
- data/lib/boson/commands/web_core.rb +0 -153
- data/lib/boson/index.rb +0 -48
- data/lib/boson/inspectors/argument_inspector.rb +0 -97
- data/lib/boson/inspectors/comment_inspector.rb +0 -100
- data/lib/boson/inspectors/method_inspector.rb +0 -98
- data/lib/boson/libraries/file_library.rb +0 -144
- data/lib/boson/libraries/gem_library.rb +0 -30
- data/lib/boson/libraries/local_file_library.rb +0 -30
- data/lib/boson/libraries/module_library.rb +0 -37
- data/lib/boson/libraries/require_library.rb +0 -23
- data/lib/boson/namespace.rb +0 -31
- data/lib/boson/pipe.rb +0 -147
- data/lib/boson/pipes.rb +0 -75
- data/lib/boson/repo.rb +0 -107
- data/lib/boson/runners/bin_runner.rb +0 -208
- data/lib/boson/runners/console_runner.rb +0 -58
- data/lib/boson/view.rb +0 -95
- data/test/argument_inspector_test.rb +0 -62
- data/test/commands_test.rb +0 -22
- data/test/comment_inspector_test.rb +0 -126
- data/test/file_library_test.rb +0 -42
- data/test/pipes_test.rb +0 -65
- data/test/repo_index_test.rb +0 -122
- data/test/repo_test.rb +0 -23
data/.gemspec
CHANGED
@@ -9,17 +9,16 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.email = "gabriel.horner@gmail.com"
|
10
10
|
s.homepage = "http://tagaholic.me/boson/"
|
11
11
|
s.summary = "A command/task framework similar to rake and thor that opens your ruby universe to the commandline and irb."
|
12
|
-
s.description = "Boson is a command/task framework
|
12
|
+
s.description = "Boson is a modular command/task framework. Thanks to its rich set of plugins, it differentiates itself from rake and thor by being usable from irb and the commandline, having optional automated views generated by hirb and allowing libraries to be written as plain ruby. Works with ruby >= 1.9.2"
|
13
13
|
s.required_rubygems_version = ">= 1.3.6"
|
14
14
|
s.executables = ['boson']
|
15
|
-
s.
|
16
|
-
s.add_dependency 'alias', '>= 0.2.2'
|
17
|
-
s.add_development_dependency 'mocha', '= 0.9.8'
|
15
|
+
s.add_development_dependency 'mocha', '~> 0.10.4'
|
18
16
|
s.add_development_dependency 'bacon', '>= 1.1.0'
|
19
17
|
s.add_development_dependency 'mocha-on-bacon'
|
20
18
|
s.add_development_dependency 'bacon-bits'
|
21
|
-
s.add_development_dependency '
|
22
|
-
s.files = Dir.glob(%w[{lib,test}/**/*.rb bin/* [A-Z]*.{txt,rdoc} ext/**/*.{rb,c} **/deps.rip]) + %w{Rakefile .gemspec .travis.yml}
|
23
|
-
s.
|
19
|
+
s.add_development_dependency 'bahia', '>= 0.4.0'
|
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
|
+
s.files += ['.rspec']
|
22
|
+
s.extra_rdoc_files = ["README.md", "LICENSE.txt"]
|
24
23
|
s.license = 'MIT'
|
25
24
|
end
|
data/.rspec
ADDED
data/.travis.yml
CHANGED
data/CHANGELOG.rdoc
CHANGED
data/README.md
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
## Description
|
2
|
+
|
3
|
+
Boson is a modular command/task framework. Thanks to its rich set of plugins,
|
4
|
+
it differentiates itself from rake and thor by being usable from irb and the
|
5
|
+
commandline, having automated views generated by hirb and allowing libraries to
|
6
|
+
be written as plain ruby. Works with on all major rubies for ruby >= 1.9.2
|
7
|
+
|
8
|
+
## New Boson
|
9
|
+
|
10
|
+
Starting with 1.0, boson has changed significantly. Please read [the upgrading
|
11
|
+
doc](http://rdoc.info/gems/boson/file/Upgrading.md) if you have an older version
|
12
|
+
or if your [reading about boson](http://tagaholic.me/blog.html#gem:name=boson)
|
13
|
+
predates 2012.
|
14
|
+
|
15
|
+
Boson has been rewritten to have a smaller core (no dependencies) with optional
|
16
|
+
plugins to hook into its various features. The major focus of 1.0 has been to
|
17
|
+
provide an easy way for third-party gems to create their executable and define
|
18
|
+
subcommands with options.
|
19
|
+
|
20
|
+
## Docs
|
21
|
+
|
22
|
+
Nicely formatted docs are available
|
23
|
+
[here](http://rdoc.info/gems/boson/file/README.md).
|
24
|
+
|
25
|
+
## Example Executable
|
26
|
+
|
27
|
+
For a gem with an executable, bin/cow:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
#!/usr/bin/env ruby
|
31
|
+
require 'boson/runner'
|
32
|
+
|
33
|
+
class CowRunner < Boson::Runner
|
34
|
+
option :urgent, type: :boolean
|
35
|
+
def say(text, options={})
|
36
|
+
text.capitalize! if options[:urgent]
|
37
|
+
puts text
|
38
|
+
end
|
39
|
+
|
40
|
+
def moo
|
41
|
+
puts "MOOOO"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
CowRunner.start
|
46
|
+
```
|
47
|
+
|
48
|
+
You can now execute cow with say and moo subcommands:
|
49
|
+
|
50
|
+
$ cow say hungry
|
51
|
+
hungry
|
52
|
+
$ cow moo
|
53
|
+
MOOOO
|
54
|
+
# use say's urgent option
|
55
|
+
$ cow say hungry -urgent
|
56
|
+
HUNGRY
|
57
|
+
|
58
|
+
You'll notice that this syntax is powerful and concise and is very similar to
|
59
|
+
thor's API. Subcommands map to ruby methods and the class represents the executable.
|
60
|
+
|
61
|
+
## Comparison to Thor
|
62
|
+
|
63
|
+
Since boson and it's rewrite are both heavily inspired by [thor](http://github.com/wycats/thor), it
|
64
|
+
makes sense to compare them.
|
65
|
+
|
66
|
+
First, what I consider pros boson has over thor. Boson
|
67
|
+
|
68
|
+
* is designed to handle plugins. This means it core parts are extendable by
|
69
|
+
modules and core components like commands can have arbitrary metadata
|
70
|
+
associated with them.
|
71
|
+
* has a rich set of plugins. See [boson-more](http://github.com/cldwalker/boson-more).
|
72
|
+
* has commands that are easily testable. Whereas thor has options that automagically
|
73
|
+
appear in command methods, boson explicitly passes options to its command
|
74
|
+
method as a hash i.e. `MyRunner.new.subcommand(arg, verbose: true)`. This
|
75
|
+
also allows commands to just be called as ruby, with no magic to consider.
|
76
|
+
* supports custom-user option types i.e. creating a Date option type. See
|
77
|
+
Boson::Options.
|
78
|
+
* supports custom method decorators i.e. methods like desc that add functionality
|
79
|
+
to a command. While boson supports option, options, desc and config out of the box,
|
80
|
+
users can create their own.
|
81
|
+
* automatically creates usage for your subcommand. With thor you need to
|
82
|
+
manually define your usage with desc: `desc "SOME USAGE", "SOME DESCRIPTION"`
|
83
|
+
* is lenient about descriptions. Describe commands at your leisure. With thor
|
84
|
+
you must define a desc.
|
85
|
+
* has a smaller blacklist for command names i.e. just Kernel + Object method
|
86
|
+
names. Thor has a bigger
|
87
|
+
[blacklist](https://github.com/wycats/thor/blob/a24b6697a37d9bc0c0ea94ef9bf2cdbb33b8abb9/lib/thor/base.rb#L18-19) due to its design.
|
88
|
+
|
89
|
+
Now for pros thor has over boson. Thor
|
90
|
+
|
91
|
+
* is widely used and thus has been community QAed thoroughly.
|
92
|
+
* supports generators as a major feature.
|
93
|
+
* is more stable as its feature set is mostly frozen.
|
94
|
+
* is used by rails and thus is guaranteed support for some time.
|
95
|
+
* supports ruby 1.8.7.
|
96
|
+
* TODO: I'm sure there's more
|
97
|
+
|
98
|
+
## Writing Plugins
|
99
|
+
|
100
|
+
The most common way to write a plugin is to extend one of the many method hooks
|
101
|
+
available. Any methods that are defined in an API or APIClassMethods module
|
102
|
+
are extendable. For example, if you want to extend what any boson-based
|
103
|
+
executable does first, extend BareRunner.start:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
module CustomStartUp
|
107
|
+
def start(*)
|
108
|
+
super
|
109
|
+
# additional startup
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
BareRunner.extend CustomStartUp
|
114
|
+
```
|
115
|
+
|
116
|
+
Notice that `extend` was used to extend a class method. To extend an instance
|
117
|
+
method you would use `include`. Also notice that you use `super` in an
|
118
|
+
overridden method to call original functionality. If you don't, you're
|
119
|
+
possibly overridden existing functionality, which is fine as long as you know
|
120
|
+
what you are overriding.
|
121
|
+
|
122
|
+
For many plugin examples, see
|
123
|
+
[boson-more](http://github.com/cldwalker/boson-more).
|
124
|
+
|
125
|
+
## Bugs/Issues
|
126
|
+
|
127
|
+
Please report them [on github](http://github.com/cldwalker/boson/issues).
|
128
|
+
If the issue is about upgrading from old boson, please file it in
|
129
|
+
[boson-more](http://github.com/cldwalker/boson-more/issues).
|
130
|
+
|
131
|
+
## Contributing
|
132
|
+
[See here](http://tagaholic.me/contributing.html)
|
133
|
+
|
134
|
+
## Motiviation
|
135
|
+
|
136
|
+
Motivation for the new boson is all the damn executables I'm making.
|
137
|
+
|
138
|
+
## Credits
|
139
|
+
Boson stands on the shoulders of these people and their ideas:
|
140
|
+
|
141
|
+
* Contributors: @mirell, @martinos
|
142
|
+
* Yehuda Katz for Thor and its awesome option parser (Boson::OptionParser).
|
143
|
+
* Daniel Berger for his original work on thor's option parser.
|
144
|
+
* Chris Wanstrath for inspiring Boson's libraries with Rip's packages.
|
data/README.rdoc
CHANGED
@@ -9,9 +9,9 @@ libraries to be written as plain ruby. For my libraries that use this, see
|
|
9
9
|
|
10
10
|
== UPDATE
|
11
11
|
Boson 0.4.x will be the last 1.8 compatible version.
|
12
|
-
Boson 0
|
12
|
+
Boson 1.0 will be a rewrite compatible with only 1.9. Work is going on in {boson2
|
13
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-
|
14
|
+
core, move everything else {to plugins}[https://github.com/cldwalker/boson-more]
|
15
15
|
and to allow gems to use boson to make executables like thor.
|
16
16
|
|
17
17
|
== Features
|
data/Upgrading.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
## Using Old Boson
|
2
|
+
|
3
|
+
Any version before 1.0 is considered the old boson. Although I will accept bug
|
4
|
+
fixes for it (branched from
|
5
|
+
[old_boson](http://github.com/cldwalker/boson/tree/old_boson)), I will *not*
|
6
|
+
accept any new features. Since the new boson supports almost all of [boson's
|
7
|
+
origin functionality](http://tagaholic.me/blog.html#gem:name=boson) via plugins,
|
8
|
+
there is little reason to hang onto this version.
|
9
|
+
|
10
|
+
## Using New Boson
|
11
|
+
|
12
|
+
To enjoy the same experience you've had with the old boson, you'll need to
|
13
|
+
also install boson-more and create a ~/.bosonrc:
|
14
|
+
|
15
|
+
$ gem install boson boson-more
|
16
|
+
$ echo "require 'boson/more'" > ~/.bosonrc
|
17
|
+
|
18
|
+
Your old boson config and libraries should just work. Please file issues with
|
19
|
+
your libraries or any upgrade issues at
|
20
|
+
[boson-more](http://github.com/cldwalker/boson-more).
|
21
|
+
|
22
|
+
If you've written custom plugins for the old Boson, you most likely have to
|
23
|
+
upgrade to the new API.
|
data/bin/boson
CHANGED
data/lib/boson.rb
CHANGED
@@ -1,31 +1,49 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
require 'boson/bare_runner'
|
2
|
+
require 'boson/manager'
|
3
|
+
require 'boson/loader'
|
4
|
+
require 'boson/inspector'
|
5
|
+
require 'boson/library'
|
6
|
+
require 'boson/method_inspector'
|
7
|
+
require 'boson/runner_library'
|
8
|
+
require 'boson/command'
|
9
|
+
require 'boson/util'
|
10
|
+
require 'boson/option_parser'
|
11
|
+
require 'boson/options'
|
12
|
+
require 'boson/scientist'
|
13
|
+
require 'boson/option_command'
|
14
|
+
require 'boson/version'
|
8
15
|
|
9
|
-
# This module stores the libraries, commands
|
16
|
+
# This module stores the libraries, commands and the main_object.
|
10
17
|
#
|
11
18
|
# Useful documentation links:
|
12
|
-
# * Boson::BinRunner - Runs the boson executable
|
13
|
-
# * Boson::ConsoleRunner - Runs Boson from the ruby console
|
14
|
-
# * Boson::Repo.config - Explains main config file
|
15
19
|
# * Boson::Library - All about libraries
|
16
|
-
# * Boson::FileLibrary - Explains creating libraries as files
|
17
20
|
# * Boson::Loader - Explains library module callbacks
|
18
21
|
# * Boson::OptionParser - All about options
|
19
22
|
module Boson
|
20
|
-
# Module which is extended by Boson.main_object to give it command functionality.
|
21
|
-
module Universe; include Commands::Namespace; end
|
22
|
-
NAMESPACE = '.' # Delimits namespace from command
|
23
23
|
extend self
|
24
|
+
|
25
|
+
# Module which is extended by Boson.main_object to give it command functionality.
|
26
|
+
module Universe; end
|
27
|
+
# Module under which most library modules are evaluated.
|
28
|
+
module Commands; end
|
29
|
+
|
30
|
+
# Default config
|
31
|
+
CONFIG = {libraries: {}, command_aliases: {}, option_underscore_search: true}
|
32
|
+
|
24
33
|
# The object which holds and executes all command functionality
|
25
34
|
attr_accessor :main_object
|
26
|
-
attr_accessor :commands, :libraries
|
27
35
|
alias_method :higgs, :main_object
|
28
36
|
|
37
|
+
attr_accessor :commands, :libraries, :config
|
38
|
+
# Prints debugging info when set
|
39
|
+
attr_accessor :debug
|
40
|
+
# Returns true if commands are being executed from a non-ruby shell i.e. bash
|
41
|
+
# Returns nil/false if in a ruby shell i.e. irb.
|
42
|
+
attr_accessor :in_shell
|
43
|
+
# Returns true if in commandline with verbose flag or if set explicitly.
|
44
|
+
# Plugins should use this to display more info.
|
45
|
+
attr_accessor :verbose
|
46
|
+
|
29
47
|
# Array of loaded Boson::Library objects.
|
30
48
|
def libraries
|
31
49
|
@libraries ||= Array.new
|
@@ -36,55 +54,29 @@ module Boson
|
|
36
54
|
@commands ||= Array.new
|
37
55
|
end
|
38
56
|
|
39
|
-
#
|
40
|
-
def
|
41
|
-
@
|
42
|
-
end
|
43
|
-
|
44
|
-
# An optional local repository which defaults to ./lib/boson or ./.boson.
|
45
|
-
def local_repo
|
46
|
-
@local_repo ||= begin
|
47
|
-
ignored_dirs = (repo.config[:ignore_directories] || []).map {|e| File.expand_path(e) }
|
48
|
-
dir = ["lib/boson", ".boson"].find {|e| File.directory?(e) &&
|
49
|
-
File.expand_path(e) != repo.dir && !ignored_dirs.include?(File.expand_path('.')) }
|
50
|
-
Repo.new(dir) if dir
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# The array of loaded repositories containing the main repo and possible local and global repos
|
55
|
-
def repos
|
56
|
-
@repos ||= [repo, local_repo, global_repo].compact
|
57
|
+
# Global config used by most classes
|
58
|
+
def config
|
59
|
+
@config ||= CONFIG
|
57
60
|
end
|
58
61
|
|
59
|
-
#
|
60
|
-
def
|
61
|
-
File.exists?('/etc/boson') ? Repo.new('/etc/boson') : nil
|
62
|
-
end
|
63
|
-
|
64
|
-
def main_object=(value) #:nodoc:
|
62
|
+
# Sets main_object and extends it with commands from Universe
|
63
|
+
def main_object=(value)
|
65
64
|
@main_object = value.extend(Universe)
|
66
65
|
end
|
67
66
|
|
68
|
-
|
67
|
+
# Finds first library that has a value of attribute
|
68
|
+
def library(query, attribute='name')
|
69
69
|
libraries.find {|e| e.send(attribute) == query }
|
70
70
|
end
|
71
71
|
|
72
|
-
# Start Boson by loading repositories and their configured libraries.
|
73
|
-
# See ConsoleRunner.start for its options.
|
74
|
-
def start(options={})
|
75
|
-
ConsoleRunner.start(options)
|
76
|
-
end
|
77
|
-
|
78
72
|
# Invoke an action on the main object.
|
79
73
|
def invoke(*args, &block)
|
80
74
|
main_object.send(*args, &block)
|
81
75
|
end
|
82
76
|
|
83
|
-
#
|
84
|
-
def full_invoke(cmd, args)
|
85
|
-
|
86
|
-
dispatcher = subcommand ? Boson.invoke(command) : Boson.main_object
|
87
|
-
dispatcher.send(subcommand || command, *args)
|
77
|
+
# Similar to invoke but accepts args as an array
|
78
|
+
def full_invoke(cmd, args)
|
79
|
+
main_object.send(cmd, *args)
|
88
80
|
end
|
89
81
|
|
90
82
|
# Boolean indicating if the main object can invoke the given method/command.
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Boson
|
2
|
+
# Base class for runners.
|
3
|
+
class BareRunner
|
4
|
+
DEFAULT_LIBRARIES = []
|
5
|
+
# Default options for parse_args
|
6
|
+
GLOBAL_OPTIONS = {
|
7
|
+
help: { type: :boolean, desc: "Displays this help message" }
|
8
|
+
}
|
9
|
+
|
10
|
+
module API
|
11
|
+
# Loads rc
|
12
|
+
def start(*)
|
13
|
+
@options ||= {}
|
14
|
+
load_rc
|
15
|
+
end
|
16
|
+
|
17
|
+
# Default libraries loaded by init
|
18
|
+
def default_libraries
|
19
|
+
DEFAULT_LIBRARIES
|
20
|
+
end
|
21
|
+
|
22
|
+
def all_libraries
|
23
|
+
default_libraries
|
24
|
+
end
|
25
|
+
|
26
|
+
# Loads default libraries
|
27
|
+
def init
|
28
|
+
Manager.load default_libraries, load_options
|
29
|
+
end
|
30
|
+
|
31
|
+
# Wrapper around abort
|
32
|
+
def abort_with(message)
|
33
|
+
abort message
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class<<self
|
38
|
+
include API
|
39
|
+
|
40
|
+
# Executes a command and handles invalid args
|
41
|
+
def execute_command(cmd, args)
|
42
|
+
Boson.full_invoke(cmd, args)
|
43
|
+
rescue ArgumentError
|
44
|
+
raise if !allowed_argument_error?($!, cmd, args)
|
45
|
+
abort_with "'#{cmd}' was called incorrectly.\nUsage: " + Command.usage(cmd)
|
46
|
+
rescue NoMethodError => err
|
47
|
+
raise if !err.backtrace.first.include?('`full_invoke')
|
48
|
+
no_command_error cmd
|
49
|
+
end
|
50
|
+
|
51
|
+
# Use to abort when no command found
|
52
|
+
def no_command_error(cmd)
|
53
|
+
abort_with %[Could not find command "#{cmd}"]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Determines if a user command argument error or an internal Boson one
|
57
|
+
def allowed_argument_error?(err, cmd, args)
|
58
|
+
(err.message[/wrong number of arguments/] &&
|
59
|
+
(cmd_obj = Command.find(cmd)) && cmd_obj.arg_size != args.size)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def parse_args(args)
|
64
|
+
@option_parser = OptionParser.new(self::GLOBAL_OPTIONS)
|
65
|
+
options = @option_parser.parse(args.dup, :opts_before_args=>true)
|
66
|
+
new_args = @option_parser.non_opts
|
67
|
+
[new_args[0], options, new_args[1..-1]]
|
68
|
+
end
|
69
|
+
|
70
|
+
def load_rc
|
71
|
+
rc = ENV['BOSONRC'] || '~/.bosonrc'
|
72
|
+
load(rc) if !rc.empty? && File.exists?(File.expand_path(rc))
|
73
|
+
rescue StandardError, SyntaxError, LoadError => err
|
74
|
+
warn "Error while loading #{rc}:\n"+
|
75
|
+
"#{err.class}: #{err.message}\n #{err.backtrace.join("\n ")}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def load_options
|
79
|
+
{:verbose=>@options[:verbose]}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|