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.
290 291 292 293 294 295 296 297 |
# File 'lib/bitferry.rb', line 290 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.
226 227 228 |
# File 'lib/bitferry.rb', line 226 def generation @generation end |
#root ⇒ Object (readonly)
Returns the value of attribute root.
229 230 231 |
# File 'lib/bitferry.rb', line 229 def root @root end |
#tag ⇒ Object (readonly)
Returns the value of attribute tag.
223 224 225 |
# File 'lib/bitferry.rb', line 223 def tag @tag end |
#vault ⇒ Object (readonly)
Returns the value of attribute vault.
232 233 234 |
# File 'lib/bitferry.rb', line 232 def vault @vault end |
Class Method Details
.[](tag) ⇒ Object
235 236 237 238 |
# File 'lib/bitferry.rb', line 235 def self.[](tag) @@registry.each_value { |volume| return volume if volume.tag == tag } nil end |
.delete(*tags, wipe: false) ⇒ Object
275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/bitferry.rb', line 275 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
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
# File 'lib/bitferry.rb', line 343 def self.endpoint(root) path = Pathname.new(root).realdirpath # FIXME select innermost or outermost volume in case of nested volumes? intact.sort { |v1, v2| v2.root.size <=> v1.root.size }.each do |volume| begin # FIXME chop trailing slashes stem = path.relative_path_from(volume.root) case stem.to_s 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
470 |
# File 'lib/bitferry.rb', line 470 def self.intact = registered.filter { |volume| volume.intact? } |
.lookup(*tags) ⇒ Object
Return list of registered volumes whose tags match at least one specified partial
242 |
# File 'lib/bitferry.rb', line 242 def self.lookup(*) = match(, registered) |
.match(tags, volumes) ⇒ Object
245 246 247 248 249 250 |
# File 'lib/bitferry.rb', line 245 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
253 254 255 256 257 |
# File 'lib/bitferry.rb', line 253 def self.new(root, **opts) volume = allocate volume.send(:create, root, **opts) register(volume) end |
.register(volume) ⇒ Object
464 |
# File 'lib/bitferry.rb', line 464 def self.register(volume) = @@registry[volume.root] = volume |
.registered ⇒ Object
467 |
# File 'lib/bitferry.rb', line 467 def self.registered = @@registry.values |
.reset ⇒ Object
461 |
# File 'lib/bitferry.rb', line 461 def self.reset = @@registry = {} |
.restore(root) ⇒ Object
260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/bitferry.rb', line 260 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
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# File 'lib/bitferry.rb', line 322 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
386 387 388 389 390 |
# File 'lib/bitferry.rb', line 386 def committed x = tasks.collect { |t| t.generation }.min @generation = x ? x : 0 @modified = false end |
#create(*args, **opts) ⇒ Object
300 301 302 303 304 |
# File 'lib/bitferry.rb', line 300 def create(*args, **opts) initialize(*args, **opts) @state = :pristine @modified = true end |
#delete(wipe: false) ⇒ Object
378 379 380 381 382 383 |
# File 'lib/bitferry.rb', line 378 def delete(wipe: false) touch @wipe = wipe @state = :removing log.info("marked volume #{tag} for deletion") end |
#endpoint(path = String.new) ⇒ Object
362 |
# File 'lib/bitferry.rb', line 362 def endpoint(path = String.new) = Endpoint::Bitferry.new(self, path) |
#externalize ⇒ Object
439 440 441 442 443 444 445 446 447 448 449 |
# File 'lib/bitferry.rb', line 439 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
411 412 413 414 415 416 417 418 419 420 421 |
# File 'lib/bitferry.rb', line 411 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
368 |
# File 'lib/bitferry.rb', line 368 def intact? = @state != :removing |
#intact_tasks ⇒ Object
458 |
# File 'lib/bitferry.rb', line 458 def intact_tasks = live_tasks.filter { |task| task.intact? } |
#live_tasks ⇒ Object
455 |
# File 'lib/bitferry.rb', line 455 def live_tasks = Task.live.filter { |task| task.refers?(self) } |
#modified? ⇒ Boolean
365 |
# File 'lib/bitferry.rb', line 365 def modified? = @modified || tasks.any? { |t| t.generation > generation } |
#remove ⇒ Object
424 425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/bitferry.rb', line 424 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
307 308 309 310 311 312 313 314 315 |
# File 'lib/bitferry.rb', line 307 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
318 |
# File 'lib/bitferry.rb', line 318 def storage = @storage ||= root.join(STORAGE) |
#storage_ ⇒ Object
319 |
# File 'lib/bitferry.rb', line 319 def storage_ = @storage_ ||= root.join(STORAGE_) |
#store ⇒ Object
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 |
# File 'lib/bitferry.rb', line 393 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
452 |
# File 'lib/bitferry.rb', line 452 def tasks = Task.registered.filter { |task| task.refers?(self) } |
#touch ⇒ Object
371 372 373 374 375 |
# File 'lib/bitferry.rb', line 371 def touch x = tasks.collect { |t| t.generation }.max @generation = x ? x + 1 : 0 @modified = true end |