rabal 0.0.1

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