boson-more 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemspec +22 -0
- data/LICENSE.txt +22 -0
- data/README.md +97 -0
- data/Rakefile +35 -0
- data/deps.rip +1 -0
- data/lib/boson/alias.rb +75 -0
- data/lib/boson/argument_inspector.rb +90 -0
- data/lib/boson/commands/core.rb +67 -0
- data/lib/boson/commands/view_core.rb +19 -0
- data/lib/boson/commands/web_core.rb +153 -0
- data/lib/boson/comment_inspector.rb +100 -0
- data/lib/boson/console.rb +40 -0
- data/lib/boson/console_runner.rb +60 -0
- data/lib/boson/index.rb +48 -0
- data/lib/boson/libraries/file_library.rb +144 -0
- data/lib/boson/libraries/gem_library.rb +30 -0
- data/lib/boson/libraries/local_file_library.rb +30 -0
- data/lib/boson/libraries/module_library.rb +37 -0
- data/lib/boson/libraries/require_library.rb +23 -0
- data/lib/boson/libraries.rb +183 -0
- data/lib/boson/more/version.rb +5 -0
- data/lib/boson/more.rb +18 -0
- data/lib/boson/more_commands.rb +14 -0
- data/lib/boson/more_inspector.rb +42 -0
- data/lib/boson/more_manager.rb +34 -0
- data/lib/boson/more_method_inspector.rb +74 -0
- data/lib/boson/more_option_parser.rb +28 -0
- data/lib/boson/more_scientist.rb +68 -0
- data/lib/boson/more_util.rb +30 -0
- data/lib/boson/namespace.rb +31 -0
- data/lib/boson/namespacer.rb +117 -0
- data/lib/boson/pipe.rb +156 -0
- data/lib/boson/pipe_runner.rb +44 -0
- data/lib/boson/pipes.rb +75 -0
- data/lib/boson/repo.rb +96 -0
- data/lib/boson/repo_index.rb +135 -0
- data/lib/boson/runner_options.rb +88 -0
- data/lib/boson/save.rb +198 -0
- data/lib/boson/science.rb +273 -0
- data/lib/boson/view.rb +98 -0
- data/lib/boson/viewable.rb +48 -0
- data/test/alias_test.rb +55 -0
- data/test/argument_inspector_test.rb +40 -0
- data/test/command_test.rb +22 -0
- data/test/commands_test.rb +53 -0
- data/test/comment_inspector_test.rb +126 -0
- data/test/console_runner_test.rb +58 -0
- data/test/deps.rip +4 -0
- data/test/file_library_test.rb +41 -0
- data/test/gem_library_test.rb +40 -0
- data/test/libraries_test.rb +55 -0
- data/test/loader_test.rb +38 -0
- data/test/module_library_test.rb +30 -0
- data/test/more_manager_test.rb +29 -0
- data/test/more_method_inspector_test.rb +42 -0
- data/test/more_scientist_test.rb +10 -0
- data/test/namespacer_test.rb +61 -0
- data/test/pipes_test.rb +65 -0
- data/test/repo_index_test.rb +123 -0
- data/test/repo_test.rb +23 -0
- data/test/runner_options_test.rb +29 -0
- data/test/save_test.rb +86 -0
- data/test/science_test.rb +58 -0
- data/test/scientist_test.rb +195 -0
- data/test/test_helper.rb +165 -0
- data/test/web_test.rb +22 -0
- metadata +169 -0
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'boson/save'
|
2
|
+
require 'boson/namespace'
|
3
|
+
require 'boson/more_util'
|
4
|
+
# order of library subclasses matters
|
5
|
+
%w{module file gem require local_file}.each {|e| require "boson/libraries/#{e}_library" }
|
6
|
+
|
7
|
+
module Boson
|
8
|
+
# == Naming a Library Module
|
9
|
+
# Although you can name a library module almost anything, here's the fine print:
|
10
|
+
# * A module can have any name if it's the only module in a library.
|
11
|
+
# * If there are multiple modules in a file library, the module's name must
|
12
|
+
# be a camelized version of the file's basename
|
13
|
+
# i.e. ~/.boson/commands/ruby_core.rb -> RubyCore.
|
14
|
+
# * Although modules are evaluated under the Boson::Commands namespace, Boson
|
15
|
+
# will warn you about creating modules whose name is the same as a top level
|
16
|
+
# class/module. The warning is to encourage users to stay away from
|
17
|
+
# error-prone libraries. Once you introduce such a module, _all_ libraries
|
18
|
+
# assume the nested module over the top level module and the top level
|
19
|
+
# module has to be prefixed with '::' _everywhere_.
|
20
|
+
#
|
21
|
+
# == Configuration
|
22
|
+
# Libraries and their commands can be configured in different ways in this order:
|
23
|
+
# * If library is a FileLibrary, commands be configured with a config method
|
24
|
+
# attribute (see Inspector).
|
25
|
+
# * If a library has a module, you can set library + command attributes via
|
26
|
+
# the config() callback (see Loader).
|
27
|
+
# === Module Callbacks
|
28
|
+
# For libraries that have a module i.e. RunnerLibrary, the following class methods
|
29
|
+
# are invoked in the order below when loading a library:
|
30
|
+
#
|
31
|
+
# [*:config*] This method returns a library's hash of attributes as explained by Library.new. This is useful
|
32
|
+
# for distributing libraries with a default configuration. The library attributes specified here
|
33
|
+
# are overridden by ones a user has in their config file except for the :commands attribute, which
|
34
|
+
# is recursively merged together.
|
35
|
+
# [*:append_features*] In addition to its normal behavior, this method's return value determines if a
|
36
|
+
# library is loaded in the current environment. This is useful for libraries that you
|
37
|
+
# want loaded by default but not in some environments i.e. different ruby versions or
|
38
|
+
# in irb but not in script/console. Remember to use super when returning true.
|
39
|
+
# [*:included*] In addition to its normal behavior, this method should be used to require external libraries.
|
40
|
+
# Although requiring dependencies could be done anywhere in a module, putting dependencies here
|
41
|
+
# are encouraged. By not having dependencies hardcoded in a module, it's possible to analyze
|
42
|
+
# and view a library's commands without having to install and load its dependencies.
|
43
|
+
# If creating commands here, note that conflicts with existing commands won't be detected.
|
44
|
+
# [*:after_included*] This method is called after included() to initialize functionality. This is useful for
|
45
|
+
# libraries that are primarily executing ruby code i.e. defining ruby extensions or
|
46
|
+
# setting irb features. This method isn't called when indexing a library.
|
47
|
+
class Library
|
48
|
+
ATTRIBUTES << :gems
|
49
|
+
|
50
|
+
module Libraries
|
51
|
+
attr_reader :gems
|
52
|
+
def local?
|
53
|
+
is_a?(LocalFileLibrary) ||
|
54
|
+
(Boson.local_repo && Boson.local_repo.dir == repo_dir)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
include Libraries
|
58
|
+
|
59
|
+
# [*:object_methods*] Boolean which detects any Object/Kernel methods created when loading a library and automatically
|
60
|
+
# adds them to a library's commands. Default is true.
|
61
|
+
module LibrariesLoader
|
62
|
+
def detect_additions(options={}, &block)
|
63
|
+
options[:object_methods] = @object_methods if !@object_methods.nil?
|
64
|
+
super(options, &block).tap do |detected|
|
65
|
+
if detected[:gems]
|
66
|
+
@gems ||= []
|
67
|
+
@gems.concat detected[:gems]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def module_callbacks
|
73
|
+
set_config(@module.config) if @module.respond_to?(:config)
|
74
|
+
if @module.respond_to?(:append_features)
|
75
|
+
raise AppendFeaturesFalseError unless @module.append_features(Module.new)
|
76
|
+
end
|
77
|
+
super
|
78
|
+
end
|
79
|
+
|
80
|
+
def before_load_commands
|
81
|
+
raise(LoaderError, "No module for library #{@name}") unless @module
|
82
|
+
if (conflict = Util.top_level_class_conflict(Boson::Commands, @module.to_s))
|
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
|
86
|
+
super
|
87
|
+
end
|
88
|
+
|
89
|
+
def after_include
|
90
|
+
@module.after_included if @module.respond_to?(:after_included) && !@index
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
include LibrariesLoader
|
95
|
+
end
|
96
|
+
|
97
|
+
# Raised when a library's append_features returns false.
|
98
|
+
class AppendFeaturesFalseError < StandardError; end
|
99
|
+
|
100
|
+
class Manager
|
101
|
+
module Libraries
|
102
|
+
def handle_load_action_error(library, load_method, err)
|
103
|
+
if err.is_a? Boson::AppendFeaturesFalseError
|
104
|
+
warn "DEBUG: Library #{library} didn't load due to append_features" if Boson.debug
|
105
|
+
else
|
106
|
+
super
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def before_create_commands(lib)
|
111
|
+
super
|
112
|
+
if lib.is_a?(FileLibrary) && lib.module
|
113
|
+
Inspector.add_method_data_to_library(lib)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def add_failed_library(library)
|
118
|
+
FileLibrary.reset_file_cache(library.to_s)
|
119
|
+
super
|
120
|
+
end
|
121
|
+
|
122
|
+
def check_for_uncreated_aliases(lib, commands)
|
123
|
+
return if lib.is_a?(GemLibrary)
|
124
|
+
super
|
125
|
+
end
|
126
|
+
end
|
127
|
+
include Libraries
|
128
|
+
end
|
129
|
+
|
130
|
+
class Command
|
131
|
+
# hack: have to override
|
132
|
+
# Array of array args with optional defaults. Scraped with MethodInspector
|
133
|
+
def args(lib=library)
|
134
|
+
@args = !@args.nil? ? @args : begin
|
135
|
+
if lib
|
136
|
+
file_string, meth = file_string_and_method_for_args(lib)
|
137
|
+
(file_string && meth && (@file_parsed_args = true) &&
|
138
|
+
MethodInspector.scrape_arguments(file_string, meth))
|
139
|
+
end || false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
module Libraries
|
144
|
+
def file_string_and_method_for_args(lib)
|
145
|
+
if !lib.is_a?(ModuleLibrary) && (klass_method = (lib.class_commands || {})[@name])
|
146
|
+
klass, meth = klass_method.split(NAMESPACE, 2)
|
147
|
+
if (meth_locations = MethodInspector.find_class_method_locations(klass, meth))
|
148
|
+
file_string = File.read meth_locations[0]
|
149
|
+
end
|
150
|
+
elsif File.exists?(lib.library_file || '')
|
151
|
+
file_string, meth = FileLibrary.read_library_file(lib.library_file), @name
|
152
|
+
end
|
153
|
+
[file_string, meth]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
include Libraries
|
157
|
+
end
|
158
|
+
|
159
|
+
class MethodInspector
|
160
|
+
module Libraries
|
161
|
+
def find_class_method_locations(klass, meth)
|
162
|
+
if (klass = Util.any_const_get(klass)) && (meth_location = klass.method(meth).source_location) &&
|
163
|
+
meth_location[0]
|
164
|
+
meth_location
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
extend Libraries
|
169
|
+
end
|
170
|
+
|
171
|
+
module Scientist
|
172
|
+
module Libraries
|
173
|
+
def help_options
|
174
|
+
@global_options[:verbose] ? ['--verbose'] : []
|
175
|
+
end
|
176
|
+
|
177
|
+
def run_help_option(cmd)
|
178
|
+
Boson.invoke :usage, cmd.full_name + " " + help_options.join(' ')
|
179
|
+
end
|
180
|
+
end
|
181
|
+
extend Libraries
|
182
|
+
end
|
183
|
+
end
|
data/lib/boson/more.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'boson/alias'
|
2
|
+
require 'boson/namespacer'
|
3
|
+
require 'boson/more_util'
|
4
|
+
require 'boson/more_manager'
|
5
|
+
require 'boson/save'
|
6
|
+
require 'boson/libraries'
|
7
|
+
require 'boson/more_commands'
|
8
|
+
require 'boson/pipe_runner'
|
9
|
+
require 'boson/more_inspector'
|
10
|
+
require 'boson/more_method_inspector'
|
11
|
+
require 'boson/more_scientist'
|
12
|
+
require 'boson/science'
|
13
|
+
require 'boson/runner_options'
|
14
|
+
require 'boson/console'
|
15
|
+
require 'boson/viewable'
|
16
|
+
|
17
|
+
module Boson::More
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'boson/commands/core'
|
2
|
+
require 'boson/commands/web_core'
|
3
|
+
require 'boson/commands/view_core'
|
4
|
+
require 'boson/bare_runner'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
Boson::BareRunner::DEFAULT_LIBRARIES << Boson::Commands::Core
|
8
|
+
Boson::BareRunner::DEFAULT_LIBRARIES << Boson::Commands::WebCore
|
9
|
+
Boson::BareRunner::DEFAULT_LIBRARIES << Boson::Commands::ViewCore
|
10
|
+
|
11
|
+
# TODO: Use Boson.home when it exists
|
12
|
+
dir = Dir.home + '/.boson/.more_commands'
|
13
|
+
FileUtils.mkdir_p dir
|
14
|
+
Boson.repos << Boson::Repo.new(dir)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'boson/comment_inspector'
|
2
|
+
require 'boson/more_method_inspector'
|
3
|
+
|
4
|
+
module Boson
|
5
|
+
# When deciding whether to use commented or normal Module methods, remember that commented Module methods allow
|
6
|
+
# independence from Boson (useful for testing).
|
7
|
+
# See CommentInspector for more about commented method attributes.
|
8
|
+
class Inspector
|
9
|
+
module MoreInspector
|
10
|
+
def add_data
|
11
|
+
super
|
12
|
+
add_comment_scraped_data
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_comment_scraped_data
|
16
|
+
(@store[:method_locations] || []).select {|k,(f,l)| f == @library_file }.each do |cmd, (file, lineno)|
|
17
|
+
scraped = CommentInspector.scrape(FileLibrary.read_library_file(file), lineno, MethodInspector.instance.current_module)
|
18
|
+
@commands_hash[cmd] ||= {}
|
19
|
+
MethodInspector::METHODS.each do |e|
|
20
|
+
add_valid_data_to_config(e, scraped[e], cmd)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
include MoreInspector
|
26
|
+
end
|
27
|
+
|
28
|
+
# This module also saves method locations so CommentInspector
|
29
|
+
# can scrape their commented method attributes.
|
30
|
+
class MethodInspector
|
31
|
+
module MoreInspector
|
32
|
+
def inspector_in_file?(meth, inspector_method)
|
33
|
+
return false if !super
|
34
|
+
if File.exists?(file_line[0]) && (options = CommentInspector.scrape(
|
35
|
+
FileLibrary.read_library_file(file_line[0]), file_line[1], @current_module, inspector_method) )
|
36
|
+
(store[inspector_method] ||= {})[meth] = options
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
include MoreInspector
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Adds dependencies to manager
|
2
|
+
module Boson
|
3
|
+
class Manager
|
4
|
+
module MoreManager
|
5
|
+
def load_dependencies(lib, options={})
|
6
|
+
lib_dependencies[lib] = Array(lib.dependencies).map do |e|
|
7
|
+
next if self.class.loaded?(e)
|
8
|
+
load_once(e, options.merge(:dependency=>true)) ||
|
9
|
+
raise(LoaderError, "Can't load dependency #{e}")
|
10
|
+
end.compact
|
11
|
+
end
|
12
|
+
|
13
|
+
def lib_dependencies
|
14
|
+
@lib_dependencies ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def during_after_load
|
18
|
+
(lib_dependencies[@library] || []).each do |e|
|
19
|
+
create_commands(e)
|
20
|
+
self.class.add_library(e)
|
21
|
+
puts "Loaded library dependency #{e.name}" if verbose
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
include MoreManager
|
26
|
+
end
|
27
|
+
|
28
|
+
# [*:dependencies*] An array of libraries that this library depends on. A library won't load
|
29
|
+
# unless its dependencies are loaded first.
|
30
|
+
class Library
|
31
|
+
ATTRIBUTES << :dependencies
|
32
|
+
attr_reader :dependencies
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Boson
|
2
|
+
class MethodInspector
|
3
|
+
# Returns argument arrays
|
4
|
+
def self.scrape_arguments(file_string, meth)
|
5
|
+
tabspace = "[ \t]"
|
6
|
+
if match = /^#{tabspace}*def#{tabspace}+(?:\w+\.)?#{Regexp.quote(meth)}#{tabspace}*($|(?:\(|\s+)([^\n\)]+)\s*\)?\s*$)/.match(file_string)
|
7
|
+
(match.to_a[2] || '').strip.split(/\s*,\s*/).map {|e| e.split(/\s*=\s*/)}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# investigate why this can't be included
|
12
|
+
def inspector_in_file?(meth, inspector_method)
|
13
|
+
!(file_line = store[:method_locations] && store[:method_locations][meth]) ?
|
14
|
+
false : true
|
15
|
+
end
|
16
|
+
|
17
|
+
module MoreMethodInspector
|
18
|
+
def during_new_method_added(mod, meth)
|
19
|
+
if store[:temp].size < ALL_METHODS.size
|
20
|
+
store[:method_locations] ||= {}
|
21
|
+
if (result = find_method_locations(mod, meth))
|
22
|
+
store[:method_locations][meth.to_s] = result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_arguments(mod, meth)
|
28
|
+
store[:args] ||= {}
|
29
|
+
file = find_method_locations(mod, meth)[0]
|
30
|
+
|
31
|
+
if File.exists?(file)
|
32
|
+
body = File.read(file)
|
33
|
+
store[:args][meth.to_s] = self.class.scrape_arguments body, meth
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_inspector_method?(meth, inspector)
|
38
|
+
(store[inspector] && store[inspector].key?(meth.to_s)) ||
|
39
|
+
inspector_in_file?(meth.to_s, inspector)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns an array of the file and line number at which a method starts
|
43
|
+
# using a method
|
44
|
+
def find_method_locations(mod, meth)
|
45
|
+
mod.instance_method(meth).source_location
|
46
|
+
end
|
47
|
+
end
|
48
|
+
include MoreMethodInspector
|
49
|
+
end
|
50
|
+
|
51
|
+
class Command
|
52
|
+
module MoreMethodInspector
|
53
|
+
# One-line usage of args with default values
|
54
|
+
def basic_usage
|
55
|
+
return '' if options.nil? && args.nil?
|
56
|
+
args ? usage_args.map {|e|
|
57
|
+
(e.size < 2) ? "[#{e[0]}]" :
|
58
|
+
"[#{e[0]}=#{@file_parsed_args ? e[1] : e[1].inspect}]"
|
59
|
+
}.join(' ') : '[*unknown]'
|
60
|
+
end
|
61
|
+
|
62
|
+
# Help string for options if a command has it.
|
63
|
+
def option_help
|
64
|
+
@options ? option_parser.to_s : ''
|
65
|
+
end
|
66
|
+
|
67
|
+
# keep?
|
68
|
+
def usage
|
69
|
+
basic_usage + option_help
|
70
|
+
end
|
71
|
+
end
|
72
|
+
include MoreMethodInspector
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Boson
|
2
|
+
class OptionParser
|
3
|
+
module MoreOptionParser
|
4
|
+
# Given options to pass to OptionParser.new, this method parses ARGV and
|
5
|
+
# returns the remaining arguments and a hash of parsed options. This is
|
6
|
+
# useful for scripts outside of Boson.
|
7
|
+
def parse(options, args=ARGV)
|
8
|
+
@opt_parser ||= new(options)
|
9
|
+
parsed_options = @opt_parser.parse(args)
|
10
|
+
[@opt_parser.non_opts, parsed_options]
|
11
|
+
end
|
12
|
+
|
13
|
+
# Usage string summarizing options defined in parse
|
14
|
+
def usage
|
15
|
+
@opt_parser.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def make_mergeable!(opts)
|
19
|
+
opts.each {|k,v|
|
20
|
+
if !v.is_a?(Hash) && !v.is_a?(Symbol)
|
21
|
+
opts[k] = {:default=>v}
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
extend MoreOptionParser
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Boson
|
2
|
+
# Any command with options comes with basic global options. For example '-hv'
|
3
|
+
# on an option command prints a help summarizing global and local options.
|
4
|
+
# Another basic global option is --pretend. This option displays what global
|
5
|
+
# options have been parsed and the actual arguments to be passed to a
|
6
|
+
# command if executed. For example:
|
7
|
+
#
|
8
|
+
# # Define this command in a library
|
9
|
+
# options :level=>:numeric, :verbose=>:boolean
|
10
|
+
# def foo(*args)
|
11
|
+
# args
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# irb>> foo 'testin -p -l=1'
|
15
|
+
# Arguments: ["testin", {:level=>1}]
|
16
|
+
# Global options: {:pretend=>true}
|
17
|
+
#
|
18
|
+
# If a global option conflicts with a local option, the local option takes
|
19
|
+
# precedence. You can get around this by passing global options after a '-'.
|
20
|
+
# For example, if the global option -h (--help) conflicts with a local -h
|
21
|
+
# (--force):
|
22
|
+
# foo 'arg1 -v -f - -f=f1,f2'
|
23
|
+
# # is the same as
|
24
|
+
# foo 'arg1 -v --fields=f1,f2 -f'
|
25
|
+
class OptionCommand
|
26
|
+
BASIC_OPTIONS.update(
|
27
|
+
:verbose=>{:type=>:boolean, :desc=>"Increase verbosity for help, errors, etc."},
|
28
|
+
:pretend=>{:type=>:boolean,
|
29
|
+
:desc=>"Display what a command would execute without executing it"}
|
30
|
+
)
|
31
|
+
end
|
32
|
+
module Scientist
|
33
|
+
module MoreScientist
|
34
|
+
def during_analyze(&block)
|
35
|
+
run_pretend_option(@args)
|
36
|
+
super unless @global_options[:pretend]
|
37
|
+
end
|
38
|
+
|
39
|
+
def analyze(*)
|
40
|
+
super
|
41
|
+
rescue OptionCommand::CommandArgumentError
|
42
|
+
run_pretend_option(@args ||= [])
|
43
|
+
return if !@global_options[:pretend] &&
|
44
|
+
run_verbose_help(option_command, @original_args)
|
45
|
+
raise unless @global_options[:pretend]
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def run_verbose_help(option_command, original_args)
|
50
|
+
global_opts = option_command.parse_global_options(original_args)
|
51
|
+
if global_opts[:help] && global_opts[:verbose]
|
52
|
+
@global_options = global_opts
|
53
|
+
run_help_option @command
|
54
|
+
return true
|
55
|
+
end
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
def run_pretend_option(args)
|
60
|
+
if @global_options[:verbose] || @global_options[:pretend]
|
61
|
+
puts "Arguments: #{args.inspect}",
|
62
|
+
"Global options: #{@global_options.inspect}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
extend MoreScientist
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Boson
|
2
|
+
module MoreUtil
|
3
|
+
# Behaves just like the unix which command, returning the full path to an executable based on ENV['PATH'].
|
4
|
+
def which(command)
|
5
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).map {|e| File.join(e, command) }.find {|e| File.exists?(e) }
|
6
|
+
end
|
7
|
+
|
8
|
+
# Deep copies any object if it can be marshaled. Useful for deep hashes.
|
9
|
+
def deep_copy(obj)
|
10
|
+
Marshal::load(Marshal::dump(obj))
|
11
|
+
end
|
12
|
+
|
13
|
+
# Safely calls require, returning false if LoadError occurs.
|
14
|
+
def safe_require(lib)
|
15
|
+
begin
|
16
|
+
require lib
|
17
|
+
true
|
18
|
+
rescue LoadError
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns name of top level class that conflicts if it exists. For example, for base module Boson::Commands,
|
24
|
+
# Boson::Commands::Hirb conflicts with Hirb if Hirb exists.
|
25
|
+
def top_level_class_conflict(base_module, conflicting_module)
|
26
|
+
(conflicting_module =~ /^#{base_module}.*::([^:]+)/) && Object.const_defined?($1) && $1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
Util.extend MoreUtil
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Boson
|
2
|
+
# Used in all things namespace.
|
3
|
+
class Namespace
|
4
|
+
# Hash of created namespace names to namespace objects
|
5
|
+
def self.namespaces
|
6
|
+
@namespaces ||= {}
|
7
|
+
end
|
8
|
+
|
9
|
+
# Creates a namespace given its name and the library it belongs to.
|
10
|
+
def self.create(name, library)
|
11
|
+
namespaces[name.to_s] = new(name, library)
|
12
|
+
Commands::Namespace.send(:define_method, name) { Boson::Namespace.namespaces[name.to_s] }
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(name, library)
|
16
|
+
raise ArgumentError unless library.module
|
17
|
+
@name, @library = name.to_s, library
|
18
|
+
class <<self; self end.send :include, @library.module
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing(method, *args, &block)
|
22
|
+
Boson.can_invoke?(method) ? Boson.invoke(method, *args, &block) : super
|
23
|
+
end
|
24
|
+
|
25
|
+
#:startdoc:
|
26
|
+
# List of subcommands for the namespace.
|
27
|
+
def boson_commands
|
28
|
+
@library.module.instance_methods.map {|e| e.to_s }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'boson/namespace'
|
2
|
+
|
3
|
+
module Boson
|
4
|
+
NAMESPACE = '.' # Delimits namespace from command
|
5
|
+
module Commands
|
6
|
+
# Used for defining namespaces.
|
7
|
+
module Namespace; end
|
8
|
+
end
|
9
|
+
Universe.send :include, Commands::Namespace
|
10
|
+
|
11
|
+
module Namespacer
|
12
|
+
# Invoke command string even with namespaces
|
13
|
+
def full_invoke(cmd, args) #:nodoc:
|
14
|
+
command, subcommand = cmd.include?(NAMESPACE) ? cmd.split(NAMESPACE, 2) : [cmd, nil]
|
15
|
+
dispatcher = subcommand ? Boson.invoke(command) : Boson.main_object
|
16
|
+
dispatcher.send(subcommand || command, *args)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
extend Namespacer
|
20
|
+
|
21
|
+
class Command
|
22
|
+
INIT_ATTRIBUTES << :namespace
|
23
|
+
module NamespacerClass
|
24
|
+
def library_attributes(library)
|
25
|
+
super.update(namespace: library.namespace)
|
26
|
+
end
|
27
|
+
|
28
|
+
def find(command, commands=Boson.commands)
|
29
|
+
if command.to_s.include?(NAMESPACE)
|
30
|
+
command, subcommand = command.to_s.split(NAMESPACE, 2)
|
31
|
+
commands.find {|current_command|
|
32
|
+
[current_command.name, current_command.alias].include?(subcommand) &&
|
33
|
+
current_command.library && (current_command.library.namespace == command)
|
34
|
+
}
|
35
|
+
else
|
36
|
+
commands.find {|e| [e.name, e.alias].include?(command) && !e.namespace}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
extend NamespacerClass
|
41
|
+
|
42
|
+
module Namespacer
|
43
|
+
attr_accessor :namespace
|
44
|
+
|
45
|
+
# Full name is only different than name if a command has a namespace.
|
46
|
+
# The full name should be what you would type to execute the command.
|
47
|
+
def full_name
|
48
|
+
@namespace ? "#{@namespace}.#{@name}" : @name
|
49
|
+
end
|
50
|
+
end
|
51
|
+
include Namespacer
|
52
|
+
end
|
53
|
+
|
54
|
+
class Library
|
55
|
+
# [*:namespace*] Boolean or string which namespaces a library. When true, the library is automatically namespaced
|
56
|
+
# to the library's name. When a string, the library is namespaced to the string. Default is nil.
|
57
|
+
module Namespacer
|
58
|
+
# Optional namespace name for a library. When enabled defaults to a library's name.
|
59
|
+
attr_writer :namespace
|
60
|
+
|
61
|
+
# The object a library uses for executing its commands.
|
62
|
+
def namespace_object
|
63
|
+
@namespace_object ||= namespace ? Boson.invoke(namespace) : Boson.main_object
|
64
|
+
end
|
65
|
+
|
66
|
+
def namespace(orig=@namespace)
|
67
|
+
@namespace = [String,FalseClass].include?(orig.class) ? orig : begin
|
68
|
+
if (@namespace == true || (Boson.config[:auto_namespace] && !@index))
|
69
|
+
@namespace = clean_name
|
70
|
+
else
|
71
|
+
@namespace = false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
include Namespacer
|
77
|
+
|
78
|
+
module NamespaceLoader
|
79
|
+
attr_reader :indexed_namespace, :object_namespace
|
80
|
+
|
81
|
+
def handle_method_conflict_error(err)
|
82
|
+
if Boson.config[:error_method_conflicts] || namespace
|
83
|
+
raise err
|
84
|
+
else
|
85
|
+
@namespace = clean_name
|
86
|
+
@method_conflict = true
|
87
|
+
$stderr.puts "#{err.message}. Attempting load into the namespace #{@namespace}..."
|
88
|
+
load_commands
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def actual_load_commands
|
93
|
+
@namespace = clean_name if @object_namespace
|
94
|
+
namespace ? Namespace.create(namespace, self) : include_in_universe
|
95
|
+
end
|
96
|
+
|
97
|
+
def method_conflicts
|
98
|
+
namespace ?
|
99
|
+
(Boson.can_invoke?(namespace) ? [namespace] : []) :
|
100
|
+
super
|
101
|
+
end
|
102
|
+
|
103
|
+
def clean_library_commands
|
104
|
+
@commands.delete(namespace) if namespace
|
105
|
+
@commands += Boson.invoke(namespace).boson_commands if namespace && !@pre_defined_commands
|
106
|
+
super
|
107
|
+
end
|
108
|
+
|
109
|
+
def set_library_commands
|
110
|
+
@namespace = false if !(@module || @class_commands)
|
111
|
+
super
|
112
|
+
@indexed_namespace = (@namespace == false) ? nil : @namespace if @index
|
113
|
+
end
|
114
|
+
end
|
115
|
+
include NamespaceLoader
|
116
|
+
end
|
117
|
+
end
|