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/README.PLUGIN
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
== How to Develop a Plugin for Rabal
|
2
|
+
|
3
|
+
Rabal uses the +gem_plugin+ gem to manage its plugins. A basic plugin
|
4
|
+
is a single class that inherits from Rabal::Plugin::Foundation.
|
5
|
+
|
6
|
+
A plugin has +parameters+ a +description+ and a flag +use_always+ which
|
7
|
+
indicates if the plugin's use is required.
|
8
|
+
|
9
|
+
== Basic Plugin - only directory/file structure
|
10
|
+
|
11
|
+
The basic Rabal::Plugin::Test that ships with rabal demonstrates the
|
12
|
+
default behavior of a plugin.
|
13
|
+
|
14
|
+
1: require 'rabal/plugin/foundation'
|
15
|
+
2: module Rabal
|
16
|
+
3: module Plugin
|
17
|
+
4: class Test < Rabal::Plugin::Base "/rabal/test"
|
18
|
+
5: description "Add a Test::Unit framework."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
This is the 'test' plugin that ships with rabal. It shows all that is
|
24
|
+
required for a basic plugin that only has a directory/file structure
|
25
|
+
associated with it.
|
26
|
+
|
27
|
+
test/
|
28
|
+
test/rabal.project_test.rb.erb
|
29
|
+
test/test_helper.rb.erb
|
30
|
+
|
31
|
+
This is the directory template associated with the test plugin. Lets
|
32
|
+
walk through the code line by line.
|
33
|
+
|
34
|
+
1. every plugin depends upon Rabal::Plugin::Foundation
|
35
|
+
2. Top level module for Rabal
|
36
|
+
3. Rabal module containing all plugins
|
37
|
+
4. Declare a *Test* plugin that is registered as +/rabal/test+ in the
|
38
|
+
plugin registry. It is *critical* that your plugin be declared in
|
39
|
+
this manner. There is great Ruby voodoo happening here. All your
|
40
|
+
Rabal plugins must be declared as inheriting from Rabal::Plugin::Base
|
41
|
+
with the registration *path* following. You will probably want to use
|
42
|
+
"/mypluginproject/pluginname" as the registration path.
|
43
|
+
5. describe your plugin
|
44
|
+
|
45
|
+
Associated with your plugin is a directory tree. This tree should be
|
46
|
+
located in the +resources+ directory under the root of your gem. This
|
47
|
+
will enable the resources to be named and accessed via the
|
48
|
+
+resource_by_name+ method available from within your plugin.
|
49
|
+
|
50
|
+
In your directory tree, if +rabal.project+ is in the file or directory
|
51
|
+
path it will be replaced with the project name from the +rabal+ command.
|
52
|
+
|
53
|
+
The parameters of your plugin, along with parameters associated with
|
54
|
+
plugins further up the directory structure are available in your ERB
|
55
|
+
templates. The scope of parameters is explained further down.
|
56
|
+
|
57
|
+
== Default Plugin Behavior
|
58
|
+
|
59
|
+
In many cases all you will need to do is declare the appropriate class,
|
60
|
+
add some parameters and a description and let the default behavior take
|
61
|
+
over. Lets look at the +initialize+ method of Rabal::Plugin::Foundation
|
62
|
+
to see what happens in the default case.
|
63
|
+
|
64
|
+
1. def initialize(options = {})
|
65
|
+
2. @parameters = OpenStruct.new(options)
|
66
|
+
3. validate_parameters
|
67
|
+
4. main_tree_name = my_main_tree_name
|
68
|
+
5. @tree = PluginTree.new(options,resource_by_name(main_tree_name), File.basename(main_tree_name))
|
69
|
+
end
|
70
|
+
|
71
|
+
The default behavior of a plugin is pretty straight forward.
|
72
|
+
|
73
|
+
1. It is initialized with a Hash representing the +parameter+'s given on
|
74
|
+
the command line that apply to this plugin.
|
75
|
+
2. Convert the +options+ into an OpenStruct. This is fairly important
|
76
|
+
as this is how the +parameters+ get exposed to the ERB templates.
|
77
|
+
3. Validate the parameters. The default mechanism is just to make sure
|
78
|
+
that each declare +parameter+ is present and has a non-nil value.
|
79
|
+
Override +validate_parameters+ to validate via your own mechanism.
|
80
|
+
If the validation fails, you should raise PluginParameterMissingError
|
81
|
+
with an appropriate message.
|
82
|
+
4. +my_main_tree_name+ is a method from the parent class of all plugins,
|
83
|
+
Rabal::Plugin::Foundation. It gives the name of the default
|
84
|
+
directory from within the +resources+ of the gem. This is assumed to
|
85
|
+
be <tt>trees/plugin_name</tt>.
|
86
|
+
5. Create a PluginTree instance from the default resource directory.
|
87
|
+
This recursively descends the source directory creating all
|
88
|
+
DirectoryTree and FileTree instances that are necessary to represent
|
89
|
+
the tree below +my_main_tree_name+.
|
90
|
+
|
91
|
+
=== IMPORTANT!!! All plugins are required to have a 'tree' method which returns something that is kind_of?(Tree)
|
92
|
+
|
93
|
+
As an example of the default behavior, from the root of the rabal gem,
|
94
|
+
the Rabal::Plugin::Test plugin has a resource directory structure of:
|
95
|
+
|
96
|
+
rabal/resources/trees/test
|
97
|
+
rabal/resources/trees/test/rabal.project_test.rb.erb
|
98
|
+
rabal/resources/trees/test/test_helper.rb.erb
|
99
|
+
|
100
|
+
This will result in the following items being created, assuming a
|
101
|
+
project name of +myproj+.
|
102
|
+
|
103
|
+
myproj/test
|
104
|
+
myproj/test/myproj_test.rb
|
105
|
+
myproj/test/test_helper.rb
|
106
|
+
|
107
|
+
The +rabal.project+ items from the path are automatically replaced with
|
108
|
+
the +project+ name given on the command line. Each of the .erb files is
|
109
|
+
run through ERB.
|
110
|
+
|
111
|
+
== Plugin with Parameters and non-default behavior
|
112
|
+
|
113
|
+
The Rabal::Plugin::License plugin that ships with Rabal is a good example of a
|
114
|
+
plugin that uses the currently available DSL for declaring plugins, and
|
115
|
+
utilizing non-default behavior.
|
116
|
+
|
117
|
+
module Rabal
|
118
|
+
module Plugin
|
119
|
+
1: class License < Rabal::Plugin::Base "/rabal/license"
|
120
|
+
2: TYPES = %w[BSD GPL LGPL MIT Ruby]
|
121
|
+
3: parameter "flavor", "Flavor of License for your project: #{TYPES.join(', ')}"
|
122
|
+
4: description "Indicate under what license your project is released."
|
123
|
+
5: use_always
|
124
|
+
|
125
|
+
7: def initialize(options)
|
126
|
+
6: @parameters = OpenStruct.new(options)
|
127
|
+
8: if not @parameters.respond_to?(:flavor) then
|
128
|
+
raise PluginParameterMissingError, "Missing parameter 'flavor' from license plugin. See --use-license --help"
|
129
|
+
end
|
130
|
+
suffix = @parameters.flavor
|
131
|
+
if TYPES.include?(suffix) then
|
132
|
+
9: resource_dir = resource_by_name(my_main_tree_name)
|
133
|
+
10: @tree = DirectoryTree.new(".")
|
134
|
+
11: Dir.glob(File.join(resource_dir,"*.#{suffix.downcase}")).each do |file|
|
135
|
+
@tree << FileTree.from_file(file)
|
136
|
+
end
|
137
|
+
else
|
138
|
+
12: raise PluginParameterMissingError, "Invalid value '#{suffix}' for 'flavor' parameter from license plugin. Only #{TYPES.join(",")} are currently supported."
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
Again lets walk through the indicated lines and describe what is
|
146
|
+
happening.
|
147
|
+
|
148
|
+
1. Declare the License plugin and register it as +/rabal/license+
|
149
|
+
2. Class level constants used internally by Rabal::Plugin::License.
|
150
|
+
3. Declare a plugin parameter. The form here is <tt>parameter name,
|
151
|
+
description</tt>. The +flavor+ parameter will be exposed on the
|
152
|
+
command line as <tt>--license-flavor</tt> and the parameter description will
|
153
|
+
appear next to the option when <tt>--help</tt> is used on the command line.
|
154
|
+
4. Declare the plugin description. This appears in <tt>AVAILABLE
|
155
|
+
MODULES</tt> section in the <tt>--help</tt>.
|
156
|
+
5. Declare that this plugin is always used. It is listed with a '*'
|
157
|
+
next to it in <tt>AVAILABLE MODULES</tt> and its <tt>MODULE
|
158
|
+
OPTIONS</tt> are always listed in <tt>--help</tt>.
|
159
|
+
6. When a plugin is instantiated, it is passed as Hash in the +options+
|
160
|
+
parameter to +initialize+. This +options+ parameter contains the
|
161
|
+
results of the command line options for this plugin. In this case if
|
162
|
+
the <tt>--license-flavor</tt> command line option was used +options+
|
163
|
+
will be a Hash with a :flavor key.
|
164
|
+
7. Convert +options+ to a local OpenStruct for easy use. The default
|
165
|
+
initializer in Rabal::Plugin::Foundation has this behavior
|
166
|
+
8. Validate that the +flavor+ parameter was given on the command line.
|
167
|
+
The default behavior if no +initialize+ method overwritten is to
|
168
|
+
validate that all of a plugins +parameters+ have a non-nil value.
|
169
|
+
9. Access the directory within the plugin containing the files that this
|
170
|
+
plugin will use. In this case that directory happens to be
|
171
|
+
+gem_directory/resources/trees/license/+
|
172
|
+
10. Every Plugin is required to have a +@tree+ instance variable. This
|
173
|
+
creates a new DirectoryTree instance and associates it with the "."
|
174
|
+
directory within the resulting project structure. This in effect
|
175
|
+
makes this DirectoryTree instance at the *root* of the project
|
176
|
+
structure.
|
177
|
+
11. Loop over the files available in the resource directory finding the
|
178
|
+
file(s) that have an +flavor+ extension. Each of the files that
|
179
|
+
matches is associated with a FileTree instance which will take care
|
180
|
+
of the ERB necessities. The FileTree instances are added to the
|
181
|
+
DirectoryTree created in the previous step. As a result the file(s)
|
182
|
+
created here will appear in the *root* directory of the resulting
|
183
|
+
project tree.
|
184
|
+
12. Raise an error indicating that the +flavor+ parameter was given an
|
185
|
+
invalid value. This error will be caught by the rabal application and
|
186
|
+
displayed to the end user.
|
187
|
+
|
188
|
+
|
189
|
+
== Packaging
|
190
|
+
|
191
|
+
It is assumed that your plugin will be packaged as a gem. To make your
|
192
|
+
plugin available to rabal it must be dependent upon both rabal and
|
193
|
+
gem_plugin. This is achieved by something similar to the following to
|
194
|
+
your gem specification.
|
195
|
+
|
196
|
+
Gem::Specification.new do |s|
|
197
|
+
...
|
198
|
+
s.add_dependency("gem_plugin")
|
199
|
+
s.add_dependency("rabal")
|
200
|
+
...
|
201
|
+
end
|
202
|
+
|
203
|
+
If this is done, when your gem is installed you can check that your gem
|
204
|
+
is seen with Rabal by doing:
|
205
|
+
|
206
|
+
rabal --use-all --help
|
207
|
+
|
208
|
+
This will display all the available plugins and their command line
|
209
|
+
options.
|
210
|
+
|
211
|
+
== Resources and Trees
|
212
|
+
|
213
|
+
At the core of rabal are the resource trees utilized by the plugins to
|
214
|
+
generate an output directory/file structure. These tree _templates_ are
|
215
|
+
stored within your gem under the +resources/trees+ directory from
|
216
|
+
withing your gem.
|
217
|
+
|
218
|
+
You can get a jump start on your plugin project with:
|
219
|
+
|
220
|
+
rabal --core-author="My Name" --core-email=author@example.com --license-flavor=MIT --use-test myrabalplugin
|
221
|
+
mkdir -p myrabalplugin/resources/trees/xyzzy
|
222
|
+
|
223
|
+
The declaration of your plugin in the .rb file should look something
|
224
|
+
like:
|
225
|
+
|
226
|
+
class MyRabalPlugin < Rabal::Plugin::Base "/myrabalplugin/xyzzy"
|
227
|
+
...
|
228
|
+
end
|
229
|
+
|
230
|
+
Notice the *xyzzy* in both the resource directory and the plugin class
|
231
|
+
declaration. That is the convention to allow the default plugin
|
232
|
+
behavior to work.
|
233
|
+
|
234
|
+
A String containing the path to the *xyzzy* directory structure is
|
235
|
+
returned from the nested calls:
|
236
|
+
|
237
|
+
resource_by_name(my_main_tree_name)
|
238
|
+
|
239
|
+
+my_main_tree_name+ returns +trees/xyzzy+ and +resource_by_name+ will
|
240
|
+
return +/path/to/gem/resources/trees/xyzzy+.
|
241
|
+
|
242
|
+
A later release of rabal will include a rabal plugin generation plugin
|
243
|
+
to simplify the creation of rabal plugins.
|
244
|
+
|
245
|
+
== Parameter scope
|
246
|
+
|
247
|
+
The internal mechanisms of rabal use a Tree data structure to represent
|
248
|
+
the directories and files that are created by rabal. Each node in the
|
249
|
+
tree is represented by a Tree or child class such as DirectoryTree or
|
250
|
+
FileTree. Each Tree has a +@parameters+ instance variable, which is
|
251
|
+
by convention an instance of OpenStruct. It can be any object though.
|
252
|
+
|
253
|
+
Any Tree instance or erb template when it is evaluated has access to any
|
254
|
+
method on its own Tree or any Tree above it in its path, terminating at
|
255
|
+
the *root* of the Tree which is created by Rabal::Plugin::Core.
|
256
|
+
|
257
|
+
Essentially, in a Tree instance or in an ERB template +method_missing+
|
258
|
+
is defined such that the method call cascades up the Tree data structure
|
259
|
+
until a Tree node is found that either responds to that method or has a
|
260
|
+
+@parameters+ instance variable that responds to the method call.
|
261
|
+
|
262
|
+
If the call cascades all the way to the *root* and nothing responds to
|
263
|
+
it, then NoMethodError is raised.
|
264
|
+
|
265
|
+
=== Core Parameters
|
266
|
+
|
267
|
+
Since Rabal::Plugin::Core is the plugin that creates the root of the
|
268
|
+
entire tree, its +@parameters+ are available. Currently these are
|
269
|
+
available during tree generation as:
|
270
|
+
|
271
|
+
* *root.name* - the name of the project from the command line
|
272
|
+
* *author* - the author of the project from the <tt>--core-author</tt> option
|
273
|
+
* *email* - the email address of the author from the <tt>--core-email</tt> option
|
274
|
+
|
275
|
+
This list will continue to grow as rabal evolves.
|
276
|
+
|
data/bin/rabal
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rabal/tree'
|
2
|
+
|
3
|
+
module Rabal
|
4
|
+
class ActionTree < Tree
|
5
|
+
|
6
|
+
def initialize(data)
|
7
|
+
super(data)
|
8
|
+
end
|
9
|
+
|
10
|
+
def before_action
|
11
|
+
end
|
12
|
+
|
13
|
+
def action
|
14
|
+
raise NotImplementedError, "Oops, forgot to implement 'action'"
|
15
|
+
end
|
16
|
+
|
17
|
+
def after_action
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Walk the tree recursively processing each subtree. The
|
22
|
+
# process is:
|
23
|
+
#
|
24
|
+
# * execute before_action
|
25
|
+
# * execute action
|
26
|
+
# * process each child
|
27
|
+
# * execute after_action
|
28
|
+
#
|
29
|
+
#
|
30
|
+
def process
|
31
|
+
before_action
|
32
|
+
|
33
|
+
action
|
34
|
+
|
35
|
+
children.each_pair do |name,child|
|
36
|
+
child.process
|
37
|
+
end
|
38
|
+
|
39
|
+
after_action
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'main'
|
2
|
+
|
3
|
+
module Rabal
|
4
|
+
#
|
5
|
+
# The Rabal application
|
6
|
+
#
|
7
|
+
class Application
|
8
|
+
|
9
|
+
attr_accessor :main
|
10
|
+
attr_accessor :plugin_manager
|
11
|
+
attr_accessor :plugin_load_option_names
|
12
|
+
attr_accessor :plugin_option_names
|
13
|
+
attr_accessor :global_option_names
|
14
|
+
attr_accessor :app_argv
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@main = nil
|
18
|
+
@plugin_manager = GemPlugin::Manager.instance
|
19
|
+
@plugin_option_names = []
|
20
|
+
@global_option_names = %w[directory use-all logfile verbosity version help]
|
21
|
+
@plugin_load_option_names = []
|
22
|
+
@app_argv = []
|
23
|
+
setup_plugins
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Load any and all plugins that are available. This includes
|
28
|
+
# built in plugins and those that are accessible via gems.
|
29
|
+
#
|
30
|
+
def setup_plugins
|
31
|
+
plugin_manager.load "rabal" => GemPlugin::INCLUDE
|
32
|
+
|
33
|
+
# make sure that we are listed as a plugin generally this is
|
34
|
+
# only going to happen if run from outside a gem. Basically
|
35
|
+
# while Jeremy is testing.
|
36
|
+
if not plugin_manager.loaded?("rabal") then
|
37
|
+
plugin_manager.gems["rabal"] = ROOT_DIR
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Each plugin has its own options so iterate over the
|
42
|
+
# available plugins, and create an instance of Main for each
|
43
|
+
# plugin. The options for the plugin will be merged with
|
44
|
+
# the global options, and depending on the --load-<plugin>
|
45
|
+
# options indicated, they will alter the --help
|
46
|
+
|
47
|
+
plugin_manager.plugins.each do |category,plugins|
|
48
|
+
plugins.each do |key,plugin|
|
49
|
+
plugin_load_name = plugin.name.split("::").last.downcase.dashify
|
50
|
+
|
51
|
+
# add the --load-<plugin> option to the global
|
52
|
+
# options
|
53
|
+
p_use_name = "use-#{plugin_load_name}"
|
54
|
+
plugin_load_option_names << p_use_name
|
55
|
+
|
56
|
+
# local variable to build up info from within eval.
|
57
|
+
pons = []
|
58
|
+
|
59
|
+
main.class.class_eval {
|
60
|
+
option(p_use_name) {
|
61
|
+
description "Use plugin #{plugin.name}"
|
62
|
+
}
|
63
|
+
|
64
|
+
# add in the plugin options. Although these are
|
65
|
+
# present in the global main, they won't
|
66
|
+
# actually get displayed in the --help
|
67
|
+
plugin.parameters.each do |pname,pconf|
|
68
|
+
p_option_name = "#{plugin_load_name}-#{pconf[0]}=[p]"
|
69
|
+
pons << p_option_name
|
70
|
+
option(p_option_name) { description pconf[1] }
|
71
|
+
end
|
72
|
+
}
|
73
|
+
plugin_option_names.concat(pons)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# Use Ara's awesome main gem to deal with command line parsing
|
80
|
+
#
|
81
|
+
def main
|
82
|
+
return @main if @main
|
83
|
+
@main = Main.new(app_argv) {
|
84
|
+
description Rabal::DESCRIPTION
|
85
|
+
author Rabal::AUTHOR
|
86
|
+
version Rabal::VERSION
|
87
|
+
|
88
|
+
# Project, the whole reason rabal exists
|
89
|
+
argument("project") {
|
90
|
+
description "The project on which rabal is executing."
|
91
|
+
}
|
92
|
+
|
93
|
+
# Global Options
|
94
|
+
option("directory=d","d") {
|
95
|
+
description "The directory in which to create the project directory."
|
96
|
+
validate { |d| File.directory?(d) }
|
97
|
+
default Dir.pwd
|
98
|
+
}
|
99
|
+
|
100
|
+
option("use-all","a") { description "Use all available plugins." }
|
101
|
+
option("logfile=l","l") {
|
102
|
+
description "The location of the logfile"
|
103
|
+
default STDOUT
|
104
|
+
}
|
105
|
+
|
106
|
+
option("verbosity=v","v") {
|
107
|
+
validate { |p| Logger::SEV_LABEL.include?(p.upcase) }
|
108
|
+
description "One of : #{Logger::SEV_LABEL.join(", ")}"
|
109
|
+
|
110
|
+
}
|
111
|
+
|
112
|
+
option("version","V") {
|
113
|
+
description "Display the version number"
|
114
|
+
}
|
115
|
+
|
116
|
+
def run
|
117
|
+
Rabal.application.rabalize
|
118
|
+
end
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# Invoke main to do its thing and kick off the application
|
124
|
+
# Main keeps a reference to the array that it originally started
|
125
|
+
# with when it was created. So you have to have that first,
|
126
|
+
# then fill it up later.
|
127
|
+
#
|
128
|
+
def run(in_argv = ARGV)
|
129
|
+
app_argv.clear
|
130
|
+
app_argv.concat in_argv.dup
|
131
|
+
begin
|
132
|
+
# create a separate usage object for rabal that can hook
|
133
|
+
# in and figure out what parameters were given on the
|
134
|
+
# command line
|
135
|
+
u = Rabal::Usage.new(self)
|
136
|
+
main.usage u
|
137
|
+
main.run
|
138
|
+
rescue ::Main::Parameter::Error => mpe
|
139
|
+
puts "Parameter Error: #{File.basename($0)}: #{mpe.message}"
|
140
|
+
puts "Try `#{File.basename($0)} --help' for more information."
|
141
|
+
exit 1
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# Get down and do stuff. Now that all the options have been
|
147
|
+
# parsed, plugins loaded, some activate, etc.
|
148
|
+
#
|
149
|
+
def rabalize
|
150
|
+
# create the core plugin to start things off
|
151
|
+
pwd = Dir.pwd
|
152
|
+
begin
|
153
|
+
core_params = params_for_plugin(Rabal::Plugin::Core)
|
154
|
+
core_params[:project] = main.params[:project].value
|
155
|
+
core = Rabal::Plugin::Core.new(core_params)
|
156
|
+
using_plugins.each do |p|
|
157
|
+
next if p == Rabal::Plugin::Core
|
158
|
+
pi = p.new(params_for_plugin(p))
|
159
|
+
core.tree << pi.tree
|
160
|
+
end
|
161
|
+
|
162
|
+
# not using chdir blocks, as that raises
|
163
|
+
# warning: conflicting chdir during another chdir block
|
164
|
+
Dir.chdir(File.expand_path(main.params[:directory].value))
|
165
|
+
core.tree.process
|
166
|
+
rescue ::Rabal::StandardError => rse
|
167
|
+
puts "Application Error: #{rse.message}"
|
168
|
+
exit 1
|
169
|
+
ensure
|
170
|
+
Dir.chdir(pwd)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
#
|
175
|
+
# Find a resource associated with the given gem
|
176
|
+
#
|
177
|
+
def plugin_resource(gem_name,resource_path)
|
178
|
+
plugin_manager.resource(gem_name,resource_path)
|
179
|
+
end
|
180
|
+
|
181
|
+
#
|
182
|
+
# return an array of all the plugin classes being used
|
183
|
+
#
|
184
|
+
def using_plugins
|
185
|
+
using = []
|
186
|
+
plugin_manager.plugins.each do |cat,plugins|
|
187
|
+
plugins.each do |key,plugin|
|
188
|
+
if main.parameters['use-all'].given? or plugin.use_always? or
|
189
|
+
main.parameters["use-#{plugin.use_name}"].given? then
|
190
|
+
using << plugin
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
using
|
195
|
+
end
|
196
|
+
|
197
|
+
#
|
198
|
+
# return a hash of all the options for a particular plugin with
|
199
|
+
# the plugins name removed from the front
|
200
|
+
#
|
201
|
+
def params_for_plugin(plugin)
|
202
|
+
plugin_hash = {}
|
203
|
+
main.parameters.select{|p| p.type == :option and p.name =~ %r{^#{plugin.use_name}}}.each do |p|
|
204
|
+
plugin_hash[p.name.gsub("#{plugin.use_name}-",'')] = p.value
|
205
|
+
end
|
206
|
+
plugin_hash
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rabal/action_tree'
|
2
|
+
require 'rabal/util'
|
3
|
+
|
4
|
+
module Rabal
|
5
|
+
include Util
|
6
|
+
#
|
7
|
+
# A directory that is to be created or traversed. When this Tree is
|
8
|
+
# invoked it is assumed that the current working directory is the
|
9
|
+
# parent of the directory this DirectoryTree represents.
|
10
|
+
#
|
11
|
+
class DirectoryTree < ActionTree
|
12
|
+
|
13
|
+
# basename of the directory this Tree represents
|
14
|
+
attr_accessor :dir_name
|
15
|
+
|
16
|
+
# the File system directory that is the parent of the directory
|
17
|
+
# this DirectoryTree represents.
|
18
|
+
attr_accessor :parent_dir
|
19
|
+
|
20
|
+
|
21
|
+
# Create a DirectoryTree based up a name, or another directory.
|
22
|
+
# Only the last portion of the directory is used. That is
|
23
|
+
# +File.basename+ is called upon name.
|
24
|
+
def initialize(name)
|
25
|
+
super(name)
|
26
|
+
@dir_name = File.basename(name)
|
27
|
+
@parent_dir = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Make the directory if it doesn't exist
|
32
|
+
#
|
33
|
+
def before_action
|
34
|
+
@parent_dir = Dir.pwd
|
35
|
+
if not File.directory?(dir_name) then
|
36
|
+
info("creating directory #{dir_name}")
|
37
|
+
Dir.mkdir(dir_name)
|
38
|
+
else
|
39
|
+
info("skipping directory #{dir_name} - already exists")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# change into the directory
|
45
|
+
#
|
46
|
+
def action
|
47
|
+
info("entering directory #{dir_name}")
|
48
|
+
Dir.chdir(dir_name)
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# change back to the parent directory
|
53
|
+
#
|
54
|
+
def after_action
|
55
|
+
info("leaving directory #{dir_name}")
|
56
|
+
Dir.chdir(parent_dir)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/rabal/error.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'rabal/action_tree'
|
2
|
+
require 'rabal/util'
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
module Rabal
|
6
|
+
include Util
|
7
|
+
#
|
8
|
+
# Represents a file to be created. Generally a FileTree will use a
|
9
|
+
# source .erb file and combine it with the Rabal::Specification to
|
10
|
+
# create +file_contents+. The +file_contents+ are what are written
|
11
|
+
# out in the action.
|
12
|
+
#
|
13
|
+
# To provide custom template or some decision making, overwrite the
|
14
|
+
# +before_action+ method. So long as +file_contents+ contains the
|
15
|
+
# data to write out to the file by the time +action+ is called all
|
16
|
+
# is well.
|
17
|
+
#
|
18
|
+
class FileTree < ActionTree
|
19
|
+
|
20
|
+
# the name of the file to create
|
21
|
+
attr_accessor :file_name
|
22
|
+
|
23
|
+
# the erb template the file is created from as a String
|
24
|
+
attr_accessor :template
|
25
|
+
|
26
|
+
# the contents of the file to write
|
27
|
+
attr_accessor :file_contents
|
28
|
+
|
29
|
+
class << self
|
30
|
+
|
31
|
+
#
|
32
|
+
# Create a new FileTree from the path to a file. The +file_name+
|
33
|
+
# by default of the FileTree is basename of the path, minus any
|
34
|
+
# extension. This can be altered with the +strip_extension+
|
35
|
+
# and +file_name+ parameters. Passing +nil+ for the
|
36
|
+
# +file_name+ achieves the default behavior.
|
37
|
+
#
|
38
|
+
# The contents of the file at path will be loaded into the
|
39
|
+
# +template+ member variable.
|
40
|
+
#
|
41
|
+
def from_file(path,strip_ext = true, non_default_file_name = nil)
|
42
|
+
template = IO.read(File.expand_path(path))
|
43
|
+
file_name = non_default_file_name || File.basename(path)
|
44
|
+
file_name = File.basename(file_name,(strip_ext ? ".*" : ""))
|
45
|
+
if file_name.index("-") then
|
46
|
+
file_name = file_name.underscore
|
47
|
+
end
|
48
|
+
FileTree.new(file_name,template)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Create a FileTree with a name from a template
|
55
|
+
#
|
56
|
+
def initialize(file_name,template)
|
57
|
+
super(file_name)
|
58
|
+
@file_name = file_name
|
59
|
+
@template = template
|
60
|
+
@file_contents = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# before the file is to be created, load the appropriate
|
65
|
+
# template and setup any variables that need to be in the
|
66
|
+
# binding for erb if there is one
|
67
|
+
#
|
68
|
+
def before_action
|
69
|
+
info("creating content for #{file_name}")
|
70
|
+
@file_contents = ERB.new(template).result(binding)
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Open up the file and write the contents. It is assumed that
|
75
|
+
# the present working directory is the location for the file to
|
76
|
+
# be created.
|
77
|
+
#
|
78
|
+
def action
|
79
|
+
if not File.file?(file_name) then
|
80
|
+
info("writing file #{file_name}")
|
81
|
+
File.open(file_name,"w+") do |f|
|
82
|
+
f.write(file_contents)
|
83
|
+
end
|
84
|
+
else
|
85
|
+
info("skipping file #{file_name} - already exists")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|