boson 0.2.0 → 0.2.1
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 +21 -14
- data/VERSION.yml +1 -1
- data/lib/boson.rb +5 -5
- data/lib/boson/command.rb +20 -21
- data/lib/boson/commands/core.rb +5 -6
- data/lib/boson/index.rb +21 -98
- data/lib/boson/inspector.rb +28 -2
- data/lib/boson/inspectors/comment_inspector.rb +5 -7
- data/lib/boson/inspectors/method_inspector.rb +4 -17
- data/lib/boson/libraries/file_library.rb +22 -27
- data/lib/boson/libraries/local_file_library.rb +30 -0
- data/lib/boson/libraries/module_library.rb +1 -1
- data/lib/boson/library.rb +15 -9
- data/lib/boson/loader.rb +3 -18
- data/lib/boson/manager.rb +4 -31
- data/lib/boson/option_command.rb +204 -0
- data/lib/boson/option_parser.rb +13 -2
- data/lib/boson/options.rb +1 -1
- data/lib/boson/pipe.rb +157 -0
- data/lib/boson/repo.rb +20 -1
- data/lib/boson/repo_index.rb +123 -0
- data/lib/boson/runner.rb +3 -4
- data/lib/boson/runners/bin_runner.rb +23 -8
- data/lib/boson/runners/console_runner.rb +1 -2
- data/lib/boson/scientist.rb +48 -225
- data/lib/boson/view.rb +50 -64
- data/test/bin_runner_test.rb +48 -7
- data/test/comment_inspector_test.rb +7 -6
- data/test/config/index.marshal +0 -0
- data/test/file_library_test.rb +1 -22
- data/test/loader_test.rb +5 -13
- data/test/manager_test.rb +4 -4
- data/test/method_inspector_test.rb +7 -2
- data/test/{view_test.rb → pipe_test.rb} +11 -11
- data/test/{index_test.rb → repo_index_test.rb} +26 -26
- data/test/scientist_test.rb +2 -2
- metadata +11 -6
data/README.rdoc
CHANGED
@@ -6,20 +6,30 @@ Works with Ruby 1.8.6 and 1.9.1.
|
|
6
6
|
Note: To read a linkable version of this README, {see here}[http://tagaholic.me/boson/doc/].
|
7
7
|
|
8
8
|
== Features
|
9
|
-
* Commands are just methods
|
9
|
+
* Simple organization: Commands are just methods on an object (default is main) and command libraries are just modules.
|
10
10
|
* Commands are accessible from the commandline (Boson::BinRunner) or irb (Boson::ConsoleRunner).
|
11
|
-
*
|
12
|
-
and use
|
11
|
+
* Libraries
|
12
|
+
* can be written in plain ruby which allows for easy testing and use independent of boson (Boson::FileLibrary).
|
13
|
+
* can exist locally as a Bosonfile (Boson::LocalFileLibrary) and under lib/boson/commands or .boson/commands.
|
14
|
+
* can be made from gems (Boson::GemLibrary) or any require-able file (Boson::RequireLibrary).
|
15
|
+
* are encouraged to be shared. Libraries can be installed with a given url. Users can customize any aspect of a third-party
|
16
|
+
library without modifying it (Boson::Library).
|
17
|
+
* Commands
|
18
|
+
* can have any number of local and global options (Boson::OptionCommand). Options are defined with Boson::OptionParser.
|
19
|
+
* can have any view associated to it (via Hirb) without adding view code to the command's method.
|
20
|
+
These views can be toggled on and manipulated via global render options (Boson::View and Boson::OptionCommand).
|
21
|
+
* can pipe their return value into multiple commands with pipe options. Default pipe options give the ability
|
22
|
+
to search and sort an array of any objects (Boson::Pipe).
|
23
|
+
* Option parser (Boson::OptionParser)
|
24
|
+
* provides option types that map to objects i.e. :array type creates Array objects.
|
25
|
+
* come with 5 default option types: boolean, array, string, hash and numeric.
|
26
|
+
* can have have custom option types defined by users (Boson::Options).
|
13
27
|
* Comes with default commands to load, search, list and install commands and libraries (Boson::Commands::Core).
|
14
|
-
* Commands can be full-blown commandline apps thanks to powerful options (Boson::OptionParser)
|
15
|
-
and hirb's views.
|
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).
|
19
|
-
* Command libraries are social as a user can install them from a url and then customize command
|
20
|
-
names and options without changing the original library.
|
21
28
|
* Namespaces are optional and when used are methods which allow for method_missing magic.
|
22
29
|
|
30
|
+
== Creating Command Libraries
|
31
|
+
See Boson::FileLibrary or here[http://tagaholic.me/boson/doc/classes/Boson/FileLibrary.html].
|
32
|
+
|
23
33
|
== Irb Example
|
24
34
|
|
25
35
|
To use in irb, drop this in your ~/.irbrc:
|
@@ -132,14 +142,11 @@ Having done that, let's start up irb:
|
|
132
142
|
|
133
143
|
# Sweet! Now we have a list and description of commands that come with irb.
|
134
144
|
|
135
|
-
== Creating Command Libraries
|
136
|
-
See Boson::FileLibrary or here[http://tagaholic.me/boson/doc/classes/Boson/FileLibrary.html].
|
137
|
-
|
138
145
|
== Todo
|
139
146
|
* More tests
|
140
147
|
* Making commands out of existing gems easier and more powerful
|
141
|
-
* Better local repositories, perhaps a BosonFile
|
142
148
|
* Consider managing extensions to core and standard libraries
|
149
|
+
* Features based on commands and their argument types i.e. aliasing, completion, piping
|
143
150
|
* Consider dropping alias gem dependency if not using its full potential
|
144
151
|
|
145
152
|
== Bugs/Issues
|
data/VERSION.yml
CHANGED
data/lib/boson.rb
CHANGED
@@ -3,9 +3,9 @@ $:.unshift File.dirname(__FILE__) unless $:.include? File.expand_path(File.dirna
|
|
3
3
|
%w{runner runners/console_runner repo manager loader inspector library}.each {|e| require "boson/#{e}" }
|
4
4
|
%w{argument method comment}.each {|e| require "boson/inspectors/#{e}_inspector" }
|
5
5
|
# order of library subclasses matters
|
6
|
-
%w{module file gem require}.each {|e| require "boson/libraries/#{e}_library" }
|
6
|
+
%w{module file gem require local_file}.each {|e| require "boson/libraries/#{e}_library" }
|
7
7
|
(%w{namespace view command util commands option_parser options} +
|
8
|
-
%w{index scientist}).each {|e| require "boson/#{e}" }
|
8
|
+
%w{index repo_index scientist option_command pipe}).each {|e| require "boson/#{e}" }
|
9
9
|
|
10
10
|
# This module stores the libraries, commands, repos and main object used throughout Boson.
|
11
11
|
#
|
@@ -17,7 +17,6 @@ $:.unshift File.dirname(__FILE__) unless $:.include? File.expand_path(File.dirna
|
|
17
17
|
# * Boson::FileLibrary - Explains creating libraries as files
|
18
18
|
# * Boson::Loader - Explains library module callbacks
|
19
19
|
# * Boson::OptionParser - All about options
|
20
|
-
# * Boson::Scientist - Explains how commands can be both shell-commands and normal ruby methods
|
21
20
|
module Boson
|
22
21
|
# Module which is extended by Boson.main_object to give it command functionality.
|
23
22
|
module Universe; include Commands::Namespace; end
|
@@ -45,13 +44,14 @@ module Boson
|
|
45
44
|
# An optional local repository which defaults to ./lib/boson or ./.boson.
|
46
45
|
def local_repo
|
47
46
|
@local_repo ||= begin
|
47
|
+
ignored_dirs = (repo.config[:ignore_directories] || []).map {|e| File.expand_path(e) }
|
48
48
|
dir = ["lib/boson", ".boson"].find {|e| File.directory?(e) &&
|
49
|
-
|
49
|
+
File.expand_path(e) != repo.dir && !ignored_dirs.include?(File.expand_path('.')) }
|
50
50
|
Repo.new(dir) if dir
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
# The array of loaded repositories
|
54
|
+
# The array of loaded repositories containing the main repo and a possible local repo
|
55
55
|
def repos
|
56
56
|
@repos ||= [repo, local_repo].compact
|
57
57
|
end
|
data/lib/boson/command.rb
CHANGED
@@ -24,28 +24,27 @@ module Boson
|
|
24
24
|
# and :lib are required keys.
|
25
25
|
#
|
26
26
|
# Attributes that can be configured:
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# *
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
27
|
+
# [*:description*] Description that shows up in command listings
|
28
|
+
# [*:alias*] Alternative name for command
|
29
|
+
# [*:options*] Hash of options passed to OptionParser
|
30
|
+
# [*:render_options*] Hash of rendering options passed to OptionParser
|
31
|
+
# [*:global_options*] Boolean to enable using global options without having to define render_options or options.
|
32
|
+
# [*:args*] Should only be set if not automatically set. This attribute is only
|
33
|
+
# important for commands that have options/render_options. Its value can be an array
|
34
|
+
# (as ArgumentInspector.scrape_with_eval produces), a number representing
|
35
|
+
# the number of arguments or '*' if the command has a variable number of arguments.
|
36
|
+
# [*:default_option*] Only for an option command that has one or zero arguments. This treats the given
|
37
|
+
# option as an optional first argument. Example:
|
38
|
+
# # For a command with default option 'query' and options --query and -v
|
39
|
+
# 'some -v' -> '--query=some -v'
|
40
|
+
# '-v' -> '-v'
|
40
41
|
def initialize(hash)
|
41
42
|
@name = hash[:name] or raise ArgumentError
|
42
43
|
@lib = hash[:lib] or raise ArgumentError
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
@namespace = hash[:namespace] if hash[:namespace]
|
48
|
-
@default_option = hash[:default_option] if hash[:default_option]
|
44
|
+
[:alias, :description, :render_options, :options, :namespace, :default_option,
|
45
|
+
:global_options].each do |e|
|
46
|
+
instance_variable_set("@#{e}", hash[e]) if hash[e]
|
47
|
+
end
|
49
48
|
if hash[:args]
|
50
49
|
if hash[:args].is_a?(Array)
|
51
50
|
@args = hash[:args]
|
@@ -75,7 +74,7 @@ module Boson
|
|
75
74
|
|
76
75
|
# Option parser for command as defined by @options.
|
77
76
|
def option_parser
|
78
|
-
@option_parser ||=
|
77
|
+
@option_parser ||= OptionParser.new(@options || {})
|
79
78
|
end
|
80
79
|
|
81
80
|
# Help string for options if a command has it.
|
@@ -107,7 +106,7 @@ module Boson
|
|
107
106
|
end
|
108
107
|
|
109
108
|
def option_command?
|
110
|
-
options || render_options
|
109
|
+
options || render_options || @global_options
|
111
110
|
end
|
112
111
|
|
113
112
|
def arg_size
|
data/lib/boson/commands/core.rb
CHANGED
@@ -24,7 +24,7 @@ module Boson::Commands::Core #:nodoc:
|
|
24
24
|
:fields=>{:default=>[:name, :commands, :gems, :library_type], :values=>library_attributes},
|
25
25
|
:filters=>{:default=>{:gems=>[:join, ','],:commands=>:size}, :desc=>"Filters to apply to library fields" }}
|
26
26
|
},
|
27
|
-
'load_library'=>{:description=>"Load
|
27
|
+
'load_library'=>{:description=>"Load a library", :options=>{[:verbose,:V]=>true}}
|
28
28
|
}
|
29
29
|
|
30
30
|
{:namespace=>false, :library_file=>File.expand_path(__FILE__), :commands=>commands}
|
@@ -39,8 +39,7 @@ module Boson::Commands::Core #:nodoc:
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def load_library(library, options={})
|
42
|
-
|
43
|
-
Boson::Manager.load(library, options)
|
42
|
+
Boson::Manager.load(library, options)
|
44
43
|
end
|
45
44
|
|
46
45
|
def render(object, options={})
|
@@ -56,11 +55,11 @@ module Boson::Commands::Core #:nodoc:
|
|
56
55
|
puts msg
|
57
56
|
if command && options[:verbose]
|
58
57
|
if command.options && !command.options.empty?
|
59
|
-
puts "\
|
58
|
+
puts "\nLOCAL OPTIONS"
|
60
59
|
command.option_parser.print_usage_table
|
61
60
|
end
|
62
|
-
puts "\nGLOBAL
|
63
|
-
Boson::Scientist.
|
61
|
+
puts "\nGLOBAL OPTIONS"
|
62
|
+
Boson::Scientist.option_command(command).option_parser.print_usage_table
|
64
63
|
end
|
65
64
|
end
|
66
65
|
end
|
data/lib/boson/index.rb
CHANGED
@@ -1,118 +1,41 @@
|
|
1
|
-
require 'digest/md5'
|
2
1
|
module Boson
|
3
|
-
# This class
|
4
|
-
#
|
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.
|
2
|
+
# This class manages indexing/storing all commands and libraries. See RepoIndex for details
|
3
|
+
# about the index created for each Repo.
|
7
4
|
module Index
|
8
5
|
extend self
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
def update(options={})
|
13
|
-
libraries_to_update = !exists? ? Runner.all_libraries : options[:libraries] || changed_libraries
|
14
|
-
read_and_transfer(libraries_to_update)
|
15
|
-
if options[:verbose]
|
16
|
-
puts !exists? ? "Generating index for all #{libraries_to_update.size} libraries. Patience ... is a bitch" :
|
17
|
-
(libraries_to_update.empty? ? "No libraries indexed" :
|
18
|
-
"Indexing the following libraries: #{libraries_to_update.join(', ')}")
|
19
|
-
end
|
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)
|
28
|
-
end
|
29
|
-
|
30
|
-
# Reads and initializes index.
|
31
|
-
def read
|
32
|
-
return if @read
|
33
|
-
@libraries, @commands, @lib_hashes = exists? ? Marshal.load(File.read(marshal_file)) : [[], [], {}]
|
34
|
-
delete_stale_libraries_and_commands
|
35
|
-
set_command_namespaces
|
36
|
-
@read = true
|
6
|
+
# Array of indexes, one per repo in Boson.repos.
|
7
|
+
def indexes
|
8
|
+
@indexes ||= Boson.repos.map {|e| RepoIndex.new(e) }
|
37
9
|
end
|
38
10
|
|
39
|
-
#
|
40
|
-
def
|
41
|
-
|
42
|
-
failed_libraries.each {|e| latest.delete(e) }
|
43
|
-
save_marshal_index Marshal.dump([Boson.libraries, Boson.commands, latest])
|
11
|
+
# Updates all repo indexes.
|
12
|
+
def update(options={})
|
13
|
+
indexes.each {|e| e.update(options) }
|
44
14
|
end
|
45
15
|
|
46
16
|
#:stopdoc:
|
47
|
-
def
|
48
|
-
read
|
49
|
-
existing_libraries = (Boson.libraries.map {|e| e.name} + ignored_libraries).uniq
|
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
|
54
|
-
end
|
55
|
-
|
56
|
-
def exists?
|
57
|
-
File.exists? marshal_file
|
58
|
-
end
|
59
|
-
|
60
|
-
def save_marshal_index(marshal_string)
|
61
|
-
File.open(marshal_file, 'w') {|f| f.write marshal_string }
|
62
|
-
end
|
63
|
-
|
64
|
-
def delete_stale_libraries_and_commands
|
65
|
-
cached_libraries = @lib_hashes.keys
|
66
|
-
libs_to_delete = @libraries.select {|e| !cached_libraries.include?(e.name) && e.is_a?(FileLibrary) }
|
67
|
-
names_to_delete = libs_to_delete.map {|e| e.name }
|
68
|
-
libs_to_delete.each {|e| @libraries.delete(e) }
|
69
|
-
@commands.delete_if {|e| names_to_delete.include? e.lib }
|
17
|
+
def read
|
18
|
+
indexes.each {|e| e.read }
|
70
19
|
end
|
71
20
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
namespace_libs.each {|lib|
|
77
|
-
(lib_commands[lib.name] || []).each {|e| e.namespace = lib.namespace }
|
21
|
+
def find_library(command)
|
22
|
+
indexes.each {|e|
|
23
|
+
lib = e.find_library(command)
|
24
|
+
return lib if lib
|
78
25
|
}
|
26
|
+
nil
|
79
27
|
end
|
80
28
|
|
81
|
-
def
|
82
|
-
|
83
|
-
nsps.delete(false)
|
84
|
-
nsps
|
85
|
-
end
|
86
|
-
|
87
|
-
def all_main_methods
|
88
|
-
@commands.reject {|e| e.namespace }.map {|e| [e.name, e.alias]}.flatten.compact + namespaces
|
89
|
-
end
|
90
|
-
|
91
|
-
def marshal_file
|
92
|
-
File.join(Boson.repo.config_dir, 'index.marshal')
|
93
|
-
end
|
94
|
-
|
95
|
-
def find_library(command)
|
96
|
-
read
|
97
|
-
namespace_command = command.split('.')[0]
|
98
|
-
if (lib = @libraries.find {|e| e.namespace == namespace_command })
|
99
|
-
lib.name
|
100
|
-
elsif (cmd = Command.find(command, @commands))
|
101
|
-
cmd.lib
|
102
|
-
end
|
29
|
+
def commands
|
30
|
+
indexes.map {|e| e.commands}.flatten
|
103
31
|
end
|
104
32
|
|
105
|
-
def
|
106
|
-
|
107
|
-
latest_hashes.select {|lib, hash| @lib_hashes[lib] != hash}.map {|e| e[0]}
|
33
|
+
def libraries
|
34
|
+
indexes.map {|e| e.libraries}.flatten
|
108
35
|
end
|
109
36
|
|
110
|
-
def
|
111
|
-
|
112
|
-
lib_file = FileLibrary.library_file(e, Boson.repo.dir)
|
113
|
-
h[e] = Digest::MD5.hexdigest(File.read(lib_file)) if File.exists?(lib_file)
|
114
|
-
h
|
115
|
-
}
|
37
|
+
def all_main_methods
|
38
|
+
indexes.map {|e| e.all_main_methods}.flatten
|
116
39
|
end
|
117
40
|
#:startdoc:
|
118
41
|
end
|
data/lib/boson/inspector.rb
CHANGED
@@ -1,6 +1,28 @@
|
|
1
1
|
module Boson
|
2
2
|
# Scrapes and processes method attributes with the inspectors (MethodInspector, CommentInspector
|
3
|
-
# and ArgumentInspector) and hands off the
|
3
|
+
# and ArgumentInspector) and hands off the data to FileLibrary objects.
|
4
|
+
#
|
5
|
+
# === Method Attributes
|
6
|
+
# Method attributes refer to (commented) Module methods placed before a command's method
|
7
|
+
# in a FileLibrary module:
|
8
|
+
# module SomeMod
|
9
|
+
# # @render_options :fields=>%w{one two}
|
10
|
+
# # @config :alias=>'so'
|
11
|
+
# options :verbose=>:boolean
|
12
|
+
# # Something descriptive perhaps
|
13
|
+
# def some_method(opts)
|
14
|
+
# # ...
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# Method attributes serve as configuration for a method's command. Available method attributes:
|
19
|
+
# * config: Hash to define any command attributes (see Command.new).
|
20
|
+
# * desc: String to define a command's description for a command. Defaults to first commented line above a method.
|
21
|
+
# * options: Hash to define an OptionParser object for a command's options.
|
22
|
+
# * render_options: Hash to define an OptionParser object for a command's local/global render options (see View).
|
23
|
+
#
|
24
|
+
# When deciding whether to use commented or normal Module methods, remember that commented Module methods allow
|
25
|
+
# independence from Boson (useful for testing). See CommentInspector for more about commented method attributes.
|
4
26
|
module Inspector
|
5
27
|
extend self
|
6
28
|
attr_reader :enabled
|
@@ -57,7 +79,11 @@ module Boson
|
|
57
79
|
|
58
80
|
def add_scraped_data_to_config(key, value, cmd)
|
59
81
|
if value.is_a?(Hash)
|
60
|
-
|
82
|
+
if key == :config
|
83
|
+
@commands_hash[cmd] = Util.recursive_hash_merge value, @commands_hash[cmd]
|
84
|
+
else
|
85
|
+
@commands_hash[cmd][key] = Util.recursive_hash_merge value, @commands_hash[cmd][key] || {}
|
86
|
+
end
|
61
87
|
else
|
62
88
|
@commands_hash[cmd][key] ||= value
|
63
89
|
end
|
@@ -1,25 +1,23 @@
|
|
1
1
|
module Boson
|
2
|
-
# Scrapes comments right before a method for its attributes.
|
3
|
-
# same as MethodInspector : desc, options, render_options. Attributes must begin with '@' i.e.:
|
4
|
-
#
|
2
|
+
# Scrapes comments right before a method for its attributes. Method attributes must begin with '@' i.e.:
|
5
3
|
# # @desc Does foo
|
6
4
|
# # @options :verbose=>true
|
7
5
|
# def foo(options={})
|
8
6
|
#
|
9
|
-
# Some rules about
|
10
|
-
# * Attribute definitions can span multiple lines. When a new attribute starts a line or the comments end
|
7
|
+
# Some rules about these attributes:
|
8
|
+
# * Attribute definitions can span multiple lines. When a new attribute starts a line or the comments end,
|
11
9
|
# then a definition ends.
|
12
10
|
# * If no @desc is found in the comment block, then the first comment line directly above the method
|
13
11
|
# is assumed to be the value for @desc. This means that no multi-line attribute definitions can occur
|
14
12
|
# without a description since the last line is assumed to be a description.
|
15
|
-
# * options and render_options attributes can take any valid ruby since they're evaled in their module's context.
|
13
|
+
# * options, config and render_options attributes can take any valid ruby since they're evaled in their module's context.
|
16
14
|
# * desc attribute is not evaled and is simply text to be set as a string.
|
17
15
|
#
|
18
16
|
# This module was inspired by
|
19
17
|
# {pragdave}[http://github.com/pragdavespc/rake/commit/45231ac094854da9f4f2ac93465ed9b9ca67b2da].
|
20
18
|
module CommentInspector
|
21
19
|
extend self
|
22
|
-
EVAL_ATTRIBUTES = [:options, :render_options]
|
20
|
+
EVAL_ATTRIBUTES = [:options, :render_options, :config]
|
23
21
|
|
24
22
|
# Given a method's file string, line number and defining module, returns a hash
|
25
23
|
# of attributes defined for that method.
|
@@ -1,26 +1,13 @@
|
|
1
1
|
module Boson
|
2
|
-
# Gathers method attributes
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# * options: Defines an OptionParser object for a method's options.
|
6
|
-
# * render_options: Defines an OptionParser object for a method's rendering options. These options
|
7
|
-
# are passed to View.render when rendering a command.
|
8
|
-
#
|
9
|
-
# These method calls must come immediately before a method i.e.:
|
10
|
-
#
|
11
|
-
# desc "Does foo"
|
12
|
-
# options :verbose=>:boolean
|
13
|
-
# def foo(options={})
|
14
|
-
#
|
15
|
-
# This module also allows for defining method attributes as comments. Although the actual
|
16
|
-
# scraping of comments is handled by CommentInspector, MethodInspector gather's the method
|
17
|
-
# location it needs with with find_method_locations().
|
2
|
+
# Gathers method attributes by redefining method_added and capturing method
|
3
|
+
# calls before a method. This module also saves method locations so CommentInspector
|
4
|
+
# can scrape their commented method attributes.
|
18
5
|
module MethodInspector
|
19
6
|
extend self
|
20
7
|
attr_accessor :current_module
|
21
8
|
attr_reader :mod_store
|
22
9
|
@mod_store ||= {}
|
23
|
-
METHODS = [:desc, :options, :render_options]
|
10
|
+
METHODS = [:config, :desc, :options, :render_options]
|
24
11
|
|
25
12
|
# The method_added used while scraping method attributes.
|
26
13
|
def new_method_added(mod, meth)
|
@@ -1,12 +1,16 @@
|
|
1
1
|
module Boson
|
2
|
-
# This
|
3
|
-
#
|
2
|
+
# This class loads a file by its path relative to the commands directory of a repository.
|
3
|
+
# For example the library 'public/misc' could refer to the file '~/.boson/commands/public/misc.rb'.
|
4
|
+
# If a file's basename is unique in its repository, then it can be loaded with its basename i.e. 'misc'
|
5
|
+
# for the previous example. When loading a library, this class searches repositories in the order given by
|
4
6
|
# Boson.repos.
|
5
7
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
+
# === Creating a FileLibrary
|
9
|
+
# Start by creating a file with a module and some methods (See Library for naming a module).
|
10
|
+
# Non-private methods are automatically loaded as a library's commands.
|
8
11
|
#
|
9
12
|
# Take for example a library brain.rb:
|
13
|
+
# # Drop this in ~/.boson/commands/brain.rb
|
10
14
|
# module Brain
|
11
15
|
# def take_over(destination)
|
12
16
|
# puts "Pinky, it's time to take over the #{destination}!"
|
@@ -32,10 +36,8 @@ module Boson
|
|
32
36
|
# bash> boson take_over world -e initiate_brainiac
|
33
37
|
# irb>> take_over 'world -e initiate_brainiac'
|
34
38
|
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
# Since boson aims to make your libraries just standard ruby, we can achieve the above
|
38
|
-
# by placing options in comments above a method:
|
39
|
+
# Since Boson aims to make your libraries just standard ruby, we can achieve the above
|
40
|
+
# by making options a commented method attribute:
|
39
41
|
# module Brain
|
40
42
|
# # @options :execute=>:string
|
41
43
|
# # Help Brain live the dream
|
@@ -46,10 +48,9 @@ module Boson
|
|
46
48
|
# end
|
47
49
|
#
|
48
50
|
# Some points about the above:
|
49
|
-
# * A '@' must prefix options and other method
|
51
|
+
# * A '@' must prefix options and other method attributes that become comments.
|
50
52
|
# * Note the comment above the method. One-line comments right before a method set a command's description.
|
51
|
-
# * See
|
52
|
-
# * See CommentInspector for the rules about commenting command attributes.
|
53
|
+
# * See Inspector for other method attributes, like config and render_options, that can be placed above a method.
|
53
54
|
#
|
54
55
|
# Once a command has a defined option, a command can also recognize a slew of global options:
|
55
56
|
# irb>> take_over '-h'
|
@@ -58,7 +59,7 @@ module Boson
|
|
58
59
|
# # prints much more verbose help
|
59
60
|
# irb>> take_over '-hv'
|
60
61
|
#
|
61
|
-
# For more about these global options see
|
62
|
+
# For more about these global options see OptionCommand and View.
|
62
63
|
class FileLibrary < Library
|
63
64
|
#:stopdoc:
|
64
65
|
def self.library_file(library, dir)
|
@@ -75,8 +76,7 @@ module Boson
|
|
75
76
|
|
76
77
|
def self.reset_file_cache(name=nil)
|
77
78
|
if name && @file_cache
|
78
|
-
|
79
|
-
@file_cache.delete(library_file(name, Boson.repo.dir))
|
79
|
+
@file_cache.delete(library_file(name, (matched_repo || Boson.repo).dir))
|
80
80
|
else
|
81
81
|
@file_cache = nil
|
82
82
|
end
|
@@ -101,14 +101,17 @@ module Boson
|
|
101
101
|
def set_name(name)
|
102
102
|
@lib_file = File.exists?(library_file(name.to_s)) ? library_file(name.to_s) :
|
103
103
|
Dir[self.class.matched_repo.commands_dir.to_s+'/**/*.rb'].find {|e| e =~ /\/#{name}\.rb$/}
|
104
|
-
|
104
|
+
@lib_file.gsub(/^#{self.class.matched_repo.commands_dir}\/|\.rb$/, '')
|
105
|
+
end
|
106
|
+
|
107
|
+
def base_module
|
108
|
+
@base_module ||= @name.include?('/') ? create_module_from_path : Commands
|
105
109
|
end
|
106
110
|
|
107
111
|
def load_source(reload=false)
|
108
112
|
library_string = self.class.read_library_file(@lib_file, reload)
|
109
|
-
@base_module = @name.include?('/') ? create_module_from_path : Commands
|
110
113
|
Inspector.enable
|
111
|
-
|
114
|
+
base_module.module_eval(library_string, @lib_file)
|
112
115
|
Inspector.disable
|
113
116
|
end
|
114
117
|
|
@@ -123,15 +126,7 @@ module Boson
|
|
123
126
|
detected = detect_additions(:modules=>true) { load_source }
|
124
127
|
@module = determine_lib_module(detected[:modules]) unless @module
|
125
128
|
#without this, module's class methods weren't showing up
|
126
|
-
@module = Util.constantize(@module) if
|
127
|
-
end
|
128
|
-
|
129
|
-
def reload_source_and_set_module
|
130
|
-
detected = detect_additions(:modules=>true) { load_source(true) }
|
131
|
-
if (@new_module = !detected[:modules].empty?)
|
132
|
-
@commands = []
|
133
|
-
@module = determine_lib_module(detected[:modules])
|
134
|
-
end
|
129
|
+
@module = Util.constantize(@module) if base_module != Commands
|
135
130
|
end
|
136
131
|
|
137
132
|
def determine_lib_module(detected_modules)
|
@@ -140,7 +135,7 @@ module Boson
|
|
140
135
|
when 0 then raise LoaderError, "Can't detect module. Make sure at least one module is defined in the library."
|
141
136
|
else
|
142
137
|
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(/^#{
|
138
|
+
command_modules = detected_modules.map {|e| e.to_s}.grep(/^#{base_module}::/)
|
144
139
|
unless command_modules.size == 1 && (lib_module = command_modules[0])
|
145
140
|
raise LoaderError, "Can't detect module. Specify a module in this library's config."
|
146
141
|
end
|