boson 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|