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.
301 302 303 304 305 306 307 308 |
# File 'lib/bitferry.rb', line 301 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.
237 238 239 |
# File 'lib/bitferry.rb', line 237 def generation @generation end |
#root ⇒ Object (readonly)
Returns the value of attribute root.
240 241 242 |
# File 'lib/bitferry.rb', line 240 def root @root end |
#tag ⇒ Object (readonly)
Returns the value of attribute tag.
234 235 236 |
# File 'lib/bitferry.rb', line 234 def tag @tag end |
#vault ⇒ Object (readonly)
Returns the value of attribute vault.
243 244 245 |
# File 'lib/bitferry.rb', line 243 def vault @vault end |
Class Method Details
.[](tag) ⇒ Object
246 247 248 249 |
# File 'lib/bitferry.rb', line 246 def self.[](tag) @@registry.each_value { |volume| return volume if volume.tag == tag } nil end |
.delete(*tags, wipe: false) ⇒ Object
286 287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/bitferry.rb', line 286 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
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
# File 'lib/bitferry.rb', line 354 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
479 |
# File 'lib/bitferry.rb', line 479 def self.intact = registered.filter { |volume| volume.intact? } |
.lookup(*tags) ⇒ Object
Return list of registered volumes whose tags match at least one specified partial
253 |
# File 'lib/bitferry.rb', line 253 def self.lookup(*) = match(, registered) |
.match(tags, volumes) ⇒ Object
256 257 258 259 260 261 |
# File 'lib/bitferry.rb', line 256 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
264 265 266 267 268 |
# File 'lib/bitferry.rb', line 264 def self.new(root, **opts) volume = allocate volume.send(:create, root, **opts) register(volume) end |
.register(volume) ⇒ Object
473 |
# File 'lib/bitferry.rb', line 473 def self.register(volume) = @@registry[volume.root] = volume |
.registered ⇒ Object
476 |
# File 'lib/bitferry.rb', line 476 def self.registered = @@registry.values |
.reset ⇒ Object
470 |
# File 'lib/bitferry.rb', line 470 def self.reset = @@registry = {} |
.restore(root) ⇒ Object
271 272 273 274 275 276 277 278 279 280 281 282 283 |
# File 'lib/bitferry.rb', line 271 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
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 |
# File 'lib/bitferry.rb', line 333 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
395 396 397 398 399 |
# File 'lib/bitferry.rb', line 395 def committed x = tasks.collect { |t| t.generation }.min @generation = x ? x : 0 @modified = false end |
#create(*args, **opts) ⇒ Object
311 312 313 314 315 |
# File 'lib/bitferry.rb', line 311 def create(*args, **opts) initialize(*args, **opts) @state = :pristine @modified = true end |
#delete(wipe: false) ⇒ Object
387 388 389 390 391 392 |
# File 'lib/bitferry.rb', line 387 def delete(wipe: false) touch @wipe = wipe @state = :removing log.info("marked volume #{tag} for deletion") end |
#endpoint(path = String.new) ⇒ Object
371 |
# File 'lib/bitferry.rb', line 371 def endpoint(path = String.new) = Endpoint::Bitferry.new(self, path) |
#externalize ⇒ Object
448 449 450 451 452 453 454 455 456 457 458 |
# File 'lib/bitferry.rb', line 448 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
420 421 422 423 424 425 426 427 428 429 430 |
# File 'lib/bitferry.rb', line 420 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
377 |
# File 'lib/bitferry.rb', line 377 def intact? = @state != :removing |
#intact_tasks ⇒ Object
467 |
# File 'lib/bitferry.rb', line 467 def intact_tasks = live_tasks.filter { |task| task.intact? } |
#live_tasks ⇒ Object
464 |
# File 'lib/bitferry.rb', line 464 def live_tasks = Task.live.filter { |task| task.refers?(self) } |
#modified? ⇒ Boolean
374 |
# File 'lib/bitferry.rb', line 374 def modified? = @modified || tasks.any? { |t| t.generation > generation } |
#remove ⇒ Object
433 434 435 436 437 438 439 440 441 442 443 444 445 |
# File 'lib/bitferry.rb', line 433 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
318 319 320 321 322 323 324 325 326 |
# File 'lib/bitferry.rb', line 318 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
329 |
# File 'lib/bitferry.rb', line 329 def storage = @storage ||= root.join(STORAGE) |
#storage_ ⇒ Object
330 |
# File 'lib/bitferry.rb', line 330 def storage_ = @storage_ ||= root.join(STORAGE_) |
#store ⇒ Object
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/bitferry.rb', line 402 def store 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
461 |
# File 'lib/bitferry.rb', line 461 def tasks = Task.registered.filter { |task| task.refers?(self) } |
#touch ⇒ Object
380 381 382 383 384 |
# File 'lib/bitferry.rb', line 380 def touch x = tasks.collect { |t| t.generation }.max @generation = x ? x + 1 : 0 @modified = true end |