tap 0.12.4 → 0.17.0

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