boson 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +15 -3
- data/Rakefile +2 -2
- data/VERSION.yml +1 -1
- data/lib/boson.rb +22 -4
- data/lib/boson/command.rb +12 -4
- data/lib/boson/commands/core.rb +19 -19
- data/lib/boson/commands/web_core.rb +18 -5
- data/lib/boson/index.rb +31 -22
- data/lib/boson/inspector.rb +12 -10
- data/lib/boson/inspectors/method_inspector.rb +1 -1
- data/lib/boson/libraries/file_library.rb +31 -11
- data/lib/boson/libraries/gem_library.rb +6 -0
- data/lib/boson/libraries/module_library.rb +24 -4
- data/lib/boson/libraries/require_library.rb +8 -0
- data/lib/boson/library.rb +75 -35
- data/lib/boson/loader.rb +41 -12
- data/lib/boson/manager.rb +19 -14
- data/lib/boson/option_parser.rb +144 -107
- data/lib/boson/options.rb +127 -0
- data/lib/boson/repo.rb +25 -6
- data/lib/boson/runner.rb +2 -2
- data/lib/boson/runners/bin_runner.rb +25 -15
- data/lib/boson/scientist.rb +98 -28
- data/lib/boson/util.rb +20 -14
- data/lib/boson/view.rb +70 -15
- data/test/bin_runner_test.rb +27 -11
- data/test/file_library_test.rb +14 -1
- data/test/index_test.rb +2 -1
- data/test/loader_test.rb +84 -21
- data/test/manager_test.rb +1 -9
- data/test/method_inspector_test.rb +2 -2
- data/test/option_parser_test.rb +176 -20
- data/test/repo_test.rb +1 -0
- data/test/scientist_test.rb +38 -2
- data/test/test_helper.rb +6 -3
- data/test/view_test.rb +58 -0
- metadata +7 -6
- data/test/commands_test.rb +0 -51
data/README.rdoc
CHANGED
@@ -3,6 +3,8 @@ A command/task framework similar to rake and thor that opens your ruby universe
|
|
3
3
|
and irb. For my libraries that use this, see {irbfiles}[http://github.com/cldwalker/irbfiles].
|
4
4
|
Works with Ruby 1.8.6 and 1.9.1.
|
5
5
|
|
6
|
+
Note: To read a linkable version of this README, {see here}[http://tagaholic.me/boson/doc/].
|
7
|
+
|
6
8
|
== Features
|
7
9
|
* Commands are just methods extended for a given object, the default being the top level object, main.
|
8
10
|
* Commands are accessible from the commandline (Boson::BinRunner) or irb (Boson::ConsoleRunner).
|
@@ -11,7 +13,9 @@ Works with Ruby 1.8.6 and 1.9.1.
|
|
11
13
|
* Comes with default commands to load, search, list and install commands and libraries (Boson::Commands::Core).
|
12
14
|
* Commands can be full-blown commandline apps thanks to powerful options (Boson::OptionParser)
|
13
15
|
and hirb's views.
|
14
|
-
*
|
16
|
+
* There are 5 default option types: boolean, array, string, hash and numeric. Custom option types
|
17
|
+
can be defined to map to any Ruby class with one method (Boson::Options).
|
18
|
+
* Commands can have views toggled without adding view code to the original command (Boson::Scientist).
|
15
19
|
* Command libraries are social as a user can install them from a url and then customize command
|
16
20
|
names and options without changing the original library.
|
17
21
|
* Namespaces are optional and when used are methods which allow for method_missing magic.
|
@@ -85,7 +89,7 @@ Having done that, let's start up irb:
|
|
85
89
|
2 rows in set
|
86
90
|
|
87
91
|
# Let's install another library
|
88
|
-
bash> boson install http://github.com/cldwalker/irbfiles/raw/master/boson/commands/irb_core.rb
|
92
|
+
bash> boson install http://github.com/cldwalker/irbfiles/raw/master/boson/commands/public/irb_core.rb
|
89
93
|
Saved to /Users/bozo/.boson/commands/irb_core.rb
|
90
94
|
|
91
95
|
# Let's start irb ...
|
@@ -132,18 +136,26 @@ Having done that, let's start up irb:
|
|
132
136
|
See Boson::FileLibrary or here[http://tagaholic.me/boson/doc/classes/Boson/FileLibrary.html].
|
133
137
|
|
134
138
|
== Todo
|
135
|
-
* More docs
|
136
139
|
* More tests
|
137
140
|
* Making commands out of existing gems easier and more powerful
|
138
141
|
* Better local repositories, perhaps a BosonFile
|
139
142
|
* Consider managing extensions to core and standard libraries
|
140
143
|
* Consider dropping alias gem dependency if not using its full potential
|
141
144
|
|
145
|
+
== Bugs/Issues
|
146
|
+
Please report them {on github}[http://github.com/cldwalker/boson/issues].
|
147
|
+
|
142
148
|
== Motivation
|
143
149
|
My {tagging obsession}[http://github.com/cldwalker/tag-tree] from the ruby console.
|
144
150
|
|
151
|
+
== Links
|
152
|
+
* http://tagaholic.me/2009/10/14/boson-command-your-ruby-universe.html
|
153
|
+
* http://tagaholic.me/2009/10/15/boson-and-hirb-interactions.html
|
154
|
+
* http://tagaholic.me/2009/10/19/how-boson-enhances-your-irb-experience.html
|
155
|
+
|
145
156
|
== Acknowledgements
|
146
157
|
Boson stands on the shoulders of these people and their ideas:
|
158
|
+
* Yehuda Katz for inspiring me with Thor's power and elegant design
|
147
159
|
* Yehuda Katz and Daniel Berger for an awesome option parser (Boson::OptionParser)
|
148
160
|
* Dave Thomas for scraping a method's comments (Boson::CommentInspector)
|
149
161
|
* Mauricio Fernandez for scraping a method's arguments (Boson::ArgumentInspector)
|
data/Rakefile
CHANGED
@@ -21,11 +21,11 @@ begin
|
|
21
21
|
s.description = "A command/task framework similar to rake and thor that opens your ruby universe to the commandline and irb."
|
22
22
|
s.summary = "Boson provides users with the power to turn any ruby method into a full-fledged commandline tool. Boson achieves this with powerful options (borrowed from thor) and views (thanks to hirb). Some other unique features that differentiate it from rake and thor include being accessible from irb and the commandline, being able to write boson commands in non-dsl ruby and toggling a pretty view of a command's output without additional view code."
|
23
23
|
s.email = "gabriel.horner@gmail.com"
|
24
|
-
s.homepage = "http://
|
24
|
+
s.homepage = "http://tagaholic.me/boson/"
|
25
25
|
s.authors = ["Gabriel Horner"]
|
26
26
|
s.has_rdoc = true
|
27
27
|
s.rubyforge_project = 'tagaholic'
|
28
|
-
s.add_dependency 'hirb', '>= 0.2.
|
28
|
+
s.add_dependency 'hirb', '>= 0.2.8'
|
29
29
|
s.add_dependency 'alias', '>= 0.2.1'
|
30
30
|
s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
|
31
31
|
s.files = FileList["Rakefile", "VERSION.yml", "README.rdoc", "LICENSE.txt", "{bin,lib,test}/**/*"]
|
data/VERSION.yml
CHANGED
data/lib/boson.rb
CHANGED
@@ -4,9 +4,20 @@ $:.unshift File.dirname(__FILE__) unless $:.include? File.expand_path(File.dirna
|
|
4
4
|
%w{argument method comment}.each {|e| require "boson/inspectors/#{e}_inspector" }
|
5
5
|
# order of library subclasses matters
|
6
6
|
%w{module file gem require}.each {|e| require "boson/libraries/#{e}_library" }
|
7
|
-
%w{namespace view command util commands option_parser
|
7
|
+
(%w{namespace view command util commands option_parser options} +
|
8
|
+
%w{index scientist}).each {|e| require "boson/#{e}" }
|
8
9
|
|
9
10
|
# This module stores the libraries, commands, repos and main object used throughout Boson.
|
11
|
+
#
|
12
|
+
# Useful documentation links:
|
13
|
+
# * Boson::BinRunner - Runs the boson executable
|
14
|
+
# * Boson::ConsoleRunner - Runs Boson from the ruby console
|
15
|
+
# * Boson::Repo.config - Explains main config file
|
16
|
+
# * Boson::Library - All about libraries
|
17
|
+
# * Boson::FileLibrary - Explains creating libraries as files
|
18
|
+
# * Boson::Loader - Explains library module callbacks
|
19
|
+
# * Boson::OptionParser - All about options
|
20
|
+
# * Boson::Scientist - Explains how commands can be both shell-commands and normal ruby methods
|
10
21
|
module Boson
|
11
22
|
# Module which is extended by Boson.main_object to give it command functionality.
|
12
23
|
module Universe; include Commands::Namespace; end
|
@@ -28,7 +39,7 @@ module Boson
|
|
28
39
|
|
29
40
|
# The main required repository which defaults to ~/.boson.
|
30
41
|
def repo
|
31
|
-
@repo ||= Repo.new("#{
|
42
|
+
@repo ||= Repo.new("#{Util.find_home}/.boson")
|
32
43
|
end
|
33
44
|
|
34
45
|
# An optional local repository which defaults to ./lib/boson or ./.boson.
|
@@ -64,9 +75,16 @@ module Boson
|
|
64
75
|
main_object.send(*args, &block)
|
65
76
|
end
|
66
77
|
|
78
|
+
# Invoke command string even with namespaces
|
79
|
+
def full_invoke(cmd, args) #:nodoc:
|
80
|
+
command, subcommand = cmd.include?('.') ? cmd.split('.', 2) : [cmd, nil]
|
81
|
+
dispatcher = subcommand ? Boson.invoke(command) : Boson.main_object
|
82
|
+
dispatcher.send(subcommand || command, *args)
|
83
|
+
end
|
84
|
+
|
67
85
|
# Boolean indicating if the main object can invoke the given method/command.
|
68
|
-
def can_invoke?(meth)
|
69
|
-
Boson.main_object.respond_to? meth
|
86
|
+
def can_invoke?(meth, priv=true)
|
87
|
+
Boson.main_object.respond_to? meth, priv
|
70
88
|
end
|
71
89
|
end
|
72
90
|
|
data/lib/boson/command.rb
CHANGED
@@ -19,7 +19,7 @@ module Boson
|
|
19
19
|
end
|
20
20
|
|
21
21
|
ATTRIBUTES = [:name, :lib, :alias, :description, :options, :args]
|
22
|
-
attr_accessor *(ATTRIBUTES + [:render_options, :namespace])
|
22
|
+
attr_accessor *(ATTRIBUTES + [:render_options, :namespace, :default_option])
|
23
23
|
# A hash of attributes which map to instance variables and values. :name
|
24
24
|
# and :lib are required keys.
|
25
25
|
#
|
@@ -32,6 +32,11 @@ module Boson
|
|
32
32
|
# important for commands that have options/render_options. Its value can be an array
|
33
33
|
# (as ArgumentInspector.scrape_with_eval produces), a number representing
|
34
34
|
# the number of arguments or '*' if the command has a variable number of arguments.
|
35
|
+
# * *:default_option* Only for an option command that has one argument. This treats the given
|
36
|
+
# option as an optional first argument. Example:
|
37
|
+
# # For a command with default option 'query' and options --query and -v
|
38
|
+
# 'some -v' -> '--query=some -v'
|
39
|
+
# '-v' -> '-v'
|
35
40
|
def initialize(hash)
|
36
41
|
@name = hash[:name] or raise ArgumentError
|
37
42
|
@lib = hash[:lib] or raise ArgumentError
|
@@ -40,6 +45,7 @@ module Boson
|
|
40
45
|
@render_options = hash[:render_options] if hash[:render_options]
|
41
46
|
@options = hash[:options] if hash[:options]
|
42
47
|
@namespace = hash[:namespace] if hash[:namespace]
|
48
|
+
@default_option = hash[:default_option] if hash[:default_option]
|
43
49
|
if hash[:args]
|
44
50
|
if hash[:args].is_a?(Array)
|
45
51
|
@args = hash[:args]
|
@@ -80,7 +86,9 @@ module Boson
|
|
80
86
|
# Usage string for command, created from options and args.
|
81
87
|
def usage
|
82
88
|
return '' if options.nil? && args.nil?
|
83
|
-
usage_args = args && @options ?
|
89
|
+
usage_args = args && @options && !has_splat_args? ?
|
90
|
+
(@default_option ? [[@default_option.to_s, @file_parsed_args ? ''.inspect : '']] + args[0..-2] :
|
91
|
+
args[0..-2]) : args
|
84
92
|
str = args ? usage_args.map {|e|
|
85
93
|
(e.size < 2) ? "[#{e[0]}]" : "[#{e[0]}=#{@file_parsed_args ? e[1] : e[1].inspect}]"
|
86
94
|
}.join(' ') : '[*unknown]'
|
@@ -116,11 +124,11 @@ module Boson
|
|
116
124
|
@args.map! {|e| e.size == 2 ? [e[0], e[1].inspect] : e }
|
117
125
|
@file_parsed_args = true
|
118
126
|
end
|
119
|
-
[@name, @alias, @lib, @description, @options, @render_options, @args]
|
127
|
+
[@name, @alias, @lib, @description, @options, @render_options, @args, @default_option]
|
120
128
|
end
|
121
129
|
|
122
130
|
def marshal_load(ary)
|
123
|
-
@name, @alias, @lib, @description, @options, @render_options, @args = ary
|
131
|
+
@name, @alias, @lib, @description, @options, @render_options, @args, @default_option = ary
|
124
132
|
end
|
125
133
|
#:startdoc:
|
126
134
|
end
|
data/lib/boson/commands/core.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Boson::Commands::Core #:nodoc:
|
2
|
-
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def config
|
3
5
|
command_attributes = Boson::Command::ATTRIBUTES + [:usage, :full_name, :render_options]
|
4
6
|
library_attributes = Boson::Library::ATTRIBUTES + [:library_type]
|
5
7
|
|
@@ -7,35 +9,33 @@ module Boson::Commands::Core #:nodoc:
|
|
7
9
|
'render'=>{:description=>"Render any object using Hirb"},
|
8
10
|
'menu'=>{:description=>"Provide a menu to multi-select elements from a given array"},
|
9
11
|
'usage'=>{:description=>"Print a command's usage", :options=>{[:verbose, :V]=>:boolean}},
|
10
|
-
'commands'=>{
|
11
|
-
:
|
12
|
-
|
13
|
-
:render_options=>{
|
12
|
+
'commands'=>{
|
13
|
+
:description=>"List or search commands", :default_option=>'query',
|
14
|
+
:options=>{ :index=>{:type=>:boolean, :desc=>"Searches index"}},
|
15
|
+
:render_options=>{
|
16
|
+
:query=>{:keys=>command_attributes, :default_keys=>'full_name'},
|
17
|
+
:fields=>{:default=>[:full_name, :lib, :alias, :usage, :description], :values=>command_attributes} }
|
14
18
|
},
|
15
|
-
'libraries'=>{
|
16
|
-
:
|
17
|
-
|
19
|
+
'libraries'=>{
|
20
|
+
:description=>"List or search libraries", :default_option=>'query',
|
21
|
+
:options=>{ :index=>{:type=>:boolean, :desc=>"Searches index"} },
|
18
22
|
:render_options=>{
|
23
|
+
:query=>{:keys=>library_attributes, :default_keys=>'name'},
|
19
24
|
:fields=>{:default=>[:name, :commands, :gems, :library_type], :values=>library_attributes},
|
20
|
-
:filters=>{:default=>{:gems=>[:join, ','],:commands=>:size}
|
25
|
+
:filters=>{:default=>{:gems=>[:join, ','],:commands=>:size}, :desc=>"Filters to apply to library fields" }}
|
21
26
|
},
|
22
27
|
'load_library'=>{:description=>"Load/reload a library", :options=>{:reload=>:boolean, [:verbose,:V]=>true}}
|
23
28
|
}
|
24
29
|
|
25
|
-
{:library_file=>File.expand_path(__FILE__), :commands=>commands}
|
30
|
+
{:namespace=>false, :library_file=>File.expand_path(__FILE__), :commands=>commands}
|
26
31
|
end
|
27
32
|
|
28
|
-
def commands(
|
29
|
-
|
30
|
-
Boson::Index.read if options[:index]
|
31
|
-
commands = options[:index] ? Boson::Index.commands : Boson.commands
|
32
|
-
query_fields.map {|e| commands.select {|f| f.send(e).to_s =~ /#{query}/i } }.flatten.uniq
|
33
|
+
def commands(options={})
|
34
|
+
options[:index] ? (Boson::Index.read || true) && Boson::Index.commands : Boson.commands
|
33
35
|
end
|
34
36
|
|
35
|
-
def libraries(
|
36
|
-
Boson::Index.read
|
37
|
-
libraries = options[:index] ? Boson::Index.libraries : Boson.libraries
|
38
|
-
options[:query_fields].map {|e| libraries.select {|f| f.send(e).to_s =~ /#{query}/i } }.flatten.uniq
|
37
|
+
def libraries(options={})
|
38
|
+
options[:index] ? (Boson::Index.read || true) && Boson::Index.libraries : Boson.libraries
|
39
39
|
end
|
40
40
|
|
41
41
|
def load_library(library, options={})
|
@@ -1,23 +1,36 @@
|
|
1
1
|
module Boson::Commands::WebCore #:nodoc:
|
2
|
-
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def config
|
3
5
|
descriptions = {
|
4
6
|
:install=>"Installs a library by url. Library should then be loaded with load_library.",
|
5
|
-
:browser=>"Opens urls in a browser on a Mac", :get=>"Gets the body of a url" }
|
7
|
+
:browser=>"Opens urls in a browser on a Mac", :get=>"Gets the body of a url", :post=>'Posts to url' }
|
6
8
|
commands = descriptions.inject({}) {|h,(k,v)| h[k.to_s] = {:description=>v}; h}
|
7
9
|
commands['install'][:options] = {:name=>{:type=>:string, :desc=>"Library name to save to"},
|
8
10
|
:force=>{:type=>:boolean, :desc=>'Overwrites an existing library'},
|
9
11
|
:module_wrap=>{:type=>:boolean, :desc=>"Wraps a module around install using library name"},
|
10
12
|
:method_wrap=>{:type=>:boolean, :desc=>"Wraps a method and module around installe library using library name"}}
|
11
|
-
{:library_file=>File.expand_path(__FILE__), :commands=>commands}
|
13
|
+
{:library_file=>File.expand_path(__FILE__), :commands=>commands, :namespace=>false}
|
12
14
|
end
|
13
15
|
|
14
|
-
def get(url)
|
16
|
+
def get(url, options={})
|
15
17
|
%w{uri net/http}.each {|e| require e }
|
16
|
-
|
18
|
+
if options[:success_only]
|
19
|
+
url = URI.parse(url)
|
20
|
+
res = Net::HTTP.start(url.host, url.port) {|http| http.get(url.request_uri) }
|
21
|
+
res.code == '200' ? res.body : nil
|
22
|
+
else
|
23
|
+
Net::HTTP.get(URI.parse(url))
|
24
|
+
end
|
17
25
|
rescue
|
18
26
|
raise "Error opening #{url}"
|
19
27
|
end
|
20
28
|
|
29
|
+
def post(url, options={})
|
30
|
+
%w{uri net/http}.each {|e| require e }
|
31
|
+
Net::HTTP.post_form(URI.parse(url), options)
|
32
|
+
end
|
33
|
+
|
21
34
|
def install(url, options={})
|
22
35
|
options[:name] ||= strip_name_from_url(url)
|
23
36
|
return puts("Please give a library name for this url.") if options[:name].empty?
|
data/lib/boson/index.rb
CHANGED
@@ -1,22 +1,30 @@
|
|
1
1
|
require 'digest/md5'
|
2
2
|
module Boson
|
3
|
-
#
|
3
|
+
# This class is used by BinRunner to index/store all of Boson's commands and libraries. When this index updates,
|
4
|
+
# it detects library files whose md5 hash have changed and reindexes them. The index is stored with Marshal
|
5
|
+
# at ~/.boson/config/index.marshal. Since the index is marshaled, putting lambdas/procs in it will break it.
|
6
|
+
# If your index gets corrupted, simply delete it and next time Boson needs it, the index will be recreated.
|
4
7
|
module Index
|
5
8
|
extend self
|
6
9
|
attr_reader :libraries, :commands
|
7
10
|
|
8
11
|
# Updates the index.
|
9
12
|
def update(options={})
|
10
|
-
|
11
|
-
libraries_to_update = options[:all] ? Runner.all_libraries : changed_libraries
|
13
|
+
libraries_to_update = !exists? ? Runner.all_libraries : options[:libraries] || changed_libraries
|
12
14
|
read_and_transfer(libraries_to_update)
|
13
15
|
if options[:verbose]
|
14
|
-
puts
|
16
|
+
puts !exists? ? "Generating index for all #{libraries_to_update.size} libraries. Patience ... is a bitch" :
|
15
17
|
(libraries_to_update.empty? ? "No libraries indexed" :
|
16
18
|
"Indexing the following libraries: #{libraries_to_update.join(', ')}")
|
17
19
|
end
|
18
|
-
Manager.
|
19
|
-
|
20
|
+
Manager.failed_libraries = []
|
21
|
+
unless libraries_to_update.empty?
|
22
|
+
Manager.load(libraries_to_update, options.merge(:index=>true))
|
23
|
+
unless Manager.failed_libraries.empty?
|
24
|
+
$stderr.puts("Error: These libraries failed to load while indexing: #{Manager.failed_libraries.join(', ')}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
write(Manager.failed_libraries)
|
20
28
|
end
|
21
29
|
|
22
30
|
# Reads and initializes index.
|
@@ -24,22 +32,25 @@ module Boson
|
|
24
32
|
return if @read
|
25
33
|
@libraries, @commands, @lib_hashes = exists? ? Marshal.load(File.read(marshal_file)) : [[], [], {}]
|
26
34
|
delete_stale_libraries_and_commands
|
27
|
-
|
35
|
+
set_command_namespaces
|
28
36
|
@read = true
|
29
37
|
end
|
30
38
|
|
31
39
|
# Writes/saves current index to config/index.marshal.
|
32
|
-
def write
|
33
|
-
|
40
|
+
def write(failed_libraries=[])
|
41
|
+
latest = latest_hashes
|
42
|
+
failed_libraries.each {|e| latest.delete(e) }
|
43
|
+
save_marshal_index Marshal.dump([Boson.libraries, Boson.commands, latest])
|
34
44
|
end
|
35
45
|
|
36
46
|
#:stopdoc:
|
37
47
|
def read_and_transfer(ignored_libraries=[])
|
38
48
|
read
|
39
49
|
existing_libraries = (Boson.libraries.map {|e| e.name} + ignored_libraries).uniq
|
40
|
-
|
41
|
-
|
42
|
-
|
50
|
+
libraries_to_add = @libraries.select {|e| !existing_libraries.include?(e.name)}
|
51
|
+
Boson.libraries += libraries_to_add
|
52
|
+
# depends on saved commands being correctly associated with saved libraries
|
53
|
+
Boson.commands += libraries_to_add.map {|e| e.command_objects(e.commands, @commands) }.flatten
|
43
54
|
end
|
44
55
|
|
45
56
|
def exists?
|
@@ -58,21 +69,19 @@ module Boson
|
|
58
69
|
@commands.delete_if {|e| names_to_delete.include? e.lib }
|
59
70
|
end
|
60
71
|
|
61
|
-
#
|
62
|
-
def
|
63
|
-
namespace_libs = Boson.repo.config[:auto_namespace] ? @libraries.map {|e| [e.name, {:namespace=>true}]} :
|
64
|
-
Boson.repo.config[:libraries].select {|k,v| v && v[:namespace] }
|
72
|
+
# set namespaces for commands
|
73
|
+
def set_command_namespaces
|
65
74
|
lib_commands = @commands.inject({}) {|t,e| (t[e.lib] ||= []) << e; t }
|
66
|
-
namespace_libs.
|
67
|
-
|
68
|
-
|
69
|
-
(lib_commands[lib.name] || []).each {|e| e.namespace = lib.namespace }
|
70
|
-
end
|
75
|
+
namespace_libs = @libraries.select {|e| e.namespace(e.indexed_namespace) }
|
76
|
+
namespace_libs.each {|lib|
|
77
|
+
(lib_commands[lib.name] || []).each {|e| e.namespace = lib.namespace }
|
71
78
|
}
|
72
79
|
end
|
73
80
|
|
74
81
|
def namespaces
|
75
|
-
@libraries.map {|e| e.namespace }.compact
|
82
|
+
nsps = @libraries.map {|e| e.namespace }.compact
|
83
|
+
nsps.delete(false)
|
84
|
+
nsps
|
76
85
|
end
|
77
86
|
|
78
87
|
def all_main_methods
|
data/lib/boson/inspector.rb
CHANGED
@@ -49,20 +49,26 @@ module Boson
|
|
49
49
|
(MethodInspector::METHODS + [:method_args]).each do |e|
|
50
50
|
key = command_key(e)
|
51
51
|
(@store[e] || []).each do |cmd, val|
|
52
|
-
|
53
|
-
|
54
|
-
end
|
52
|
+
@commands_hash[cmd] ||= {}
|
53
|
+
add_scraped_data_to_config(key, val, cmd)
|
55
54
|
end
|
56
55
|
end
|
57
56
|
end
|
58
57
|
|
58
|
+
def add_scraped_data_to_config(key, value, cmd)
|
59
|
+
if value.is_a?(Hash)
|
60
|
+
@commands_hash[cmd][key] = Util.recursive_hash_merge value, @commands_hash[cmd][key] || {}
|
61
|
+
else
|
62
|
+
@commands_hash[cmd][key] ||= value
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
59
66
|
def add_comment_scraped_data
|
60
67
|
(@store[:method_locations] || []).select {|k,(f,l)| f == @library_file }.each do |cmd, (file, lineno)|
|
61
68
|
scraped = CommentInspector.scrape(FileLibrary.read_library_file(file), lineno, MethodInspector.current_module)
|
69
|
+
@commands_hash[cmd] ||= {}
|
62
70
|
MethodInspector::METHODS.each do |e|
|
63
|
-
|
64
|
-
(@commands_hash[cmd] ||= {})[command_key(e)] = scraped[e]
|
65
|
-
end
|
71
|
+
add_scraped_data_to_config(command_key(e), scraped[e], cmd)
|
66
72
|
end
|
67
73
|
end
|
68
74
|
end
|
@@ -71,10 +77,6 @@ module Boson
|
|
71
77
|
def command_key(key)
|
72
78
|
{:method_args=>:args, :desc=>:description}[key] || key
|
73
79
|
end
|
74
|
-
|
75
|
-
def no_command_config_for(cmd, attribute)
|
76
|
-
!@commands_hash[cmd] || (@commands_hash[cmd] && !@commands_hash[cmd].key?(attribute))
|
77
|
-
end
|
78
80
|
#:startdoc:
|
79
81
|
end
|
80
82
|
end
|
@@ -3,8 +3,8 @@ module Boson
|
|
3
3
|
# Since there can be multiple repositories, a library's file is looked for in the order given by
|
4
4
|
# Boson.repos.
|
5
5
|
#
|
6
|
-
# To create this library, simply create a file with a module and some methods
|
7
|
-
# are automatically loaded as a library's commands.
|
6
|
+
# To create this library, simply create a file with a module and some methods (See Library
|
7
|
+
# for naming a module). Non-private methods are automatically loaded as a library's commands.
|
8
8
|
#
|
9
9
|
# Take for example a library brain.rb:
|
10
10
|
# module Brain
|
@@ -83,30 +83,47 @@ module Boson
|
|
83
83
|
end
|
84
84
|
|
85
85
|
handles {|source|
|
86
|
-
@repo = Boson.repos.find {|e|
|
87
|
-
|
88
|
-
|
86
|
+
@repo = Boson.repos.find {|e| File.exists? library_file(source.to_s, e.dir) } ||
|
87
|
+
Boson.repos.find {|e|
|
88
|
+
Dir["#{e.commands_dir}/**/*.rb"].grep(/\/#{source}\.rb/).size == 1
|
89
|
+
}
|
89
90
|
!!@repo
|
90
91
|
}
|
91
92
|
|
92
|
-
def library_file
|
93
|
-
self.class.library_file(
|
93
|
+
def library_file(name=@name)
|
94
|
+
self.class.library_file(name, @repo_dir)
|
94
95
|
end
|
95
96
|
|
96
97
|
def set_repo
|
97
98
|
self.class.matched_repo
|
98
99
|
end
|
99
100
|
|
101
|
+
def set_name(name)
|
102
|
+
@lib_file = File.exists?(library_file(name.to_s)) ? library_file(name.to_s) :
|
103
|
+
Dir[self.class.matched_repo.commands_dir.to_s+'/**/*.rb'].find {|e| e =~ /\/#{name}\.rb$/}
|
104
|
+
super @lib_file.gsub(/^#{self.class.matched_repo.commands_dir}\/|\.rb$/, '')
|
105
|
+
end
|
106
|
+
|
100
107
|
def load_source(reload=false)
|
101
|
-
library_string = self.class.read_library_file(
|
108
|
+
library_string = self.class.read_library_file(@lib_file, reload)
|
109
|
+
@base_module = @name.include?('/') ? create_module_from_path : Commands
|
102
110
|
Inspector.enable
|
103
|
-
|
111
|
+
@base_module.module_eval(library_string, @lib_file)
|
104
112
|
Inspector.disable
|
105
113
|
end
|
106
114
|
|
115
|
+
def create_module_from_path
|
116
|
+
@name.split('/')[0..-2].inject(Boson::Commands) {|base, e|
|
117
|
+
base.const_defined?(sub_mod = Util.camelize(e)) ? base.const_get(sub_mod) :
|
118
|
+
Util.create_module(base, e)
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
107
122
|
def load_source_and_set_module
|
108
123
|
detected = detect_additions(:modules=>true) { load_source }
|
109
124
|
@module = determine_lib_module(detected[:modules]) unless @module
|
125
|
+
#without this, module's class methods weren't showing up
|
126
|
+
@module = Util.constantize(@module) if @base_module != Commands
|
110
127
|
end
|
111
128
|
|
112
129
|
def reload_source_and_set_module
|
@@ -122,8 +139,11 @@ module Boson
|
|
122
139
|
when 1 then lib_module = detected_modules[0]
|
123
140
|
when 0 then raise LoaderError, "Can't detect module. Make sure at least one module is defined in the library."
|
124
141
|
else
|
125
|
-
unless (
|
126
|
-
|
142
|
+
unless (lib_module = Util.constantize("boson/commands/#{@name}")) && lib_module.to_s[/^Boson::Commands/]
|
143
|
+
command_modules = detected_modules.map {|e| e.to_s}.grep(/^#{@base_module}::/)
|
144
|
+
unless command_modules.size == 1 && (lib_module = command_modules[0])
|
145
|
+
raise LoaderError, "Can't detect module. Specify a module in this library's config."
|
146
|
+
end
|
127
147
|
end
|
128
148
|
end
|
129
149
|
lib_module
|