tap 0.12.4 → 0.17.0

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 (102) hide show
  1. data/History +34 -0
  2. data/README +62 -41
  3. data/bin/tap +36 -40
  4. data/cmd/console.rb +14 -6
  5. data/cmd/manifest.rb +62 -58
  6. data/cmd/run.rb +49 -31
  7. data/doc/API +84 -0
  8. data/doc/Class Reference +83 -115
  9. data/doc/Examples/Command Line +36 -0
  10. data/doc/Examples/Workflow +40 -0
  11. data/lib/tap/app.rb +293 -214
  12. data/lib/tap/app/node.rb +43 -0
  13. data/lib/tap/app/queue.rb +77 -0
  14. data/lib/tap/app/stack.rb +16 -0
  15. data/lib/tap/app/state.rb +22 -0
  16. data/lib/tap/constants.rb +2 -2
  17. data/lib/tap/env.rb +400 -314
  18. data/lib/tap/env/constant.rb +227 -0
  19. data/lib/tap/env/gems.rb +63 -0
  20. data/lib/tap/env/manifest.rb +89 -0
  21. data/lib/tap/env/minimap.rb +292 -0
  22. data/lib/tap/{support → env}/string_ext.rb +2 -2
  23. data/lib/tap/exe.rb +113 -125
  24. data/lib/tap/join.rb +175 -0
  25. data/lib/tap/joins.rb +9 -0
  26. data/lib/tap/joins/switch.rb +44 -0
  27. data/lib/tap/joins/sync.rb +99 -0
  28. data/lib/tap/root.rb +100 -491
  29. data/lib/tap/root/utils.rb +220 -0
  30. data/lib/tap/{support → root}/versions.rb +31 -29
  31. data/lib/tap/schema.rb +248 -0
  32. data/lib/tap/schema/parser.rb +413 -0
  33. data/lib/tap/schema/utils.rb +82 -0
  34. data/lib/tap/support/intern.rb +19 -6
  35. data/lib/tap/support/templater.rb +8 -3
  36. data/lib/tap/task.rb +175 -171
  37. data/lib/tap/tasks/dump.rb +58 -0
  38. data/lib/tap/tasks/load.rb +62 -0
  39. metadata +30 -73
  40. data/cmd/destroy.rb +0 -27
  41. data/cmd/generate.rb +0 -27
  42. data/doc/Command Reference +0 -105
  43. data/doc/Syntax Reference +0 -234
  44. data/doc/Tutorial +0 -348
  45. data/lib/tap/dump.rb +0 -142
  46. data/lib/tap/file_task.rb +0 -384
  47. data/lib/tap/generator/arguments.rb +0 -13
  48. data/lib/tap/generator/base.rb +0 -176
  49. data/lib/tap/generator/destroy.rb +0 -60
  50. data/lib/tap/generator/generate.rb +0 -93
  51. data/lib/tap/generator/generators/command/command_generator.rb +0 -21
  52. data/lib/tap/generator/generators/command/templates/command.erb +0 -32
  53. data/lib/tap/generator/generators/config/config_generator.rb +0 -98
  54. data/lib/tap/generator/generators/generator/generator_generator.rb +0 -37
  55. data/lib/tap/generator/generators/generator/templates/task.erb +0 -27
  56. data/lib/tap/generator/generators/generator/templates/test.erb +0 -26
  57. data/lib/tap/generator/generators/root/root_generator.rb +0 -84
  58. data/lib/tap/generator/generators/root/templates/MIT-LICENSE +0 -22
  59. data/lib/tap/generator/generators/root/templates/README +0 -14
  60. data/lib/tap/generator/generators/root/templates/Rakefile +0 -84
  61. data/lib/tap/generator/generators/root/templates/Rapfile +0 -11
  62. data/lib/tap/generator/generators/root/templates/gemspec +0 -27
  63. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -3
  64. data/lib/tap/generator/generators/task/task_generator.rb +0 -25
  65. data/lib/tap/generator/generators/task/templates/task.erb +0 -14
  66. data/lib/tap/generator/generators/task/templates/test.erb +0 -19
  67. data/lib/tap/generator/manifest.rb +0 -20
  68. data/lib/tap/generator/preview.rb +0 -69
  69. data/lib/tap/load.rb +0 -64
  70. data/lib/tap/spec.rb +0 -41
  71. data/lib/tap/support/aggregator.rb +0 -65
  72. data/lib/tap/support/audit.rb +0 -333
  73. data/lib/tap/support/constant.rb +0 -143
  74. data/lib/tap/support/constant_manifest.rb +0 -126
  75. data/lib/tap/support/dependencies.rb +0 -54
  76. data/lib/tap/support/dependency.rb +0 -44
  77. data/lib/tap/support/executable.rb +0 -198
  78. data/lib/tap/support/executable_queue.rb +0 -125
  79. data/lib/tap/support/gems.rb +0 -43
  80. data/lib/tap/support/join.rb +0 -144
  81. data/lib/tap/support/joins.rb +0 -12
  82. data/lib/tap/support/joins/switch.rb +0 -27
  83. data/lib/tap/support/joins/sync_merge.rb +0 -38
  84. data/lib/tap/support/manifest.rb +0 -171
  85. data/lib/tap/support/minimap.rb +0 -90
  86. data/lib/tap/support/node.rb +0 -176
  87. data/lib/tap/support/parser.rb +0 -450
  88. data/lib/tap/support/schema.rb +0 -385
  89. data/lib/tap/support/shell_utils.rb +0 -67
  90. data/lib/tap/test.rb +0 -77
  91. data/lib/tap/test/assertions.rb +0 -38
  92. data/lib/tap/test/env_vars.rb +0 -29
  93. data/lib/tap/test/extensions.rb +0 -73
  94. data/lib/tap/test/file_test.rb +0 -362
  95. data/lib/tap/test/file_test_class.rb +0 -15
  96. data/lib/tap/test/regexp_escape.rb +0 -87
  97. data/lib/tap/test/script_test.rb +0 -46
  98. data/lib/tap/test/script_tester.rb +0 -115
  99. data/lib/tap/test/subset_test.rb +0 -260
  100. data/lib/tap/test/subset_test_class.rb +0 -99
  101. data/lib/tap/test/tap_test.rb +0 -109
  102. data/lib/tap/test/utils.rb +0 -231
