lingo 1.8.2 → 1.8.3

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.
Files changed (76) hide show
  1. data/ChangeLog +33 -0
  2. data/README +6 -5
  3. data/Rakefile +6 -4
  4. data/{lib/lingo/cachable.rb → bin/lingosrv} +30 -58
  5. data/bin/lingoweb +30 -0
  6. data/de.lang +2 -13
  7. data/en/lingo-irr.txt +266 -0
  8. data/en/lingo-wdn.txt +37319 -0
  9. data/en.lang +2 -15
  10. data/lib/lingo/app.rb +82 -0
  11. data/lib/lingo/attendee/abbreviator.rb +22 -26
  12. data/lib/lingo/attendee/debugger.rb +8 -4
  13. data/lib/lingo/attendee/decomposer.rb +0 -1
  14. data/lib/lingo/attendee/dehyphenizer.rb +2 -2
  15. data/lib/lingo/attendee/multi_worder.rb +20 -13
  16. data/lib/lingo/attendee/noneword_filter.rb +2 -7
  17. data/lib/lingo/attendee/sequencer.rb +43 -19
  18. data/lib/lingo/attendee/stemmer/porter.rb +2 -2
  19. data/lib/lingo/attendee/stemmer.rb +1 -1
  20. data/lib/lingo/attendee/synonymer.rb +1 -9
  21. data/lib/lingo/attendee/text_reader.rb +42 -29
  22. data/lib/lingo/attendee/text_writer.rb +3 -6
  23. data/lib/lingo/attendee/tokenizer.rb +87 -69
  24. data/lib/lingo/attendee/variator.rb +7 -5
  25. data/lib/lingo/attendee/vector_filter.rb +11 -11
  26. data/lib/lingo/attendee/word_searcher.rb +1 -9
  27. data/lib/lingo/attendee.rb +24 -105
  28. data/lib/lingo/buffered_attendee.rb +2 -9
  29. data/lib/lingo/call.rb +18 -13
  30. data/lib/lingo/cli.rb +5 -10
  31. data/lib/lingo/config.rb +40 -7
  32. data/lib/lingo/ctl.rb +69 -57
  33. data/lib/lingo/database/hash_store.rb +9 -4
  34. data/lib/lingo/database/sdbm_store.rb +4 -7
  35. data/lib/lingo/database/source/multi_key.rb +1 -1
  36. data/lib/lingo/database/source/multi_value.rb +1 -1
  37. data/lib/lingo/database/source.rb +2 -20
  38. data/lib/lingo/database.rb +30 -19
  39. data/lib/lingo/debug.rb +79 -0
  40. data/lib/lingo/{core_ext.rb → language/char.rb} +43 -42
  41. data/lib/lingo/language/dictionary.rb +38 -46
  42. data/lib/lingo/language/grammar.rb +40 -57
  43. data/lib/lingo/language/lexical.rb +4 -7
  44. data/lib/lingo/language/lexical_hash.rb +17 -35
  45. data/lib/lingo/language/token.rb +4 -0
  46. data/lib/lingo/language/word.rb +7 -8
  47. data/lib/lingo/language/word_form.rb +4 -4
  48. data/lib/lingo/language.rb +2 -1
  49. data/lib/lingo/srv/config.ru +4 -0
  50. data/lib/lingo/srv/lingosrv.cfg +14 -0
  51. data/lib/lingo/{reportable.rb → srv.rb} +59 -61
  52. data/lib/lingo/version.rb +1 -1
  53. data/lib/lingo/web/config.ru +4 -0
  54. data/lib/lingo/web/lingoweb.cfg +14 -0
  55. data/lib/lingo/web/public/lingo.png +0 -0
  56. data/lib/lingo/web/public/lingoweb.css +74 -0
  57. data/lib/lingo/web/views/index.erb +92 -0
  58. data/lib/lingo/web.rb +94 -0
  59. data/lib/lingo.rb +27 -29
  60. data/lingo.cfg +1 -1
  61. data/lir.cfg +24 -0
  62. data/ru/lingo-dic.txt +22342 -0
  63. data/ru/lingo-mul.txt +5151 -0
  64. data/ru/lingo-syn.txt +0 -0
  65. data/ru.lang +99 -0
  66. data/test/attendee/ts_sequencer.rb +2 -2
  67. data/test/attendee/ts_text_reader.rb +36 -2
  68. data/test/attendee/ts_text_writer.rb +6 -6
  69. data/test/lir.vec +3 -3
  70. data/test/test_helper.rb +104 -102
  71. data/test/ts_database.rb +1 -1
  72. data/test/ts_language.rb +55 -96
  73. data/txt/artikel-ru.txt +45 -0
  74. data/txt/lir.txt +1 -3
  75. metadata +143 -83
  76. 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']).each { |a|
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], &method(:quit))
95
- @opts.update(File.open(file, encoding: ENC, &YAML.method(:load)))
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
- ALIASES['ls'] # OCCUPY
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 do_usage('Required argument `name\' missing.')
99
+ name = ARGV.shift or missing_arg(:name)
107
100
  no_args
108
101
 
109
- file = Lingo.find(what, name, path: path_for_scope, &method(:do_usage))
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
- do_usage('Source and target are the same.') if OPTIONS[:scope] == :local
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
- do_usage('Source and target are the same.') if source == target
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', 'Restrict command to the user\'s personal Lingo directory') {
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 do_usage("Invalid scope `#{scope.inspect}'.")
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
- do_usage('Too many arguments.') unless ARGV.empty?
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!(&Proc.new) if block_given?
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
- def to_h
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.length > 950
54
+ if val.bytesize > MAX_LENGTH
58
55
  warn "Warning: Entry `#{key}' (#{@srcfile}) too long for SDBM. Truncating..."
59
- val = val[0, 950]
56
+ val = val.byteslice(0, MAX_LENGTH)
60
57
  end
61
58
 
62
59
  super
@@ -50,7 +50,7 @@ class Lingo
50
50
  private
51
51
 
52
52
  def convert_line(line, key, val)
53
- values = line.split(@sep).each(&:strip!)
53
+ values = line.split(@sep).each { |i| i.strip! }
54
54
  [values.shift, values]
55
55
  end
56
56
 
@@ -49,7 +49,7 @@ class Lingo
49
49
  private
50
50
 
51
51
  def convert_line(line, key, val)
52
- [nil, line.split(@sep).each(&:strip!)]
52
+ [nil, line.split(@sep).each { |i| i.strip! }]
53
53
  end
54
54
 
55
55
  end
@@ -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 = "(?:#{PRINTABLE_CHAR}|#{LEGAL_CHAR})+"
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)
@@ -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.prepend('.')] = name }
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
- @db.nil? || _closed?
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
- @db.close unless closed?
110
+ _close unless closed?
112
111
  @db = nil
113
112
 
114
113
  self
115
114
  end
116
115
 
117
116
  def to_h
118
- {}.tap { |hash| @db.each { |key, val|
119
- hash[key.force_encoding(ENC).freeze] = val.force_encoding(ENC)
120
- } unless closed? }
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.dup
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
- arg = [key, val.join(FLD_SEP)]
144
- _set(*@crypter ? @crypter.encode(*arg) : arg)
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.force_encoding(ENC)
221
+ _encode!(val)
211
222
  @crypter ? @crypter.decode(key, val) : val
212
223
  end
213
224
  end
214
225
 
215
- def warn(*msg)
216
- @lingo.warn(*msg)
226
+ def _encode!(str)
227
+ str.force_encoding(ENC)
217
228
  end
218
229
 
219
230
  def convert(verbose = @lingo.config.stderr.tty?)
@@ -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