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.
Files changed (56) hide show
  1. data/COPYING +339 -0
  2. data/LICENSE +54 -0
  3. data/README +172 -0
  4. data/README.PLUGIN +276 -0
  5. data/bin/rabal +12 -0
  6. data/lib/rabal/action_tree.rb +42 -0
  7. data/lib/rabal/application.rb +209 -0
  8. data/lib/rabal/directory_tree.rb +59 -0
  9. data/lib/rabal/error.rb +7 -0
  10. data/lib/rabal/file_tree.rb +89 -0
  11. data/lib/rabal/logger.rb +18 -0
  12. data/lib/rabal/plugin/bin.rb +8 -0
  13. data/lib/rabal/plugin/core.rb +24 -0
  14. data/lib/rabal/plugin/foundation.rb +154 -0
  15. data/lib/rabal/plugin/license.rb +36 -0
  16. data/lib/rabal/plugin/spec.rb +8 -0
  17. data/lib/rabal/plugin/test.rb +8 -0
  18. data/lib/rabal/plugin.rb +5 -0
  19. data/lib/rabal/plugin_tree.rb +61 -0
  20. data/lib/rabal/project_tree.rb +17 -0
  21. data/lib/rabal/tree.rb +231 -0
  22. data/lib/rabal/usage.rb +100 -0
  23. data/lib/rabal/util.rb +62 -0
  24. data/lib/rabal/version.rb +18 -0
  25. data/lib/rabal.rb +49 -0
  26. data/resources/trees/bin/rabal.project +10 -0
  27. data/resources/trees/core/CHANGES +4 -0
  28. data/resources/trees/core/README +15 -0
  29. data/resources/trees/core/Rakefile +0 -0
  30. data/resources/trees/core/lib/rabal.project/version.rb.erb +17 -0
  31. data/resources/trees/core/lib/rabal.project.rb.erb +27 -0
  32. data/resources/trees/license/COPYING.gpl +339 -0
  33. data/resources/trees/license/COPYING.lgpl +504 -0
  34. data/resources/trees/license/COPYING.ruby +339 -0
  35. data/resources/trees/license/LICENSE.bsd +31 -0
  36. data/resources/trees/license/LICENSE.mit +19 -0
  37. data/resources/trees/license/LICENSE.ruby +54 -0
  38. data/resources/trees/spec/rabal.project_spec.rb.erb +16 -0
  39. data/resources/trees/spec/spec_helper.rb.erb +5 -0
  40. data/resources/trees/test/rabal.project_test.rb.erb +10 -0
  41. data/resources/trees/test/test_helper.rb.erb +3 -0
  42. data/spec/action_tree_spec.rb +28 -0
  43. data/spec/bin_plugin_spec.rb +23 -0
  44. data/spec/core_plugin_spec.rb +29 -0
  45. data/spec/directory_tree_spec.rb +28 -0
  46. data/spec/file_tree_spec.rb +48 -0
  47. data/spec/license_plugin_spec.rb +39 -0
  48. data/spec/plugin_tree_spec.rb +53 -0
  49. data/spec/project_tree_spec.rb +39 -0
  50. data/spec/spec_helper.rb +59 -0
  51. data/spec/spec_plugin_spec.rb +23 -0
  52. data/spec/test_plugin_spec.rb +23 -0
  53. data/spec/tree_spec.rb +80 -0
  54. data/spec/utils_spec.rb +45 -0
  55. data/spec/version_spec.rb +26 -0
  56. 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,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'rabal'
7
+ rescue LoadError => e
8
+ $: << File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
9
+ retry
10
+ end
11
+
12
+ Rabal.application.run(ARGV)
@@ -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
@@ -0,0 +1,7 @@
1
+ module Rabal
2
+ class StandardError < ::StandardError; end
3
+ class TemplateNotFoundError < Rabal::StandardError ; end
4
+ class PathNotFoundError < Rabal::StandardError ; end
5
+ class PluginParameterMissingError < Rabal::StandardError ; end
6
+ end
7
+
@@ -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