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.
346 347 348 349 350 351 352 353 354 355 356 |
# File 'lib/synqa.rb', line 346 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
337 338 339 |
# File 'lib/synqa.rb', line 337 def copyDestination @copyDestination end |
#dirByName ⇒ Object (readonly)
immediate sub-directories of this directory, indexed by name
334 335 336 |
# File 'lib/synqa.rb', line 334 def dirByName @dirByName end |
#dirs ⇒ Object (readonly)
immediate sub-directories of this directory
328 329 330 |
# File 'lib/synqa.rb', line 328 def dirs @dirs end |
#fileByName ⇒ Object (readonly)
the files within this sub-directory, indexed by file name
331 332 333 |
# File 'lib/synqa.rb', line 331 def fileByName @fileByName end |
#files ⇒ Object (readonly)
files within this sub-directory (as FileContent’s)
325 326 327 |
# File 'lib/synqa.rb', line 325 def files @files end |
#name ⇒ Object (readonly)
name of the sub-directory within the containing directory (or nil if this is the base directory)
319 320 321 |
# File 'lib/synqa.rb', line 319 def name @name end |
#pathElements ⇒ Object (readonly)
path elements from base directory leading to this one
322 323 324 |
# File 'lib/synqa.rb', line 322 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.
344 345 346 |
# File 'lib/synqa.rb', line 344 def time @time end |
#toBeDeleted ⇒ Object (readonly)
whether this directory should be deleted
340 341 342 |
# File 'lib/synqa.rb', line 340 def toBeDeleted @toBeDeleted end |
Class Method Details
.readFromFile(fileName) ⇒ Object
read a content tree from a file (in format written by writeToFile)
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
# File 'lib/synqa.rb', line 490 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
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 |
# File 'lib/synqa.rb', line 520 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
390 391 392 393 394 395 396 397 |
# File 'lib/synqa.rb', line 390 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
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 |
# File 'lib/synqa.rb', line 409 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)
369 370 371 |
# File 'lib/synqa.rb', line 369 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)
379 380 381 382 383 384 385 386 387 |
# File 'lib/synqa.rb', line 379 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
547 548 549 |
# File 'lib/synqa.rb', line 547 def getDir(dir) return dirByName.fetch(dir, nil) end |
#getFile(file) ⇒ Object
Get the named file & hash value, if it exists
552 553 554 |
# File 'lib/synqa.rb', line 552 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)
374 375 376 |
# File 'lib/synqa.rb', line 374 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.
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 |
# File 'lib/synqa.rb', line 559 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.
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 |
# File 'lib/synqa.rb', line 579 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
541 542 543 544 |
# File 'lib/synqa.rb', line 541 def markSyncOperationsForDestination(destination) markCopyOperations(destination) destination.markDeleteOptions(self) end |
#markToCopy(destinationDirectory) ⇒ Object
mark this directory to be copied to a destination directory
359 360 361 |
# File 'lib/synqa.rb', line 359 def markToCopy(destinationDirectory) @copyDestination = destinationDirectory end |
#markToDelete ⇒ Object
mark this directory (on a remote system) to be deleted
364 365 366 |
# File 'lib/synqa.rb', line 364 def markToDelete @toBeDeleted = true end |
#showIndented(name = "", indent = " ", currentIndent = "") ⇒ Object
pretty-print this content tree
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 |
# File 'lib/synqa.rb', line 430 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 do dir.showIndented("#{dir.name}/", indent = indent, currentIndent = nextIndent) end for file in files do 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
400 401 402 403 404 405 406 |
# File 'lib/synqa.rb', line 400 def sort! dirs.sort_by! {|dir| dir.name} files.sort_by! {|file| file.name} for dir in dirs do dir.sort! end end |
#writeLinesToFile(outFile, prefix = "") ⇒ Object
write this content tree to an open file, indented
459 460 461 462 463 464 465 466 467 468 469 470 |
# File 'lib/synqa.rb', line 459 def writeLinesToFile(outFile, prefix = "") if time != nil outFile.puts("T #{time.strftime(@@dateTimeFormat)}\n") end for dir in dirs do outFile.puts("D #{prefix}#{dir.name}\n") dir.writeLinesToFile(outFile, "#{prefix}#{dir.name}/") end for file in files do 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)
473 474 475 476 477 478 |
# File 'lib/synqa.rb', line 473 def writeToFile(fileName) puts "Writing content tree to file #{fileName} ..." File.open(fileName, "w") do |outFile| writeLinesToFile(outFile) end end |