lingo 1.8.6 → 1.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ChangeLog +40 -4
- data/README +22 -51
- data/Rakefile +3 -17
- data/config/lingo.cfg +24 -15
- data/config/lir.cfg +25 -16
- data/dict/de/test_muh.txt +6 -0
- data/dict/en/lingo-dic.txt +2 -3
- data/lang/de.lang +10 -9
- data/lang/en.lang +1 -1
- data/lib/lingo.rb +4 -4
- data/lib/lingo/attendee.rb +27 -7
- data/lib/lingo/attendee/analysis_filter.rb +81 -0
- data/lib/lingo/attendee/debug_filter.rb +42 -0
- data/lib/lingo/attendee/debugger.rb +2 -11
- data/lib/lingo/attendee/decomposer.rb +6 -3
- data/lib/lingo/attendee/formatter.rb +6 -6
- data/lib/lingo/attendee/hal_filter.rb +94 -0
- data/lib/lingo/attendee/lsi_filter.rb +99 -0
- data/lib/lingo/attendee/multi_worder.rb +69 -43
- data/lib/lingo/attendee/sequencer.rb +32 -19
- data/lib/lingo/attendee/synonymer.rb +2 -2
- data/lib/lingo/attendee/text_reader.rb +63 -92
- data/lib/lingo/attendee/text_writer.rb +12 -21
- data/lib/lingo/attendee/tokenizer.rb +32 -21
- data/lib/lingo/attendee/variator.rb +3 -3
- data/lib/lingo/attendee/vector_filter.rb +7 -9
- data/lib/lingo/attendee/word_searcher.rb +3 -3
- data/lib/lingo/buffered_attendee.rb +3 -36
- data/lib/lingo/config.rb +1 -1
- data/lib/lingo/ctl.rb +7 -155
- data/lib/lingo/ctl/analysis.rb +136 -0
- data/lib/lingo/ctl/files.rb +86 -0
- data/lib/lingo/ctl/other.rb +140 -0
- data/lib/lingo/database.rb +64 -60
- data/lib/lingo/database/crypter.rb +7 -5
- data/lib/lingo/error.rb +5 -4
- data/lib/lingo/language.rb +13 -5
- data/lib/lingo/language/grammar.rb +13 -7
- data/lib/lingo/language/token.rb +6 -0
- data/lib/lingo/language/word.rb +23 -36
- data/lib/lingo/language/word_form.rb +5 -1
- data/lib/lingo/srv.rb +2 -2
- data/lib/lingo/text_utils.rb +96 -0
- data/lib/lingo/version.rb +1 -1
- data/lib/lingo/web/views/index.erb +1 -1
- data/test/attendee/ts_decomposer.rb +23 -5
- data/test/attendee/ts_multi_worder.rb +66 -0
- data/test/attendee/ts_sequencer.rb +28 -4
- data/test/attendee/ts_text_reader.rb +20 -0
- data/test/attendee/ts_tokenizer.rb +20 -0
- data/test/attendee/ts_variator.rb +1 -1
- data/test/attendee/ts_word_searcher.rb +39 -3
- data/test/lir3.txt +12 -0
- data/test/ref/artikel.non +1 -12
- data/test/ref/artikel.seq +3 -1
- data/test/ref/artikel.vec +1 -0
- data/test/ref/artikel.vef +35 -34
- data/test/ref/artikel.ven +8 -7
- data/test/ref/artikel.ver +34 -33
- data/test/ref/artikel.vet +2573 -2563
- data/test/ref/lir.non +77 -78
- data/test/ref/lir.seq +9 -7
- data/test/ref/lir.syn +1 -1
- data/test/ref/lir.vec +41 -41
- data/test/ref/lir.vef +210 -210
- data/test/ref/lir.ven +46 -46
- data/test/ref/lir.ver +72 -72
- data/test/ref/lir.vet +329 -329
- data/test/ts_database.rb +166 -62
- data/test/ts_language.rb +23 -23
- metadata +53 -34
- data/lib/lingo/attendee/dehyphenizer.rb +0 -120
- data/lib/lingo/attendee/noneword_filter.rb +0 -115
- data/test/attendee/ts_noneword_filter.rb +0 -15
@@ -0,0 +1,136 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
###############################################################################
|
5
|
+
# #
|
6
|
+
# Lingo -- A full-featured automatic indexing system #
|
7
|
+
# #
|
8
|
+
# Copyright (C) 2005-2007 John Vorhauer #
|
9
|
+
# Copyright (C) 2007-2015 John Vorhauer, Jens Wille #
|
10
|
+
# #
|
11
|
+
# Lingo is free software; you can redistribute it and/or modify it under the #
|
12
|
+
# terms of the GNU Affero General Public License as published by the Free #
|
13
|
+
# Software Foundation; either version 3 of the License, or (at your option) #
|
14
|
+
# any later version. #
|
15
|
+
# #
|
16
|
+
# Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
|
17
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
18
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
|
19
|
+
# more details. #
|
20
|
+
# #
|
21
|
+
# You should have received a copy of the GNU Affero General Public License #
|
22
|
+
# along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
|
23
|
+
# #
|
24
|
+
###############################################################################
|
25
|
+
#++
|
26
|
+
|
27
|
+
require 'csv'
|
28
|
+
|
29
|
+
class Lingo
|
30
|
+
|
31
|
+
module Ctl
|
32
|
+
|
33
|
+
{ stats: [:s, 'Extract statistics from analysis file(s)', 'path...'],
|
34
|
+
trans: [:t, 'Transpose columns and rows of analysis file(s)', 'path...']
|
35
|
+
}.each { |n, (s, *a)| cmd("analysis#{n}", "a#{s}", *a) }
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def do_analysisstats
|
40
|
+
require 'nuggets/array/histogram'
|
41
|
+
|
42
|
+
paths, write = paths_write
|
43
|
+
stats, patts = Hash.array(1), Hash.array(1)
|
44
|
+
|
45
|
+
csv_foreach(paths) { |path, _, token, word, pattern|
|
46
|
+
token ? stats[:tokens][path] << token : word ? begin
|
47
|
+
stats[:words][path] << word
|
48
|
+
patts[word][path] << pattern if pattern
|
49
|
+
end : nil
|
50
|
+
}
|
51
|
+
|
52
|
+
stats.each { |k, h| write.(k) { |csv|
|
53
|
+
csv << ['file', *c = columns(g = histograms(h))]
|
54
|
+
histograms_to_csv(csv, c, g)
|
55
|
+
h.values.map(&:size)
|
56
|
+
} }
|
57
|
+
|
58
|
+
write.(:patterns) { |csv|
|
59
|
+
csv << ['file', 'word', *c = columns(patts, :values)]
|
60
|
+
patts.sort.each { |k, h| histograms_to_csv(csv, c, histograms(h), k) }
|
61
|
+
c.size - 1
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def do_analysistrans
|
66
|
+
paths, write = paths_write
|
67
|
+
|
68
|
+
rows, comm, more, less = Hash.array(1), {}, Hash.array(1), Hash.array(1)
|
69
|
+
|
70
|
+
csv_foreach(paths) { |path, string, token, word, _|
|
71
|
+
rows[token || word][path] << string }
|
72
|
+
|
73
|
+
c = rows.keys.sort.each { |k|
|
74
|
+
a = (h = rows[k]).first.last
|
75
|
+
|
76
|
+
paths.size == 1 ? comm[k] = a : begin
|
77
|
+
comm[k] = a & (o = h.drop(1)).flat_map(&:last)
|
78
|
+
o.each { |path, b| more[path][k] = b - a; less[path][k] = a - b }
|
79
|
+
end
|
80
|
+
}
|
81
|
+
|
82
|
+
rows.clear
|
83
|
+
|
84
|
+
write.(:transpose) { |csv| transpose_csv(csv, c, comm) }
|
85
|
+
|
86
|
+
{ transmore: more, transless: less }.each { |k, v| v.each { |path, h|
|
87
|
+
csv_writer(path, *paths).(k) { |csv| transpose_csv(csv, c, h) } } }
|
88
|
+
end
|
89
|
+
|
90
|
+
def paths_write
|
91
|
+
ARGV.empty? ? missing_arg(:path) : [a = ARGV.each { |x|
|
92
|
+
abort "No such file: #{x}" unless File.exist?(x) }, csv_writer(*a)]
|
93
|
+
end
|
94
|
+
|
95
|
+
def csv_writer(*paths)
|
96
|
+
name = File.join(File.dirname(paths.first), paths.map { |path|
|
97
|
+
File.basename(path.chomp(File.extname(path))) }.uniq.join('-'))
|
98
|
+
|
99
|
+
lambda { |key, &block| overwrite?(file = "#{name}.#{key}.csv") &&
|
100
|
+
puts("#{file}: #{Array(CSV.open(file, 'wb', &block)).join(' / ')}") }
|
101
|
+
end
|
102
|
+
|
103
|
+
def csv_foreach(paths)
|
104
|
+
paths.each { |path| CSV.foreach(path, headers: true) { |row|
|
105
|
+
yield path, *row.values_at(*%w[string token word pattern]) } }
|
106
|
+
end
|
107
|
+
|
108
|
+
def columns(hash, map = :keys)
|
109
|
+
hash.values.map(&map).flatten.uniq.sort
|
110
|
+
end
|
111
|
+
|
112
|
+
def histograms(hash)
|
113
|
+
hash.each_with_object({}) { |(k, v), h|
|
114
|
+
h[k] = v.histogram.tap { |x| x.default = nil } }
|
115
|
+
end
|
116
|
+
|
117
|
+
def histograms_to_csv(csv, columns, histograms, *args)
|
118
|
+
histograms.each { |key, histogram|
|
119
|
+
others = histograms.values_at(*histograms.keys - [key])
|
120
|
+
others = [{}] if others.empty?
|
121
|
+
|
122
|
+
csv << args.dup.unshift(key).concat(columns.map { |header|
|
123
|
+
value = histogram[header] or next
|
124
|
+
value if others.any? { |other| other[header] != value }
|
125
|
+
})
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
def transpose_csv(csv, columns, rows)
|
130
|
+
csv << columns; values = rows.values_at(*columns); rows.clear
|
131
|
+
values.map(&:size).max.times { |i| csv << values.map { |v| v[i] } }
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
###############################################################################
|
5
|
+
# #
|
6
|
+
# Lingo -- A full-featured automatic indexing system #
|
7
|
+
# #
|
8
|
+
# Copyright (C) 2005-2007 John Vorhauer #
|
9
|
+
# Copyright (C) 2007-2015 John Vorhauer, Jens Wille #
|
10
|
+
# #
|
11
|
+
# Lingo is free software; you can redistribute it and/or modify it under the #
|
12
|
+
# terms of the GNU Affero General Public License as published by the Free #
|
13
|
+
# Software Foundation; either version 3 of the License, or (at your option) #
|
14
|
+
# any later version. #
|
15
|
+
# #
|
16
|
+
# Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
|
17
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
18
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
|
19
|
+
# more details. #
|
20
|
+
# #
|
21
|
+
# You should have received a copy of the GNU Affero General Public License #
|
22
|
+
# along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
|
23
|
+
# #
|
24
|
+
###############################################################################
|
25
|
+
#++
|
26
|
+
|
27
|
+
class Lingo
|
28
|
+
|
29
|
+
module Ctl
|
30
|
+
|
31
|
+
{ config: %w[c configuration],
|
32
|
+
lang: %w[l language],
|
33
|
+
dict: %w[d dictionary dictionaries],
|
34
|
+
store: %w[s store],
|
35
|
+
sample: %w[e sample\ text\ file]
|
36
|
+
}.each { |n, (s, q, r)|
|
37
|
+
t = n == :store
|
38
|
+
|
39
|
+
cmd([:list, :l, n], s, "List available #{r || "#{q}s"}", '[name...]') if !t
|
40
|
+
cmd([:find, :f, n], s, "Find #{q} in Lingo search path", 'name')
|
41
|
+
cmd([:copy, :c, n], s, "Copy #{q} to local Lingo directory", 'name') if !t
|
42
|
+
cmd([:clear, :c, n], s, 'Remove store files to force rebuild', 'name') if t
|
43
|
+
}
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def list(what, doit = true)
|
48
|
+
names = Regexp.union(*ARGV.empty? ? '' : ARGV)
|
49
|
+
|
50
|
+
Lingo.list(what, path: path_for_scope).select { |file|
|
51
|
+
File.basename(file) =~ names ? doit ? puts(file) : true : false
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def find(what, doit = true, path = path_for_scope)
|
56
|
+
name = ARGV.shift or missing_arg(:name)
|
57
|
+
no_args
|
58
|
+
|
59
|
+
file = Lingo.find(what, name, path: path) { |err| usage(err) }
|
60
|
+
doit ? puts(file) : file
|
61
|
+
end
|
62
|
+
|
63
|
+
def copy(what)
|
64
|
+
usage('Source and target are the same.') if OPTIONS[:scope] == :local
|
65
|
+
|
66
|
+
local_path = path_for_scope(:local)
|
67
|
+
|
68
|
+
source = find(what, false, path_for_scope || Lingo::PATH - local_path)
|
69
|
+
target = File.expand_path(Lingo.basepath(what, source), local_path[0])
|
70
|
+
|
71
|
+
usage('Source and target are the same.') if source == target
|
72
|
+
|
73
|
+
return unless overwrite?(target)
|
74
|
+
|
75
|
+
FileUtils.mkdir_p(File.dirname(target))
|
76
|
+
FileUtils.cp(source, target, verbose: true)
|
77
|
+
end
|
78
|
+
|
79
|
+
def do_clearstore
|
80
|
+
store = Dir["#{find(:store, false)}.*"]
|
81
|
+
FileUtils.rm(store, verbose: true) unless store.empty?
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
###############################################################################
|
5
|
+
# #
|
6
|
+
# Lingo -- A full-featured automatic indexing system #
|
7
|
+
# #
|
8
|
+
# Copyright (C) 2005-2007 John Vorhauer #
|
9
|
+
# Copyright (C) 2007-2015 John Vorhauer, Jens Wille #
|
10
|
+
# #
|
11
|
+
# Lingo is free software; you can redistribute it and/or modify it under the #
|
12
|
+
# terms of the GNU Affero General Public License as published by the Free #
|
13
|
+
# Software Foundation; either version 3 of the License, or (at your option) #
|
14
|
+
# any later version. #
|
15
|
+
# #
|
16
|
+
# Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
|
17
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
18
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
|
19
|
+
# more details. #
|
20
|
+
# #
|
21
|
+
# You should have received a copy of the GNU Affero General Public License #
|
22
|
+
# along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
|
23
|
+
# #
|
24
|
+
###############################################################################
|
25
|
+
#++
|
26
|
+
|
27
|
+
require 'zip'
|
28
|
+
Zip.unicode_names = true
|
29
|
+
|
30
|
+
class Lingo
|
31
|
+
|
32
|
+
module Ctl
|
33
|
+
|
34
|
+
{ demo: [:d, 'Initialize demo directory', '[path]', 'current directory'],
|
35
|
+
archive: [:a, 'Create archive of directory', '[path]', 'current directory'],
|
36
|
+
rackup: [:r, 'Print path to rackup file', 'name'],
|
37
|
+
path: [:p, 'Print search path for dictionaries and configurations'],
|
38
|
+
help: [:h, 'Print help for available commands', '[command...]'],
|
39
|
+
version: [:v, 'Print Lingo version number']
|
40
|
+
}.each { |n, (s, *a)| cmd(n.to_s, s.to_s, *a) }
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def do_archive
|
45
|
+
OPTIONS.update(path: ARGV.shift, scope: :local)
|
46
|
+
no_args
|
47
|
+
|
48
|
+
source = File.expand_path(path_for_scope.first)
|
49
|
+
target = "#{source}.zip"
|
50
|
+
|
51
|
+
abort "No such directory: #{source}" unless Dir.exist?(source)
|
52
|
+
|
53
|
+
return unless overwrite?(target, true)
|
54
|
+
|
55
|
+
base, name = File.split(source)
|
56
|
+
|
57
|
+
Dir.chdir(base) {
|
58
|
+
Zip::File.open(target, Zip::File::CREATE) { |zipfile|
|
59
|
+
Dir[File.join(name, '**', '*')].each { |file|
|
60
|
+
zipfile.add(file, file)
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
puts "Directory successfully archived at `#{target}'."
|
66
|
+
end
|
67
|
+
|
68
|
+
def do_demo
|
69
|
+
OPTIONS.update(path: ARGV.shift, scope: :system)
|
70
|
+
no_args
|
71
|
+
|
72
|
+
path = path_for_scope(:local).first
|
73
|
+
|
74
|
+
copy_list(:config) { |i| !File.basename(i).start_with?('test') }
|
75
|
+
copy_list(:lang)
|
76
|
+
copy_list(:dict) { |i| File.basename(i).start_with?('user') }
|
77
|
+
copy_list(:sample)
|
78
|
+
|
79
|
+
puts "Demo directory successfully initialized at `#{path}'."
|
80
|
+
end
|
81
|
+
|
82
|
+
def do_rackup(doit = true)
|
83
|
+
name = ARGV.shift or missing_arg(:name)
|
84
|
+
no_args
|
85
|
+
|
86
|
+
require 'lingo/app'
|
87
|
+
|
88
|
+
if file = Lingo::App.rackup(name)
|
89
|
+
doit ? puts(file) : file
|
90
|
+
else
|
91
|
+
usage("Invalid app name `#{name.inspect}'.")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def do_path
|
96
|
+
no_args
|
97
|
+
puts path_for_scope || PATH
|
98
|
+
end
|
99
|
+
|
100
|
+
def do_help(opts = nil)
|
101
|
+
msg = opts ? [opts, 'Commands:'] : []
|
102
|
+
|
103
|
+
args = ARGV unless opts || ARGV.empty?
|
104
|
+
|
105
|
+
aliases = Hash.array
|
106
|
+
ALIASES.each { |k, v| aliases[v] << k }
|
107
|
+
|
108
|
+
COMMANDS.each { |c, (d, *e)|
|
109
|
+
a = aliases[c]
|
110
|
+
next if args && ([c, *a] & args).empty?
|
111
|
+
|
112
|
+
c = "#{c} (#{a.join(', ')})" unless a.empty?
|
113
|
+
|
114
|
+
if opts
|
115
|
+
msg << " %-#{OPTWIDTH}s %s" % [c, d]
|
116
|
+
else
|
117
|
+
msg << "#{c}" << " - #{d}"
|
118
|
+
e.each { |i| msg << " + #{i}" }
|
119
|
+
end
|
120
|
+
}
|
121
|
+
|
122
|
+
msg.empty? ? abort : abort(msg.join("\n"))
|
123
|
+
end
|
124
|
+
|
125
|
+
def do_version(doit = true)
|
126
|
+
no_args
|
127
|
+
|
128
|
+
msg = "Lingo v#{Lingo::VERSION}"
|
129
|
+
doit ? puts(msg) : msg
|
130
|
+
end
|
131
|
+
|
132
|
+
def copy_list(what)
|
133
|
+
files = list(what, false)
|
134
|
+
files.select! { |i| yield i } if block_given?
|
135
|
+
files.each { |file| ARGV.replace([file]); copy(what) }
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
data/lib/lingo/database.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# Lingo -- A full-featured automatic indexing system #
|
7
7
|
# #
|
8
8
|
# Copyright (C) 2005-2007 John Vorhauer #
|
9
|
-
# Copyright (C) 2007-
|
9
|
+
# Copyright (C) 2007-2015 John Vorhauer, Jens Wille #
|
10
10
|
# #
|
11
11
|
# Lingo is free software; you can redistribute it and/or modify it under the #
|
12
12
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -67,22 +67,33 @@ class Lingo
|
|
67
67
|
def initialize(id, lingo)
|
68
68
|
@id, @lingo, @config, @db = id, lingo, lingo.database_config(id), nil
|
69
69
|
|
70
|
-
@srcfile =
|
71
|
-
|
72
|
-
|
73
|
-
@val = Hash.nest { [] }
|
70
|
+
@val, @crypt, @srcfile = Hash.array, config.key?('crypt'),
|
71
|
+
Lingo.find(:dict, config['name'], relax: true)
|
74
72
|
|
75
73
|
begin
|
76
74
|
@stofile = Lingo.find(:store, @srcfile)
|
77
75
|
FileUtils.mkdir_p(File.dirname(@stofile))
|
78
76
|
rescue SourceFileNotFoundError => err
|
79
77
|
@stofile = skip_ext = err.id
|
80
|
-
|
78
|
+
|
79
|
+
unless err.name
|
80
|
+
if name = BACKEND_BY_EXT[File.extname(@stofile)]
|
81
|
+
backend = get_backend(name, @stofile)
|
82
|
+
else
|
83
|
+
raise BackendNotFoundError.new(@stofile)
|
84
|
+
end
|
85
|
+
end
|
81
86
|
rescue NoWritableStoreError
|
82
87
|
backend = HashStore
|
83
88
|
end
|
84
89
|
|
85
|
-
|
90
|
+
unless backend ||= get_backend(ENV['LINGO_BACKEND'])
|
91
|
+
BACKENDS.find { |name| backend = get_backend(name, nil, true) }
|
92
|
+
end
|
93
|
+
|
94
|
+
extend(@backend = backend || HashStore)
|
95
|
+
|
96
|
+
@stofile << store_ext unless skip_ext || !respond_to?(:store_ext)
|
86
97
|
|
87
98
|
convert unless uptodate?
|
88
99
|
end
|
@@ -131,7 +142,7 @@ class Lingo
|
|
131
142
|
val.uniq!
|
132
143
|
|
133
144
|
val = val.join(FLD_SEP)
|
134
|
-
@
|
145
|
+
@crypt ? _set(*Crypter.encode(key, val)) : _set(key, val)
|
135
146
|
end
|
136
147
|
|
137
148
|
def warn(*msg)
|
@@ -140,26 +151,13 @@ class Lingo
|
|
140
151
|
|
141
152
|
private
|
142
153
|
|
143
|
-
def
|
144
|
-
|
145
|
-
backend = get_backend(mod) and break if mod
|
146
|
-
} unless backend
|
147
|
-
|
148
|
-
extend(@backend = backend || HashStore)
|
149
|
-
|
150
|
-
@stofile << store_ext if !skip_ext && respond_to?(:store_ext)
|
151
|
-
end
|
152
|
-
|
153
|
-
def get_backend(mod)
|
154
|
-
self.class.const_get("#{mod}Store") if Object.const_defined?(mod)
|
155
|
-
rescue TypeError, NameError
|
156
|
-
end
|
157
|
-
|
158
|
-
def backend_from_file(file)
|
159
|
-
ext = File.extname(file)
|
154
|
+
def get_backend(name, file = nil, relax = false)
|
155
|
+
return unless name
|
160
156
|
|
161
|
-
|
162
|
-
|
157
|
+
Object.const_get(name)
|
158
|
+
self.class.const_get("#{name}Store")
|
159
|
+
rescue TypeError, NameError => err
|
160
|
+
raise BackendNotAvailableError.new(name, file, err) unless relax
|
163
161
|
end
|
164
162
|
|
165
163
|
def config_hash
|
@@ -223,9 +221,9 @@ class Lingo
|
|
223
221
|
end
|
224
222
|
|
225
223
|
def _val(key)
|
226
|
-
if val = _get(@
|
224
|
+
if val = _get(@crypt ? Crypter.digest(key) : key)
|
227
225
|
_encode!(val)
|
228
|
-
@
|
226
|
+
@crypt ? Crypter.decode(key, val) : val
|
229
227
|
end
|
230
228
|
end
|
231
229
|
|
@@ -236,32 +234,41 @@ class Lingo
|
|
236
234
|
def convert(verbose = lingo.config.stderr.tty?)
|
237
235
|
src = Source.get(config.fetch('txt-format', 'key_value'), @id, lingo)
|
238
236
|
|
239
|
-
sep, key_map, val_map = prepare_lex
|
237
|
+
sep, hyphenate, key_map, val_map = prepare_lex
|
240
238
|
|
241
239
|
Progress.new(self, src, verbose) { |progress| create {
|
242
240
|
src.each { |key, val|
|
243
241
|
progress << src.pos
|
244
242
|
|
245
|
-
|
246
|
-
key.
|
247
|
-
|
248
|
-
if sep && key.include?(sep)
|
249
|
-
key = key.split(sep).map!(&key_map).join(sep)
|
250
|
-
val = val.map { |v| val_map[v.split(sep)].join(sep) } if val_map
|
243
|
+
set_key(src, key, val, sep) { |keys, cnt|
|
244
|
+
key = keys.map!(&key_map).join(sep)
|
245
|
+
val = val.map { |v| val_map[v.split(sep)].join(sep) } if val_map
|
251
246
|
|
252
|
-
|
253
|
-
|
254
|
-
end
|
255
|
-
end
|
256
|
-
end
|
247
|
+
hyphenate.repeated_permutation(cnt - 1) { |h| set_key(src, keys.
|
248
|
+
zip(h).join, val, sep) unless h.uniq.size == 1 } if hyphenate
|
257
249
|
|
258
|
-
|
250
|
+
[key, val]
|
251
|
+
}
|
259
252
|
}
|
260
253
|
|
261
254
|
uptodate!
|
262
255
|
} }
|
263
256
|
end
|
264
257
|
|
258
|
+
def set_key(src, key, val, sep, len = 3)
|
259
|
+
if key
|
260
|
+
key.chomp!('.')
|
261
|
+
|
262
|
+
if sep && key.include?(sep)
|
263
|
+
keys = key.split(sep); cnt = keys.size
|
264
|
+
key, val = yield keys, cnt if block_given?
|
265
|
+
self[keys[0, len].join(sep)] = ["#{KEY_REF}#{cnt}"] if cnt > len
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
src.set(self, key, val)
|
270
|
+
end
|
271
|
+
|
265
272
|
def prepare_lex
|
266
273
|
use_lex = config['use-lex'] or return
|
267
274
|
|
@@ -275,6 +282,9 @@ class Lingo
|
|
275
282
|
|
276
283
|
args = nil
|
277
284
|
|
285
|
+
wac = Language::WA_COMPOUND
|
286
|
+
lac = Language::LA_COMPOUND
|
287
|
+
|
278
288
|
if inflect = config['inflect']
|
279
289
|
inflect, wc = inflect == true ? %w[s e] : inflect.split(SEP_RE), 'a'
|
280
290
|
|
@@ -286,19 +296,16 @@ class Lingo
|
|
286
296
|
end
|
287
297
|
end
|
288
298
|
|
289
|
-
[' ', lambda { |form|
|
299
|
+
[sep = ' ', config['hyphenate'] && [sep, '-'], lambda { |form|
|
290
300
|
word = dic.find_word(form)
|
291
301
|
|
292
302
|
if word.unknown?
|
293
|
-
|
303
|
+
comp = gra.find_compound(form)
|
294
304
|
|
295
|
-
|
296
|
-
|
297
|
-
else
|
298
|
-
compo.norm
|
299
|
-
end
|
305
|
+
comp.attr == wac && comp.lex_form(lac) ||
|
306
|
+
(comp.identified? ? comp.lex_form : comp.form)
|
300
307
|
else
|
301
|
-
word.
|
308
|
+
word.identified? ? word.lex_form : word.form
|
302
309
|
end
|
303
310
|
}, inflect && lambda { |forms|
|
304
311
|
inflectables = []
|
@@ -306,24 +313,21 @@ class Lingo
|
|
306
313
|
forms.each { |form|
|
307
314
|
word = dic.find_word(word_form = form[re])
|
308
315
|
|
309
|
-
if word.identified?
|
310
|
-
inflectables << form if form ==
|
316
|
+
if word.identified? && _form = word.lex_form(wc)
|
317
|
+
inflectables << form if form == _form
|
311
318
|
else
|
312
319
|
unless inflectables.empty?
|
313
|
-
|
314
|
-
word = comp.head || comp if comp && !comp.unknown?
|
320
|
+
word = gra.find_compound_head(word_form) || word if word.unknown?
|
315
321
|
|
316
|
-
if word.attr?(*inflect)
|
317
|
-
|
318
|
-
inflectables.each { |lex_form| lex_form << suffix }
|
322
|
+
if word.attr?(*inflect) && suffix =
|
323
|
+
suffixes[word.genders.compact.first]
|
324
|
+
inflectables.each { |lex_form| lex_form << suffix }
|
319
325
|
end
|
320
326
|
end
|
321
327
|
|
322
|
-
break
|
328
|
+
break forms
|
323
329
|
end
|
324
330
|
}
|
325
|
-
|
326
|
-
forms
|
327
331
|
}]
|
328
332
|
end
|
329
333
|
|