Class: Bitferry::Volume
- Inherits:
-
Object
- Object
- Bitferry::Volume
- Extended by:
- Logging
- Includes:
- Logging
- Defined in:
- lib/bitferry.rb
Constant Summary collapse
- STORAGE =
'.bitferry'
- STORAGE_ =
'.bitferry~'
- STORAGE_MASK =
'.bitferry*'
Instance Attribute Summary collapse
-
#generation ⇒ Object
readonly
Returns the value of attribute generation.
-
#root ⇒ Object
readonly
Returns the value of attribute root.
-
#tag ⇒ Object
readonly
Returns the value of attribute tag.
-
#vault ⇒ Object
readonly
Returns the value of attribute vault.
Class Method Summary collapse
- .[](tag) ⇒ Object
- .delete(*tags, wipe: false) ⇒ Object
- .endpoint(root) ⇒ Object
- .intact ⇒ Object
-
.lookup(*tags) ⇒ Object
Return list of registered volumes whose tags match at least one specified partial.
- .match(tags, volumes) ⇒ Object
- .new(root, **opts) ⇒ Object
- .register(volume) ⇒ Object
- .registered ⇒ Object
- .reset ⇒ Object
- .restore(root) ⇒ Object
Instance Method Summary collapse
- #commit ⇒ Object
- #committed ⇒ Object
- #create(*args, **opts) ⇒ Object
- #delete(wipe: false) ⇒ Object
- #endpoint(path = String.new) ⇒ Object
- #externalize ⇒ Object
- #format ⇒ Object
-
#initialize(root, tag: Bitferry.tag, modified: DateTime.now, overwrite: false) ⇒ Volume
constructor
A new instance of Volume.
- #intact? ⇒ Boolean
- #intact_tasks ⇒ Object
- #live_tasks ⇒ Object
- #modified? ⇒ Boolean
- #remove ⇒ Object
- #restore(root) ⇒ Object
- #storage ⇒ Object
- #storage_ ⇒ Object
- #store ⇒ Object
- #tasks ⇒ Object
- #touch ⇒ Object
Methods included from Logging
Constructor Details
#initialize(root, tag: Bitferry.tag, modified: DateTime.now, overwrite: false) ⇒ Volume
Returns a new instance of Volume.
308 309 310 311 312 313 314 315 |
# File 'lib/bitferry.rb', line 308 def initialize(root, tag: Bitferry.tag, modified: DateTime.now, overwrite: false) @tag = tag @generation = 0 @vault = {} @modified = modified @overwrite = overwrite @root = Pathname.new(root).realdirpath end |
Instance Attribute Details
#generation ⇒ Object (readonly)
Returns the value of attribute generation.
244 245 246 |
# File 'lib/bitferry.rb', line 244 def generation @generation end |
#root ⇒ Object (readonly)
Returns the value of attribute root.
247 248 249 |
# File 'lib/bitferry.rb', line 247 def root @root end |
#tag ⇒ Object (readonly)
Returns the value of attribute tag.
241 242 243 |
# File 'lib/bitferry.rb', line 241 def tag @tag end |
#vault ⇒ Object (readonly)
Returns the value of attribute vault.
250 251 252 |
# File 'lib/bitferry.rb', line 250 def vault @vault end |
Class Method Details
.[](tag) ⇒ Object
253 254 255 256 |
# File 'lib/bitferry.rb', line 253 def self.[](tag) @@registry.each_value { |volume| return volume if volume.tag == tag } nil end |
.delete(*tags, wipe: false) ⇒ Object
293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/bitferry.rb', line 293 def self.delete(*, wipe: false) process = [] .each do |tag| case (volumes = Volume.lookup(tag)).size when 0 then log.warn("no volumes matching (partial) tag #{tag}") when 1 then process += volumes else = volumes.collect { |v| v.tag }.join(', ') raise ArgumentError, "multiple volumes matching (partial) tag #{tag}: #{}" end end process.each { |volume| volume.delete(wipe: wipe) } end |
.endpoint(root) ⇒ Object
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/bitferry.rb', line 361 def self.endpoint(root) path = Pathname.new(root).realdirpath intact.sort { |v1, v2| v2.root.to_s.size <=> v1.root.to_s.size }.each do |volume| begin stem = path.relative_path_from(volume.root).to_s #.chomp('/') case stem when '.' then return volume.endpoint when /^[^\.].*/ then return volume.endpoint(stem) end rescue ArgumentError # Catch different prefix error on Windows end end raise ArgumentError, "no intact volume encompasses path #{root}" end |
.intact ⇒ Object
487 |
# File 'lib/bitferry.rb', line 487 def self.intact = registered.filter { |volume| volume.intact? } |
.lookup(*tags) ⇒ Object
Return list of registered volumes whose tags match at least one specified partial
260 |
# File 'lib/bitferry.rb', line 260 def self.lookup(*) = match(, registered) |
.match(tags, volumes) ⇒ Object
263 264 265 266 267 268 |
# File 'lib/bitferry.rb', line 263 def self.match(, volumes) rxs = .collect { |x| Regexp.new(x) } volumes.filter do |volume| rxs.any? { |rx| !(rx =~ volume.tag).nil? } end end |
.new(root, **opts) ⇒ Object
271 272 273 274 275 |
# File 'lib/bitferry.rb', line 271 def self.new(root, **opts) volume = allocate volume.send(:create, root, **opts) register(volume) end |
.register(volume) ⇒ Object
481 |
# File 'lib/bitferry.rb', line 481 def self.register(volume) = @@registry[volume.root] = volume |
.registered ⇒ Object
484 |
# File 'lib/bitferry.rb', line 484 def self.registered = @@registry.values |
.reset ⇒ Object
478 |
# File 'lib/bitferry.rb', line 478 def self.reset = @@registry = {} |
.restore(root) ⇒ Object
278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/bitferry.rb', line 278 def self.restore(root) begin volume = allocate volume.send(:restore, root) volume = register(volume) log.info("restored volume #{volume.tag} from #{root}") volume rescue => e log.error("failed to restore volume from #{root}") log.error(e.) if $DEBUG raise end end |
Instance Method Details
#commit ⇒ Object
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/bitferry.rb', line 340 def commit if modified? log.info("commit volume #{tag} (modified)") case @state when :pristine format store when :intact store when :removing remove else raise end committed else log.info("skipped committing volume #{tag} (unmodified)") end end |
#committed ⇒ Object
402 403 404 405 406 |
# File 'lib/bitferry.rb', line 402 def committed x = tasks.collect { |t| t.generation }.min @generation = x ? x : 0 @modified = false end |
#create(*args, **opts) ⇒ Object
318 319 320 321 322 |
# File 'lib/bitferry.rb', line 318 def create(*args, **opts) initialize(*args, **opts) @state = :pristine @modified = true end |
#delete(wipe: false) ⇒ Object
394 395 396 397 398 399 |
# File 'lib/bitferry.rb', line 394 def delete(wipe: false) touch @wipe = wipe @state = :removing log.info("marked volume #{tag} for deletion") end |
#endpoint(path = String.new) ⇒ Object
378 |
# File 'lib/bitferry.rb', line 378 def endpoint(path = String.new) = Endpoint::Bitferry.new(self, path) |
#externalize ⇒ Object
456 457 458 459 460 461 462 463 464 465 466 |
# File 'lib/bitferry.rb', line 456 def externalize tasks = live_tasks v = vault.filter { |t| !Task[t].nil? && Task[t].live? } # Purge entries from non-existing (deleted) tasks { bitferry: "0", volume: tag, modified: (@modified = DateTime.now), tasks: tasks.empty? ? nil : tasks.collect(&:externalize), vault: v.empty? ? nil : v }.compact end |
#format ⇒ Object
428 429 430 431 432 433 434 435 436 437 438 |
# File 'lib/bitferry.rb', line 428 def format raise IOError, "refuse to overwrite existing volume storage #{storage}" if !@overwrite && File.exist?(storage) if Bitferry.simulate? log.info("skipped storage formatting (simulation)") else FileUtils.mkdir_p(root) FileUtils.rm_f [storage, storage_] log.info("formatted volume #{tag} in #{root}") end @state = nil end |
#intact? ⇒ Boolean
384 |
# File 'lib/bitferry.rb', line 384 def intact? = @state != :removing |
#intact_tasks ⇒ Object
475 |
# File 'lib/bitferry.rb', line 475 def intact_tasks = live_tasks.filter { |task| task.intact? } |
#live_tasks ⇒ Object
472 |
# File 'lib/bitferry.rb', line 472 def live_tasks = Task.live.filter { |task| task.refers?(self) } |
#modified? ⇒ Boolean
381 |
# File 'lib/bitferry.rb', line 381 def modified? = @modified || tasks.any? { |t| t.generation > generation } |
#remove ⇒ Object
441 442 443 444 445 446 447 448 449 450 451 452 453 |
# File 'lib/bitferry.rb', line 441 def remove unless Bitferry.simulate? if @wipe FileUtils.rm_rf(Dir[File.join(root, '*'), File.join(root, '.*')]) log.info("wiped entire volume directory #{root}") else FileUtils.rm_f [storage, storage_] log.info("deleted volume #{tag} storage files #{File.join(root, STORAGE_MASK)}") end end @@registry.delete(root) @state = nil end |
#restore(root) ⇒ Object
325 326 327 328 329 330 331 332 333 |
# File 'lib/bitferry.rb', line 325 def restore(root) hash = JSON.load_file(storage = Pathname(root).join(STORAGE), { symbolize_names: true }) raise IOError, "bad volume storage #{storage}" unless hash.fetch(:bitferry) == "0" initialize(root, tag: hash.fetch(:volume), modified: DateTime.parse(hash.fetch(:modified))) hash.fetch(:tasks, []).each { |hash| Task::ROUTE.fetch(hash.fetch(:operation).intern).restore(hash) } @vault = hash.fetch(:vault, {}).transform_keys { |key| key.to_s } @state = :intact @modified = false end |
#storage ⇒ Object
336 |
# File 'lib/bitferry.rb', line 336 def storage = @storage ||= root.join(STORAGE) |
#storage_ ⇒ Object
337 |
# File 'lib/bitferry.rb', line 337 def storage_ = @storage_ ||= root.join(STORAGE_) |
#store ⇒ Object
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
# File 'lib/bitferry.rb', line 409 def store require 'neatjson' tasks.each(&:commit) hash = JSON.neat_generate(externalize, short: false, wrap: 200, afterColon: 1, afterComma: 1) if Bitferry.simulate? log.info("skipped volume #{tag} storage modification (simulation)") else begin File.write(storage_, hash) FileUtils.mv(storage_, storage) log.info("written volume #{tag} storage #{storage}") ensure FileUtils.rm_f(storage_) end end @state = :intact end |
#tasks ⇒ Object
469 |
# File 'lib/bitferry.rb', line 469 def tasks = Task.registered.filter { |task| task.refers?(self) } |
#touch ⇒ Object
387 388 389 390 391 |
# File 'lib/bitferry.rb', line 387 def touch x = tasks.collect { |t| t.generation }.max @generation = x ? x + 1 : 0 @modified = true end |