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.
496 497 498 499 500 501 502 503 504 505 506 |
# File 'lib/synqa.rb', line 496 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
487 488 489 |
# File 'lib/synqa.rb', line 487 def copyDestination @copyDestination end |
#dirByName ⇒ Object (readonly)
immediate sub-directories of this directory, indexed by name
484 485 486 |
# File 'lib/synqa.rb', line 484 def dirByName @dirByName end |
#dirs ⇒ Object (readonly)
immediate sub-directories of this directory
478 479 480 |
# File 'lib/synqa.rb', line 478 def dirs @dirs end |
#fileByName ⇒ Object (readonly)
the files within this sub-directory, indexed by file name
481 482 483 |
# File 'lib/synqa.rb', line 481 def fileByName @fileByName end |
#files ⇒ Object (readonly)
files within this sub-directory (as FileContent’s)
475 476 477 |
# File 'lib/synqa.rb', line 475 def files @files end |
#name ⇒ Object (readonly)
name of the sub-directory within the containing directory (or nil if this is the base directory)
469 470 471 |
# File 'lib/synqa.rb', line 469 def name @name end |
#pathElements ⇒ Object (readonly)
path elements from base directory leading to this one
472 473 474 |
# File 'lib/synqa.rb', line 472 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.
494 495 496 |
# File 'lib/synqa.rb', line 494 def time @time end |
#toBeDeleted ⇒ Object (readonly)
whether this directory should be deleted
490 491 492 |
# File 'lib/synqa.rb', line 490 def toBeDeleted @toBeDeleted end |
Class Method Details
.readFromFile(fileName) ⇒ Object
read a content tree from a file (in format written by writeToFile)
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 |
# File 'lib/synqa.rb', line 640 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
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 |
# File 'lib/synqa.rb', line 670 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
540 541 542 543 544 545 546 547 |
# File 'lib/synqa.rb', line 540 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
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 |
# File 'lib/synqa.rb', line 559 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)
529 530 531 532 533 534 535 536 537 |
# File 'lib/synqa.rb', line 529 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
697 698 699 |
# File 'lib/synqa.rb', line 697 def getDir(dir) return dirByName.fetch(dir, nil) end |
#getFile(file) ⇒ Object
Get the named file & hash value, if it exists
702 703 704 |
# File 'lib/synqa.rb', line 702 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)
524 525 526 |
# File 'lib/synqa.rb', line 524 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.
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 |
# File 'lib/synqa.rb', line 709 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.
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 |
# File 'lib/synqa.rb', line 729 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
691 692 693 694 |
# File 'lib/synqa.rb', line 691 def markSyncOperationsForDestination(destination) markCopyOperations(destination) destination.markDeleteOptions(self) end |
#markToCopy(destinationDirectory) ⇒ Object
mark this directory to be copied to a destination directory
509 510 511 |
# File 'lib/synqa.rb', line 509 def markToCopy(destinationDirectory) @copyDestination = destinationDirectory end |
#markToDelete ⇒ Object
mark this directory (on a remote system) to be deleted
514 515 516 |
# File 'lib/synqa.rb', line 514 def markToDelete @toBeDeleted = true end |
#relativePath ⇒ Object
the path of the directory that this content tree represents, relative to the base directory
519 520 521 |
# File 'lib/synqa.rb', line 519 def relativePath return @pathElements.join("/") end |
#showIndented(name = "", indent = " ", currentIndent = "") ⇒ Object
pretty-print this content tree
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 |
# File 'lib/synqa.rb', line 580 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
550 551 552 553 554 555 556 |
# File 'lib/synqa.rb', line 550 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
609 610 611 612 613 614 615 616 617 618 619 620 |
# File 'lib/synqa.rb', line 609 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)
623 624 625 626 627 628 |
# File 'lib/synqa.rb', line 623 def writeToFile(fileName) puts "Writing content tree to file #{fileName} ..." File.open(fileName, "w") do |outFile| writeLinesToFile(outFile) end end |