class Lingo::Database

Constants

BACKENDS
BACKEND_BY_EXT
FLD_SEP
KEY_REF
KEY_REF_RE
SYS_KEY

Attributes

backend[R]
config[R]
lingo[R]

Public Class Methods

backend_by_ext(file, ext = File.extname(file)) click to toggle source
# File lib/lingo/database.rb, line 62
def backend_by_ext(file, ext = File.extname(file))
  get_backend(BACKEND_BY_EXT[ext], file) or
    raise BackendNotFoundError.new(file)
end
find_backend(env = 'LINGO_BACKEND') click to toggle source
# File lib/lingo/database.rb, line 67
def find_backend(env = 'LINGO_BACKEND')
  env && get_backend(ENV[env]) || BACKENDS.find { |name|
    backend = get_backend(name, nil, true) and return backend }
end
get_backend(name, file = nil, relax = false) click to toggle source
# File lib/lingo/database.rb, line 72
def get_backend(name, file = nil, relax = false)
  return unless name

  Object.const_get(name)
  const_get("#{name}Store")
rescue TypeError, NameError => err
  raise BackendNotAvailableError.new(name, file, err) unless relax
end
new(id, lingo) click to toggle source
# File lib/lingo/database.rb, line 87
def initialize(id, lingo)
  @id, @lingo, @config, @db = id, lingo, lingo.database_config(id), nil

  @val, @crypt, @srcfile = Hash.array, config.key?('crypt'),
    Lingo.find(:dict, config['name'], relax: true)

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

  extend(@backend = backend || self.class.find_backend || HashStore)

  @stofile << store_ext unless skip_ext || !respond_to?(:store_ext)

  convert unless uptodate?
