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.
289 290 291 292 293 294 295 296 |
# File 'lib/bitferry.rb', line 289 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.
225 226 227 |
# File 'lib/bitferry.rb', line 225 def generation @generation end |
#root ⇒ Object (readonly)
Returns the value of attribute root.
228 229 230 |
# File 'lib/bitferry.rb', line 228 def root @root end |
#tag ⇒ Object (readonly)
Returns the value of attribute tag.
222 223 224 |
# File 'lib/bitferry.rb', line 222 def tag @tag end |
#vault ⇒ Object (readonly)
Returns the value of attribute vault.
231 232 233 |
# File 'lib/bitferry.rb', line 231 def vault @vault end |
Class Method Details
.[](tag) ⇒ Object
234 235 236 237 |
# File 'lib/bitferry.rb', line 234 def self.[](tag) @@registry.each_value { |volume| return volume if volume.tag == tag } nil end |
.delete(*tags, wipe: false) ⇒ Object
274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/bitferry.rb', line 274 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
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/bitferry.rb', line 342 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
469 |
# File 'lib/bitferry.rb', line 469 def self.intact = registered.filter { |volume| volume.intact? } |
.lookup(*tags) ⇒ Object
Return list of registered volumes whose tags match at least one specified partial
241 |
# File 'lib/bitferry.rb', line 241 def self.lookup(*) = match(, registered) |
.match(tags, volumes) ⇒ Object
244 245 246 247 248 249 |
# File 'lib/bitferry.rb', line 244 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
252 253 254 255 256 |
# File 'lib/bitferry.rb', line 252 def self.new(root, **opts) volume = allocate volume.send(:create, root, **opts) register(volume) end |
.register(volume) ⇒ Object
463 |
# File 'lib/bitferry.rb', line 463 def self.register(volume) = @@registry[volume.root] = volume |
.registered ⇒ Object
466 |
# File 'lib/bitferry.rb', line 466 def self.registered = @@registry.values |
.reset ⇒ Object
460 |
# File 'lib/bitferry.rb', line 460 def self.reset = @@registry = {} |
.restore(root) ⇒ Object
259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/bitferry.rb', line 259 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
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
# File 'lib/bitferry.rb', line 321 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
385 386 387 388 389 |
# File 'lib/bitferry.rb', line 385 def committed x = tasks.collect { |t| t.generation }.min @generation = x ? x : 0 @modified = false end |
#create(*args, **opts) ⇒ Object
299 300 301 302 303 |
# File 'lib/bitferry.rb', line 299 def create(*args, **opts) initialize(*args, **opts) @state = :pristine @modified = true end |
#delete(wipe: false) ⇒ Object
377 378 379 380 381 382 |
# File 'lib/bitferry.rb', line 377 def delete(wipe: false) touch @wipe = wipe @state = :removing log.info("marked volume #{tag} for deletion") end |
#endpoint(path = String.new) ⇒ Object
361 |
# File 'lib/bitferry.rb', line 361 def endpoint(path = String.new) = Endpoint::Bitferry.new(self, path) |
#externalize ⇒ Object
438 439 440 441 442 443 444 445 446 447 448 |
# File 'lib/bitferry.rb', line 438 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
410 411 412 413 414 415 416 417 418 419 420 |
# File 'lib/bitferry.rb', line 410 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
367 |
# File 'lib/bitferry.rb', line 367 def intact? = @state != :removing |
#intact_tasks ⇒ Object
457 |
# File 'lib/bitferry.rb', line 457 def intact_tasks = live_tasks.filter { |task| task.intact? } |
#live_tasks ⇒ Object
454 |
# File 'lib/bitferry.rb', line 454 def live_tasks = Task.live.filter { |task| task.refers?(self) } |
#modified? ⇒ Boolean
364 |
# File 'lib/bitferry.rb', line 364 def modified? = @modified || tasks.any? { |t| t.generation > generation } |
#remove ⇒ Object
423 424 425 426 427 428 429 430 431 432 433 434 435 |
# File 'lib/bitferry.rb', line 423 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
306 307 308 309 310 311 312 313 314 |
# File 'lib/bitferry.rb', line 306 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
317 |
# File 'lib/bitferry.rb', line 317 def storage = @storage ||= root.join(STORAGE) |
#storage_ ⇒ Object
318 |
# File 'lib/bitferry.rb', line 318 def storage_ = @storage_ ||= root.join(STORAGE_) |
#store ⇒ Object
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
# File 'lib/bitferry.rb', line 392 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
451 |
# File 'lib/bitferry.rb', line 451 def tasks = Task.registered.filter { |task| task.refers?(self) } |
#touch ⇒ Object
370 371 372 373 374 |
# File 'lib/bitferry.rb', line 370 def touch x = tasks.collect { |t| t.generation }.max @generation = x ? x + 1 : 0 @modified = true end |