boson 0.4.0 → 1.0.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 +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/lib/boson/library.rb
CHANGED
@@ -1,98 +1,51 @@
|
|
1
1
|
module Boson
|
2
|
-
# A library is a group of commands (Command objects) usually grouped together
|
3
|
-
# Libraries are loaded from different sources depending on the
|
4
|
-
#
|
5
|
-
# See Loader for callbacks a library's module can have.
|
6
|
-
#
|
7
|
-
# == Naming a Library Module
|
8
|
-
# Although you can name a library module almost anything, here's the fine print:
|
9
|
-
# * A module can have any name if it's the only module in a library.
|
10
|
-
# * If there are multiple modules in a file library, the module's name must be a camelized version
|
11
|
-
# of the file's basename i.e. ~/.boson/commands/ruby_core.rb -> RubyCore.
|
12
|
-
# * Although modules are evaluated under the Boson::Commands namespace, Boson will warn you about creating
|
13
|
-
# modules whose name is the same as a top level class/module. The warning is to encourage users to stay
|
14
|
-
# away from error-prone libraries. Once you introduce such a module, _all_ libraries assume the nested module
|
15
|
-
# over the top level module and the top level module has to be prefixed with '::' _everywhere_.
|
16
|
-
#
|
17
|
-
# == Configuration
|
18
|
-
# Libraries and their commands can be configured in different ways in this order:
|
19
|
-
# * If library is a FileLibrary, commands be configured with a config method attribute (see Inspector).
|
20
|
-
# * If a library has a module, you can set library + command attributes via the config() callback (see Loader).
|
21
|
-
# * All libraries can be configured by passing a hash of {library attributes}[link:classes/Boson/Library.html#M000077] under
|
22
|
-
# {the :libraries key}[link:classes/Boson/Repo.html#M000070] to the main config file ~/.boson/config/boson.yml.
|
23
|
-
# For most libraries this may be the only way to configure a library's commands.
|
24
|
-
# An example of a GemLibrary config:
|
25
|
-
# :libraries:
|
26
|
-
# httparty:
|
27
|
-
# :class_commands:
|
28
|
-
# delete: HTTParty.delete
|
29
|
-
# :commands:
|
30
|
-
# delete:
|
31
|
-
# :alias: d
|
32
|
-
# :desc: Http delete a given url
|
33
|
-
#
|
34
|
-
# When installing a third-party library, use the config file as a way to override default library and command attributes
|
35
|
-
# without modifying the library.
|
2
|
+
# A library is a group of commands (Command objects) usually grouped together
|
3
|
+
# by a module. Libraries are loaded from different sources depending on the
|
4
|
+
# library subclass.
|
36
5
|
#
|
37
6
|
# === Creating Your Own Library
|
38
|
-
# To create your own subclass you need to define what sources the subclass can
|
39
|
-
#
|
40
|
-
#
|
7
|
+
# To create your own subclass you need to define what sources the subclass can
|
8
|
+
# handle with handles(). See Loader to see what instance methods to override
|
9
|
+
# for a subclass.
|
41
10
|
class Library
|
42
11
|
include Loader
|
43
12
|
class <<self
|
44
|
-
#:stopdoc:
|
45
13
|
attr_accessor :handle_blocks
|
14
|
+
# Returns true when the subclass is chosen to load.
|
46
15
|
def handles(&block)
|
47
16
|
(Library.handle_blocks ||= []) << [self,block]
|
48
17
|
end
|
49
|
-
#:startdoc:
|
50
18
|
end
|
51
19
|
|
52
20
|
# Public attributes for use outside of Boson.
|
53
|
-
ATTRIBUTES = [:
|
54
|
-
attr_reader *(ATTRIBUTES + [:commands_hash, :library_file
|
21
|
+
ATTRIBUTES = [:commands, :loaded, :module, :name]
|
22
|
+
attr_reader *(ATTRIBUTES + [:commands_hash, :library_file])
|
55
23
|
# Private attribute for use within Boson.
|
56
|
-
attr_reader :
|
57
|
-
# Optional namespace name for a library. When enabled defaults to a library's name.
|
58
|
-
attr_writer :namespace
|
24
|
+
attr_reader :new_module, :new_commands, :lib_file
|
59
25
|
|
60
|
-
# Creates a library object with
|
61
|
-
#
|
62
|
-
#
|
63
|
-
# can also be set via a library module's config() method (see Loader).
|
26
|
+
# Creates a library object with the given hash. Each hash pair maps
|
27
|
+
# directly to an instance variable and value. Defaults for attributes are
|
28
|
+
# read from config[:libraries][@library_name][@attribute].
|
64
29
|
#
|
65
|
-
#
|
66
|
-
# [
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
# to create commands of the same name. Example:
|
76
|
-
# :class_commands=>{'spy'=>'Bond.spy', 'create'=>'Alias.manager.create',
|
77
|
-
# 'Boson::Util'=>['detect', 'any_const_get']}
|
78
|
-
# [*:force*] Boolean which forces a library to ignore when a library's methods are overriding existing ones.
|
79
|
-
# Use with caution. Default is false.
|
80
|
-
# [*:object_methods*] Boolean which detects any Object/Kernel methods created when loading a library and automatically
|
81
|
-
# adds them to a library's commands. Default is true.
|
82
|
-
# [*:namespace*] Boolean or string which namespaces a library. When true, the library is automatically namespaced
|
83
|
-
# to the library's name. When a string, the library is namespaced to the string. Default is nil.
|
84
|
-
# To control the namespacing of all libraries see Boson::Repo.config.
|
85
|
-
# [*:no_alias_creation*] Boolean which doesn't create aliases for a library. Useful for libraries that configure command
|
86
|
-
# aliases outside of Boson's control. Default is false.
|
30
|
+
# @param [Hash] hash
|
31
|
+
# @option hash [String] :name Required attribute
|
32
|
+
# @option hash [Array,Hash] :commands Commands belonging to a library. A
|
33
|
+
# hash configures command attributes for the given commands with command
|
34
|
+
# names pointing to their configs. See Command.new for a command's
|
35
|
+
# configurable attributes. If an array, the commands are set for the
|
36
|
+
# given library, overidding default command detection. Example:
|
37
|
+
# :commands=>{'commands'=>{:desc=>'Lists commands', :alias=>'com'}}
|
38
|
+
# @option hash [Boolean] :force Forces a library to ignore when a library's
|
39
|
+
# methods are overriding existing ones. Use with caution. Default is false.
|
87
40
|
def initialize(hash)
|
88
|
-
|
89
|
-
@
|
90
|
-
|
41
|
+
before_initialize
|
42
|
+
@name = set_name(hash.delete(:name)) or
|
43
|
+
raise ArgumentError, "Library missing required key :name"
|
91
44
|
@loaded = false
|
92
45
|
@commands_hash = {}
|
93
46
|
@commands = []
|
94
|
-
set_config (
|
95
|
-
set_command_aliases(
|
47
|
+
set_config (config[:libraries][@name] || {}).merge(hash), true
|
48
|
+
set_command_aliases(config[:command_aliases])
|
96
49
|
end
|
97
50
|
|
98
51
|
# A concise symbol version of a library type i.e. FileLibrary -> :file.
|
@@ -101,33 +54,56 @@ module Boson
|
|
101
54
|
str.downcase.to_sym
|
102
55
|
end
|
103
56
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
@namespace = clean_name
|
108
|
-
else
|
109
|
-
@namespace = false
|
110
|
-
end
|
111
|
-
end
|
57
|
+
# handles names under directories
|
58
|
+
def clean_name
|
59
|
+
@name[/\w+$/]
|
112
60
|
end
|
113
61
|
|
114
|
-
#
|
115
|
-
def
|
116
|
-
|
62
|
+
# sets name
|
63
|
+
def set_name(name)
|
64
|
+
name.to_s
|
117
65
|
end
|
118
66
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
67
|
+
module API
|
68
|
+
# The object a library uses for executing its commands.
|
69
|
+
def namespace_object
|
70
|
+
@namespace_object ||= Boson.main_object
|
71
|
+
end
|
72
|
+
|
73
|
+
# Method hook called at the beginning of initialize
|
74
|
+
def before_initialize
|
75
|
+
end
|
76
|
+
|
77
|
+
# Determines if library is local i.e. scoped to current directory/project
|
78
|
+
def local?
|
79
|
+
false
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Hash] Attributes used internally by a library. Defaults to
|
83
|
+
# using Boson.config but can be overridden to be library-specific.
|
84
|
+
def config
|
85
|
+
Boson.config
|
86
|
+
end
|
123
87
|
end
|
88
|
+
include API
|
124
89
|
|
125
|
-
|
126
|
-
|
90
|
+
# Command objects of library's commands
|
91
|
+
def command_objects(names=self.commands, command_array=Boson.commands)
|
92
|
+
command_array.select {|e| names.include?(e.name) && e.lib == self.name }
|
127
93
|
end
|
128
94
|
|
129
|
-
|
130
|
-
|
95
|
+
# Command object for given command name
|
96
|
+
def command_object(name)
|
97
|
+
command_objects([name])[0]
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def set_attributes(hash, force=false)
|
102
|
+
hash.each do |k,v|
|
103
|
+
if instance_variable_get("@#{k}").nil? || force
|
104
|
+
instance_variable_set("@#{k}", v)
|
105
|
+
end
|
106
|
+
end
|
131
107
|
end
|
132
108
|
|
133
109
|
def set_config(config, force=false)
|
@@ -150,30 +126,5 @@ module Boson
|
|
150
126
|
@commands_hash[cmd][:alias] ||= cmd_alias
|
151
127
|
end
|
152
128
|
end
|
153
|
-
|
154
|
-
def set_repo
|
155
|
-
Boson.repo
|
156
|
-
end
|
157
|
-
|
158
|
-
def set_attributes(hash, force=false)
|
159
|
-
hash.each {|k,v| instance_variable_set("@#{k}", v) if instance_variable_get("@#{k}").nil? || force }
|
160
|
-
end
|
161
|
-
|
162
|
-
def command_objects(names=self.commands, command_array=Boson.commands)
|
163
|
-
command_array.select {|e| names.include?(e.name) && e.lib == self.name }
|
164
|
-
end
|
165
|
-
|
166
|
-
def command_object(name)
|
167
|
-
command_objects([name])[0]
|
168
|
-
end
|
169
|
-
|
170
|
-
def marshal_dump
|
171
|
-
[@name, @commands, @gems, @module.to_s, @repo_dir, @indexed_namespace]
|
172
|
-
end
|
173
|
-
|
174
|
-
def marshal_load(ary)
|
175
|
-
@name, @commands, @gems, @module, @repo_dir, @indexed_namespace = ary
|
176
|
-
end
|
177
|
-
#:startdoc:
|
178
129
|
end
|
179
|
-
end
|
130
|
+
end
|
data/lib/boson/loader.rb
CHANGED
@@ -1,118 +1,107 @@
|
|
1
1
|
module Boson
|
2
|
-
# Raised if a library has
|
3
|
-
class MethodConflictError < LoaderError
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# [*:config*] This method returns a library's hash of attributes as explained by Library.new. This is useful
|
14
|
-
# for distributing libraries with a default configuration. The library attributes specified here
|
15
|
-
# are overridden by ones a user has in their config file except for the :commands attribute, which
|
16
|
-
# is recursively merged together.
|
17
|
-
# [*:append_features*] In addition to its normal behavior, this method's return value determines if a
|
18
|
-
# library is loaded in the current environment. This is useful for libraries that you
|
19
|
-
# want loaded by default but not in some environments i.e. different ruby versions or
|
20
|
-
# in irb but not in script/console. Remember to use super when returning true.
|
21
|
-
# [*:included*] In addition to its normal behavior, this method should be used to require external libraries.
|
22
|
-
# Although requiring dependencies could be done anywhere in a module, putting dependencies here
|
23
|
-
# are encouraged. By not having dependencies hardcoded in a module, it's possible to analyze
|
24
|
-
# and view a library's commands without having to install and load its dependencies.
|
25
|
-
# If creating commands here, note that conflicts with existing commands won't be detected.
|
26
|
-
# [*:after_included*] This method is called after included() to initialize functionality. This is useful for
|
27
|
-
# libraries that are primarily executing ruby code i.e. defining ruby extensions or
|
28
|
-
# setting irb features. This method isn't called when indexing a library.
|
2
|
+
# Raised if a library has methods which conflict with existing methods
|
3
|
+
class MethodConflictError < LoaderError
|
4
|
+
MESSAGE = "The following commands conflict with existing commands: %s"
|
5
|
+
def initialize(conflicts)
|
6
|
+
super MESSAGE % conflicts.join(', ')
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# This module is mixed into Library to give it load() functionality. When
|
11
|
+
# creating your own Library subclass, you should at least override
|
12
|
+
# load_source_and_set_module.
|
29
13
|
module Loader
|
30
|
-
# Loads a library and its dependencies and returns true if library loads
|
14
|
+
# Loads a library and its dependencies and returns true if library loads
|
15
|
+
# correctly.
|
31
16
|
def load
|
32
|
-
@gems ||= []
|
33
17
|
load_source_and_set_module
|
34
18
|
module_callbacks if @module
|
35
|
-
yield if block_given?
|
36
|
-
|
19
|
+
yield if block_given? # load dependencies
|
20
|
+
detect_additions { load_commands } if load_commands?
|
37
21
|
set_library_commands
|
38
|
-
@indexed_namespace = (@namespace == false) ? nil : @namespace if @index
|
39
22
|
loaded_correctly? && (@loaded = true)
|
40
23
|
end
|
41
24
|
|
42
|
-
#
|
25
|
+
# Method hook at the beginning of #load. This method should load the source
|
26
|
+
# and set instance variables necessary to make a library valid i.e. @module.
|
43
27
|
def load_source_and_set_module; end
|
44
28
|
|
29
|
+
# Method hook for @module before loading
|
30
|
+
def module_callbacks; end
|
31
|
+
|
32
|
+
# Determines if load_commands should be called
|
33
|
+
def load_commands?
|
34
|
+
@module
|
35
|
+
end
|
36
|
+
|
37
|
+
# Wraps around module loading for unexpected additions
|
38
|
+
def detect_additions(options={}, &block)
|
39
|
+
Util.detect(options, &block).tap do |detected|
|
40
|
+
@commands.concat detected[:methods].map(&:to_s)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Prepares for command loading, loads commands and rescues certain errors.
|
45
|
+
def load_commands
|
46
|
+
@module = @module ? Util.constantize(@module) :
|
47
|
+
Util.create_module(Boson::Commands, clean_name)
|
48
|
+
before_load_commands
|
49
|
+
check_for_method_conflicts unless @force
|
50
|
+
actual_load_commands
|
51
|
+
rescue MethodConflictError => err
|
52
|
+
handle_method_conflict_error err
|
53
|
+
end
|
54
|
+
|
45
55
|
# Boolean which indicates if library loaded correctly.
|
46
56
|
def loaded_correctly?
|
47
57
|
!!@module
|
48
58
|
end
|
49
59
|
|
50
|
-
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
60
|
+
# Method hook for @module after it's been included
|
61
|
+
def after_include; end
|
62
|
+
|
63
|
+
# called when MethodConflictError is rescued
|
64
|
+
def handle_method_conflict_error(err)
|
65
|
+
raise err
|
56
66
|
end
|
57
67
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
@namespace = clean_name
|
65
|
-
@method_conflict = true
|
66
|
-
$stderr.puts "#{e.message}. Attempting load into the namespace #{@namespace}..."
|
67
|
-
initialize_library_module
|
68
|
-
end
|
68
|
+
# Method hook called after @module has been created
|
69
|
+
def before_load_commands; end
|
70
|
+
|
71
|
+
# Actually includes module and its commands
|
72
|
+
def actual_load_commands
|
73
|
+
include_in_universe
|
69
74
|
end
|
70
75
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
@commands += detected[:methods].map {|e| e.to_s }
|
76
|
-
detected
|
76
|
+
# Returns array of method conflicts
|
77
|
+
def method_conflicts
|
78
|
+
(@module.instance_methods + @module.private_instance_methods) &
|
79
|
+
(Boson.main_object.methods + Boson.main_object.private_methods)
|
77
80
|
end
|
78
81
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
warn "Library module '#{@module}' may conflict with top level class/module '#{conflict}' references in"+
|
84
|
-
" your libraries. Rename your module to avoid this warning."
|
85
|
-
end
|
82
|
+
# Handles setting and cleaning @commands
|
83
|
+
def set_library_commands
|
84
|
+
clean_library_commands
|
85
|
+
end
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
87
|
+
# Cleans @commands from set_library_commands
|
88
|
+
def clean_library_commands
|
89
|
+
aliases = @commands_hash.select {|k,v| @commands.include?(k) }.
|
90
|
+
map {|k,v| v[:alias] }.compact
|
91
|
+
@commands -= aliases
|
92
|
+
@commands.uniq!
|
92
93
|
end
|
93
94
|
|
95
|
+
private
|
94
96
|
def include_in_universe(lib_module=@module)
|
95
97
|
Boson::Universe.send :include, lib_module
|
96
|
-
|
98
|
+
after_include
|
97
99
|
Boson::Universe.send :extend_object, Boson.main_object
|
98
100
|
end
|
99
101
|
|
100
102
|
def check_for_method_conflicts
|
101
|
-
conflicts =
|
102
|
-
|
103
|
-
Boson.main_object.private_methods)
|
104
|
-
unless conflicts.empty?
|
105
|
-
raise MethodConflictError,"The following commands conflict with existing commands: #{conflicts.join(', ')}"
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def set_library_commands
|
110
|
-
aliases = @commands_hash.select {|k,v| @commands.include?(k) }.map {|k,v| v[:alias]}.compact
|
111
|
-
@commands -= aliases
|
112
|
-
@commands.delete(namespace) if namespace
|
113
|
-
@commands += Boson.invoke(namespace).boson_commands if namespace && !@pre_defined_commands
|
114
|
-
@commands.uniq!
|
103
|
+
conflicts = method_conflicts
|
104
|
+
raise MethodConflictError.new(conflicts) unless conflicts.empty?
|
115
105
|
end
|
116
|
-
#:startdoc:
|
117
106
|
end
|
118
107
|
end
|
data/lib/boson/manager.rb
CHANGED
@@ -1,169 +1,165 @@
|
|
1
1
|
module Boson
|
2
|
-
# Base class for library loading errors. Raised mostly in Boson::Loader and
|
2
|
+
# Base class for library loading errors. Raised mostly in Boson::Loader and
|
3
|
+
# rescued by Boson::Manager.
|
3
4
|
class LoaderError < StandardError; end
|
4
|
-
# Raised when a library's append_features returns false.
|
5
|
-
class AppendFeaturesFalseError < StandardError; end
|
6
5
|
|
7
6
|
# Handles loading of libraries and commands.
|
8
7
|
class Manager
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
Array(libraries).map {|e|
|
22
|
-
(@library = load_once(e, options)) ? after_load : false
|
23
|
-
}.all?
|
24
|
-
end
|
8
|
+
# Loads a library or an array of libraries with options. Manager loads the
|
9
|
+
# first library subclass to return true for Library#handles. Any options
|
10
|
+
# that aren't listed here are passed as library attributes to the libraries
|
11
|
+
# (see Library.new)
|
12
|
+
#
|
13
|
+
# @param [Hash] options
|
14
|
+
# @option options [Boolean] :verbose Prints each library's loaded status
|
15
|
+
# along with more verbose errors. Default is false.
|
16
|
+
# @example Manager.load MyRunner
|
17
|
+
def self.load(libraries, options={})
|
18
|
+
instance.load(libraries, options)
|
19
|
+
end
|
25
20
|
|
26
|
-
|
27
|
-
def failed_libraries
|
28
|
-
@failed_libraries ||= []
|
29
|
-
end
|
21
|
+
class <<self; attr_accessor :instance; end
|
30
22
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
23
|
+
def self.instance
|
24
|
+
@instance ||= new
|
25
|
+
end
|
35
26
|
|
36
|
-
|
37
|
-
|
38
|
-
|
27
|
+
# Adds a library to Boson.libraries
|
28
|
+
def self.add_library(lib)
|
29
|
+
Boson.libraries.delete(Boson.library(lib.name))
|
30
|
+
Boson.libraries << lib
|
31
|
+
end
|
39
32
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
rescue LoaderError=>e
|
45
|
-
FileLibrary.reset_file_cache(library.to_s)
|
46
|
-
failed_libraries << library
|
47
|
-
$stderr.puts "Unable to #{load_method} library #{library}. Reason: #{e.message}"
|
48
|
-
rescue StandardError, SyntaxError, LoadError =>e
|
49
|
-
FileLibrary.reset_file_cache(library.to_s)
|
50
|
-
failed_libraries << library
|
51
|
-
message = "Unable to #{load_method} library #{library}. Reason: #{$!}"
|
52
|
-
if Runner.debug
|
53
|
-
message += "\n" + e.backtrace.map {|e| " " + e }.join("\n")
|
54
|
-
elsif @options[:verbose]
|
55
|
-
message += "\n" + e.backtrace.slice(0,3).map {|e| " " + e }.join("\n")
|
56
|
-
end
|
57
|
-
$stderr.puts message
|
58
|
-
ensure
|
59
|
-
Inspector.disable if Inspector.enabled
|
60
|
-
end
|
33
|
+
# Given a library name, determines if it's loaded
|
34
|
+
def self.loaded?(lib_name)
|
35
|
+
((lib = Boson.library(lib_name)) && lib.loaded) ? true : false
|
36
|
+
end
|
61
37
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
if loaded?(lib.name)
|
67
|
-
$stderr.puts "Library #{lib.name} already exists." if options[:verbose] && !options[:dependency]
|
68
|
-
false
|
69
|
-
else
|
70
|
-
if lib.load { load_dependencies(lib, options) }
|
71
|
-
lib
|
72
|
-
else
|
73
|
-
$stderr.puts "Library #{lib.name} did not load successfully." if !options[:dependency]
|
74
|
-
$stderr.puts " "+lib.inspect if Runner.debug
|
75
|
-
false
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
38
|
+
attr_accessor :failed_libraries, :verbose
|
39
|
+
def initialize
|
40
|
+
@failed_libraries = []
|
41
|
+
end
|
80
42
|
|
81
|
-
|
82
|
-
|
83
|
-
|
43
|
+
# Loads libraries
|
44
|
+
def load(libraries, options={})
|
45
|
+
Array(libraries).map {|e|
|
46
|
+
(@library = load_once(e, options)) ? after_load : false
|
47
|
+
}.all?
|
48
|
+
end
|
84
49
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
raise(LoaderError, "Can't load dependency #{e}")
|
90
|
-
end.compact
|
91
|
-
end
|
50
|
+
# Adds a library to the failed list
|
51
|
+
def add_failed_library(library)
|
52
|
+
failed_libraries << library
|
53
|
+
end
|
92
54
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
55
|
+
# Called after a library is loaded
|
56
|
+
def after_load
|
57
|
+
create_commands(@library)
|
58
|
+
self.class.add_library(@library)
|
59
|
+
puts "Loaded library #{@library.name}" if verbose
|
60
|
+
during_after_load
|
61
|
+
true
|
62
|
+
end
|
97
63
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
(
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
107
|
-
true
|
64
|
+
# Redefines commands
|
65
|
+
def redefine_commands(lib, commands)
|
66
|
+
option_commands = lib.command_objects(commands).select(&:option_command?)
|
67
|
+
accepted, rejected = option_commands.partition {|e|
|
68
|
+
e.args(lib) || e.arg_size }
|
69
|
+
if verbose && rejected.size > 0
|
70
|
+
puts "Following commands cannot have options until their arguments " +
|
71
|
+
"are configured: " + rejected.map {|e| e.name}.join(', ')
|
108
72
|
end
|
73
|
+
accepted.each {|cmd| Scientist.redefine_command(lib.namespace_object, cmd) }
|
74
|
+
end
|
109
75
|
|
110
|
-
|
111
|
-
|
112
|
-
|
76
|
+
module API
|
77
|
+
# Method hook for loading dependencies or anything else before loading
|
78
|
+
# a library
|
79
|
+
def load_dependencies(lib, options); end
|
113
80
|
|
114
|
-
|
115
|
-
|
116
|
-
commands.each {|e| Boson.commands << Command.create(e, lib)}
|
117
|
-
create_command_aliases(lib, commands) if commands.size > 0 && !lib.no_alias_creation
|
118
|
-
redefine_commands(lib, commands)
|
119
|
-
end
|
81
|
+
# Method hook in middle of after_load
|
82
|
+
def during_after_load; end
|
120
83
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
puts "Following commands cannot have options until their arguments are configured: " +
|
126
|
-
rejected.map {|e| e.name}.join(', ')
|
84
|
+
# Method hook called before create_commands
|
85
|
+
def before_create_commands(lib)
|
86
|
+
if lib.is_a?(RunnerLibrary) && lib.module
|
87
|
+
Inspector.add_method_data_to_library(lib)
|
127
88
|
end
|
128
|
-
accepted.each {|cmd| Scientist.redefine_command(lib.namespace_object, cmd) }
|
129
|
-
end
|
130
|
-
|
131
|
-
def create_command_aliases(lib, commands)
|
132
|
-
lib.module ? prep_and_create_instance_aliases(commands, lib.module) : check_for_uncreated_aliases(lib, commands)
|
133
89
|
end
|
134
90
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
91
|
+
# Method hook called after create_commands
|
92
|
+
def after_create_commands(lib, commands); end
|
93
|
+
|
94
|
+
# Handles an error from a load action
|
95
|
+
def handle_load_action_error(library, load_method, err)
|
96
|
+
case err
|
97
|
+
when LoaderError
|
98
|
+
add_failed_library library
|
99
|
+
warn "Unable to #{load_method} library #{library}. Reason: #{err.message}"
|
100
|
+
else
|
101
|
+
add_failed_library library
|
102
|
+
message = "Unable to #{load_method} library #{library}. Reason: #{err}"
|
103
|
+
if Boson.debug
|
104
|
+
message << "\n" + err.backtrace.map {|e| " " + e }.join("\n")
|
105
|
+
elsif verbose
|
106
|
+
message << "\n" + err.backtrace.slice(0,3).map {|e| " " + e }.join("\n")
|
142
107
|
end
|
108
|
+
warn message
|
143
109
|
end
|
144
|
-
create_instance_aliases(aliases_hash)
|
145
110
|
end
|
111
|
+
end
|
112
|
+
include API
|
113
|
+
|
114
|
+
private
|
115
|
+
def call_load_action(library, load_method)
|
116
|
+
yield
|
117
|
+
rescue StandardError, SyntaxError, LoadError => err
|
118
|
+
handle_load_action_error(library, load_method, err)
|
119
|
+
ensure
|
120
|
+
Inspector.disable if Inspector.enabled
|
121
|
+
end
|
146
122
|
|
147
|
-
|
148
|
-
|
149
|
-
end
|
123
|
+
def load_once(source, options={})
|
124
|
+
self.verbose = options[:verbose]
|
150
125
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
126
|
+
call_load_action(source, :load) do
|
127
|
+
lib = loader_create(source, options)
|
128
|
+
if self.class.loaded?(lib.name)
|
129
|
+
if verbose && !options[:dependency]
|
130
|
+
warn "Library #{lib.name} already exists."
|
155
131
|
end
|
156
|
-
|
157
|
-
|
132
|
+
false
|
133
|
+
else
|
134
|
+
actual_load_once lib, options
|
135
|
+
end
|
158
136
|
end
|
137
|
+
end
|
159
138
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
139
|
+
def actual_load_once(lib, options)
|
140
|
+
if lib.load { load_dependencies(lib, options) }
|
141
|
+
lib
|
142
|
+
else
|
143
|
+
if !options[:dependency]
|
144
|
+
warn "Library #{lib.name} did not load successfully."
|
164
145
|
end
|
146
|
+
warn " "+lib.inspect if Boson.debug
|
147
|
+
false
|
165
148
|
end
|
166
|
-
|
149
|
+
end
|
150
|
+
|
151
|
+
def loader_create(source, options)
|
152
|
+
options = options.dup.tap {|h| h.delete(:verbose) }
|
153
|
+
lib_class = Library.handle_blocks.find {|k,v| v.call(source) } or
|
154
|
+
raise(LoaderError, "Library #{source} not found.")
|
155
|
+
lib_class[0].new(options.merge(name: source))
|
156
|
+
end
|
157
|
+
|
158
|
+
def create_commands(lib, commands=lib.commands)
|
159
|
+
before_create_commands(lib)
|
160
|
+
commands.each {|e| Boson.commands << Command.create(e, lib)}
|
161
|
+
after_create_commands(lib, commands)
|
162
|
+
redefine_commands(lib, commands)
|
167
163
|
end
|
168
164
|
end
|
169
165
|
end
|