end
open(*args, &block) click to toggle source
# File lib/lingo/database.rb, line 81
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 54
def register(klass, ext, prio = -1, meth = true)
  BACKENDS.insert(prio, name = klass.name[/::(\w+)Store\z/, 1])
  Array(ext).each { |i| BACKEND_BY_EXT[i.insert(0, '.')] = 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 142
def [](key)
  val = _val(key) unless closed?
  val.split(FLD_SEP) if val
end
[]=(key, val) click to toggle source
# File lib/lingo/database.rb, line 147
def []=(key, val)
  return if closed?

  val = @val[key].concat(val)
  val.uniq!

  val = val.join(FLD_SEP)
  @crypt ? _set(*Crypter.encode(key, val)) : _set(key, val)
end
close() click to toggle source
# File lib/lingo/database.rb, line 125
def close
  _close unless closed?
  @db = nil

  self
end
closed?() click to toggle source
# File lib/lingo/database.rb, line 112
def closed?
  !@db || _closed?
end
each() { |_encode!(key), _encode!(val)| ... } click to toggle source
# File lib/lingo/database.rb, line 138
def each
  _each { |key, val| yield _encode!(key), _encode!(val) } unless closed?
end
open() { |self| ... } click to toggle source
# File lib/lingo/database.rb, line 116
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 132
def to_h
  hash = {}
  each { |key, val| hash[key.freeze] = val }
  hash
end
warn(*msg) click to toggle source
# File lib/lingo/database.rb, line 157
def warn(*msg)
  lingo.warn(*msg)
end

Private Instance Methods

_clear() click to toggle source
# File lib/lingo/database.rb, line 195
def _clear
  File.delete(@stofile) if File.exist?(@stofile)
end
_close() click to toggle source
# File lib/lingo/database.rb, line 203
def _close
  @db.close
end
_closed?() click to toggle source
# File lib/lingo/database.rb, line 207
def _closed?
  @db.closed?
end
_each() { |key, val| ... } click to toggle source
# File lib/lingo/database.rb, line 211
def _each
  @db.each { |key, val| yield key, val }
end
_encode!(str) click to toggle source
# File lib/lingo/database.rb, line 230
def _encode!(str)
  str.force_encoding(ENCODING)
end
_get(key) click to toggle source
# File lib/lingo/database.rb, line 219
def _get(key)
  @db[key]
end
_open() click to toggle source
# File lib/lingo/database.rb, line 199
def _open
  raise NotImplementedError
end
_set(key, val) click to toggle source
# File lib/lingo/database.rb, line 215
def _set(key, val)
  @db[key] = val
end
_val(key) click to toggle source
# File lib/lingo/database.rb, line 223
def _val(key)
  if val = _get(@crypt ? Crypter.digest(key) : key)
    _encode!(val)
    @crypt ? Crypter.decode(key, val) : val
  end
end
config_hash() click to toggle source
# File lib/lingo/database.rb, line 163
def config_hash
  hashes = [config]

  if use_lex = config['use-lex']
    hashes.concat(lingo.
      dictionary_config['databases'].
      values_at(*use_lex.split(SEP_RE)))
  end

  Crypter.digest(hashes.inspect)
end
convert(verbose = lingo.config.stderr.tty?) click to toggle source
# File lib/lingo/database.rb, line 234
def convert(verbose = lingo.config.stderr.tty?)
  src = Source.from_config(config, @id)

  sep, hyphenate, key_map, val_map = prepare_lex

  Progress.new(self, src, verbose) { |progress| create {
    src.each { |key, val|
      progress << src.pos

      set_key(src, key, val, sep) { |keys, cnt|
        key = keys.map!(&key_map).join(sep)
        val = val.map { |v| val_map[v.split(sep)].join(sep) } if val_map

        hyphenate.repeated_permutation(cnt - 1) { |h| set_key(src, keys.
          zip(h).join, val, sep) unless h.uniq.size == 1 } if hyphenate

        [key, val]
      }
    }

    uptodate!
  } }
end
create() { || ... } click to toggle source
# File lib/lingo/database.rb, line 190
def create
  _clear
  open { yield }
end
prepare_lex() click to toggle source
# File lib/lingo/database.rb, line 272
def prepare_lex
  use_lex = config['use-lex'] or return

  args = [{
    'source' => use_lex.split(SEP_RE),
    'mode'   => config['lex-mode']
  }, lingo]

  dic = Language::Dictionary.new(*args)
  gra = Language::Grammar.new(*args)

  args = nil

  wac = Language::WA_COMPOUND
  lac = Language::LA_COMPOUND

  if inflect = config['inflect']
    inflect, wc = inflect == true ? %w[s e] : inflect.split(SEP_RE), 'a'

    if cfg = lingo.dictionary_config['inflect'] and suffixes = cfg[wc]
      wc, re = /#{wc}/, /\A[^#]+/
    else
      warn "#{self.class}: No suffixes to inflect ##{wc}: #{@id}"
      inflect = false
    end
  end

  [sep = ' ', config['hyphenate'] && [sep, '-'], lambda { |form|
    word = dic.find_word(form)

    if word.unknown?
      comp = gra.find_compound(form)

      comp.attr == wac && comp.lex_form(lac) ||
        (comp.identified? ? comp.lex_form : comp.form)
    else
      word.identified? ? word.lex_form : word.form
    end
  }, inflect && lambda { |forms|
    inflectables = []

    forms.each { |form|
      word = dic.find_word(word_form = form[re])

      if word.identified? && _form = word.lex_form(wc)
        inflectables << form if form == _form
      else
        unless inflectables.empty?
          word = gra.find_compound_head(word_form) || word if word.unknown?

          if word.attr?(*inflect) && suffix =
            suffixes[word.genders.compact.first]
            inflectables.each { |lex_form| lex_form << suffix }
          end
        end

        break forms
      end
    }
  }]
end
set_key(src, key, val, sep, len = 3) { |keys, cnt| ... } click to toggle source
# File lib/lingo/database.rb, line 258
def set_key(src, key, val, sep, len = 3)
  if key
    key.chomp!('.')

    if sep && key.include?(sep)
      keys = key.split(sep); cnt = keys.size
      key, val = yield keys, cnt if block_given?
      self[keys[0, len].join(sep)] = ["#{KEY_REF}#{cnt}"] if cnt > len
    end
  end

  src.set(self, key, val)
end
uptodate!() click to toggle source
# File lib/lingo/database.rb, line 186
def uptodate!
  @db[SYS_KEY] = @source_key.call
end
uptodate?(file = @stofile) click to toggle source
# File lib/lingo/database.rb, line 175
def uptodate?(file = @stofile)
  src = Pathname.new(@srcfile)

  @source_key = lambda {
    [src.size, src.mtime, VERSION, config_hash].join(FLD_SEP)
  }

  sys_key = open { @db[SYS_KEY] } if File.exist?(file)
  sys_key && (!src.exist? || sys_key == @source_key.call)
end