Class: Tap::FileTask
- Includes:
- Support::ShellUtils
- Defined in:
- lib/tap/file_task.rb
Overview
FileTask is a base class for tasks that work with a file system. FileTask tracks changes it makes so they may be rolled back to their original state. Rollback automatically occurs on an execute error.
File.open("file.txt", "w") {|file| file << "original content"}
t = FileTask.intern do |task, raise_error|
task.mkdir_p("some/dir") # marked for rollback
task.prepare("file.txt") do |file| # marked for rollback
file << "new content"
end
# raise an error to start rollback
raise "error!" if raise_error
end
begin
t.execute(true)
rescue
$!. # => "error!"
File.exists?("some/dir") # => false
File.read("file.txt") # => "original content"
end
t.execute(false)
File.exists?("some/dir") # => true
File.read("file.txt") # => "new content"
Direct Known Subclasses
Constant Summary
Constants inherited from Task
Instance Attribute Summary
Attributes inherited from Task
Attributes included from Support::Executable
#app, #batch, #dependencies, #method_name, #on_complete_block
Instance Method Summary collapse
-
#backup(path, backup_using_copy = false) ⇒ Object
Makes a backup of path to backup_filepath(path) and returns the backup path.
-
#backup_filepath(path) ⇒ Object
Makes a backup filepath relative to backup_dir by using name, the basename of filepath, and an index.
-
#basename(path, extname = true) ⇒ Object
Returns the basename of path, exchanging the extension with extname.
-
#basepath(path, extname = false) ⇒ Object
Returns the path, exchanging the extension with extname.
-
#cleanup(cleanup_dirs = true) ⇒ Object
Removes backup files.
-
#cleanup_dir(dir) ⇒ Object
Removes the directory if empty, and all empty parent directories.
-
#cp(source, target) ⇒ Object
Copies source to target.
-
#cp_r(source, target) ⇒ Object
Copies source to target.
-
#filepath(dir, *paths) ⇒ Object
Constructs a filepath using the dir, name, and the specified paths.
-
#initialize(config = {}, name = nil, app = App.instance) ⇒ FileTask
constructor
A new instance of FileTask.
-
#initialize_copy(orig) ⇒ Object
Initializes a copy that will rollback independent of self.
-
#log_basename(action, paths, level = Logger::INFO) ⇒ Object
Logs the given action, with the basenames of the input paths.
-
#mkdir(dir) ⇒ Object
Creates a directory.
-
#mkdir_p(dir) ⇒ Object
Creates a directory and all its parent directories.
-
#mv(source, target, backup_source = true) ⇒ Object
Moves source to target.
-
#prepare(path, backup_using_copy = false) ⇒ Object
Prepares the path by backing up any existing file and ensuring that the parent directory for path exists.
-
#rm(path) ⇒ Object
Removes a file.
-
#rm_r(path) ⇒ Object
Removes a file.
-
#rmdir(dir) ⇒ Object
Removes an empty directory.
-
#rollback ⇒ Object
Rolls back any actions capable of being rolled back.
-
#uptodate?(targets, sources = []) ⇒ Boolean
Returns true if all of the targets are up to date relative to all of the listed sources.
Methods included from Support::ShellUtils
#capture_sh, #redirect_sh, #sh
Methods inherited from Task
execute, help, inherited, #initialize_batch_obj, #inspect, instance, intern, load, #log, parse, parse!, #process, #to_s, use
Methods included from Support::Executable
#_execute, #batch_index, #batch_with, #batched?, #check_terminate, #depends_on, #enq, #execute, #fork, initialize, #initialize_batch_obj, #merge, #on_complete, #reset_dependencies, #resolve_dependencies, #sequence, #switch, #sync_merge, #unbatched_depends_on, #unbatched_enq, #unbatched_on_complete
Constructor Details
#initialize(config = {}, name = nil, app = App.instance) ⇒ FileTask
Returns a new instance of FileTask.
44 45 46 47 |
# File 'lib/tap/file_task.rb', line 44 def initialize(config={}, name=nil, app=App.instance) super @actions = [] end |
Instance Method Details
#backup(path, backup_using_copy = false) ⇒ Object
Makes a backup of path to backup_filepath(path) and returns the backup path. If backup_using_copy is true, the backup is a copy of path, otherwise the file or directory at path is moved to the backup path. Raises an error if the backup file already exists.
Backups are restored on rollback.
file = "file.txt"
File.open(file, "w") {|f| f << "file content"}
t = FileTask.new
backup_file = t.backup(file)
File.exists?(file) # => false
File.exists?(backup_file) # => true
File.read(backup_file) # => "file content"
File.open(file, "w") {|f| f << "new content"}
t.rollback
File.exists?(file) # => true
File.exists?(backup_file ) # => false
File.read(file) # => "file content"
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/tap/file_task.rb', line 162 def backup(path, backup_using_copy=false) return nil unless File.exists?(path) source = File.(path) target = backup_filepath(source) raise "backup file already exists: #{target}" if File.exists?(target) mkdir_p File.dirname(target) log :backup, "#{source} to #{target}", Logger::DEBUG if backup_using_copy FileUtils.cp(source, target) else FileUtils.mv(source, target) end actions << [:backup, source, target] target end |
#backup_filepath(path) ⇒ Object
109 110 111 112 113 |
# File 'lib/tap/file_task.rb', line 109 def backup_filepath(path) extname = File.extname(path) backup_path = filepath(backup_dir, File.basename(path).chomp(extname)) next_indexed_path(backup_path, 0, extname) end |
#basename(path, extname = true) ⇒ Object
Returns the basename of path, exchanging the extension with extname. A false or nil extname removes the extension, while true preserves the existing extension.
t = FileTask.new
t.basename('path/to/file.txt') # => 'file.txt'
t.basename('path/to/file.txt', '.html') # => 'file.html'
t.basename('path/to/file.txt', false) # => 'file'
t.basename('path/to/file.txt', true) # => 'file.txt'
Compare to basepath.
88 89 90 |
# File 'lib/tap/file_task.rb', line 88 def basename(path, extname=true) basepath(File.basename(path), extname) end |
#basepath(path, extname = false) ⇒ Object
Returns the path, exchanging the extension with extname.
A false or nil extname removes the extension, while true preserves the existing extension (and effectively does nothing).
t = FileTask.new
t.basepath('path/to/file.txt') # => 'path/to/file'
t.basepath('path/to/file.txt', '.html') # => 'path/to/file.html'
t.basepath('path/to/file.txt', false) # => 'path/to/file'
t.basepath('path/to/file.txt', true) # => 'path/to/file.txt'
Compare to basename.
68 69 70 71 72 73 74 |
# File 'lib/tap/file_task.rb', line 68 def basepath(path, extname=false) case extname when false, nil then path.chomp(File.extname(path)) when true then path else Root.exchange(path, extname) end end |
#cleanup(cleanup_dirs = true) ⇒ Object
Removes backup files. Cleanup cannot be rolled back and prevents rollback of actions up to when cleanup is called. If cleanup_dirs is true, empty directories containing the backup files will be removed.
331 332 333 334 335 336 337 338 339 340 |
# File 'lib/tap/file_task.rb', line 331 def cleanup(cleanup_dirs=true) actions.each do |action, source, target| if action == :backup log :cleanup, target, Logger::DEBUG FileUtils.rm_r(target) if File.exists?(target) cleanup_dir(File.dirname(target)) if cleanup_dirs end end actions.clear end |
#cleanup_dir(dir) ⇒ Object
Removes the directory if empty, and all empty parent directories. This method cannot be rolled back.
344 345 346 347 348 349 350 |
# File 'lib/tap/file_task.rb', line 344 def cleanup_dir(dir) while Root.empty?(dir) log :rmdir, dir, Logger::DEBUG FileUtils.rmdir(dir) dir = File.dirname(dir) end end |
#cp(source, target) ⇒ Object
Copies source to target. Files and directories copied by cp are restored upon an execution error.
273 274 275 276 277 278 279 |
# File 'lib/tap/file_task.rb', line 273 def cp(source, target) target = File.join(target, File.basename(source)) if File.directory?(target) prepare(target) log :cp, "#{source} to #{target}", Logger::DEBUG FileUtils.cp(source, target) end |
#cp_r(source, target) ⇒ Object
Copies source to target. If source is a directory, the contents are copied recursively. If target is a directory, copies source to target/source. Files and directories copied by cp are restored upon an execution error.
285 286 287 288 289 290 291 |
# File 'lib/tap/file_task.rb', line 285 def cp_r(source, target) target = File.join(target, File.basename(source)) if File.directory?(target) prepare(target) log :cp_r, "#{source} to #{target}", Logger::DEBUG FileUtils.cp_r(source, target) end |
#filepath(dir, *paths) ⇒ Object
99 100 101 |
# File 'lib/tap/file_task.rb', line 99 def filepath(dir, *paths) app.filepath(dir, name, *paths) end |
#initialize_copy(orig) ⇒ Object
Initializes a copy that will rollback independent of self.
50 51 52 53 |
# File 'lib/tap/file_task.rb', line 50 def initialize_copy(orig) super @actions = [] end |
#log_basename(action, paths, level = Logger::INFO) ⇒ Object
Logs the given action, with the basenames of the input paths.
353 354 355 356 |
# File 'lib/tap/file_task.rb', line 353 def log_basename(action, paths, level=Logger::INFO) msg = [paths].flatten.collect {|path| File.basename(path) }.join(',') log(action, msg, level) end |
#mkdir(dir) ⇒ Object
Creates a directory. Directories created by mkdir removed on rollback.
197 198 199 200 201 202 203 204 205 |
# File 'lib/tap/file_task.rb', line 197 def mkdir(dir) dir = File.(dir) unless File.exists?(dir) log :mkdir, dir, Logger::DEBUG FileUtils.mkdir(dir) actions << [:make, dir] end end |
#mkdir_p(dir) ⇒ Object
Creates a directory and all its parent directories. Directories created by mkdir_p removed on rollback.
184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/tap/file_task.rb', line 184 def mkdir_p(dir) dir = File.(dir) dirs = [] while !File.exists?(dir) dirs.unshift(dir) dir = File.dirname(dir) end dirs.each {|dir| mkdir(dir) } end |
#mv(source, target, backup_source = true) ⇒ Object
Moves source to target. Files and directories moved by mv are restored upon an execution error.
295 296 297 298 299 300 301 |
# File 'lib/tap/file_task.rb', line 295 def mv(source, target, backup_source=true) backup(source, true) if backup_source prepare(target) log :mv, "#{source} to #{target}", Logger::DEBUG FileUtils.mv(source, target) end |
#prepare(path, backup_using_copy = false) ⇒ Object
Prepares the path by backing up any existing file and ensuring that the parent directory for path exists. If a block is given, a file is opened and yielded to it (as in File.open). Prepared paths are removed and the backups restored on rollback.
Returns the expanded path.
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/tap/file_task.rb', line 213 def prepare(path, backup_using_copy=false) raise "not a file: #{path}" if File.directory?(path) path = File.(path) if File.exists?(path) # backup or remove existing files backup(path, backup_using_copy) else # ensure the parent directory exists # for non-existant files mkdir_p File.dirname(path) end log :prepare, path, Logger::DEBUG actions << [:make, path] if block_given? File.open(path, "w") {|file| yield(file) } end path end |
#rm(path) ⇒ Object
Removes a file. Directories cannot be removed by this method. Files removed by rm are restored upon an execution error.
260 261 262 263 264 265 266 267 268 269 |
# File 'lib/tap/file_task.rb', line 260 def rm(path) path = File.(path) unless File.file?(path) raise "not a file: #{path}" end backup(path, false) log :rm, path, Logger::DEBUG end |
#rm_r(path) ⇒ Object
Removes a file. If a directory is provided, it’s contents are removed recursively. Files and directories removed by rm_r are restored upon an execution error.
238 239 240 241 242 243 |
# File 'lib/tap/file_task.rb', line 238 def rm_r(path) path = File.(path) backup(path, false) log :rm_r, path, Logger::DEBUG end |
#rmdir(dir) ⇒ Object
Removes an empty directory. Directories removed by rmdir are restored upon an execution error.
247 248 249 250 251 252 253 254 255 256 |
# File 'lib/tap/file_task.rb', line 247 def rmdir(dir) dir = File.(dir) unless Root.empty?(dir) raise "not an empty directory: #{dir}" end backup(dir, false) log :rmdir, dir, Logger::DEBUG end |
#rollback ⇒ Object
Rolls back any actions capable of being rolled back. Rollback is forceful; for instance if you make a folder using mkdir rollback removes that directory using FileUtils.rm_r. Any files added to the folder will be removed even if they were not added by self.
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/tap/file_task.rb', line 308 def rollback while !actions.empty? action, source, target = actions.pop case action when :make log :rollback, "#{source}", Logger::DEBUG FileUtils.rm_r(source) when :backup log :rollback, "#{target} to #{source}", Logger::DEBUG dir = File.dirname(source) FileUtils.mkdir_p(dir) unless File.exists?(dir) FileUtils.mv(target, source, :force => true) else raise "unknown action: #{[action, source, target].inspect}" end end end |
#uptodate?(targets, sources = []) ⇒ Boolean
Returns true if all of the targets are up to date relative to all of the listed sources. Single values or arrays can be provided for both targets and sources.
Returns false (ie ‘not up to date’) if app.force is true.
– TODO: add check vs date reference (ex config_file date)
123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/tap/file_task.rb', line 123 def uptodate?(targets, sources=[]) if app.force log_basename(:force, *targets) false else targets = [targets] unless targets.kind_of?(Array) sources = [sources] unless sources.kind_of?(Array) targets.each do |target| return false unless FileUtils.uptodate?(target, sources) end true end end |