Module: Bitferry

Extended by:
Logging
Includes:
Logging
Defined in:
lib/bitferry.rb

Defined Under Namespace

Modules: Logging, Rclone, Restic Classes: Endpoint, Task, Volume

Constant Summary collapse

VERSION =
'0.0.6'
PATH_LIST_SEPARATOR =

Specify OS-specific path name list separator (such as in the $PATH environment variable)

windows? ? ';' : ':'
UNIX_SYSTEM_MOUNTS =

Match OS-specific system mount points (/dev /proc etc.) which normally should be omitted when scanning for Bitferry voulmes

%r!^/(dev|sys|proc|efi)!

Class Method Summary collapse

Methods included from Logging

log, log

Class Method Details

.commitObject



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/bitferry.rb', line 59

def self.commit
  log.info('committing changes')
  result = true
  modified = false
  Volume.registered.each do |volume|
    begin
      modified = true if volume.modified?
      volume.commit
    rescue IOError => e
       log.error(e.message)
       result = false
    end
  end
  if result
    log.info(modified ? 'changes committed' : 'commits skipped (no changes)')
  else
    log.warn('commit failure(s) reported')
  end
  result
end

.endpoint(root) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/bitferry.rb', line 123

def self.endpoint(root)
  case root
    when /^:(\w+):(.*)/
      volumes = Volume.lookup($1)
      volume = case volumes.size
        when 0 then raise ArgumentError, "no intact volume matching (partial) tag #{$1}"
        when 1 then volumes.first
        else
          tags = volumes.collect { |v| v.tag }.join(', ')
          raise ArgumentError, "multiple intact volumes matching (partial) tag #{$1}: #{tags}"
      end
      Endpoint::Bitferry.new(volume, $2)
    when /^(?:local)?:(.*)/ then Endpoint::Local.new($1)
    when /^(\w{2,}):(.*)/ then Endpoint::Rclone.new($1, $2)
    else Volume.endpoint(root)
  end
end

.environment_mountsObject

Return list of live user-provided mounts (mount points on *NIX and disk drives on Windows) which may contain Bitferry volumes Look for the $BITFERRY_PATH environment variable



159
160
161
# File 'lib/bitferry.rb', line 159

def self.environment_mounts
  ENV['BITFERRY_PATH'].split(PATH_LIST_SEPARATOR).collect { |path| File.directory?(path) ? path : nil }.compact rescue []
end

.intact_tasksObject



88
# File 'lib/bitferry.rb', line 88

def self.intact_tasks = Volume.intact.collect { |volume| volume.intact_tasks }.flatten.uniq

.optional(option, route) ⇒ Object



485
486
487
488
489
490
491
492
# File 'lib/bitferry.rb', line 485

def self.optional(option, route)
  case option
  when Array then option # Array is passed verbatim
  when '-' then nil # Disable adding any options with -
  when /^-/ then option.split(',') # Split comma-separated string into array --foo,bar --> [--foo, bar]
  else route.fetch(option.nil? ? nil : option.to_sym) # Obtain options from the profile database
  end
end

.process(*tags, &block) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/bitferry.rb', line 90

def self.process(*tags, &block)
  log.info('processing tasks')
  tasks = intact_tasks
  if tags.empty?
    process = tasks
  else
    process = []
    tags.each do |tag|
      case (tasks = Task.match([tag], tasks)).size
        when 0 then log.warn("no tasks matching (partial) tag #{tag}")
        when 1 then process += tasks
        else
          tags = tasks.collect { |v| v.tag }.join(', ')
          raise ArgumentError, "multiple tasks matching (partial) tag #{tag}: #{tags}"
      end
    end
  end
  tasks = process.uniq
  total = tasks.size
  processed = 0
  failed = 0
  result = tasks.all? do |task|
    r = task.process
    processed += 1
    failed += 1 unless r
    yield(total, processed, failed) if block_given?
    r
  end
  result ? log.info('tasks processed') : log.warn('task process failure(s) reported')
  result
end

.resetObject



81
82
83
84
85
# File 'lib/bitferry.rb', line 81

def self.reset
  log.info('resetting state')
  Volume.reset
  Task.reset
end

.restoreObject



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/bitferry.rb', line 38

def self.restore
  reset
  log.info('restoring volumes')
  result = true
  roots = (environment_mounts + system_mounts + [Dir.home]).uniq
  log.info("distilled volume search path: #{roots.join(', ')}")
  roots.each do |root|
    if File.exist?(File.join(root, Volume::STORAGE))
      log.info("trying to restore volume from #{root}")
      Volume.restore(root) rescue result = false
    end
  end
  if result
    log.info('volumes restored')
  else
    log.warn('volume restore failure(s) reported')
  end
  result
end

.simulate=(mode) ⇒ Object



144
# File 'lib/bitferry.rb', line 144

def self.simulate=(mode) @simulate = mode end

.simulate?Boolean

Returns:

  • (Boolean)


143
# File 'lib/bitferry.rb', line 143

def self.simulate? = @simulate

.system_mountsObject



175
176
177
# File 'lib/bitferry.rb', line 175

def self.system_mounts
  java.nio.file.FileSystems.getDefault.getFileStores.collect {|x| /^(.*)\s+\(.*\)$/.match(x.to_s)[1]}
end

.tagObject



35
# File 'lib/bitferry.rb', line 35

def self.tag = format('%08x', 2**32*rand)

.verbosityObject



148
# File 'lib/bitferry.rb', line 148

def self.verbosity = @verbosity

.verbosity=(mode) ⇒ Object



149
# File 'lib/bitferry.rb', line 149

def self.verbosity=(mode) @verbosity = mode end

.windows?Boolean

Return true if run in the real Windows environment (e.g. not in real *NIX or various emulation layers such as MSYS, Cygwin etc.)

Returns:

  • (Boolean)


153
154
155
# File 'lib/bitferry.rb', line 153

def self.windows?
  @windows ||= /^(mingw)/.match?(RbConfig::CONFIG['target_os']) # RubyInstaller's MRI, other MinGW-build MRI
end