rabal 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|