boson 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +29 -12
- data/Rakefile +2 -2
- data/VERSION.yml +2 -2
- data/lib/boson.rb +4 -3
- data/lib/boson/command.rb +11 -1
- data/lib/boson/commands/core.rb +6 -6
- data/lib/boson/commands/web_core.rb +17 -4
- data/lib/boson/index.rb +30 -15
- data/lib/boson/inspector.rb +2 -2
- data/lib/boson/inspectors/argument_inspector.rb +4 -4
- data/lib/boson/inspectors/comment_inspector.rb +2 -2
- data/lib/boson/inspectors/method_inspector.rb +8 -6
- data/lib/boson/libraries/file_library.rb +60 -3
- data/lib/boson/libraries/module_library.rb +1 -1
- data/lib/boson/library.rb +23 -2
- data/lib/boson/loader.rb +2 -2
- data/lib/boson/manager.rb +3 -2
- data/lib/boson/namespace.rb +18 -13
- data/lib/boson/option_parser.rb +63 -18
- data/lib/boson/repo.rb +29 -10
- data/lib/boson/runner.rb +19 -13
- data/lib/boson/runners/bin_runner.rb +42 -21
- data/lib/boson/runners/console_runner.rb +54 -0
- data/lib/boson/scientist.rb +92 -17
- data/lib/boson/util.rb +22 -3
- data/lib/boson/view.rb +15 -3
- data/test/bin_runner_test.rb +18 -8
- data/test/loader_test.rb +1 -1
- data/test/manager_test.rb +2 -2
- data/test/repo_test.rb +1 -1
- data/test/runner_test.rb +7 -7
- data/test/scientist_test.rb +8 -7
- data/test/test_helper.rb +14 -3
- metadata +5 -6
- data/lib/boson/runners/repl_runner.rb +0 -40
- data/test/config/index.marshal +0 -0
data/lib/boson/library.rb
CHANGED
@@ -24,9 +24,31 @@ module Boson
|
|
24
24
|
attr_reader :except, :no_alias_creation, :new_module, :new_commands
|
25
25
|
# Optional namespace name for a library. When enabled defaults to a library's name.
|
26
26
|
attr_writer :namespace
|
27
|
+
|
27
28
|
# Creates a library object with a hash of attributes which must include a :name attribute.
|
28
29
|
# Each hash pair maps directly to an instance variable and value. Defaults for attributes
|
29
|
-
# are read from config[:libraries][@library_name].
|
30
|
+
# are read from config[:libraries][@library_name][@attribute].
|
31
|
+
#
|
32
|
+
# Attributes that can be configured:
|
33
|
+
# * *:dependencies*: An array of libraries that this library depends on. A library won't load
|
34
|
+
# unless its dependencies are loaded first.
|
35
|
+
# * *:commands*: A hash or array of commands that belong to this library. A hash configures command attributes
|
36
|
+
# for the given commands with command names pointing to their configs. See Command.new for a
|
37
|
+
# command's configurable attributes. If an array, the commands are set for the given library,
|
38
|
+
# overidding default command detection.
|
39
|
+
# Example:
|
40
|
+
# :commands=>{'commands'=>{:description=>'Lists commands', :alias=>'com'}}
|
41
|
+
# * *:class_commands*: A hash of commands to create. Hash should map command names to any string of ruby code
|
42
|
+
# that ends with a method call.
|
43
|
+
# Example:
|
44
|
+
# :class_commands=>{'spy'=>'Bond.spy', 'create'=>'Alias.manager.create'}
|
45
|
+
# * *:force*: Boolean which forces a library to ignore when a library's methods are overriding existing ones.
|
46
|
+
# Use with caution. Default is false.
|
47
|
+
# * *:object_methods*: Boolean which detects any Object/Kernel methods created when loading a library and automatically
|
48
|
+
# adds them to a library's commands. Default is true.
|
49
|
+
# * *:namespace*: Boolean or string which namespaces a library. When true, the library is automatically namespaced
|
50
|
+
# to the library's name. When a string, the library is namespaced to the string. Default is nil. To control the
|
51
|
+
# namespacing of all libraries see Boson::Repo.config.
|
30
52
|
def initialize(hash)
|
31
53
|
@name = set_name hash.delete(:name)
|
32
54
|
@loaded = false
|
@@ -35,7 +57,6 @@ module Boson
|
|
35
57
|
@commands_hash = {}
|
36
58
|
@commands = []
|
37
59
|
set_config (repo.config[:libraries][@name] || {}).merge(hash)
|
38
|
-
@commands_hash = repo.config[:commands].merge @commands_hash
|
39
60
|
set_command_aliases(repo.config[:command_aliases])
|
40
61
|
@namespace = true if Boson.repo.config[:auto_namespace] && @namespace.nil? &&
|
41
62
|
!Boson::Runner.default_libraries.include?(@module)
|
data/lib/boson/loader.rb
CHANGED
@@ -4,7 +4,7 @@ module Boson
|
|
4
4
|
|
5
5
|
# This module is mixed into Library to give it load() and reload() functionality.
|
6
6
|
# When creating your own Library subclass, you should override load_source_and_set_module and
|
7
|
-
# reload_source_and_set_module. You can override other methods in this module as needed.
|
7
|
+
# reload_source_and_set_module . You can override other methods in this module as needed.
|
8
8
|
module Loader
|
9
9
|
# Loads a library and its dependencies and returns true if library loads correctly.
|
10
10
|
def load
|
@@ -64,7 +64,7 @@ module Boson
|
|
64
64
|
options[:object_methods] = @object_methods if !@object_methods.nil?
|
65
65
|
detected = Util.detect(options, &block)
|
66
66
|
@gems += detected[:gems] if detected[:gems]
|
67
|
-
@commands += detected[:methods]
|
67
|
+
@commands += detected[:methods].map {|e| e.to_s }
|
68
68
|
detected
|
69
69
|
end
|
70
70
|
|
data/lib/boson/manager.rb
CHANGED
@@ -4,6 +4,7 @@ module Boson
|
|
4
4
|
# Raised when a library's append_features returns false.
|
5
5
|
class AppendFeaturesFalseError < StandardError; end
|
6
6
|
|
7
|
+
# Handles loading and reloading of libraries and commands.
|
7
8
|
class Manager
|
8
9
|
class <<self
|
9
10
|
# Loads a library or an array of libraries with options. Manager loads the first library subclass
|
@@ -62,7 +63,7 @@ module Boson
|
|
62
63
|
rescue Exception=>e
|
63
64
|
FileLibrary.reset_file_cache(library.to_s)
|
64
65
|
print_error_message "Unable to #{load_method} library #{library}. Reason: #{$!}" + "\n" +
|
65
|
-
e.backtrace.slice(0,3).join("\n")
|
66
|
+
e.backtrace.slice(0,3).map {|e| " " + e }.join("\n")
|
66
67
|
ensure
|
67
68
|
Inspector.disable if Inspector.enabled
|
68
69
|
end
|
@@ -103,7 +104,7 @@ module Boson
|
|
103
104
|
|
104
105
|
def loader_create(source)
|
105
106
|
lib_class = Library.handle_blocks.find {|k,v| v.call(source) } or raise(LoaderError, "Library #{source} not found.")
|
106
|
-
lib_class[0].new(:name=>source
|
107
|
+
lib_class[0].new(@options.merge(:name=>source))
|
107
108
|
end
|
108
109
|
|
109
110
|
def after_load
|
data/lib/boson/namespace.rb
CHANGED
@@ -1,25 +1,29 @@
|
|
1
1
|
module Boson
|
2
|
+
# Used in all things namespace.
|
2
3
|
class Namespace
|
3
|
-
|
4
|
-
obj = library.namespace_object
|
5
|
-
obj.instance_eval("class<<self;self;end").send(:define_method, :boson_commands) {
|
6
|
-
self.class.instance_methods(false) }
|
7
|
-
obj.instance_eval("class<<self;self;end").send(:define_method, :object_delegate?) { true }
|
8
|
-
namespaces[name.to_s] = obj
|
9
|
-
end
|
10
|
-
|
4
|
+
# Hash of created namespace names to namespace objects
|
11
5
|
def self.namespaces
|
12
6
|
@namespaces ||= {}
|
13
7
|
end
|
14
8
|
|
9
|
+
# Creates a namespace given its name and the library it belongs to.
|
15
10
|
def self.create(name, library)
|
16
|
-
if library.object_namespace && library.module.instance_methods.include?(name)
|
11
|
+
if library.object_namespace && library.module.instance_methods.map {|e| e.to_s}.include?(name)
|
17
12
|
library.include_in_universe
|
18
13
|
create_object_namespace(name, library)
|
19
14
|
else
|
20
15
|
create_basic_namespace(name, library)
|
21
16
|
end
|
22
17
|
end
|
18
|
+
#:stopdoc:
|
19
|
+
|
20
|
+
def self.create_object_namespace(name, library)
|
21
|
+
obj = library.namespace_object
|
22
|
+
obj.instance_eval("class<<self;self;end").send(:define_method, :boson_commands) {
|
23
|
+
self.class.instance_methods(false) }
|
24
|
+
obj.instance_eval("class<<self;self;end").send(:define_method, :object_delegate?) { true }
|
25
|
+
namespaces[name.to_s] = obj
|
26
|
+
end
|
23
27
|
|
24
28
|
def self.create_basic_namespace(name, library)
|
25
29
|
namespaces[name.to_s] = new(name, library)
|
@@ -32,14 +36,15 @@ module Boson
|
|
32
36
|
class <<self; self end.send :include, @library.module
|
33
37
|
end
|
34
38
|
|
35
|
-
def boson_commands
|
36
|
-
@library.module.instance_methods
|
37
|
-
end
|
38
|
-
|
39
39
|
def object_delegate?; false; end
|
40
40
|
|
41
41
|
def method_missing(method, *args, &block)
|
42
42
|
Boson.can_invoke?(method) ? Boson.invoke(method, *args, &block) : super
|
43
43
|
end
|
44
|
+
#:startdoc:
|
45
|
+
# List of subcommands for the namespace.
|
46
|
+
def boson_commands
|
47
|
+
@library.module.instance_methods.map {|e| e.to_s }
|
48
|
+
end
|
44
49
|
end
|
45
50
|
end
|
data/lib/boson/option_parser.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Boson
|
2
|
-
# Simple Hash with indifferent access
|
3
|
-
class IndifferentAccessHash < ::Hash
|
2
|
+
# Simple Hash with indifferent access. Used by OptionParser.
|
3
|
+
class IndifferentAccessHash < ::Hash #:nodoc:
|
4
4
|
def initialize(hash)
|
5
5
|
super()
|
6
|
-
|
6
|
+
hash.each {|k,v| self[k] = v }
|
7
7
|
end
|
8
8
|
|
9
9
|
def [](key)
|
@@ -24,9 +24,21 @@ module Boson
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
# This class provides option parsing for boolean, string, numeric and array
|
28
|
+
# values given a simple hash of options. Setting option values should be straightforward for
|
29
|
+
# *nix people. By option type:
|
30
|
+
# * *:boolean*: These don't have values i.e. '--debug'. To toogle a boolean, prepend with --no- i.e. '--no-debug'.
|
31
|
+
# Multiple booleans can be joined together i.e. '-d -f -t' == '-dft'.
|
32
|
+
# * *:string*: Separate name from value with space or '=' i.e. '--color red' or '--color=red'.
|
33
|
+
# * *:numeric*: Receives values as :string does or by appending number right after name i.e.
|
34
|
+
# '-N3' == '-N=3'.
|
35
|
+
# * *:array*: Receives values as :string does. Multiple values are split by ',' i.e.
|
36
|
+
# '--fields 1,2,3' -> ['1','2','3']. The split character can be configured as explained at
|
37
|
+
# OptionParser.new .
|
27
38
|
# This is a modified version of Yehuda Katz's Thor::Options class which is a modified version
|
28
|
-
# of Daniel Berger's Getopt::Long class
|
39
|
+
# of Daniel Berger's Getopt::Long class (licensed under Ruby's license).
|
29
40
|
class OptionParser
|
41
|
+
# Raised for all OptionParser errors
|
30
42
|
class Error < StandardError; end
|
31
43
|
|
32
44
|
NUMERIC = /(\d*\.\d+|\d+)/
|
@@ -37,28 +49,54 @@ module Boson
|
|
37
49
|
SHORT_NUM = /^(-[a-zA-Z])#{NUMERIC}$/i
|
38
50
|
|
39
51
|
attr_reader :leading_non_opts, :trailing_non_opts, :opt_aliases
|
40
|
-
|
52
|
+
|
53
|
+
# Array of arguments left after defined options have been parsed out by parse.
|
41
54
|
def non_opts
|
42
55
|
leading_non_opts + trailing_non_opts
|
43
56
|
end
|
44
57
|
|
45
|
-
# Takes
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
58
|
+
# Takes a hash of options. Each option, a key-value pair, must provide the option's
|
59
|
+
# name and type. Names longer than one character are accessed with '--' while
|
60
|
+
# one character names are accessed with '-'. Names can be symbols, strings
|
61
|
+
# or even dasherized strings:
|
62
|
+
#
|
63
|
+
# Boson::OptionParser.new :debug=>:boolean, 'level'=>:numeric,
|
64
|
+
# '--fields'=>:array
|
65
|
+
#
|
66
|
+
# Options can have default values and implicit types simply by changing the
|
67
|
+
# option type for the default value:
|
68
|
+
#
|
69
|
+
# Boson::OptionParser.new :debug=>true, 'level'=>3.1, :fields=>%w{f1 f2}
|
70
|
+
#
|
71
|
+
# By default every option name longer than one character is given an alias,
|
72
|
+
# the first character from its name. For example, the --fields option
|
73
|
+
# has -f as its alias. You can override the default alias by providing your own
|
74
|
+
# option aliases as an array in the option's key.
|
75
|
+
#
|
76
|
+
# Boson::OptionParser.new [:debug, :damnit, :D]=>true
|
77
|
+
#
|
78
|
+
# Note that aliases are accessed the same way as option names. For the above,
|
79
|
+
# --debug, --damnit and -D all refer to the same option.
|
50
80
|
#
|
51
|
-
#
|
52
|
-
#
|
81
|
+
# Options can have additional attributes by passing a hash to the option value instead of
|
82
|
+
# a type or default:
|
83
|
+
#
|
84
|
+
# Boson::OptionParser.new :fields=>{:type=>:array, :values=>%w{f1 f2 f3},
|
85
|
+
# :enum=>false}
|
53
86
|
#
|
54
|
-
#
|
87
|
+
# Here are the available option attributes:
|
55
88
|
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
89
|
+
# * *:type*: This or :default is required. Available types are :string, :boolean, :array, :numeric.
|
90
|
+
# * *:default*: This or :type is required. This is the default value an option has when not passed.
|
91
|
+
# * *:values*: An array of values an option can have. Available for :array and :string options. Values here
|
92
|
+
# can be aliased by typing a unique string it starts with. For example:
|
93
|
+
#
|
94
|
+
# For values foo, odd, optional: f refers to foo, o to odd and op to optional.
|
61
95
|
#
|
96
|
+
# * *:enum*: Boolean indicating if an option enforces values in :values. Default is true. Available for
|
97
|
+
# :array and :string options.
|
98
|
+
# * *:split*: Only for :array options. A string or regular expression on which an array value splits
|
99
|
+
# to produce an array of values. Default is ','.
|
62
100
|
def initialize(opts)
|
63
101
|
@defaults = {}
|
64
102
|
@opt_aliases = {}
|
@@ -114,6 +152,11 @@ module Boson
|
|
114
152
|
}
|
115
153
|
end
|
116
154
|
|
155
|
+
# Parses an array of arguments for defined options to return a hash. Once the parser
|
156
|
+
# recognizes a valid option, it continues to parse until an non option argument is detected.
|
157
|
+
# Flags that can be passed to the parser:
|
158
|
+
# * :opts_before_args: When true options must come before arguments. Default is false.
|
159
|
+
# * :delete_invalid_opts: When true deletes any invalid options left after parsing. Default is false.
|
117
160
|
def parse(args, flags={})
|
118
161
|
@args = args
|
119
162
|
# start with defaults
|
@@ -151,6 +194,7 @@ module Boson
|
|
151
194
|
hash
|
152
195
|
end
|
153
196
|
|
197
|
+
# One-line option usage
|
154
198
|
def formatted_usage
|
155
199
|
return "" if @opt_types.empty?
|
156
200
|
@opt_types.map do |opt, type|
|
@@ -173,6 +217,7 @@ module Boson
|
|
173
217
|
|
174
218
|
alias :to_s :formatted_usage
|
175
219
|
|
220
|
+
# More verbose option help in the form of a table.
|
176
221
|
def print_usage_table(render_options={})
|
177
222
|
aliases = @opt_aliases.invert
|
178
223
|
additional = [:desc, :values].select {|e| (@option_attributes || {}).values.any? {|f| f.key?(e) } }
|
data/lib/boson/repo.rb
CHANGED
@@ -1,35 +1,54 @@
|
|
1
1
|
%w{yaml fileutils}.each {|e| require e }
|
2
2
|
module Boson
|
3
|
+
# A class for repositories. A repository has a root directory with required subdirectories config/ and
|
4
|
+
# commands/ and optional subdirectory lib/. Each repository has a primary config file at config/boson.yml.
|
3
5
|
class Repo
|
4
|
-
def self.commands_dir(dir)
|
6
|
+
def self.commands_dir(dir) #:nodoc:
|
5
7
|
File.join(dir, 'commands')
|
6
8
|
end
|
7
9
|
|
8
10
|
attr_accessor :dir, :config
|
11
|
+
# Creates a repository given a root directory.
|
9
12
|
def initialize(dir)
|
10
13
|
@dir = dir
|
11
14
|
end
|
12
15
|
|
16
|
+
# Points to the config/ subdirectory and is automatically created when called. Used for config files.
|
13
17
|
def config_dir
|
14
|
-
@config_dir ||= FileUtils.mkdir_p
|
18
|
+
@config_dir ||= FileUtils.mkdir_p("#{dir}/config") && "#{dir}/config"
|
15
19
|
end
|
16
20
|
|
21
|
+
# Points to the commands/ subdirectory and is automatically created when called. Used for command libraries.
|
17
22
|
def commands_dir
|
18
|
-
@commands_dir ||=
|
23
|
+
@commands_dir ||= (cdir = self.class.commands_dir(@dir)) && FileUtils.mkdir_p(cdir) && cdir
|
19
24
|
end
|
20
25
|
|
26
|
+
# A hash read from the YAML config file at config/boson.yml.
|
27
|
+
# {See here}[http://github.com/cldwalker/irbfiles/blob/master/boson/config/boson.yml] for an example config file.
|
21
28
|
# ==== Valid config keys:
|
22
|
-
# [:libraries] Hash of libraries mapping their name to attribute hashes.
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# [:
|
26
|
-
#
|
27
|
-
#
|
29
|
+
# [:libraries] Hash of libraries mapping their name to attribute hashes. See Library.new for configurable attributes.
|
30
|
+
# Example:
|
31
|
+
# :libraries=>{'completion'=>{:namespace=>true}}
|
32
|
+
# [:command_aliases] Hash of commands names and their aliases. Since this is global it will be read by _all_ libraries.
|
33
|
+
# This is useful for quickly creating aliases without having to worry about placing them under
|
34
|
+
# the correct library config. For non-global aliasing, aliases should be placed under the :command_aliases
|
35
|
+
# key of a library entry in :libraries.
|
36
|
+
# Example:
|
37
|
+
# :command_aliases=>{'libraries'=>'lib', 'commands'=>'com'}
|
38
|
+
# [:console_defaults] Array of libraries to load at start up when used in irb. Default is to load all library files and libraries defined
|
39
|
+
# in the config.
|
40
|
+
# [:bin_defaults] Array of libraries to load at start up when used from the commandline. Default is no libraries.
|
41
|
+
# [:add_load_path] Boolean specifying whether to add a load path pointing to the lib subdirectory/. This is useful in sharing
|
42
|
+
# classes between libraries without resorting to packaging them as gems. Defaults to false if the lib
|
43
|
+
# subdirectory doesn't exist in the boson directory.
|
44
|
+
# [:error_method_conflicts] Boolean specifying library loading behavior when its methods conflicts with existing methods in
|
28
45
|
# the global namespace. When set to false, Boson automatically puts the library in its own namespace.
|
29
46
|
# When set to true, the library fails to load explicitly. Default is false.
|
47
|
+
# [:console] Console to load when using --console from commandline. Default is irb.
|
48
|
+
# [:auto_namespace] Boolean which automatically namespaces all user-defined libraries. Default is false.
|
30
49
|
def config(reload=false)
|
31
50
|
if reload || @config.nil?
|
32
|
-
default = {:
|
51
|
+
default = {:libraries=>{}, :command_aliases=>{}, :console_defaults=>[]}
|
33
52
|
@config = default.merge(YAML::load_file(config_dir + '/boson.yml')) rescue default
|
34
53
|
end
|
35
54
|
@config
|
data/lib/boson/runner.rb
CHANGED
@@ -1,37 +1,43 @@
|
|
1
1
|
module Boson
|
2
|
+
# Base class for runners.
|
2
3
|
class Runner
|
3
4
|
class<<self
|
5
|
+
# Enables view, adds local load path and loads default_libraries
|
4
6
|
def init
|
5
7
|
View.enable
|
6
8
|
add_load_path
|
7
9
|
Manager.load default_libraries, load_options
|
8
10
|
end
|
9
11
|
|
10
|
-
|
11
|
-
Boson.repos.each {|repo|
|
12
|
-
if repo.config[:add_load_path] || File.exists?(File.join(repo.dir, 'lib'))
|
13
|
-
$: << File.join(repo.dir, 'lib') unless $:.include? File.expand_path(File.join(repo.dir, 'lib'))
|
14
|
-
end
|
15
|
-
}
|
16
|
-
end
|
17
|
-
|
18
|
-
def load_options
|
19
|
-
{:verbose=>@options[:verbose]}
|
20
|
-
end
|
21
|
-
|
12
|
+
# Libraries that come with Boson
|
22
13
|
def default_libraries
|
23
14
|
[Boson::Commands::Core, Boson::Commands::WebCore]
|
24
15
|
end
|
25
16
|
|
17
|
+
# Libraries detected in repositories
|
26
18
|
def detected_libraries
|
27
19
|
Boson.repos.map {|repo| Dir[File.join(repo.commands_dir, '**/*.rb')].
|
28
20
|
map {|e| e.gsub(/.*commands\//,'').gsub('.rb','') } }.flatten
|
29
21
|
end
|
30
22
|
|
23
|
+
# Libraries specified in config files and detected_libraries
|
31
24
|
def all_libraries
|
32
25
|
(detected_libraries + Boson.repos.map {|e| e.config[:libraries].keys}.flatten).uniq
|
33
26
|
end
|
34
27
|
|
28
|
+
#:stopdoc:
|
29
|
+
def add_load_path
|
30
|
+
Boson.repos.each {|repo|
|
31
|
+
if repo.config[:add_load_path] || File.exists?(File.join(repo.dir, 'lib'))
|
32
|
+
$: << File.join(repo.dir, 'lib') unless $:.include? File.expand_path(File.join(repo.dir, 'lib'))
|
33
|
+
end
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_options
|
38
|
+
{:verbose=>@options[:verbose]}
|
39
|
+
end
|
40
|
+
|
35
41
|
def define_autoloader
|
36
42
|
class << ::Boson.main_object
|
37
43
|
def method_missing(method, *args, &block)
|
@@ -45,7 +51,7 @@ module Boson
|
|
45
51
|
end
|
46
52
|
end
|
47
53
|
end
|
48
|
-
|
54
|
+
#:startdoc:
|
49
55
|
end
|
50
56
|
end
|
51
57
|
end
|
@@ -1,36 +1,53 @@
|
|
1
1
|
module Boson
|
2
|
+
# Runs Boson from the commandline. Usage for the boson shell command looks like this:
|
3
|
+
# boson [GLOBAL OPTIONS] [COMMAND] [ARGS] [COMMAND OPTIONS]
|
4
|
+
#
|
5
|
+
# The boson executable comes with these global options:
|
6
|
+
# [:help] Gives a basic help of global options. When a command is given the help shifts to a command's help.
|
7
|
+
# [:verbose] Using this along with :help option shows more help. Also gives verbosity to other actions i.e. loading.
|
8
|
+
# [:index] Updates index. This should be called in the unusual case that Boson doesn't detect new commands
|
9
|
+
# and libraries.
|
10
|
+
# [:execute] Like ruby -e, this executes a string of ruby code. However, this has the advantage that all
|
11
|
+
# commands are available as normal methods, automatically loading as needed. This is a good
|
12
|
+
# way to call commands that take non-string arguments.
|
13
|
+
# [:console] This drops Boson into irb after having loaded default commands and any explict libraries with
|
14
|
+
# :load option. This is a good way to start irb with only certain libraries loaded.
|
15
|
+
# [:load] Explicitly loads a list of libraries separated by commas. Most useful when used with :console option.
|
16
|
+
# Can also be used to explicitly load libraries that aren't being detected automatically.
|
17
|
+
# [:render] Pretty formats the results of commands without options. Handy for commands that return arrays.
|
2
18
|
class BinRunner < Runner
|
3
19
|
GLOBAL_OPTIONS = {
|
4
20
|
:verbose=>{:type=>:boolean, :desc=>"Verbose description of loading libraries or help"},
|
5
|
-
:index=>{:type=>:boolean, :desc=>"Updates index"},
|
21
|
+
:index=>{:type=>:boolean, :desc=>"Updates index for libraries and commands"},
|
6
22
|
:execute=>{:type=>:string, :desc=>"Executes given arguments as a one line script"},
|
7
|
-
:
|
23
|
+
:console=>{:type=>:boolean, :desc=>"Drops into irb with default and explicit libraries loaded"},
|
8
24
|
:help=>{:type=>:boolean, :desc=>"Displays this help message or a command's help if given a command"},
|
9
|
-
:load=>{:type=>:array, :values=>all_libraries, :enum=>false, :desc=>"A comma delimited array of libraries to load"}
|
10
|
-
|
25
|
+
:load=>{:type=>:array, :values=>all_libraries, :enum=>false, :desc=>"A comma delimited array of libraries to load"},
|
26
|
+
:render=>{:type=>:boolean, :desc=>"Renders a Hirb view from result of command without options"}
|
27
|
+
} #:nodoc:
|
11
28
|
|
12
29
|
class <<self
|
13
30
|
attr_accessor :command
|
31
|
+
# Starts, processes and ends a commandline request.
|
14
32
|
def start(args=ARGV)
|
15
33
|
@command, @options, @args = parse_args(args)
|
16
|
-
return print_usage if args.empty? || (@command.nil? && !@options[:
|
17
|
-
return
|
34
|
+
return print_usage if args.empty? || (@command.nil? && !@options[:console] && !@options[:execute])
|
35
|
+
return ConsoleRunner.bin_start(@options[:console], @options[:load]) if @options[:console]
|
18
36
|
init
|
19
37
|
|
20
38
|
if @options[:help]
|
21
|
-
|
39
|
+
Boson.invoke(:usage, @command, :verbose=>@options[:verbose])
|
22
40
|
elsif @options[:execute]
|
23
41
|
Boson.main_object.instance_eval @options[:execute]
|
24
42
|
else
|
25
43
|
execute_command
|
26
44
|
end
|
27
45
|
rescue Exception
|
28
|
-
|
46
|
+
print_error_message (@command && !Boson.can_invoke?(@command[/\w+/])) ?
|
29
47
|
"Error: Command '#{@command}' not found" : "Error: #{$!.message}"
|
30
|
-
message += "\nActual error: #{$!}\n" + $!.backtrace.inspect if @options && @options[:verbose]
|
31
|
-
$stderr.puts message
|
32
48
|
end
|
33
49
|
|
50
|
+
# Loads the given command.
|
34
51
|
def init
|
35
52
|
super
|
36
53
|
Index.update(:verbose=>true) if @options[:index]
|
@@ -43,6 +60,12 @@ module Boson
|
|
43
60
|
end
|
44
61
|
end
|
45
62
|
|
63
|
+
#:stopdoc:
|
64
|
+
def print_error_message(message)
|
65
|
+
message += "\nActual error: #{$!}\n" + $!.backtrace.inspect if @options && @options[:verbose]
|
66
|
+
$stderr.puts message
|
67
|
+
end
|
68
|
+
|
46
69
|
def load_command_by_index
|
47
70
|
Index.update(:verbose=>@options[:verbose]) if !@options[:index] && Boson.can_invoke?(@command) && !@options[:help]
|
48
71
|
if !Boson.can_invoke?(@command) && ((lib = Index.find_library(@command)) ||
|
@@ -58,15 +81,12 @@ module Boson
|
|
58
81
|
def execute_command
|
59
82
|
command, subcommand = @command.include?('.') ? @command.split('.', 2) : [@command, nil]
|
60
83
|
dispatcher = subcommand ? Boson.invoke(command) : Boson.main_object
|
61
|
-
@args = @args.join(" ") if ((com = Boson::Command.find(@command)) && com.option_command?)
|
62
84
|
render_output dispatcher.send(subcommand || command, *@args)
|
63
85
|
rescue ArgumentError
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
def print_command_help
|
69
|
-
Boson.invoke(:usage, @command, :verbose=>@options[:verbose])
|
86
|
+
# for the rare case it's raise outside of boson
|
87
|
+
raise unless $!.backtrace.first.include?('boson/')
|
88
|
+
print_error_message "'#{@command}' was called incorrectly."
|
89
|
+
Boson.invoke(:usage, @command)
|
70
90
|
end
|
71
91
|
|
72
92
|
def parse_args(args)
|
@@ -77,10 +97,10 @@ module Boson
|
|
77
97
|
end
|
78
98
|
|
79
99
|
def render_output(output)
|
80
|
-
if Scientist.global_options
|
81
|
-
puts output.inspect
|
82
|
-
|
83
|
-
View.render(output)
|
100
|
+
if Scientist.global_options && !Scientist.rendered && !View.silent_object?(output)
|
101
|
+
puts output.inspect
|
102
|
+
elsif !Scientist.global_options && @options[:render]
|
103
|
+
View.render(output, :silence_booleans=>true)
|
84
104
|
end
|
85
105
|
end
|
86
106
|
|
@@ -95,6 +115,7 @@ module Boson
|
|
95
115
|
Boson.invoke :commands, "", :fields=>["name", "usage", "description"], :description=>false
|
96
116
|
end
|
97
117
|
end
|
118
|
+
#:startdoc:
|
98
119
|
end
|
99
120
|
end
|
100
121
|
end
|