class Lingo::Database

Die Klasse Database stellt eine einheitliche Schnittstelle auf Lingo-Datenbanken bereit. Die Identifizierung der Datenbank erfolgt über die ID der Datenbank, so wie sie in der Sprachkonfigurationsdatei de.lang unter language/dictionary/databases hinterlegt ist.

Das Lesen und Schreiben der Datenbank erfolgt über die Funktionen []() und []=().

Constants

BACKENDS
BACKEND_BY_EXT
FLD_SEP
IDX_REF
IDX_REF_ESC
INDEX_PATTERN
KEY_REF
KEY_REF_ESC
SYS_KEY

Attributes

backend[R]

Public Class Methods

new(id, lingo) click to toggle source
# File lib/lingo/database.rb, line 74
def initialize(id, lingo)
  @id, @lingo, @config, @db = id, lingo, lingo.database_config(id), nil

  @srcfile = Lingo.find(:dict, @config['name'], relax: true)
  @crypter = @config.has_key?('crypt') && Crypter.new

  begin
    @stofile = Lingo.find(:store, @srcfile)
    FileUtils.mkdir_p(File.dirname(@stofile))
  rescue SourceFileNotFoundError => err
    @stofile = skip_ext = err.id
    backend = backend_from_file(@stofile) unless err.name
  rescue NoWritableStoreError
    backend = HashStore
  end

  use_backend(backend, skip_ext)
  init_cachable

  convert unless uptodate?
end
open(*args, &block) click to toggle source
# File lib/lingo/database.rb, line 66
def open(*args, &block)
  new(*args).open(&block)
end
register(klass, ext, prio = -1, meth = true) click to toggle source
# File lib/lingo/database.rb, line 58
def register(klass, ext, prio = -1, meth = true)
  BACKENDS.insert(prio, name = klass.name[%r::(\w+)Store\z/, 1])
  Array(ext).each { |i| BACKEND_BY_EXT[i.prepend('.')] = name }

  klass.const_set(:EXT, ext)
  klass.class_eval('def store_ext; EXT; end', __FILE__, __LINE__) if meth
end

Public Instance Methods

[](key) click to toggle source
# File lib/lingo/database.rb, line 122
def [](key)
  val = _val(key) unless closed?
  return unless val

  # Äquvalenzklassen behandeln

  val.split(FLD_SEP).map { |v|
    v =~ INDEX_PATTERN ? _val(v) : v
  }.compact.join(FLD_SEP).split(FLD_SEP)
end
[]=(key, val) click to toggle source
# File lib/lingo/database.rb, line 132
def []=(key, val)
  return if closed?

  val = val.dup
  val.concat(retrieve(key)) if hit?(key)

  val.sort!
  val.uniq!
  store(key, val)

  arg = [key, val.join(FLD_SEP)]
  _set(*@crypter ? @crypter.encode(*arg) : arg)
end
close() click to toggle source
# File lib/lingo/database.rb, line 109
def close
  @db.close unless closed?
  @db = nil

  self
end
closed?() click to toggle source
# File lib/lingo/database.rb, line 96
def closed?
  @db.nil? || _closed?
end
open() { |self| ... } click to toggle source
# File lib/lingo/database.rb, line 100
def open
  @db = _open if closed?
  block_given? ? yield(self) : self
rescue => err
  raise DatabaseError.new(:open, @stofile, err)
ensure
  close if @db && block_given?
end
to_h() click to toggle source
# File lib/lingo/database.rb, line 116
def to_h
  {}.tap { |hash| @db.each { |key, val|
    hash[key.force_encoding(ENC).freeze] = val.force_encoding(ENC)
  } unless closed? }
end

Private Instance Methods

_clear() click to toggle source
# File lib/lingo/database.rb, line 187
def _clear
  File.delete(@stofile) if File.exist?(@stofile)
end
_closed?() click to toggle source
# File lib/lingo/database.rb, line 195
def _closed?
  @db.closed?
end
_get(key) click to toggle source
# File lib/lingo/database.rb, line 203
def _get(key)
  @db[key]
end
_open() click to toggle source
# File lib/lingo/database.rb, line 191
def _open
  raise NotImplementedError
end
_set(key, val) click to toggle source
# File lib/lingo/database.rb, line 199
def _set(key, val)
  @db[key] = val
end
_val(key) click to toggle source
# File lib/lingo/database.rb, line 207
def _val(key)
  if val = _get(@crypter ? @crypter.digest(key) : key)
    val.force_encoding(ENC)
    @crypter ? @crypter.decode(key, val) : val
  end
end
backend_from_file(file) click to toggle source
# File lib/lingo/database.rb, line 163
def backend_from_file(file)
  ext = File.extname(file)

  mod = BACKEND_BY_EXT[ext] or raise BackendNotFoundError.new(file)
  get_backend(mod) or raise BackendNotAvailableError.new(mod, file)
end
convert(verbose = @lingo.config.stderr.tty?) click to toggle source
# File lib/lingo/database.rb, line 218
def convert(verbose = @lingo.config.stderr.tty?)
  src = Source.get(@config.fetch('txt-format', 'key_value'), @id, @lingo)

  if lex = @config['use-lex']
    a = [{ 'source' => lex.split(SEP_RE), 'mode' => @config['lex-mode'] }, @lingo]
    d, g = Language::Dictionary.new(*a), Language::Grammar.new(*a); a = nil

    sep, block = ' ', lambda { |f|
      (r = d.find_word(f)).unknown? &&
        (c = (r = g.find_compound(f)).compo_form) ? c.form : r.norm
    }
  end

  ShowProgress.new(self, src.size, verbose) { |progress| create {
    src.each { |key, val|
      progress[src.pos]

      if key
        key.chomp!('.')

        if lex && key.include?(sep)
          k = key.split(sep).map!(&block).join(sep)

          c = k.count(sep) + 1
          self[k.split(sep)[0, 3].join(sep)] = ["#{KEY_REF}#{c}"] if c > 3

          key, val = k, val.map { |v| v.start_with?('#') ? key + v : v }
        end
      end

      src.set(self, key, val)
    }

    uptodate!
  } }
end
create() { || ... } click to toggle source
# File lib/lingo/database.rb, line 182
def create
  _clear
  open { yield }
end
get_backend(mod) click to toggle source
# File lib/lingo/database.rb, line 158
def get_backend(mod)
  self.class.const_get("#{mod}Store") if Object.const_defined?(mod)
rescue TypeError, NameError
end
uptodate!() click to toggle source
# File lib/lingo/database.rb, line 178
def uptodate!
  @db[SYS_KEY] = @source_key.call
end
uptodate?(file = @stofile) click to toggle source
# File lib/lingo/database.rb, line 170
def uptodate?(file = @stofile)
  src = Pathname.new(@srcfile)
  @source_key = lambda { [src.size, src.mtime].join(FLD_SEP) }

  sys_key = open { @db[SYS_KEY] } if File.exist?(file)
  sys_key && (!src.exist? || sys_key == @source_key.call)
end
use_backend(backend = nil, skip_ext = false) click to toggle source
# File lib/lingo/database.rb, line 148
def use_backend(backend = nil, skip_ext = false)
  [ENV['LINGO_BACKEND'], *BACKENDS].each { |mod|
    backend = get_backend(mod) and break if mod
  } unless backend

  extend(@backend = backend || HashStore)

  @stofile << store_ext if !skip_ext && respond_to?(:store_ext)
end
warn(*msg) click to toggle source
# File lib/lingo/database.rb, line 214
def warn(*msg)
  @lingo.warn(*msg)
end