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.
-
#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.
-
#relativePath ⇒ Object
the path of the directory that this content tree represents, relative to the base directory.
-
#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.
458 459 460 461 462 463 464 465 466 467 468 |
# File 'lib/synqa.rb', line 458 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
449 450 451 |
# File 'lib/synqa.rb', line 449 def copyDestination @copyDestination end |
#dirByName ⇒ Object (readonly)
immediate sub-directories of this directory, indexed by name
446 447 448 |
# File 'lib/synqa.rb', line 446 def dirByName @dirByName end |
#dirs ⇒ Object (readonly)
immediate sub-directories of this directory
440 441 442 |
# File 'lib/synqa.rb', line 440 def dirs @dirs end |
#fileByName ⇒ Object (readonly)
the files within this sub-directory, indexed by file name
443 444 445 |
# File 'lib/synqa.rb', line 443 def fileByName @fileByName end |
#files ⇒ Object (readonly)
files within this sub-directory (as FileContent’s)
437 438 439 |
# File 'lib/synqa.rb', line 437 def files @files end |
#name ⇒ Object (readonly)
name of the sub-directory within the containing directory (or nil if this is the base directory)
431 432 433 |
# File 'lib/synqa.rb', line 431 def name @name end |
#pathElements ⇒ Object (readonly)
path elements from base directory leading to this one
434 435 436 |
# File 'lib/synqa.rb', line 434 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.
456 457 458 |
# File 'lib/synqa.rb', line 456 def time @time end |
#toBeDeleted ⇒ Object (readonly)
whether this directory should be deleted
452 453 454 |
# File 'lib/synqa.rb', line 452 def toBeDeleted @toBeDeleted end |
Class Method Details
.readFromFile(fileName) ⇒ Object
read a content tree from a file (in format written by writeToFile)
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 |
# File 'lib/synqa.rb', line 602 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
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 |
# File 'lib/synqa.rb', line 632 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
502 503 504 505 506 507 508 509 |
# File 'lib/synqa.rb', line 502 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
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 |
# File 'lib/synqa.rb', line 521 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 |
#getContentTreeForSubDir(subDir) ⇒ Object
get the content tree for a sub-directory (creating it if it doesn’t yet exist)
491 492 493 494 495 496 497 498 499 |
# File 'lib/synqa.rb', line 491 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
659 660 661 |
# File 'lib/synqa.rb', line 659 def getDir(dir) return dirByName.fetch(dir, nil) end |
#getFile(file) ⇒ Object
Get the named file & hash value, if it exists
664 665 666 |
# File 'lib/synqa.rb', line 664 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)
486 487 488 |
# File 'lib/synqa.rb', line 486 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.
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 |
# File 'lib/synqa.rb', line 671 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.
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 |
# File 'lib/synqa.rb', line 691 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
653 654 655 656 |
# File 'lib/synqa.rb', line 653 def markSyncOperationsForDestination(destination) markCopyOperations(destination) destination.markDeleteOptions(self) end |
#markToCopy(destinationDirectory) ⇒ Object
mark this directory to be copied to a destination directory
471 472 473 |
# File 'lib/synqa.rb', line 471 def markToCopy(destinationDirectory) @copyDestination = destinationDirectory end |
#markToDelete ⇒ Object
mark this directory (on a remote system) to be deleted
476 477 478 |
# File 'lib/synqa.rb', line 476 def markToDelete @toBeDeleted = true end |
#relativePath ⇒ Object
the path of the directory that this content tree represents, relative to the base directory
481 482 483 |
# File 'lib/synqa.rb', line 481 def relativePath return @pathElements.join("/") end |
#showIndented(name = "", indent = " ", currentIndent = "") ⇒ Object
pretty-print this content tree
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 |
# File 'lib/synqa.rb', line 542 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.relativePath}]" 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.relativePath}]" end if file.toBeDeleted puts "#{nextIndent} [DELETE]" end end end |
#sort! ⇒ Object
recursively sort the files and sub-directories of this content tree alphabetically
512 513 514 515 516 517 518 |
# File 'lib/synqa.rb', line 512 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
571 572 573 574 575 576 577 578 579 580 581 582 |
# File 'lib/synqa.rb', line 571 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)
585 586 587 588 589 590 |
# File 'lib/synqa.rb', line 585 def writeToFile(fileName) puts "Writing content tree to file #{fileName} ..." File.open(fileName, "w") do |outFile| writeLinesToFile(outFile) end end |