rabal 0.0.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/COPYING +339 -0
- data/LICENSE +54 -0
- data/README +172 -0
- data/README.PLUGIN +276 -0
- data/bin/rabal +12 -0
- data/lib/rabal/action_tree.rb +42 -0
- data/lib/rabal/application.rb +209 -0
- data/lib/rabal/directory_tree.rb +59 -0
- data/lib/rabal/error.rb +7 -0
- data/lib/rabal/file_tree.rb +89 -0
- data/lib/rabal/logger.rb +18 -0
- data/lib/rabal/plugin/bin.rb +8 -0
- data/lib/rabal/plugin/core.rb +24 -0
- data/lib/rabal/plugin/foundation.rb +154 -0
- data/lib/rabal/plugin/license.rb +36 -0
- data/lib/rabal/plugin/spec.rb +8 -0
- data/lib/rabal/plugin/test.rb +8 -0
- data/lib/rabal/plugin.rb +5 -0
- data/lib/rabal/plugin_tree.rb +61 -0
- data/lib/rabal/project_tree.rb +17 -0
- data/lib/rabal/tree.rb +231 -0
- data/lib/rabal/usage.rb +100 -0
- data/lib/rabal/util.rb +62 -0
- data/lib/rabal/version.rb +18 -0
- data/lib/rabal.rb +49 -0
- data/resources/trees/bin/rabal.project +10 -0
- data/resources/trees/core/CHANGES +4 -0
- data/resources/trees/core/README +15 -0
- data/resources/trees/core/Rakefile +0 -0
- data/resources/trees/core/lib/rabal.project/version.rb.erb +17 -0
- data/resources/trees/core/lib/rabal.project.rb.erb +27 -0
- data/resources/trees/license/COPYING.gpl +339 -0
- data/resources/trees/license/COPYING.lgpl +504 -0
- data/resources/trees/license/COPYING.ruby +339 -0
- data/resources/trees/license/LICENSE.bsd +31 -0
- data/resources/trees/license/LICENSE.mit +19 -0
- data/resources/trees/license/LICENSE.ruby +54 -0
- data/resources/trees/spec/rabal.project_spec.rb.erb +16 -0
- data/resources/trees/spec/spec_helper.rb.erb +5 -0
- data/resources/trees/test/rabal.project_test.rb.erb +10 -0
- data/resources/trees/test/test_helper.rb.erb +3 -0
- data/spec/action_tree_spec.rb +28 -0
- data/spec/bin_plugin_spec.rb +23 -0
- data/spec/core_plugin_spec.rb +29 -0
- data/spec/directory_tree_spec.rb +28 -0
- data/spec/file_tree_spec.rb +48 -0
- data/spec/license_plugin_spec.rb +39 -0
- data/spec/plugin_tree_spec.rb +53 -0
- data/spec/project_tree_spec.rb +39 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/spec_plugin_spec.rb +23 -0
- data/spec/test_plugin_spec.rb +23 -0
- data/spec/tree_spec.rb +80 -0
- data/spec/utils_spec.rb +45 -0
- data/spec/version_spec.rb +26 -0
- metadata +134 -0
data/lib/rabal/logger.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'logger'
|
2
|
+
module Rabal
|
3
|
+
module Log
|
4
|
+
LOGGER = Logger.new(STDOUT)
|
5
|
+
LOGGER.datetime_format = "%Y-%m-%d %H:%M:%S"
|
6
|
+
|
7
|
+
|
8
|
+
class << self
|
9
|
+
%w(debug info warn error fatal).each do |m|
|
10
|
+
module_eval <<-code
|
11
|
+
def #{ m } *a, &b
|
12
|
+
LOGGER.#{ m } *a, &b
|
13
|
+
end
|
14
|
+
code
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rabal/plugin/foundation'
|
2
|
+
module Rabal
|
3
|
+
module Plugin
|
4
|
+
class Core < Rabal::Plugin::Base "/rabal/core"
|
5
|
+
parameter "author", "Author of the project"
|
6
|
+
parameter "email", "Email address of the author"
|
7
|
+
use_always
|
8
|
+
description <<-DESC
|
9
|
+
The core functionality and baseline information needed by every project.
|
10
|
+
DESC
|
11
|
+
|
12
|
+
# core is slightly different from the default and uses a
|
13
|
+
# ProjecTree instead of a PluginTree. It does attach a
|
14
|
+
# PluginTree below the project tree with the basic file
|
15
|
+
# structure
|
16
|
+
def initialize(options)
|
17
|
+
@parameters = OpenStruct.new(options)
|
18
|
+
validate_parameters
|
19
|
+
@tree = ProjectTree.new(options[:project] || options["project"],options)
|
20
|
+
@tree << PluginTree.new({},resource_by_name(my_main_tree_name))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'rabal'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'main'
|
4
|
+
|
5
|
+
module Rabal
|
6
|
+
module Plugin
|
7
|
+
#
|
8
|
+
# Base plugin that all other plugins will inherit from, but not
|
9
|
+
# directly. New plugins are declared with:
|
10
|
+
#
|
11
|
+
# class NewPlugin < Rabal::Plugin::Base "/foo/bar"
|
12
|
+
# ...
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# This process uses GemPlugin under the covers, it has just been
|
16
|
+
# wrapped to provide a different +Base+ class for everything to
|
17
|
+
# inherit from.
|
18
|
+
#
|
19
|
+
class Foundation
|
20
|
+
|
21
|
+
# the PluginTree that the plugin creates.
|
22
|
+
attr_reader :tree
|
23
|
+
|
24
|
+
class << self
|
25
|
+
|
26
|
+
# Called when another class inherits from Foundation.
|
27
|
+
# when that happens that class is registered in the
|
28
|
+
# GemPlugin::Manager
|
29
|
+
def inherited(by_class)
|
30
|
+
register_key = "/" + by_class.to_s.downcase
|
31
|
+
by_class.register_path register_as
|
32
|
+
GemPlugin::Manager.instance.register(register_as,register_key,by_class)
|
33
|
+
register_as = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# allows Plugin::Base to store the registration
|
37
|
+
# information in the class variable.
|
38
|
+
attribute :register_as => nil
|
39
|
+
|
40
|
+
# part of the mini DSL for describing a Plugin
|
41
|
+
def parameter(pname,description)
|
42
|
+
@parameters ||= {}
|
43
|
+
@parameters[pname] = [pname, description]
|
44
|
+
end
|
45
|
+
|
46
|
+
# get the parameters bac
|
47
|
+
def parameters
|
48
|
+
@parameters ||= {}
|
49
|
+
end
|
50
|
+
|
51
|
+
def use_always?
|
52
|
+
@use_always
|
53
|
+
end
|
54
|
+
|
55
|
+
def use_always(d = true)
|
56
|
+
@use_always = d
|
57
|
+
end
|
58
|
+
|
59
|
+
attribute :description => "I Need a Description"
|
60
|
+
attribute :register_path
|
61
|
+
|
62
|
+
def use_name
|
63
|
+
name.split("::").last.dashify
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# A Plugin is instantiated and the options passed to it are
|
68
|
+
# the results of the command line options for this plugin.
|
69
|
+
#
|
70
|
+
# The default action for any Plugin is to create a
|
71
|
+
# PluginTree from its options utilizing the +register_path+
|
72
|
+
# to determine a location within the gem's resources. The
|
73
|
+
# resources is used to instantiate a PluginTree and that is
|
74
|
+
# set to +tree+ and by default, this tree will be 'mounted'
|
75
|
+
# at the root of some other tree.
|
76
|
+
def initialize(options = {})
|
77
|
+
@parameters = OpenStruct.new(options)
|
78
|
+
validate_parameters
|
79
|
+
main_tree_name = my_main_tree_name
|
80
|
+
@tree = PluginTree.new(options,resource_by_name(main_tree_name),
|
81
|
+
File.basename(main_tree_name))
|
82
|
+
end
|
83
|
+
|
84
|
+
############################################################
|
85
|
+
# regular instance methods, provide for convenience to the
|
86
|
+
# child plugins
|
87
|
+
############################################################
|
88
|
+
|
89
|
+
#
|
90
|
+
# validate the parameters of the plugin in the simplest way
|
91
|
+
# possible. Make sure that each listend parameters is not
|
92
|
+
# null. This assumes that @parameters has a method for each
|
93
|
+
# parameter naem
|
94
|
+
#
|
95
|
+
def validate_parameters
|
96
|
+
self.class.parameters.each do |param,desc|
|
97
|
+
if not @parameters.respond_to?(param) or @parameters.send(param).nil? then
|
98
|
+
raise PluginParameterMissingError, "Missing parameter '#{param}' from #{self.class.use_name} plugin. See --use-#{self.class.use_name} --help"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
# What gem a plugin belongs to. This is necessary to access
|
103
|
+
# the resources of the gem the plugin may use. Overload
|
104
|
+
# this in a child plugin to return what you want. By
|
105
|
+
# default it uses the first part of the path the gem is
|
106
|
+
# registered under.
|
107
|
+
#
|
108
|
+
# That is when the plugin is defined
|
109
|
+
#
|
110
|
+
# class MyPlugin < Rabal::Plugin::Base "/my-plugin/something"
|
111
|
+
# ...
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# 'my-plugin' is defined as being the default gem name.
|
115
|
+
def my_gem_name
|
116
|
+
self.class.register_path.split("/").find{|p| p.length > 0}
|
117
|
+
end
|
118
|
+
|
119
|
+
# The main resource for this Plugin. This is generally a
|
120
|
+
# file or a directory that the plugin will use as a template
|
121
|
+
# and create a PluginTree from.
|
122
|
+
def my_main_tree_name
|
123
|
+
tree_name = self.class.register_path.split("/").find_all {|p| p.length > 0}
|
124
|
+
tree_name.shift
|
125
|
+
tree_name.unshift "trees"
|
126
|
+
tree_name.join("/")
|
127
|
+
end
|
128
|
+
|
129
|
+
# Access a resource utilized by the gem. The name is the
|
130
|
+
# path to a file or directory under the 'resources'
|
131
|
+
# directory in the gem this plugin is a part of.
|
132
|
+
def resource_by_name(resource_name)
|
133
|
+
Rabal.application.plugin_resource(my_gem_name,resource_name)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# This is the happen'n way to do things. Camping does it,
|
140
|
+
# GemPlugin does it. Come on You can do it too.
|
141
|
+
#
|
142
|
+
# Put +register_path+ into the class variable @@path of
|
143
|
+
# Plugin::Foundation and then return the Plugin::Foundation class
|
144
|
+
# which will be used as the parent class for the new class.
|
145
|
+
#
|
146
|
+
# Upon declaration of the new class, Foundation.inherited will be
|
147
|
+
# invoked which will register the new class with GemPlugin::Manager
|
148
|
+
# an clear out @@path
|
149
|
+
#
|
150
|
+
def Plugin::Base(register_path)
|
151
|
+
Plugin::Foundation.register_as = register_path
|
152
|
+
Plugin::Foundation
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rabal/plugin/foundation'
|
2
|
+
module Rabal
|
3
|
+
module Plugin
|
4
|
+
#
|
5
|
+
# The license plugin helps the user pick a license for their
|
6
|
+
# project. The current available license to choose from are
|
7
|
+
# BSD, GPL, LGPG, MIT and Ruby
|
8
|
+
#
|
9
|
+
class License < Rabal::Plugin::Base "/rabal/license"
|
10
|
+
TYPES = %w[BSD GPL LGPL MIT Ruby]
|
11
|
+
parameter "flavor", "Flavor of License for your project: #{TYPES.join(', ')}"
|
12
|
+
description "Indicate under what license your project is released."
|
13
|
+
use_always
|
14
|
+
|
15
|
+
def initialize(options)
|
16
|
+
@parameters = OpenStruct.new(options)
|
17
|
+
if not @parameters.respond_to?(:flavor) then
|
18
|
+
raise PluginParameterMissingError, "Missing parameter 'flavor' from license plugin. See --use-license --help"
|
19
|
+
end
|
20
|
+
suffix = @parameters.flavor
|
21
|
+
# look at all files in our resource directory and any
|
22
|
+
# that have the same suffix as the 'flavor' load it into
|
23
|
+
# the tree.
|
24
|
+
if TYPES.include?(suffix) then
|
25
|
+
resource_dir = resource_by_name(my_main_tree_name)
|
26
|
+
@tree = DirectoryTree.new(".")
|
27
|
+
Dir.glob(File.join(resource_dir,"*.#{suffix.downcase}")).each do |file|
|
28
|
+
@tree << FileTree.from_file(file)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
raise PluginParameterMissingError, "Invalid value '#{suffix}' for 'flavor' parameter from license plugin. Only #{TYPES.join(",")} are currently supported."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/rabal/plugin.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rabal'
|
2
|
+
require 'find'
|
3
|
+
require 'rabal/util'
|
4
|
+
|
5
|
+
module Rabal
|
6
|
+
#
|
7
|
+
# Represents the root of a plugin directory structure. This plugin
|
8
|
+
# could also only represent a single FileTree by having the
|
9
|
+
# 'directory' it represents being '.'
|
10
|
+
#
|
11
|
+
class PluginTree < DirectoryTree
|
12
|
+
|
13
|
+
include Util
|
14
|
+
|
15
|
+
# the source directory from which the project is generated
|
16
|
+
attr_accessor :src_directory
|
17
|
+
|
18
|
+
# create a new Plugin Tree based upon a source directory. This
|
19
|
+
# 'mounts' the src_directory into the dest_directory in the
|
20
|
+
# project. The dest_directory defaults to "." . The +options+
|
21
|
+
# are a hash that are propogated to the +parameters+ member
|
22
|
+
# variable.
|
23
|
+
def initialize(options,src_directory,dest_directory= ".")
|
24
|
+
super(dest_directory)
|
25
|
+
@src_directory = src_directory
|
26
|
+
@parameters = OpenStruct.new(options)
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# populating the tree needs to take place after the PluginTree
|
31
|
+
# has been added to the Tree, but before the processing of the
|
32
|
+
# tree takes place
|
33
|
+
#
|
34
|
+
def post_add
|
35
|
+
populate_tree(src_directory)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
#
|
41
|
+
# Given a source directory populate a PluginTree based upon the
|
42
|
+
# contents of the directory. All files will be mapped to
|
43
|
+
# FileTree and directories will be mapped to DirectoryTree.
|
44
|
+
#
|
45
|
+
def populate_tree(src_directory,tree=self)
|
46
|
+
Dir.chdir(src_directory) do |pwd|
|
47
|
+
Dir.entries(".").each do |entry|
|
48
|
+
next if entry[0] == ?.
|
49
|
+
next if pwd == entry
|
50
|
+
|
51
|
+
if File.file?(entry) then
|
52
|
+
tree << FileTree.from_file(entry,true,replace_known_words(entry))
|
53
|
+
elsif File.directory?(entry) then
|
54
|
+
tree << dir = DirectoryTree.new(replace_known_words(entry))
|
55
|
+
populate_tree(entry,dir)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rabal/directory_tree'
|
2
|
+
module Rabal
|
3
|
+
#
|
4
|
+
# The ProjectTree represents the master configuration/specification
|
5
|
+
# of a project.
|
6
|
+
#
|
7
|
+
class ProjectTree < DirectoryTree
|
8
|
+
|
9
|
+
# FIXME: this should derive a whole bunch of instance variables
|
10
|
+
# and delegate items to a Gem specification.
|
11
|
+
def initialize(project_name,options)
|
12
|
+
super(project_name)
|
13
|
+
@parameters = OpenStruct.new(options)
|
14
|
+
@parameters.project_name = project_name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/rabal/tree.rb
ADDED
@@ -0,0 +1,231 @@
|
|
1
|
+
require 'rabal'
|
2
|
+
|
3
|
+
module Rabal
|
4
|
+
#
|
5
|
+
# A basic Tree structure
|
6
|
+
#
|
7
|
+
class Tree
|
8
|
+
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
# The name of this Tree
|
12
|
+
attr_accessor :name
|
13
|
+
|
14
|
+
# The parent of this node. If this is nil then this Tree is a
|
15
|
+
# root.
|
16
|
+
attr_accessor :parent
|
17
|
+
|
18
|
+
# The children of this node. If this Array is empty, then this
|
19
|
+
# Tree is a leaf.
|
20
|
+
attr_accessor :children
|
21
|
+
|
22
|
+
# An abstract data point that can be utilized by child classes
|
23
|
+
# for whatever they like. If this is non-nil and responds to
|
24
|
+
# method calls it will be searched as part of the
|
25
|
+
# 'method_missing' protocol.
|
26
|
+
attr_accessor :parameters
|
27
|
+
|
28
|
+
#
|
29
|
+
# Create a new Tree with the given object.to_s as its +name+.
|
30
|
+
#
|
31
|
+
def initialize(name)
|
32
|
+
@name = name.to_s
|
33
|
+
@parent = nil
|
34
|
+
@children = {}
|
35
|
+
@parameters = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Return true if this Tree has no children.
|
40
|
+
#
|
41
|
+
def is_leaf?
|
42
|
+
@children.empty?
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Return true if this Tree has no parent.
|
47
|
+
#
|
48
|
+
def is_root?
|
49
|
+
@parent.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Return the root node of the tree
|
54
|
+
#
|
55
|
+
def root
|
56
|
+
return self if is_root?
|
57
|
+
return @parent.root
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# Return the distance from the root
|
62
|
+
#
|
63
|
+
def depth
|
64
|
+
return 0 if is_root?
|
65
|
+
return (1 + @parent.depth)
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Attach the given tree at the indicated path. The path given
|
70
|
+
# is always assumed to be from the root of the Tree being
|
71
|
+
# attached to.
|
72
|
+
#
|
73
|
+
# The path is given as a string separated by '/'. Each portion
|
74
|
+
# of the string is matched against the name of the particular
|
75
|
+
# tree.
|
76
|
+
#
|
77
|
+
# Given :
|
78
|
+
#
|
79
|
+
# a --- b --- c
|
80
|
+
# \
|
81
|
+
# d - e --- f
|
82
|
+
# \
|
83
|
+
# g - h
|
84
|
+
#
|
85
|
+
# * the path +a/b/c+ will match the path to Tree +c+
|
86
|
+
# * the path +d/e/g+ will _not_ match anything as the path must start at +a+ here
|
87
|
+
# * the path +a/d/e+ will _not_ match anytthin as +e+ is not a child of +d+
|
88
|
+
# * the path +a/d/e/g+ will match node +g+
|
89
|
+
#
|
90
|
+
# Leading and trailing '/' on the path are not necessary and removed.
|
91
|
+
#
|
92
|
+
def add_at_path(path,subtree)
|
93
|
+
parent_tree = tree_at_path(path)
|
94
|
+
parent_tree << subtree
|
95
|
+
return self
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
#
|
100
|
+
# Return the Tree that resides at the given path
|
101
|
+
#
|
102
|
+
def tree_at_path(path_str)
|
103
|
+
path_str = path_str.chomp("/").reverse.chomp("/").reverse
|
104
|
+
path = path_str.split("/")
|
105
|
+
|
106
|
+
# strip of the redundant first match if it is the same as
|
107
|
+
# the current node
|
108
|
+
find_subtree(path)
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Add the given object to the Tree as a child of this node. If
|
113
|
+
# the given object is not a Tree then wrap it with a Tree.
|
114
|
+
#
|
115
|
+
def <<(subtree)
|
116
|
+
# this should not generally be the case, but wrap things
|
117
|
+
# up to be nice.
|
118
|
+
if not subtree.kind_of?(Tree) then
|
119
|
+
subtree = Tree.new(subtree)
|
120
|
+
end
|
121
|
+
|
122
|
+
subtree.parent = self
|
123
|
+
|
124
|
+
# Don't overwrite any existing children with the same name,
|
125
|
+
# just put this one's children in that one, I think this
|
126
|
+
# works recursively now.
|
127
|
+
if children.has_key?(subtree.name) then
|
128
|
+
subtree.children.each do |n,tree|
|
129
|
+
children[n] = tree
|
130
|
+
end
|
131
|
+
else
|
132
|
+
children[subtree.name] = subtree
|
133
|
+
end
|
134
|
+
|
135
|
+
subtree.post_add
|
136
|
+
|
137
|
+
return self
|
138
|
+
end
|
139
|
+
|
140
|
+
alias :add :<<
|
141
|
+
|
142
|
+
#
|
143
|
+
# Allow for Enumerable to be included. This just wraps walk.
|
144
|
+
#
|
145
|
+
def each
|
146
|
+
self.walk(self,lambda { |tree| yield tree })
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# Count how many items are in the tree
|
151
|
+
#
|
152
|
+
def size
|
153
|
+
inject(0) { |count,n| count + 1 }
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# Walk the tree in a depth first manner, visiting the Tree
|
158
|
+
# first, then its children
|
159
|
+
#
|
160
|
+
def walk(tree,method)
|
161
|
+
method.call(tree)
|
162
|
+
tree.children.each_pair do |name,child|
|
163
|
+
walk(child,method)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# Allow for a method call to cascade up the tree looking for a
|
169
|
+
# Tree that responds to the call.
|
170
|
+
#
|
171
|
+
def method_missing(method_id,*params,&block)
|
172
|
+
if not parameters.nil? and parameters.respond_to?(method_id) then
|
173
|
+
return parameters.send(method_id, *params, &block)
|
174
|
+
elsif not is_root? then
|
175
|
+
@parent.send method_id, *params, &block
|
176
|
+
else
|
177
|
+
raise NoMethodError, "undefined method `#{method_id}' for #{name}:Tree"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
#
|
183
|
+
# allow for a hook so newly added Tree objects may do custom
|
184
|
+
# processing after being added to the tree, but before the tree
|
185
|
+
# is walked or processed
|
186
|
+
#
|
187
|
+
def post_add
|
188
|
+
end
|
189
|
+
#
|
190
|
+
# find the current path of the tree from root to here, return it
|
191
|
+
# as a '/' separates string.
|
192
|
+
#
|
193
|
+
def current_path
|
194
|
+
return "*#{name}*" if is_root?
|
195
|
+
return ([name,parent.current_path]).flatten.reverse.join("/")
|
196
|
+
end
|
197
|
+
|
198
|
+
#
|
199
|
+
# Given the initial path to match as an Array find the Tree that
|
200
|
+
# matches that path.
|
201
|
+
#
|
202
|
+
def find_subtree(path)
|
203
|
+
|
204
|
+
if path.empty? then
|
205
|
+
return self
|
206
|
+
else
|
207
|
+
possible_child = path.shift
|
208
|
+
if children.has_key?(possible_child) then
|
209
|
+
children[possible_child].find_subtree(path)
|
210
|
+
else
|
211
|
+
raise PathNotFoundError, "`#{possible_child}' does not match anything at the current path in the Tree (#{current_path})"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
private
|
217
|
+
|
218
|
+
#
|
219
|
+
# Log to the Rabal::Log padding the message with 2 spaces *
|
220
|
+
# depth.
|
221
|
+
#
|
222
|
+
%w(debug info warn error fatal).each do |m|
|
223
|
+
class_eval <<-code
|
224
|
+
def #{ m }(msg)
|
225
|
+
msg = (" " * depth) + msg
|
226
|
+
Rabal::Log.#{ m }(msg)
|
227
|
+
end
|
228
|
+
code
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
data/lib/rabal/usage.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'main'
|
2
|
+
module Rabal
|
3
|
+
|
4
|
+
# Rabal has unique Usage output requirements and as such the default
|
5
|
+
# usage provided by the Main gem although nice are not sufficient.
|
6
|
+
#
|
7
|
+
# Rabal requires the Usage to be generated after the parameters have
|
8
|
+
# been parsed as the parameters that are on the commandline
|
9
|
+
# determine how the help is printed.
|
10
|
+
class Usage
|
11
|
+
|
12
|
+
attr_reader :app
|
13
|
+
attr_reader :old_usage
|
14
|
+
|
15
|
+
def initialize(app)
|
16
|
+
@app = app
|
17
|
+
@old_usage = {}
|
18
|
+
app.main.usage.each_pair do |key,value|
|
19
|
+
@old_usage[key] = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# some of the generated usage is quite useful, others we want
|
24
|
+
# to dump, or rename
|
25
|
+
def to_s
|
26
|
+
u = ::Main::Usage.new
|
27
|
+
# just transfer directly over these
|
28
|
+
%w[name synopsis description].each do |chunk|
|
29
|
+
u[chunk.dup] = old_usage[chunk].to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
arguments = app.main.parameters.select{|p| p.type == :argument}
|
33
|
+
global_options = app.main.parameters.select{|p| p.type == :option and app.global_option_names.include?(p.name) }
|
34
|
+
load_options = app.main.parameters.select{|p| p.type == :option and app.plugin_load_option_names.include?(p.name) }
|
35
|
+
plugin_options = app.main.parameters.select{|p| p.type == :option and app.plugin_option_names.include?(p.name) }
|
36
|
+
|
37
|
+
# rework the formatting of the parameters limiting them to
|
38
|
+
# the globals, force
|
39
|
+
u['global options'] = section_options(" ",global_options)
|
40
|
+
|
41
|
+
# format the available modules
|
42
|
+
s = ""
|
43
|
+
s << ::Main::Util.columnize("Force any module to be used by giving the --use-[modulename] option. Modules with a '*' next to them are always used.",:indent => 0, :width => 72)
|
44
|
+
s << "\n\n"
|
45
|
+
s << ""
|
46
|
+
module_options = []
|
47
|
+
app.plugin_manager.plugins.sort_by{|c,p| c}.each do |cat,plugins|
|
48
|
+
plugins.each do |key,plugin|
|
49
|
+
pre = " " + (plugin.use_always? ? "*" : " ")
|
50
|
+
s << option_format(pre,"#{plugin.use_name} (#{plugin.register_path})",plugin.description,30,32,78)
|
51
|
+
s << "\n"
|
52
|
+
|
53
|
+
# create the module options for this one, if the
|
54
|
+
# plugin is use_always.
|
55
|
+
if app.main.parameters['use-all'].given? or plugin.use_always? or
|
56
|
+
app.main.parameters["use-#{plugin.use_name}"].given? then
|
57
|
+
plugin_options = app.main.parameters.select{|p| p.type == :option and p.name =~ %r{^#{plugin.use_name}}}
|
58
|
+
module_options << ["Module options - #{plugin.use_name.camelize}", section_options(" ",plugin_options)]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
u['Available Modules'] = s
|
65
|
+
|
66
|
+
module_options.each { |k,v| u[k] = v }
|
67
|
+
|
68
|
+
u['author'] = old_usage['author']
|
69
|
+
u.to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
# take in an option and a description and some formatting
|
73
|
+
# criteria and format a single option
|
74
|
+
def option_format(pre,option,description,col1,col2,width)
|
75
|
+
s = ""
|
76
|
+
s << "#{pre}#{option}".ljust(col1)
|
77
|
+
if description then
|
78
|
+
lines = ::Main::Util.columnize(description,:indent => col2, :width => width).split("\n")
|
79
|
+
inter_first = ' ' * (col2 - col1 - 2)
|
80
|
+
s << lines.shift.sub(/^\s*/,"#{inter_first}- ")
|
81
|
+
if lines.size > 0 then
|
82
|
+
s << "\n"
|
83
|
+
s << lines.join("\n")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
s
|
87
|
+
end
|
88
|
+
|
89
|
+
def section_options(pre,list)
|
90
|
+
if list.size == 0 then
|
91
|
+
return "No options available\n"
|
92
|
+
end
|
93
|
+
list.sort_by{|p| p.name}.collect do |p|
|
94
|
+
ps = ""
|
95
|
+
ps << option_format(pre,p.short_synopsis,p.description,35,39,78)
|
96
|
+
ps
|
97
|
+
end.join("\n")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|