Class: Synqa::ContentTree
- Inherits:
-
Object
- Object
- Synqa::ContentTree
- Defined in:
- lib/synqa.rb
Overview
A “content tree” consisting of a description of the contents of files and sub-directories within a base directory. The file contents are described via cryptographic hash values. Each sub-directory within a content tree is also represented as a ContentTree.
Constant Summary collapse
- @@dateTimeFormat =
date-time format for reading and writing times, e.g. “2007-12-23 13:03:99.012 +0000”
"%Y-%m-%d %H:%M:%S.%L %z"
- @@dirLineRegex =
regular expression for directory entries in content tree file
/^D (.*)$/
- @@fileLineRegex =
regular expression for file entries in content tree file
/^F ([^ ]*) (.*)$/
- @@timeRegex =
regular expression for time entry in content tree file
/^T (.*)$/
Instance Attribute Summary collapse
-
#copyDestination ⇒ Object
readonly
where this directory should be copied to.
-
#dirByName ⇒ Object
readonly
immediate sub-directories of this directory, indexed by name.
-
#dirs ⇒ Object
readonly
immediate sub-directories of this directory.
-
#fileByName ⇒ Object
readonly
the files within this sub-directory, indexed by file name.
-
#files ⇒ Object
readonly
files within this sub-directory (as FileContent’s).
-
#name ⇒ Object
readonly
name of the sub-directory within the containing directory (or nil if this is the base directory).
-
#pathElements ⇒ Object
readonly
path elements from base directory leading to this one.
-
#time ⇒ Object
the UTC time (on the local system, even if this content tree represents a remote directory) that this content tree was constructed.
-
#toBeDeleted ⇒ Object
readonly
whether this directory should be deleted.
Class Method Summary collapse
-
.readFromFile(fileName) ⇒ Object
read a content tree from a file (in format written by writeToFile).
-
.readMapOfHashesFromFile(fileName) ⇒ Object
read a content tree as a map of hashes, i.e.
Instance Method Summary collapse
-
#addDir(dirPath) ⇒ Object
add a sub-directory to this content tree.
-
#addFile(filePath, hash) ⇒ Object
given a relative path, add a file and hash value to this content tree.
-
#fullPath ⇒ Object
the full path of the directory that this content tree represents (relative to the base directory).
-
#getContentTreeForSubDir(subDir) ⇒ Object
get the content tree for a sub-directory (creating it if it doesn’t yet exist).
-
#getDir(dir) ⇒ Object
Get the named sub-directory content tree, if it exists.
-
#getFile(file) ⇒ Object
Get the named file & hash value, if it exists.
-
#getPathElements(path) ⇒ Object
convert a path string to an array of path elements (or return it as is if it’s already an array).
-
#initialize(name = nil, parentPathElements = nil) ⇒ ContentTree
constructor
A new instance of ContentTree.
-
#markCopyOperations(destinationDir) ⇒ Object
Mark copy operations, given that the corresponding destination directory already exists.
-
#markDeleteOptions(sourceDir) ⇒ Object
Mark delete operations, given that the corresponding source directory exists.
-
#markSyncOperationsForDestination(destination) ⇒ Object
Mark operations for this (source) content tree and the destination content tree in order to synch the destination content tree with this one.
-
#markToCopy(destinationDirectory) ⇒ Object
mark this directory to be copied to a destination directory.
-
#markToDelete ⇒ Object
mark this directory (on a remote system) to be deleted.
-
#showIndented(name = "", indent = " ", currentIndent = "") ⇒ Object
pretty-print this content tree.
-
#sort! ⇒ Object
recursively sort the files and sub-directories of this content tree alphabetically.
-
#writeLinesToFile(outFile, prefix = "") ⇒ Object
write this content tree to an open file, indented.
-
#writeToFile(fileName) ⇒ Object
write this content tree to a file (in a format which readFromFile can read back in).
Constructor Details
#initialize(name = nil, parentPathElements = nil) ⇒ ContentTree
Returns a new instance of ContentTree.
357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/synqa.rb', line 357 def initialize(name = nil, parentPathElements = nil) @name = name @pathElements = name == nil ? [] : parentPathElements + [name] @files = [] @dirs = [] @fileByName = {} @dirByName = {} @copyDestination = nil @toBeDeleted = false @time = nil end |
Instance Attribute Details
#copyDestination ⇒ Object (readonly)
where this directory should be copied to
348 349 350 |
# File 'lib/synqa.rb', line 348 def copyDestination @copyDestination end |
#dirByName ⇒ Object (readonly)
immediate sub-directories of this directory, indexed by name
345 346 347 |
# File 'lib/synqa.rb', line 345 def dirByName @dirByName end |
#dirs ⇒ Object (readonly)
immediate sub-directories of this directory
339 340 341 |
# File 'lib/synqa.rb', line 339 def dirs @dirs end |
#fileByName ⇒ Object (readonly)
the files within this sub-directory, indexed by file name
342 343 344 |
# File 'lib/synqa.rb', line 342 def fileByName @fileByName end |
#files ⇒ Object (readonly)
files within this sub-directory (as FileContent’s)
336 337 338 |
# File 'lib/synqa.rb', line 336 def files @files end |
#name ⇒ Object (readonly)
name of the sub-directory within the containing directory (or nil if this is the base directory)
330 331 332 |
# File 'lib/synqa.rb', line 330 def name @name end |
#pathElements ⇒ Object (readonly)
path elements from base directory leading to this one
333 334 335 |
# File 'lib/synqa.rb', line 333 def pathElements @pathElements end |
#time ⇒ Object
the UTC time (on the local system, even if this content tree represents a remote directory) that this content tree was constructed. Only set for the base directory.
355 356 357 |
# File 'lib/synqa.rb', line 355 def time @time end |
#toBeDeleted ⇒ Object (readonly)
whether this directory should be deleted
351 352 353 |
# File 'lib/synqa.rb', line 351 def toBeDeleted @toBeDeleted end |
Class Method Details
.readFromFile(fileName) ⇒ Object
read a content tree from a file (in format written by writeToFile)
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 |
# File 'lib/synqa.rb', line 501 def self.readFromFile(fileName) contentTree = ContentTree.new() puts "Reading content tree from #{fileName} ..." IO.foreach(fileName) do |line| dirLineMatch = @@dirLineRegex.match(line) if dirLineMatch dirName = dirLineMatch[1] contentTree.addDir(dirName) else fileLineMatch = @@fileLineRegex.match(line) if fileLineMatch hash = fileLineMatch[1] fileName = fileLineMatch[2] contentTree.addFile(fileName, hash) else timeLineMatch = @@timeRegex.match(line) if timeLineMatch timeString = timeLineMatch[1] contentTree.time = Time.strptime(timeString, @@dateTimeFormat) else raise "Invalid line in content tree file: #{line.inspect}" end end end end return contentTree end |
.readMapOfHashesFromFile(fileName) ⇒ Object
read a content tree as a map of hashes, i.e. from relative file path to hash value for the file Actually returns an array of the time entry (if any) and the map of hashes
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
# File 'lib/synqa.rb', line 531 def self.readMapOfHashesFromFile(fileName) mapOfHashes = {} time = nil File.open(fileName).each_line do |line| fileLineMatch = @@fileLineRegex.match(line) if fileLineMatch hash = fileLineMatch[1] fileName = fileLineMatch[2] mapOfHashes[fileName] = hash end timeLineMatch = @@timeRegex.match(line) if timeLineMatch timeString = timeLineMatch[1] time = Time.strptime(timeString, @@dateTimeFormat) end end return [time, mapOfHashes] end |
Instance Method Details
#addDir(dirPath) ⇒ Object
add a sub-directory to this content tree
401 402 403 404 405 406 407 408 |
# File 'lib/synqa.rb', line 401 def addDir(dirPath) pathElements = getPathElements(dirPath) if pathElements.length > 0 pathStart = pathElements[0] restOfPath = pathElements[1..-1] getContentTreeForSubDir(pathStart).addDir(restOfPath) end end |
#addFile(filePath, hash) ⇒ Object
given a relative path, add a file and hash value to this content tree
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 |
# File 'lib/synqa.rb', line 420 def addFile(filePath, hash) pathElements = getPathElements(filePath) if pathElements.length == 0 raise "Invalid file path: #{filePath.inspect}" end if pathElements.length == 1 fileName = pathElements[0] fileContent = FileContent.new(fileName, hash, @pathElements) files << fileContent fileByName[fileName] = fileContent else pathStart = pathElements[0] restOfPath = pathElements[1..-1] getContentTreeForSubDir(pathStart).addFile(restOfPath, hash) end end |
#fullPath ⇒ Object
the full path of the directory that this content tree represents (relative to the base directory)
380 381 382 |
# File 'lib/synqa.rb', line 380 def fullPath return @pathElements.join("/") end |
#getContentTreeForSubDir(subDir) ⇒ Object
get the content tree for a sub-directory (creating it if it doesn’t yet exist)
390 391 392 393 394 395 396 397 398 |
# File 'lib/synqa.rb', line 390 def getContentTreeForSubDir(subDir) dirContentTree = dirByName.fetch(subDir, nil) if dirContentTree == nil dirContentTree = ContentTree.new(subDir, @pathElements) dirs << dirContentTree dirByName[subDir] = dirContentTree end return dirContentTree end |
#getDir(dir) ⇒ Object
Get the named sub-directory content tree, if it exists
558 559 560 |
# File 'lib/synqa.rb', line 558 def getDir(dir) return dirByName.fetch(dir, nil) end |
#getFile(file) ⇒ Object
Get the named file & hash value, if it exists
563 564 565 |
# File 'lib/synqa.rb', line 563 def getFile(file) return fileByName.fetch(file, nil) end |
#getPathElements(path) ⇒ Object
convert a path string to an array of path elements (or return it as is if it’s already an array)
385 386 387 |
# File 'lib/synqa.rb', line 385 def getPathElements(path) return path.is_a?(String) ? (path == "" ? [] : path.split("/")) : path end |
#markCopyOperations(destinationDir) ⇒ Object
Mark copy operations, given that the corresponding destination directory already exists. For files and directories that don’t exist in the destination, mark them to be copied. For sub-directories that do exist, recursively mark the corresponding sub-directory copy operations.
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 |
# File 'lib/synqa.rb', line 570 def markCopyOperations(destinationDir) for dir in dirs destinationSubDir = destinationDir.getDir(dir.name) if destinationSubDir != nil dir.markCopyOperations(destinationSubDir) else dir.markToCopy(destinationDir) end end for file in files destinationFile = destinationDir.getFile(file.name) if destinationFile == nil or destinationFile.hash != file.hash file.markToCopy(destinationDir) end end end |
#markDeleteOptions(sourceDir) ⇒ Object
Mark delete operations, given that the corresponding source directory exists. For files and directories that don’t exist in the source, mark them to be deleted. For sub-directories that do exist, recursively mark the corresponding sub-directory delete operations.
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 |
# File 'lib/synqa.rb', line 590 def markDeleteOptions(sourceDir) for dir in dirs sourceSubDir = sourceDir.getDir(dir.name) if sourceSubDir == nil dir.markToDelete() else dir.markDeleteOptions(sourceSubDir) end end for file in files sourceFile = sourceDir.getFile(file.name) if sourceFile == nil file.markToDelete() end end end |
#markSyncOperationsForDestination(destination) ⇒ Object
Mark operations for this (source) content tree and the destination content tree in order to synch the destination content tree with this one
552 553 554 555 |
# File 'lib/synqa.rb', line 552 def markSyncOperationsForDestination(destination) markCopyOperations(destination) destination.markDeleteOptions(self) end |
#markToCopy(destinationDirectory) ⇒ Object
mark this directory to be copied to a destination directory
370 371 372 |
# File 'lib/synqa.rb', line 370 def markToCopy(destinationDirectory) @copyDestination = destinationDirectory end |
#markToDelete ⇒ Object
mark this directory (on a remote system) to be deleted
375 376 377 |
# File 'lib/synqa.rb', line 375 def markToDelete @toBeDeleted = true end |
#showIndented(name = "", indent = " ", currentIndent = "") ⇒ Object
pretty-print this content tree
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
# File 'lib/synqa.rb', line 441 def showIndented(name = "", indent = " ", currentIndent = "") if time != nil puts "#{currentIndent}[TIME: #{time.strftime(@@dateTimeFormat)}]" end if name != "" puts "#{currentIndent}#{name}" end if copyDestination != nil puts "#{currentIndent} [COPY to #{copyDestination.fullPath}]" end if toBeDeleted puts "#{currentIndent} [DELETE]" end nextIndent = currentIndent + indent for dir in dirs dir.showIndented("#{dir.name}/", indent = indent, currentIndent = nextIndent) end for file in files puts "#{nextIndent}#{file.name} - #{file.hash}" if file.copyDestination != nil puts "#{nextIndent} [COPY to #{file.copyDestination.fullPath}]" end if file.toBeDeleted puts "#{nextIndent} [DELETE]" end end end |
#sort! ⇒ Object
recursively sort the files and sub-directories of this content tree alphabetically
411 412 413 414 415 416 417 |
# File 'lib/synqa.rb', line 411 def sort! dirs.sort_by! {|dir| dir.name} files.sort_by! {|file| file.name} for dir in dirs dir.sort! end end |
#writeLinesToFile(outFile, prefix = "") ⇒ Object
write this content tree to an open file, indented
470 471 472 473 474 475 476 477 478 479 480 481 |
# File 'lib/synqa.rb', line 470 def writeLinesToFile(outFile, prefix = "") if time != nil outFile.puts("T #{time.strftime(@@dateTimeFormat)}\n") end for dir in dirs outFile.puts("D #{prefix}#{dir.name}\n") dir.writeLinesToFile(outFile, "#{prefix}#{dir.name}/") end for file in files outFile.puts("F #{file.hash} #{prefix}#{file.name}\n") end end |
#writeToFile(fileName) ⇒ Object
write this content tree to a file (in a format which readFromFile can read back in)
484 485 486 487 488 489 |
# File 'lib/synqa.rb', line 484 def writeToFile(fileName) puts "Writing content tree to file #{fileName} ..." File.open(fileName, "w") do |outFile| writeLinesToFile(outFile) end end |