data/lib/tap/file_task.rb DELETED
@@ -1,384 +0,0 @@
1
- require 'tap/support/shell_utils'
2
-
3
- module Tap
4
-
5
- # FileTask is a base class for tasks that work with a file system. FileTask
6
- # tracks changes it makes so they may be rolled back to their original state.
7
- # Rollback automatically occurs on an execute error.
8
- #
9
- # File.open("file.txt", "w") {|file| file << "original content"}
10
- #
11
- # t = FileTask.intern do |task, raise_error|
12
- # task.mkdir_p("some/dir") # marked for rollback
13
- # task.prepare("file.txt") do |file| # marked for rollback
14
- # file << "new content"
15
- # end
16
- #
17
- # # raise an error to start rollback
18
- # raise "error!" if raise_error
19
- # end
20
- #
21
- # begin
22
- # t.execute(true)
23
- # rescue
24
- # $!.message # => "error!"
25
- # File.exists?("some/dir") # => false
26
- # File.read("file.txt") # => "original content"
27
- # end
28
- #
29
- # t.execute(false)
30
- # File.exists?("some/dir") # => true
31
- # File.read("file.txt") # => "new content"
32
- #
33
- class FileTask < Task
34
- include Tap::Support::ShellUtils
35
-
36
- # The backup directory
37
- config_attr :backup_dir, 'backup' # The backup directory
38
-
39
- # A flag indicating whether or track changes
40
- # for rollback on execution error
41
- config :rollback_on_error, true, &c.switch # Rollback changes on error
42
-
43
- def initialize(config={}, name=nil, app=App.instance)
44
- super
45
- @actions = []
46
- end
47
-
48
- # Initializes a copy that will rollback independent of self.
49
- def initialize_copy(orig)
50
- super
51
- @actions = []
52
- end
53
-
54
- # Returns the path, exchanging the extension with extname.
55
- # A false or nil extname removes the extension, while true
56
- # preserves the existing extension (and effectively does
57
- # nothing).
58
- #
59
- # t = FileTask.new
60
- # t.basepath('path/to/file.txt') # => 'path/to/file'
61
- # t.basepath('path/to/file.txt', '.html') # => 'path/to/file.html'
62
- #
63
- # t.basepath('path/to/file.txt', false) # => 'path/to/file'
64
- # t.basepath('path/to/file.txt', true) # => 'path/to/file.txt'
65
- #
66
- # Compare to basename.
67
- def basepath(path, extname=false)
68
- case extname
69
- when false, nil then path.chomp(File.extname(path))
70
- when true then path
71
- else Root.exchange(path, extname)
72
- end
73
- end
74
-
75
- # Returns the basename of path, exchanging the extension
76
- # with extname. A false or nil extname removes the
77
- # extension, while true preserves the existing extension.
78
- #
79
- # t = FileTask.new
80
- # t.basename('path/to/file.txt') # => 'file.txt'
81
- # t.basename('path/to/file.txt', '.html') # => 'file.html'
82
- #
83
- # t.basename('path/to/file.txt', false) # => 'file'
84
- # t.basename('path/to/file.txt', true) # => 'file.txt'
85
- #
86
- # Compare to basepath.
87
- def basename(path, extname=true)
88
- basepath(File.basename(path), extname)
89
- end
90
-
91
- # Constructs a filepath using the dir, name, and the specified paths.
92
- #
93
- # t = FileTask.new
94
- # t.name # => "tap/file_task"
95
- # t.filepath('data', "result.txt") # => File.expand_path("data/tap/file_task/result.txt")
96
- #
97
- def filepath(dir, *paths)
98
- File.expand_path(File.join(dir, name, *paths))
99
- end
100
-
101
- # Makes a backup filepath relative to backup_dir by using name, the
102
- # basename of filepath, and an index.
103
- #
104
- # t = FileTask.new({:backup_dir => "/backup"}, "name")
105
- # t.backup_filepath("path/to/file.txt", time) # => "/backup/name/file.0.txt"
106
- #
107
- def backup_filepath(path)
108
- extname = File.extname(path)
109
- backup_path = filepath(backup_dir, File.basename(path).chomp(extname))
110
- next_indexed_path(backup_path, 0, extname)
111
- end
112
-
113
- # Returns true if all of the targets are up to date relative to all of the
114
- # listed sources. Single values or arrays can be provided for both targets
115
- # and sources.
116
- #
117
- # Returns false (ie 'not up to date') if app.force is true.
118
- #
119
- #--
120
- # TODO: add check vs date reference (ex config_file date)
121
- def uptodate?(targets, sources=[])
122
- if app.force
123
- log_basename(:force, *targets)
124
- false
125
- else
126
- targets = [targets] unless targets.kind_of?(Array)
127
- sources = [sources] unless sources.kind_of?(Array)
128
-
129
- targets.each do |target|
130
- return false unless FileUtils.uptodate?(target, sources)
131
- end
132
- true
133
- end
134
- end
135
-
136
- # Makes a backup of path to backup_filepath(path) and returns the backup path.
137
- # If backup_using_copy is true, the backup is a copy of path, otherwise the
138
- # file or directory at path is moved to the backup path. Raises an error if
139
- # the backup path already exists.
140
- #
141
- # Backups are restored on rollback.
142
- #
143
- # file = "file.txt"
144
- # File.open(file, "w") {|f| f << "file content"}
145
- #
146
- # t = FileTask.new
147
- # backup_file = t.backup(file)
148
- #
149
- # File.exists?(file) # => false
150
- # File.exists?(backup_file) # => true
151
- # File.read(backup_file) # => "file content"
152
- #
153
- # File.open(file, "w") {|f| f << "new content"}
154
- # t.rollback
155
- #
156
- # File.exists?(file) # => true
157
- # File.exists?(backup_file ) # => false
158
- # File.read(file) # => "file content"
159
- #
160
- def backup(path, backup_using_copy=false)
161
- return nil unless File.exists?(path)
162
-
163
- source = File.expand_path(path)
164
- target = backup_filepath(source)
165
- raise "backup already exists: #{target}" if File.exists?(target)
166
-
167
- mkdir_p File.dirname(target)
168
-
169
- log :backup, "#{source} to #{target}", Logger::DEBUG
170
- if backup_using_copy
171
- FileUtils.cp(source, target)
172
- else
173
- FileUtils.mv(source, target)
174
- end
175
-
176
- actions << [:backup, source, target]
177
- target
178
- end
179
-
180
- # Creates a directory and all its parent directories. Directories created
181
- # by mkdir_p removed on rollback.
182
- def mkdir_p(dir)
183
- dir = File.expand_path(dir)
184
-
185
- dirs = []
186
- while !File.exists?(dir)
187
- dirs.unshift(dir)
188
- dir = File.dirname(dir)
189
- end
190
-
191
- dirs.each {|d| mkdir(d) }
192
- end
193
-
194
- # Creates a directory. Directories created by mkdir removed on rollback.
195
- def mkdir(dir)
196
- dir = File.expand_path(dir)
197
-
198
- unless File.exists?(dir)
199
- log :mkdir, dir, Logger::DEBUG
200
- FileUtils.mkdir(dir)
201
- actions << [:make, dir]
202
- end
203
- end
204
-
205
- # Prepares the path by backing up any existing file and ensuring that
206
- # the parent directory for path exists. If a block is given, a file
207
- # is opened and yielded to it (as in File.open). Prepared paths are
208
- # removed and the backups restored on rollback.
209
- #
210
- # Returns the expanded path.
211
- def prepare(path, backup_using_copy=false)
212
- raise "not a file: #{path}" if File.directory?(path)
213
- path = File.expand_path(path)
214
-
215
- if File.exists?(path)
216
- # backup or remove existing files
217
- backup(path, backup_using_copy)
218
- else
219
- # ensure the parent directory exists
220
- # for non-existant files
221
- mkdir_p File.dirname(path)
222
- end
223
- log :prepare, path, Logger::DEBUG
224
- actions << [:make, path]
225
-
226
- if block_given?
227
- File.open(path, "w") {|file| yield(file) }
228
- end
229
-
230
- path
231
- end
232
-
233
- # Removes a file. If a directory is provided, it's contents are removed
234
- # recursively. Files and directories removed by rm_r are restored
235
- # upon an execution error.
236
- def rm_r(path)
237
- path = File.expand_path(path)
238
-
239
- backup(path, false)
240
- log :rm_r, path, Logger::DEBUG
241
- end
242
-
243
- # Removes an empty directory. Directories removed by rmdir are restored
244
- # upon an execution error.
245
- def rmdir(dir)
246
- dir = File.expand_path(dir)
247
-
248
- unless Root.empty?(dir)
249
- raise "not an empty directory: #{dir}"
250
- end
251
-
252
- backup(dir, false)
253
- log :rmdir, dir, Logger::DEBUG
254
- end
255
-
256
- # Removes a file. Directories cannot be removed by this method.
257
- # Files removed by rm are restored upon an execution error.
258
- def rm(path)
259
- path = File.expand_path(path)
260
-
261
- unless File.file?(path)
262
- raise "not a file: #{path}"
263
- end
264
-
265
- backup(path, false)
266
- log :rm, path, Logger::DEBUG
267
- end
268
-
269
- # Copies source to target. Files and directories copied by cp are
270
- # restored upon an execution error.
271
- def cp(source, target)
272
- target = File.join(target, File.basename(source)) if File.directory?(target)
273
- prepare(target)
274
-
275
- log :cp, "#{source} to #{target}", Logger::DEBUG
276
- FileUtils.cp(source, target)
277
- end
278
-
279
- # Copies source to target. If source is a directory, the contents
280
- # are copied recursively. If target is a directory, copies source
281
- # to target/source. Files and directories copied by cp are restored
282
- # upon an execution error.
283
- def cp_r(source, target)
284
- target = File.join(target, File.basename(source)) if File.directory?(target)
285
- prepare(target)
286
-
287
- log :cp_r, "#{source} to #{target}", Logger::DEBUG
288
- FileUtils.cp_r(source, target)
289
- end
290
-
291
- # Moves source to target. Files and directories moved by mv are
292
- # restored upon an execution error.
293
- def mv(source, target, backup_source=true)
294
- backup(source, true) if backup_source
295
- prepare(target)
296
-
297
- log :mv, "#{source} to #{target}", Logger::DEBUG
298
- FileUtils.mv(source, target)
299
- end
300
-
301
- # Rolls back any actions capable of being rolled back.
302
- #
303
- # Rollback is forceful. For instance if you make a folder using
304
- # mkdir, rollback will remove the folder and all files within it
305
- # even if they were not added by self.
306
- def rollback
307
- while !actions.empty?
308
- action, source, target = actions.pop
309
-
310
- case action
311
- when :make
312
- log :rollback, "#{source}", Logger::DEBUG
313
- FileUtils.rm_r(source)
314
- when :backup
315
- log :rollback, "#{target} to #{source}", Logger::DEBUG
316
- dir = File.dirname(source)
317
- FileUtils.mkdir_p(dir) unless File.exists?(dir)
318
- FileUtils.mv(target, source, :force => true)
319
- else
320
- raise "unknown action: #{[action, source, target].inspect}"
321
- end
322
- end
323
- end
324
-
325
- # Removes backup files. Cleanup cannot be rolled back and prevents
326
- # rollback of actions up to when cleanup is called. If cleanup_dirs
327
- # is true, empty directories containing the backup files will be
328
- # removed.
329
- def cleanup(cleanup_dirs=true)
330
- actions.each do |action, source, target|
331
- if action == :backup
332
- log :cleanup, target, Logger::DEBUG
333
- FileUtils.rm_r(target) if File.exists?(target)
334
- cleanup_dir(File.dirname(target)) if cleanup_dirs
335
- end
336
- end
337
- actions.clear
338
- end
339
-
340
- # Removes the directory if empty, and all empty parent directories. This
341
- # method cannot be rolled back.
342
- def cleanup_dir(dir)
343
- while Root.empty?(dir)
344
- log :rmdir, dir, Logger::DEBUG
345
- FileUtils.rmdir(dir)
346
- dir = File.dirname(dir)
347
- end
348
- end
349
-
350
- # Logs the given action, with the basenames of the input paths.
351
- def log_basename(action, paths, level=Logger::INFO)
352
- msg = [paths].flatten.collect {|path| File.basename(path) }.join(',')
353
- log(action, msg, level)
354
- end
355
-
356
- protected
357
-
358
- # An array tracking actions (backup, rm, mv, etc) performed by self,
359
- # allowing rollback on an execution error. Not intended to be
360
- # modified manually.
361
- attr_reader :actions
362
-
363
- # Clears actions so that a failure will not affect previous executions
364
- def before_execute
365
- actions.clear
366
- end
367
-
368
- # Removes made files/dirs and restores backed-up files upon
369
- # an execute error.
370
- def on_execute_error(original_error)
371
- rollback if rollback_on_error
372
- raise original_error
373
- end
374
-
375
- private
376
-
377
- # utility method for backup_filepath; increments index until the
378
- # path base.indexext does not exist.
379
- def next_indexed_path(base, index, ext) # :nodoc:
380
- path = sprintf('%s.%d%s', base, index, ext)
381
- File.exists?(path) ? next_indexed_path(base, index + 1, ext) : path
382
- end
383
- end
384
- end
@@ -1,13 +0,0 @@
1
- module Tap
2
- module Generator
3
-
4
- # A special type of Lazydoc::Arguments that shifts off the standard 'm'
5
- # argument on generator manifest methods, to properly reflect how may
6
- # arguments the generator should receive.
7
- class Arguments < Lazydoc::Arguments
8
- def arguments(shift_manifest_arg=true)
9
- shift_manifest_arg ? @arguments[1..-1] : @arguments
10
- end
11
- end
12
- end
13
- end
@@ -1,176 +0,0 @@
1
- require 'tap'
2
- require 'tap/generator/manifest'
3
- require 'tap/generator/arguments'
4
-
5
- module Tap
6
- module Generator
7
-
8
- # :startdoc:::-
9
- # Base provides the basic structure of a generator and custom generators
10
- # inherit from it. Base is patterned after the {Ruby on Rails}[http://rubyonrails.org/]
11
- # generators, but obviously takes on all the advantages of Tasks.
12
- #
13
- # === Usage
14
- #
15
- # Tap generators define a manifest method that defines what files and
16
- # directories are created by the generator. Then, at execution time,
17
- # a mixin with the appropriate funtion (ie Generate or Destory) is
18
- # overlaid to figure out how to roll those actions forward or backwards.
19
- #
20
- # Unlike typical tasks, generators must be named like '<Name>Generator' and
21
- # are identified using the ::generator flag rather than ::manifest. These
22
- # requirements make generators available to the generate/destroy commands
23
- # and not run.
24
- #
25
- # Typically, generators live in a directory structure like this:
26
- #
27
- # sample
28
- # |- sample_generator.rb
29
- # `- templates
30
- # `- template_file.erb
31
- #
32
- # And take the form:
33
- #
34
- # [sample/sample_generator.rb]
35
- # require 'tap/generator/base'
36
- #
37
- # # SampleGenerator::generator generates a directory, and two files
38
- # #
39
- # # An extended description of the
40
- # # generator goes here...
41
- # #
42
- # class SampleGenerator < Tap::Generator::Base
43
- #
44
- # config :key, 'value' # a sample config
45
- #
46
- # def manifest(m, *args)
47
- # # make a directory
48
- # m.directory('path/to/dir')
49
- #
50
- # # make a file
51
- # m.file('path/to/file.txt') do |file|
52
- # file << "some content"
53
- # end
54
- #
55
- # # template a file using config
56
- # m.template('path/to/result.txt', 'template_file.erb', config.to_hash)
57
- # end
58
- # end
59
- #
60
- # As with any task, generators can have configurations and take arguments
61
- # specified by manifest (minus the 'm' argument which is standard).
62
- # Creating directories and files is straightforward, as above. Template
63
- # generates a target file using the source file in the templates' directory;
64
- # any attributes specified by the last argument will be available in the erb
65
- # template.
66
- # :startdoc:::+
67
- class Base < Tap::Task
68
- lazy_attr :manifest, 'generator'
69
- lazy_attr :args, :manifest
70
- lazy_register :manifest, Arguments
71
-
72
- config :destination_root, Dir.pwd # The destination root directory
73
- config :pretend, false, &c.flag # Run but rollback any changes.
74
- config :force, false, &c.flag # Overwrite files that already exist.
75
- config :skip, false, &c.flag # Skip files that already exist.
76
-
77
- # The generator-specific templates directory. By default:
78
- # 'path/to/name/templates' for 'path/to/name/name_generator.rb'
79
- attr_accessor :template_dir
80
-
81
- # The IO used to pull prompt inputs (default: $stdin)
82
- attr_accessor :prompt_in
83
-
84
- # The IO used to prompt users for input (default: $stdout)
85
- attr_accessor :prompt_out
86
-
87
- def initialize(*args)
88
- super
89
- @prompt_in = $stdin
90
- @prompt_out = $stdout
91
- @template_dir = File.dirname(self.class.source_file) + '/templates'
92
- end
93
-
94
- # Builds the manifest, then executes the actions of the manifest.
95
- # Process returns the results of iterate, which normally will be
96
- # an array of files and directories created (or destroyed) by self.
97
- def process(*argv)
98
- actions = []
99
- manifest(Manifest.new(actions), *argv)
100
-
101
- iterate(actions) do |action, args, block|
102
- send(action, *args, &block)
103
- end
104
- end
105
-
106
- # Overridden in subclasses to add actions to the input Manifest.
107
- # Any arguments passed to process will be passed to manifest
108
- # unchanged.
109
- def manifest(m, *argv)
110
- raise NotImplementedError
111
- end
112
-
113
- # Peforms each of the input actions in order, and collects the
114
- # results. The process method returns these results.
115
- def iterate(actions)
116
- actions.collect {|action| yield(action) }
117
- end
118
-
119
- # Constructs a path relative to destination_root.
120
- def path(*paths)
121
- File.expand_path(File.join(*paths), destination_root)
122
- end
123
-
124
- # Peforms a directory action (ex generate or destroy). Must be
125
- # overridden by one of the action mixins (ex Generate or Destroy).
126
- def directory(target, options={})
127
- raise NotImplementedError
128
- end
129
-
130
- # Peforms a file action (ex generate or destroy). Calls to file specify
131
- # input for a target by providing a block; the block recieves an IO and
132
- # pushes content to it. Must be overridden by one of the action mixins
133
- # (ex Generate or Destroy).
134
- def file(target, options={}) # :yields: io
135
- raise NotImplementedError
136
- end
137
-
138
- # Makes (or destroys) the root and each of the targets, relative
139
- # to root. Options are passed onto directory.
140
- def directories(root, targets, options={})
141
- directory(root, options)
142
- targets.each do |target|
143
- directory(File.join(root, target), options)
144
- end
145
- end
146
-
147
- # Makes (or destroys) the target by templating the source using
148
- # the specified attributes. Source is expanded relative to
149
- # template_dir. Options are passed onto file.
150
- def template(target, source, attributes={}, options={})
151
- template_path = File.expand_path(source, template_dir)
152
- templater = Support::Templater.new(File.read(template_path), attributes)
153
-
154
- file(target, options) do |file|
155
- file << templater.build
156
- end
157
- end
158
-
159
- # Yields each source file under template_dir to the block, with
160
- # a target path of the source relative to template_dir.
161
- def template_files
162
- Dir.glob(template_dir + "/**/*").sort.each do |source|
163
- next unless File.file?(source)
164
-
165
- target = Tap::Root.relative_filepath(template_dir, source)
166
- yield(source, target)
167
- end
168
- end
169
-
170
- # Logs the action with the relative filepath from Dir.pwd to path.
171
- def log_relative(action, path)
172
- log(action, Root.relative_filepath(Dir.pwd, path))
173
- end
174
- end
175
- end
176
- end