lingo 1.8.2 → 1.8.3

Sign up to get free protection for your applications and to get access to all the features.
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