lingo 1.8.2 → 1.8.3
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +33 -0
- data/README +6 -5
- data/Rakefile +6 -4
- data/{lib/lingo/cachable.rb → bin/lingosrv} +30 -58
- data/bin/lingoweb +30 -0
- data/de.lang +2 -13
- data/en/lingo-irr.txt +266 -0
- data/en/lingo-wdn.txt +37319 -0
- data/en.lang +2 -15
- data/lib/lingo/app.rb +82 -0
- data/lib/lingo/attendee/abbreviator.rb +22 -26
- data/lib/lingo/attendee/debugger.rb +8 -4
- data/lib/lingo/attendee/decomposer.rb +0 -1
- data/lib/lingo/attendee/dehyphenizer.rb +2 -2
- data/lib/lingo/attendee/multi_worder.rb +20 -13
- data/lib/lingo/attendee/noneword_filter.rb +2 -7
- data/lib/lingo/attendee/sequencer.rb +43 -19
- data/lib/lingo/attendee/stemmer/porter.rb +2 -2
- data/lib/lingo/attendee/stemmer.rb +1 -1
- data/lib/lingo/attendee/synonymer.rb +1 -9
- data/lib/lingo/attendee/text_reader.rb +42 -29
- data/lib/lingo/attendee/text_writer.rb +3 -6
- data/lib/lingo/attendee/tokenizer.rb +87 -69
- data/lib/lingo/attendee/variator.rb +7 -5
- data/lib/lingo/attendee/vector_filter.rb +11 -11
- data/lib/lingo/attendee/word_searcher.rb +1 -9
- data/lib/lingo/attendee.rb +24 -105
- data/lib/lingo/buffered_attendee.rb +2 -9
- data/lib/lingo/call.rb +18 -13
- data/lib/lingo/cli.rb +5 -10
- data/lib/lingo/config.rb +40 -7
- data/lib/lingo/ctl.rb +69 -57
- data/lib/lingo/database/hash_store.rb +9 -4
- data/lib/lingo/database/sdbm_store.rb +4 -7
- data/lib/lingo/database/source/multi_key.rb +1 -1
- data/lib/lingo/database/source/multi_value.rb +1 -1
- data/lib/lingo/database/source.rb +2 -20
- data/lib/lingo/database.rb +30 -19
- data/lib/lingo/debug.rb +79 -0
- data/lib/lingo/{core_ext.rb → language/char.rb} +43 -42
- data/lib/lingo/language/dictionary.rb +38 -46
- data/lib/lingo/language/grammar.rb +40 -57
- data/lib/lingo/language/lexical.rb +4 -7
- data/lib/lingo/language/lexical_hash.rb +17 -35
- data/lib/lingo/language/token.rb +4 -0
- data/lib/lingo/language/word.rb +7 -8
- data/lib/lingo/language/word_form.rb +4 -4
- data/lib/lingo/language.rb +2 -1
- data/lib/lingo/srv/config.ru +4 -0
- data/lib/lingo/srv/lingosrv.cfg +14 -0
- data/lib/lingo/{reportable.rb → srv.rb} +59 -61
- data/lib/lingo/version.rb +1 -1
- data/lib/lingo/web/config.ru +4 -0
- data/lib/lingo/web/lingoweb.cfg +14 -0
- data/lib/lingo/web/public/lingo.png +0 -0
- data/lib/lingo/web/public/lingoweb.css +74 -0
- data/lib/lingo/web/views/index.erb +92 -0
- data/lib/lingo/web.rb +94 -0
- data/lib/lingo.rb +27 -29
- data/lingo.cfg +1 -1
- data/lir.cfg +24 -0
- data/ru/lingo-dic.txt +22342 -0
- data/ru/lingo-mul.txt +5151 -0
- data/ru/lingo-syn.txt +0 -0
- data/ru.lang +99 -0
- data/test/attendee/ts_sequencer.rb +2 -2
- data/test/attendee/ts_text_reader.rb +36 -2
- data/test/attendee/ts_text_writer.rb +6 -6
- data/test/lir.vec +3 -3
- data/test/test_helper.rb +104 -102
- data/test/ts_database.rb +1 -1
- data/test/ts_language.rb +55 -96
- data/txt/artikel-ru.txt +45 -0
- data/txt/lir.txt +1 -3
- metadata +143 -83
- data/TODO +0 -23
data/lib/lingo/config.rb
CHANGED
@@ -27,11 +27,15 @@
|
|
27
27
|
require 'yaml'
|
28
28
|
require_relative 'cli'
|
29
29
|
|
30
|
+
YAML::ENGINE.yamler = 'psych'
|
31
|
+
|
30
32
|
class Lingo
|
31
33
|
|
32
34
|
class Config
|
33
35
|
|
34
36
|
def initialize(*args)
|
37
|
+
@deprecated = Hash.new { |h, k| h[k] = true; false }
|
38
|
+
|
35
39
|
@cli, @opts = CLI.new, {}
|
36
40
|
|
37
41
|
@cli.execute(*args)
|
@@ -40,9 +44,10 @@ class Lingo
|
|
40
44
|
load_config('language', :lang)
|
41
45
|
load_config('config')
|
42
46
|
|
43
|
-
Array(self['meeting/attendees']).
|
44
|
-
r = a['text_reader'] || a['textreader'] or next # DEPRECATE textreader
|
47
|
+
deprecate(:textreader, :text_reader) if Array(self['meeting/attendees']).map(&:keys).flatten.include?('textreader')
|
45
48
|
|
49
|
+
if r = get('meeting/attendees', 'text_reader') ||
|
50
|
+
get('meeting/attendees', 'textreader') # DEPRECATE textreader
|
46
51
|
f = @cli.files
|
47
52
|
|
48
53
|
if i = r['files']
|
@@ -50,9 +55,7 @@ class Lingo
|
|
50
55
|
elsif !f.empty?
|
51
56
|
r['files'] = f
|
52
57
|
end
|
53
|
-
|
54
|
-
break
|
55
|
-
}
|
58
|
+
end
|
56
59
|
end
|
57
60
|
|
58
61
|
def [](key)
|
@@ -64,6 +67,23 @@ class Lingo
|
|
64
67
|
(self[nodes_to_key(nodes)] ||= {})[node] = val
|
65
68
|
end
|
66
69
|
|
70
|
+
def get(key, *names)
|
71
|
+
val = self[key]
|
72
|
+
|
73
|
+
while name = names.shift
|
74
|
+
case val
|
75
|
+
when Hash then val = val[name]
|
76
|
+
when Array then val = val.find { |h|
|
77
|
+
k, v = h.dup.shift
|
78
|
+
break v if k == name
|
79
|
+
}
|
80
|
+
else break
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
val
|
85
|
+
end
|
86
|
+
|
67
87
|
def stdin
|
68
88
|
@cli.stdin
|
69
89
|
end
|
@@ -76,10 +96,23 @@ class Lingo
|
|
76
96
|
@cli.stderr
|
77
97
|
end
|
78
98
|
|
99
|
+
def warn(*msg)
|
100
|
+
stderr.puts(*msg)
|
101
|
+
end
|
102
|
+
|
79
103
|
def quit(*args)
|
80
104
|
@cli.send(:quit, *args)
|
81
105
|
end
|
82
106
|
|
107
|
+
def deprecate(old, new, obj = self)
|
108
|
+
unless @deprecated[[source = obj.class.name.sub(/\ALingo::/, ''), old]]
|
109
|
+
warn(
|
110
|
+
"DEPRECATION WARNING: #{source} option `#{old}' is deprecated " <<
|
111
|
+
"and will be removed in Lingo 1.9. Please use `#{new}' instead."
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
83
116
|
private
|
84
117
|
|
85
118
|
def key_to_nodes(key)
|
@@ -91,8 +124,8 @@ class Lingo
|
|
91
124
|
end
|
92
125
|
|
93
126
|
def load_config(key, type = key.to_sym)
|
94
|
-
file = Lingo.find(type, @opts[key]
|
95
|
-
@opts.update(File.open(file, encoding: ENC
|
127
|
+
file = Lingo.find(type, @opts[key]) { quit }
|
128
|
+
@opts.update(File.open(file, encoding: ENC) { |f| YAML.load(f) })
|
96
129
|
end
|
97
130
|
|
98
131
|
end
|
data/lib/lingo/ctl.rb
CHANGED
@@ -39,59 +39,52 @@ class Lingo
|
|
39
39
|
h[k] = COMMANDS.has_key?(k) ? k : 'usage'
|
40
40
|
}
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
{ config: %w[configuration],
|
45
|
-
lang: %w[language],
|
46
|
-
dict: %w[dictionary dictionaries],
|
47
|
-
store: %w[store],
|
48
|
-
sample: %w[sample\ text\ file] }.each { |what, (sing, plur)|
|
49
|
-
COMMANDS["list#{what}"] = [
|
50
|
-
"List available #{plur || "#{sing}s"}", 'Arguments: [name...]'
|
51
|
-
] if what != :store
|
52
|
-
COMMANDS["find#{what}"] = [
|
53
|
-
"Find #{sing} in Lingo search path", 'Arguments: name'
|
54
|
-
]
|
55
|
-
COMMANDS["copy#{what}"] = [
|
56
|
-
"Copy #{sing} to local Lingo directory", 'Arguments: name'
|
57
|
-
] if what != :store
|
58
|
-
|
59
|
-
%w[list find copy].each { |method|
|
60
|
-
next unless COMMANDS.has_key?(name = "#{method}#{what}")
|
61
|
-
class_eval %Q{def do_#{name}; #{method}(:#{what}); end}
|
62
|
-
|
63
|
-
[0, -1].repeated_permutation(2) { |i, j|
|
64
|
-
key = "#{method[i]}#{what[j]}"
|
65
|
-
break ALIASES[key] = name unless ALIASES.has_key?(key)
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
if what == :store
|
70
|
-
COMMANDS['clearstore'] = [
|
71
|
-
'Remove store files to force rebuild', 'Arguments: name'
|
72
|
-
]
|
73
|
-
ALIASES['cs'] = 'clearstore'
|
74
|
-
end
|
75
|
-
}
|
76
|
-
|
77
|
-
{ demo: ['Initialize demo directory (Default: current directory)',
|
78
|
-
'Arguments: [path]'],
|
79
|
-
path: 'Print search path for dictionaries and configurations',
|
80
|
-
help: 'Print help for available commands',
|
81
|
-
version: 'Print Lingo version number' }.each { |what, description|
|
82
|
-
COMMANDS[name = what.to_s] = description; ALIASES[name[0]] = name
|
83
|
-
}
|
84
|
-
|
85
|
-
USAGE = <<EOT
|
42
|
+
USAGE = <<-EOT
|
86
43
|
Usage: #{PROG} <command> [arguments] [options]
|
87
44
|
#{PROG} [-h|--help] [--version]
|
88
|
-
EOT
|
45
|
+
EOT
|
89
46
|
|
90
47
|
def ctl
|
91
48
|
parse_options
|
92
49
|
send("do_#{ALIASES[ARGV.shift]}")
|
93
50
|
end
|
94
51
|
|
52
|
+
def self.cmd(name, short, desc, args = nil, default = nil)
|
53
|
+
if name.is_a?(Array)
|
54
|
+
m, f, k = name
|
55
|
+
name, short = "#{m}#{k}", "#{f}#{short}"
|
56
|
+
class_eval %Q{private; def do_#{name}; #{m}(:#{k}); end}
|
57
|
+
end
|
58
|
+
|
59
|
+
if args
|
60
|
+
desc = [desc, args = "Arguments: #{args}"]
|
61
|
+
args << " (Default: #{default})" if default
|
62
|
+
end
|
63
|
+
|
64
|
+
COMMANDS[name], ALIASES[short] = desc, name
|
65
|
+
end
|
66
|
+
|
67
|
+
{ config: %w[c configuration],
|
68
|
+
lang: %w[l language],
|
69
|
+
dict: %w[d dictionary dictionaries],
|
70
|
+
store: %w[s store],
|
71
|
+
sample: %w[e sample\ text\ file]
|
72
|
+
}.each { |n, (s, q, r)|
|
73
|
+
t = n == :store
|
74
|
+
|
75
|
+
cmd([:list, :l, n], s, "List available #{r || "#{q}s"}", '[name...]') if !t
|
76
|
+
cmd([:find, :f, n], s, "Find #{q} in Lingo search path", 'name')
|
77
|
+
cmd([:copy, :c, n], s, "Copy #{q} to local Lingo directory", 'name') if !t
|
78
|
+
cmd([:clear, :c, n], s, 'Remove store files to force rebuild', 'name') if t
|
79
|
+
}
|
80
|
+
|
81
|
+
{ demo: [:d, 'Initialize demo directory', '[path]', 'current directory'],
|
82
|
+
rackup: [:r, 'Print path to rackup file', 'name'],
|
83
|
+
path: [:p, 'Print search path for dictionaries and configurations'],
|
84
|
+
help: [:h, 'Print help for available commands'],
|
85
|
+
version: [:v, 'Print Lingo version number']
|
86
|
+
}.each { |n, (s, *a)| cmd(n.to_s, s.to_s, *a) }
|
87
|
+
|
95
88
|
private
|
96
89
|
|
97
90
|
def list(what, doit = true)
|
@@ -103,20 +96,20 @@ EOT
|
|
103
96
|
end
|
104
97
|
|
105
98
|
def find(what, doit = true)
|
106
|
-
name = ARGV.shift or
|
99
|
+
name = ARGV.shift or missing_arg(:name)
|
107
100
|
no_args
|
108
101
|
|
109
|
-
file = Lingo.find(what, name, path: path_for_scope
|
102
|
+
file = Lingo.find(what, name, path: path_for_scope) { usage }
|
110
103
|
doit ? puts(file) : file
|
111
104
|
end
|
112
105
|
|
113
106
|
def copy(what)
|
114
|
-
|
107
|
+
usage('Source and target are the same.') if OPTIONS[:scope] == :local
|
115
108
|
|
116
109
|
source = find(what, false)
|
117
110
|
target = File.join(path_for_scope(:local), Lingo.basepath(what, source))
|
118
111
|
|
119
|
-
|
112
|
+
usage('Source and target are the same.') if source == target
|
120
113
|
|
121
114
|
FileUtils.mkdir_p(File.dirname(target))
|
122
115
|
FileUtils.cp(source, target, verbose: true)
|
@@ -137,6 +130,19 @@ EOT
|
|
137
130
|
copy_list(:sample)
|
138
131
|
end
|
139
132
|
|
133
|
+
def do_rackup(doit = true)
|
134
|
+
name = ARGV.shift or missing_arg(:name)
|
135
|
+
no_args
|
136
|
+
|
137
|
+
require 'lingo/app'
|
138
|
+
|
139
|
+
if file = Lingo::App.rackup(name)
|
140
|
+
doit ? puts(file) : file
|
141
|
+
else
|
142
|
+
usage("Invalid app name `#{name.inspect}'.")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
140
146
|
def do_path
|
141
147
|
no_args
|
142
148
|
puts path_for_scope || PATH
|
@@ -172,10 +178,6 @@ EOT
|
|
172
178
|
doit ? puts(msg) : msg
|
173
179
|
end
|
174
180
|
|
175
|
-
def do_usage(msg = nil)
|
176
|
-
abort "#{"#{PROGNAME}: #{msg}\n\n" if msg}#{USAGE}"
|
177
|
-
end
|
178
|
-
|
179
181
|
def parse_options
|
180
182
|
OptionParser.new(USAGE, OPTWIDTH) { |opts|
|
181
183
|
opts.separator ''
|
@@ -185,7 +187,7 @@ EOT
|
|
185
187
|
OPTIONS[:scope] = :system
|
186
188
|
}
|
187
189
|
|
188
|
-
opts.on('--global',
|
190
|
+
opts.on('--global', "Restrict command to the user's personal Lingo directory") {
|
189
191
|
OPTIONS[:scope] = :global
|
190
192
|
}
|
191
193
|
|
@@ -212,17 +214,27 @@ EOT
|
|
212
214
|
when :global then [HOME]
|
213
215
|
when :local then [OPTIONS[:path] || CURR]
|
214
216
|
when nil
|
215
|
-
else
|
217
|
+
else usage("Invalid scope `#{scope.inspect}'.")
|
216
218
|
end
|
217
219
|
end
|
218
220
|
|
221
|
+
def usage(msg = nil)
|
222
|
+
abort "#{"#{PROGNAME}: #{msg}\n\n" if msg}#{USAGE}"
|
223
|
+
end
|
224
|
+
|
225
|
+
alias_method :do_usage, :usage
|
226
|
+
|
227
|
+
def missing_arg(arg)
|
228
|
+
usage("Required argument `#{arg}' missing.")
|
229
|
+
end
|
230
|
+
|
219
231
|
def no_args
|
220
|
-
|
232
|
+
usage('Too many arguments.') unless ARGV.empty?
|
221
233
|
end
|
222
234
|
|
223
235
|
def copy_list(what)
|
224
236
|
files = list(what, false)
|
225
|
-
files.select!
|
237
|
+
files.select! { |i| yield i } if block_given?
|
226
238
|
files.each { |file| ARGV.replace([file]); copy(what) }
|
227
239
|
end
|
228
240
|
|
@@ -30,20 +30,19 @@ class Lingo
|
|
30
30
|
|
31
31
|
module HashStore
|
32
32
|
|
33
|
-
|
34
|
-
@db.dup
|
35
|
-
end
|
36
|
-
|
33
|
+
# Never close, because we can't restore.
|
37
34
|
def close
|
38
35
|
self
|
39
36
|
end
|
40
37
|
|
41
38
|
private
|
42
39
|
|
40
|
+
# Never up-to-date.
|
43
41
|
def uptodate?
|
44
42
|
false
|
45
43
|
end
|
46
44
|
|
45
|
+
# Nothing to do.
|
47
46
|
def uptodate!
|
48
47
|
nil
|
49
48
|
end
|
@@ -56,10 +55,16 @@ class Lingo
|
|
56
55
|
{}
|
57
56
|
end
|
58
57
|
|
58
|
+
# Never closed.
|
59
59
|
def _closed?
|
60
60
|
false
|
61
61
|
end
|
62
62
|
|
63
|
+
# Dup key, because we're reusing everything.
|
64
|
+
def _each
|
65
|
+
@db.each { |key, val| yield key.dup, val }
|
66
|
+
end
|
67
|
+
|
63
68
|
end
|
64
69
|
|
65
70
|
end
|
@@ -34,6 +34,8 @@ class Lingo
|
|
34
34
|
|
35
35
|
Database.register(self, %w[dir pag], -1, false)
|
36
36
|
|
37
|
+
MAX_LENGTH = 950
|
38
|
+
|
37
39
|
private
|
38
40
|
|
39
41
|
def uptodate?
|
@@ -48,15 +50,10 @@ class Lingo
|
|
48
50
|
SDBM.open(@stofile)
|
49
51
|
end
|
50
52
|
|
51
|
-
def _get(key)
|
52
|
-
val = super
|
53
|
-
val && val.encode(ENC)
|
54
|
-
end
|
55
|
-
|
56
53
|
def _set(key, val)
|
57
|
-
if val.
|
54
|
+
if val.bytesize > MAX_LENGTH
|
58
55
|
warn "Warning: Entry `#{key}' (#{@srcfile}) too long for SDBM. Truncating..."
|
59
|
-
val = val
|
56
|
+
val = val.byteslice(0, MAX_LENGTH)
|
60
57
|
end
|
61
58
|
|
62
59
|
super
|
@@ -47,24 +47,6 @@ class Lingo
|
|
47
47
|
|
48
48
|
class Source
|
49
49
|
|
50
|
-
# Define printable characters for tokenizer for UTF-8 encoding
|
51
|
-
UTF8_DIGIT = '[0-9]'
|
52
|
-
# Define Basic Latin printable characters for UTF-8 encoding from U+0000 to U+007f
|
53
|
-
UTF8_BASLAT = '[A-Za-z]'
|
54
|
-
# Define Latin-1 Supplement printable characters for UTF-8 encoding from U+0080 to U+00ff
|
55
|
-
UTF8_LAT1SP = '[\xc3\x80-\xc3\x96\xc3\x98-\xc3\xb6\xc3\xb8-\xc3\xbf]'
|
56
|
-
# Define Latin Extended-A printable characters for UTF-8 encoding from U+0100 to U+017f
|
57
|
-
UTF8_LATEXA = '[\xc4\x80-\xc4\xbf\xc5\x80-\xc5\xbf]'
|
58
|
-
# Define Latin Extended-B printable characters for UTF-8 encoding from U+0180 to U+024f
|
59
|
-
UTF8_LATEXB = '[\xc6\x80-\xc6\xbf\xc7\x80-\xc7\xbf\xc8\x80-\xc8\xbf\xc9\x80-\xc9\x8f]'
|
60
|
-
# Define IPA Extension printable characters for UTF-8 encoding from U+024f to U+02af
|
61
|
-
UTF8_IPAEXT = '[\xc9\xa0-\xc9\xbf\xca\xa0-\xca\xaf]'
|
62
|
-
# Collect all UTF-8 printable characters in Unicode range U+0000 to U+02af
|
63
|
-
UTF8_CHAR = "#{UTF8_DIGIT}|#{UTF8_BASLAT}|#{UTF8_LAT1SP}|#{UTF8_LATEXA}|#{UTF8_LATEXB}|#{UTF8_IPAEXT}"
|
64
|
-
|
65
|
-
PRINTABLE_CHAR = "#{UTF8_CHAR}|[<>-]"
|
66
|
-
LEGAL_CHAR = '[ /&()\[\].,-]'
|
67
|
-
|
68
50
|
def self.get(name, *args)
|
69
51
|
Lingo.get_const(name, self).new(*args)
|
70
52
|
end
|
@@ -89,7 +71,7 @@ class Lingo
|
|
89
71
|
@def = @config.fetch('def-wc', Language::LA_UNKNOWN).downcase
|
90
72
|
@sep = @config['separator']
|
91
73
|
|
92
|
-
@wrd = "(?:#{
|
74
|
+
@wrd = "(?:#{Language::Char::ANY})+"
|
93
75
|
@pat = /^#{@wrd}$/
|
94
76
|
|
95
77
|
@pos = 0
|
@@ -108,7 +90,7 @@ class Lingo
|
|
108
90
|
next if line =~ /\A\s*#/ || line.strip.empty?
|
109
91
|
|
110
92
|
line.chomp!
|
111
|
-
line.downcase
|
93
|
+
line.replace(Unicode.downcase(line))
|
112
94
|
|
113
95
|
if length < 4096 && line =~ @pat
|
114
96
|
yield convert_line(line, $1, $2)
|
data/lib/lingo/database.rb
CHANGED
@@ -39,8 +39,6 @@ class Lingo
|
|
39
39
|
|
40
40
|
class Database
|
41
41
|
|
42
|
-
include Cachable
|
43
|
-
|
44
42
|
FLD_SEP = '|'
|
45
43
|
IDX_REF = '^'
|
46
44
|
KEY_REF = '*'
|
@@ -58,7 +56,7 @@ class Lingo
|
|
58
56
|
|
59
57
|
def register(klass, ext, prio = -1, meth = true)
|
60
58
|
BACKENDS.insert(prio, name = klass.name[/::(\w+)Store\z/, 1])
|
61
|
-
Array(ext).each { |i| BACKEND_BY_EXT[i.
|
59
|
+
Array(ext).each { |i| BACKEND_BY_EXT[i.insert(0, '.')] = name }
|
62
60
|
|
63
61
|
klass.const_set(:EXT, ext)
|
64
62
|
klass.class_eval('def store_ext; EXT; end', __FILE__, __LINE__) if meth
|
@@ -78,6 +76,8 @@ class Lingo
|
|
78
76
|
@srcfile = Lingo.find(:dict, @config['name'], relax: true)
|
79
77
|
@crypter = @config.has_key?('crypt') && Crypter.new
|
80
78
|
|
79
|
+
@val = Hash.new { |h, k| h[k] = [] }
|
80
|
+
|
81
81
|
begin
|
82
82
|
@stofile = Lingo.find(:store, @srcfile)
|
83
83
|
FileUtils.mkdir_p(File.dirname(@stofile))
|
@@ -89,13 +89,12 @@ class Lingo
|
|
89
89
|
end
|
90
90
|
|
91
91
|
use_backend(backend, skip_ext)
|
92
|
-
init_cachable
|
93
92
|
|
94
93
|
convert unless uptodate?
|
95
94
|
end
|
96
95
|
|
97
96
|
def closed?
|
98
|
-
|
97
|
+
!@db || _closed?
|
99
98
|
end
|
100
99
|
|
101
100
|
def open
|
@@ -108,16 +107,20 @@ class Lingo
|
|
108
107
|
end
|
109
108
|
|
110
109
|
def close
|
111
|
-
|
110
|
+
_close unless closed?
|
112
111
|
@db = nil
|
113
112
|
|
114
113
|
self
|
115
114
|
end
|
116
115
|
|
117
116
|
def to_h
|
118
|
-
|
119
|
-
|
120
|
-
|
117
|
+
hash = {}
|
118
|
+
each { |key, val| hash[key.freeze] = val }
|
119
|
+
hash
|
120
|
+
end
|
121
|
+
|
122
|
+
def each
|
123
|
+
_each { |key, val| yield _encode!(key), _encode!(val) } unless closed?
|
121
124
|
end
|
122
125
|
|
123
126
|
def [](key)
|
@@ -133,15 +136,15 @@ class Lingo
|
|
133
136
|
def []=(key, val)
|
134
137
|
return if closed?
|
135
138
|
|
136
|
-
val = val.
|
137
|
-
val.concat(retrieve(key)) if hit?(key)
|
138
|
-
|
139
|
-
val.sort!
|
139
|
+
val = @val[key].concat(val).sort!
|
140
140
|
val.uniq!
|
141
|
-
store(key, val)
|
142
141
|
|
143
|
-
|
144
|
-
|
142
|
+
val = val.join(FLD_SEP)
|
143
|
+
@crypter ? _set(*@crypter.encode(key, val)) : _set(key, val)
|
144
|
+
end
|
145
|
+
|
146
|
+
def warn(*msg)
|
147
|
+
@lingo.warn(*msg)
|
145
148
|
end
|
146
149
|
|
147
150
|
private
|
@@ -193,10 +196,18 @@ class Lingo
|
|
193
196
|
raise NotImplementedError
|
194
197
|
end
|
195
198
|
|
199
|
+
def _close
|
200
|
+
@db.close
|
201
|
+
end
|
202
|
+
|
196
203
|
def _closed?
|
197
204
|
@db.closed?
|
198
205
|
end
|
199
206
|
|
207
|
+
def _each
|
208
|
+
@db.each { |key, val| yield key, val }
|
209
|
+
end
|
210
|
+
|
200
211
|
def _set(key, val)
|
201
212
|
@db[key] = val
|
202
213
|
end
|
@@ -207,13 +218,13 @@ class Lingo
|
|
207
218
|
|
208
219
|
def _val(key)
|
209
220
|
if val = _get(@crypter ? @crypter.digest(key) : key)
|
210
|
-
val
|
221
|
+
_encode!(val)
|
211
222
|
@crypter ? @crypter.decode(key, val) : val
|
212
223
|
end
|
213
224
|
end
|
214
225
|
|
215
|
-
def
|
216
|
-
|
226
|
+
def _encode!(str)
|
227
|
+
str.force_encoding(ENC)
|
217
228
|
end
|
218
229
|
|
219
230
|
def convert(verbose = @lingo.config.stderr.tty?)
|
data/lib/lingo/debug.rb
ADDED
@@ -0,0 +1,79 @@
|
|
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-2012 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 Debug
|
30
|
+
|
31
|
+
extend self
|
32
|
+
|
33
|
+
PS_COMMAND = ENV['LINGO_PS_COMMAND'] || '/bin/ps'
|
34
|
+
PS_COLUMNS = ENV['LINGO_PS_COLUMNS'] || 'vsz,rss,sz,%mem,%cpu,time,etime,pid'
|
35
|
+
|
36
|
+
PS_RE = File.executable?(PS_COMMAND) ? %r{\A#{ENV['LINGO_DEBUG_PS']}\z} : nil
|
37
|
+
|
38
|
+
PS_NO_HEADING = Hash.new { |h, k| h[k] = true; false }
|
39
|
+
|
40
|
+
def ps(name)
|
41
|
+
system(PS_COMMAND,
|
42
|
+
'-o', PS_COLUMNS,
|
43
|
+
"--#{'no-' if PS_NO_HEADING[name]}heading",
|
44
|
+
Process.pid.to_s) if name =~ PS_RE
|
45
|
+
end
|
46
|
+
|
47
|
+
def profile(base)
|
48
|
+
return yield unless base
|
49
|
+
|
50
|
+
require 'ruby-prof'
|
51
|
+
|
52
|
+
result = RubyProf.profile { yield }
|
53
|
+
result.eliminate_methods! [/\b(?:Gem|HighLine)\b/,
|
54
|
+
/\A(?:Benchmark|FileUtils|Pathname|Util)\b/]
|
55
|
+
|
56
|
+
if base.is_a?(IO)
|
57
|
+
RubyProf::FlatPrinter.new(result).print(base)
|
58
|
+
else
|
59
|
+
FileUtils.mkdir_p(File.dirname(base))
|
60
|
+
|
61
|
+
mode = ENV['RUBY_PROF_MEASURE_MODE']
|
62
|
+
base += "-#{mode}" if mode && !mode.empty?
|
63
|
+
|
64
|
+
{
|
65
|
+
:txt => :FlatPrinter,
|
66
|
+
:lines => :FlatPrinterWithLineNumbers,
|
67
|
+
:html => :GraphHtmlPrinter,
|
68
|
+
:stack => :CallStackPrinter
|
69
|
+
}.each { |ext, name|
|
70
|
+
File.open("#{base}.#{ext}", 'a+', encoding: ENC) { |f|
|
71
|
+
RubyProf.const_get(name).new(result).print(f)
|
72
|
+
}
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|