class Sgfa::BinderFs

An implementation of {Binder} using file system storage, {LockFs}, and {JacketFs}.

Public Instance Methods

close() click to toggle source

Close the binder

@return [BinderFs] self

# File lib/sgfa/binder_fs.rb, line 138
def close()
  raise Error::Sanity, 'Binder not open' if !@path
  @lock.close
  @lock = nil
  @id_hash = nil
  @id_text = nil
  @path = nil
  return self
end
create(path, tr, init) click to toggle source

Create a new binder

@param path [String] Path to the binder @param tr (see Binder#jacket_create) @param init [Hash] The binder initialization options @return [String] The hash ID of the new binder

# File lib/sgfa/binder_fs.rb, line 80
def create(path, tr, init)
  raise Error::Sanity, 'Binder already open' if @path
  Binder.limits_create(init)

  # create completely empty binder
  id_hash = create_raw(path, init[:id_text])

  # do the common creation
  @path = path
  @id_hash = id_hash
  @jackets = {}
  @users = {}
  @values = {}
  jck = _jacket_open(0)
  _create(jck, tr, init)
  jck.close
  _cache_write
  @path = nil
  @id_hash = nil

  return id_hash
end
create_raw(path, id_text) click to toggle source

Create a raw binder

@note You almost certainly want to use {#create} not this method

@param path [String] Path to the binder @param id_text [String] The text ID @return [String] The hash ID

# File lib/sgfa/binder_fs.rb, line 36
def create_raw(path, id_text)
  
  # create directory
  begin
    Dir.mkdir(path)
  rescue Errno::EEXIST
    raise Error::Conflict, 'Binder path already exists'
  end

  # create control binder
  dn_ctrl = File.join(path, '0')
  id_hash = JacketFs.create(dn_ctrl, id_text)

  # write info
  info = {
    'sgfa_binder_ver' => 1,
    'id_hash' => id_hash,
    'id_text' => id_text,
  }
  json = JSON.pretty_generate(info) + "\n"
  fn_info = File.join(path, 'sgfa_binder.json')
  File.open(fn_info, 'w', :encoding => 'utf-8'){|fi| fi.write json }
  
  # create empty cache
  @path = path
  @id_hash = id_hash
  @jackets = {}
  @users = {}
  @values = {}
  _cache_write
  @path = nil
  @id_hash = nil

  return id_hash    
end
open(path) click to toggle source

Open a binder

@param path [String] Path to the binder @return [BinderFs] self

# File lib/sgfa/binder_fs.rb, line 109
def open(path)
  raise Error::Sanity, 'Binder already open' if @path

  @lock = LockFs.new
  begin
    json = @lock.open(File.join(path, 'sgfa_binder.json'))
  rescue Errno::ENOENT
    raise Error::NonExistent, 'Binder does not exist'
  end
  begin
    info = JSON.parse(json, :symbolize_names => true)
  rescue JSON::NestingError, JSON::ParserError
    @lock.close
    @lock = nil
    raise Error::Corrupt, 'Binder info corrupt'
  end

  @id_hash = info[:id_hash]
  @id_text = info[:id_text]
  @path = path.dup
  
  return self
end
temp() click to toggle source

Temporary file to write attachments to

@return [Tempfile] Temporary file

# File lib/sgfa/binder_fs.rb, line 153
def temp()
  raise Error::Sanity, 'Binder not open' if !@path
  Tempfile.new('blob', @path, :encoding => 'utf-8')
end

Private Instance Methods

_cache_read() click to toggle source

Read cache

# File lib/sgfa/binder_fs.rb, line 213
def _cache_read
  fnc = File.join(@path, 'cache.json')
  json = nil
  info = nil
  
  begin
    json = File.read(fnc, :encoding => 'utf-8')
    info = JSON.parse(json, :symbolize_names => true)
  rescue Error::ENOENT
    raise Error::Corrupt, 'Binder cache does not exist'
  rescue JSON::NestingError, JSON::ParserError
    raise Error::Corrupt, 'Binder cache does not parse'
  end

  @jackets = {}
  info[:jackets].each{|hash| @jackets[hash[:name]] = hash }
  @users = {}
  info[:users].each{|ha| @users[ha[:name]] = ha[:perms] }
  @values = {}
  info[:values].each{|val, sta| @values[val] = sta }
end
_cache_write() click to toggle source

Write cache

# File lib/sgfa/binder_fs.rb, line 192
def _cache_write
  jck = []
  @jackets.each{|nam, hash| jck.push hash}
  vals = []
  @values.each{|val, sta| vals.push [val, sta]}
  usrs = []
  @users.each{|un, pl| usrs.push({name: un, perms: pl}) }
  info = {
    jackets: jck,
    values: vals,
    users: usrs,
  }
  json = JSON.pretty_generate(info) + "\n"
  
  fnc = File.join(@path, 'cache.json')
  File.open(fnc, 'w', :encoding => 'utf-8'){|fi| fi.write json}
end
_jacket_create(num) click to toggle source

Create a jacket

# File lib/sgfa/binder_fs.rb, line 164
def _jacket_create(num)
  jp = File.join(@path, num.to_s)
  id_text = 'binder %s jacket %d %s' % 
    [@id_hash, num, Time.now.utc.strftime('%F %T')]
  id_hash = JacketFs.create(jp, id_text)
  [id_text, id_hash]
end
_jacket_create_raw(info) click to toggle source

Create a jacket from backup

# File lib/sgfa/binder_fs.rb, line 175
def _jacket_create_raw(info)
  jp = File.join(@path, info[:num].to_s)
  JacketFs.create_raw(jp, info[:id_text], info[:id_hash])
end
_jacket_open(num) click to toggle source

Open a jacket

# File lib/sgfa/binder_fs.rb, line 183
def _jacket_open(num)
  jck = JacketFs.new
  jck.open(File.join(@path, num.to_s))
  return jck
end