logaling-command 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +18 -0
- data/README.md +5 -5
- data/bin/loga +1 -2
- data/lib/logaling/command/application.rb +383 -0
- data/lib/logaling/command/version.rb +22 -0
- data/lib/logaling/command.rb +2 -357
- data/lib/logaling/config.rb +86 -0
- data/lib/logaling/external_glossaries/freebsd_jpman.rb +38 -0
- data/lib/logaling/external_glossaries/mozilla_japan.rb +48 -0
- data/lib/logaling/glossary.rb +27 -17
- data/lib/logaling/glossary_db.rb +102 -53
- data/lib/logaling/repository.rb +8 -4
- data/logaling-command.gemspec +3 -3
- data/spec/logaling/command_spec.rb +78 -91
- data/spec/logaling/glossary_spec.rb +13 -11
- data/spec/logaling/repository_spec.rb +67 -42
- data/spec/spec_helper.rb +13 -3
- metadata +24 -19
data/CHANGES
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
= 0.1.3 / 2012-02-29
|
2
|
+
|
3
|
+
* Mozilla Japan プロジェクトの対訳用語集をインポートできるようにしました。(kdmsnr さん)
|
4
|
+
|
5
|
+
* FreeBSD jpman プロジェクトの対訳用語集をインポートできるようにしました。
|
6
|
+
|
7
|
+
* lookup 時にCSV、JSON形式で出力できるように --output オプションを用意しました。
|
8
|
+
|
9
|
+
* lookup 時に翻訳言語が違う用語集や対訳からも検索出来るように --dictionary オプションを用意しました。
|
10
|
+
|
11
|
+
* lookup、show、list を pager なしでも実行できるように、--no-pager オプションを用意しました。
|
12
|
+
|
13
|
+
* lookup の検索結果に色付けをさせない --no-color オプションを用意しました。
|
14
|
+
|
15
|
+
* .logaling を任意の場所に指定できるように --logaling-config オプションを用意しました。
|
16
|
+
|
17
|
+
* --logaling-home を指定できなくなっていた不具合を修正しました。
|
18
|
+
|
1
19
|
= 0.1.2 / 2012-01-30
|
2
20
|
|
3
21
|
* 英和 / 和英 辞書のインポートができるようになりました。他プロジェクトの用語集をインポートする `loga import` と同じ手順でインポートすることができます。
|
data/README.md
CHANGED
@@ -5,22 +5,22 @@ You can add/update/delete terms to your glossary, and search glossaries at once.
|
|
5
5
|
|
6
6
|
### To install:
|
7
7
|
|
8
|
-
|
8
|
+
% gem install logaling-command
|
9
9
|
|
10
10
|
### Usage:
|
11
11
|
|
12
12
|
Create new glossary:
|
13
13
|
|
14
|
-
|
14
|
+
% loga new my_glossary en ja
|
15
15
|
|
16
16
|
Add term to glossary:
|
17
17
|
|
18
|
-
|
18
|
+
% loga add developer 開発者
|
19
19
|
|
20
20
|
Lookup glossary:
|
21
21
|
|
22
|
-
|
23
|
-
developer
|
22
|
+
% loga lookup develop
|
23
|
+
developer 開発者 (my_glossary)
|
24
24
|
|
25
25
|
### License
|
26
26
|
|
data/bin/loga
CHANGED
@@ -0,0 +1,383 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011 Miho SUZUKI
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
require 'thor'
|
19
|
+
require 'rainbow'
|
20
|
+
require "logaling/repository"
|
21
|
+
require "logaling/glossary"
|
22
|
+
require "logaling/config"
|
23
|
+
|
24
|
+
module Logaling::Command
|
25
|
+
LOGALING_CONFIG = '.logaling'
|
26
|
+
|
27
|
+
class Application < Thor
|
28
|
+
|
29
|
+
def initialize(*args)
|
30
|
+
super
|
31
|
+
@logaling_home = options["logaling-home"] ? options["logaling-home"] : LOGALING_HOME
|
32
|
+
@repository = Logaling::Repository.new(@logaling_home)
|
33
|
+
@config = Logaling::Config.load(@repository.config_path)
|
34
|
+
|
35
|
+
@dotfile_path = options["logaling-config"] ? options["logaling-config"] : find_dotfile
|
36
|
+
@project_config_path = File.join(@dotfile_path, 'config')
|
37
|
+
@config.load(@project_config_path)
|
38
|
+
rescue Logaling::CommandFailed # can't find .logaling
|
39
|
+
@project_config_path = nil
|
40
|
+
ensure
|
41
|
+
@config.merge!(options)
|
42
|
+
end
|
43
|
+
|
44
|
+
map '-a' => :add,
|
45
|
+
'-d' => :delete,
|
46
|
+
'-u' => :update,
|
47
|
+
'-l' => :lookup,
|
48
|
+
'-i' => :import,
|
49
|
+
'-n' => :new,
|
50
|
+
'-r' => :register,
|
51
|
+
'-U' => :unregister,
|
52
|
+
'-L' => :list,
|
53
|
+
'-s' => :show,
|
54
|
+
'-v' => :version
|
55
|
+
|
56
|
+
class_option "glossary", type: :string, aliases: "-g"
|
57
|
+
class_option "source-language", type: :string, aliases: "-S"
|
58
|
+
class_option "target-language", type: :string, aliases: "-T"
|
59
|
+
class_option "logaling-home", type: :string, aliases: "-h"
|
60
|
+
class_option "logaling-config", type: :string, aliases: "-c"
|
61
|
+
|
62
|
+
desc 'new [PROJECT NAME] [SOURCE LANGUAGE] [TARGET LANGUAGE(optional)]', 'Create .logaling'
|
63
|
+
method_option "no-register", type: :boolean, default: false
|
64
|
+
def new(project_name, source_language, target_language=nil)
|
65
|
+
unless File.exist?(logaling_config_path)
|
66
|
+
FileUtils.mkdir_p(File.join(logaling_config_path, "glossary"))
|
67
|
+
|
68
|
+
config = Logaling::Config.new("glossary" => project_name, "source-language" => source_language)
|
69
|
+
config.merge!("target-language" => target_language) if target_language
|
70
|
+
config.save(File.join(logaling_config_path, "config"))
|
71
|
+
|
72
|
+
unless options["no-register"]
|
73
|
+
@dotfile_path = options["logaling-config"] ? options["logaling-config"] : find_dotfile
|
74
|
+
@project_config_path = File.join(@dotfile_path, 'config')
|
75
|
+
@config.load(@project_config_path)
|
76
|
+
register
|
77
|
+
end
|
78
|
+
say "Successfully created #{logaling_config_path}"
|
79
|
+
else
|
80
|
+
say "#{logaling_config_path} already exists."
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
desc 'import', 'Import external glossary'
|
85
|
+
method_option "list", type: :boolean, default: false
|
86
|
+
def import(external_glossary=nil)
|
87
|
+
require "logaling/external_glossary"
|
88
|
+
Logaling::ExternalGlossary.load
|
89
|
+
if options["list"]
|
90
|
+
Logaling::ExternalGlossary.list.each {|glossary| say "#{glossary.name.bright} : #{glossary.description}" }
|
91
|
+
else
|
92
|
+
@repository.import(Logaling::ExternalGlossary.get(external_glossary))
|
93
|
+
@repository.index
|
94
|
+
end
|
95
|
+
rescue Logaling::ExternalGlossaryNotFound
|
96
|
+
say "'#{external_glossary}' can't find in import list."
|
97
|
+
say "Try 'loga import --list' and confirm import list."
|
98
|
+
end
|
99
|
+
|
100
|
+
desc 'register', 'Register .logaling'
|
101
|
+
def register
|
102
|
+
@config.check_required_option("glossary" => "input glossary name '-g <glossary name>'")
|
103
|
+
raise Logaling::CommandFailed, "Try 'loga new' first." unless File.exist?(@dotfile_path)
|
104
|
+
|
105
|
+
@repository.register(@dotfile_path, @config.glossary)
|
106
|
+
@repository.index
|
107
|
+
say "#{@config.glossary} is now registered to logaling."
|
108
|
+
rescue Logaling::CommandFailed => e
|
109
|
+
say e.message
|
110
|
+
rescue Logaling::GlossaryAlreadyRegistered => e
|
111
|
+
say "#{@config.glossary} is already registered."
|
112
|
+
end
|
113
|
+
|
114
|
+
desc 'unregister', 'Unregister .logaling'
|
115
|
+
def unregister
|
116
|
+
@config.check_required_option("glossary" => "input glossary name '-g <glossary name>'")
|
117
|
+
|
118
|
+
@repository.unregister(@config.glossary)
|
119
|
+
@repository.index
|
120
|
+
say "#{@config.glossary} is now unregistered."
|
121
|
+
rescue Logaling::CommandFailed => e
|
122
|
+
say e.message
|
123
|
+
rescue Logaling::GlossaryNotFound => e
|
124
|
+
say "#{@config.glossary} is not yet registered."
|
125
|
+
end
|
126
|
+
|
127
|
+
desc 'config [KEY] [VALUE] [--global(optional)]', 'Set config.'
|
128
|
+
method_option "global", type: :boolean, default: false
|
129
|
+
def config(key, value)
|
130
|
+
config_path = options["global"] ? File.join(@logaling_home, "config") : @project_config_path
|
131
|
+
config = Logaling::Config.load(config_path)
|
132
|
+
config.add(key, value)
|
133
|
+
config.save(config_path)
|
134
|
+
say "Successfully set config."
|
135
|
+
rescue Logaling::CommandFailed => e
|
136
|
+
say e.message
|
137
|
+
end
|
138
|
+
|
139
|
+
desc 'add [SOURCE TERM] [TARGET TERM] [NOTE(optional)]', 'Add term to glossary.'
|
140
|
+
def add(source_term, target_term, note='')
|
141
|
+
required_options = {
|
142
|
+
"glossary" => "input glossary name '-g <glossary name>'",
|
143
|
+
"source-language" => "input source-language code '-S <source-language code>'",
|
144
|
+
"target-language" => "input target-language code '-T <target-language code>'"
|
145
|
+
}
|
146
|
+
@config.check_required_option(required_options)
|
147
|
+
@repository.index
|
148
|
+
|
149
|
+
if @repository.bilingual_pair_exists?(source_term, target_term, @config.glossary)
|
150
|
+
raise Logaling::TermError, "term '#{source_term}: #{target_term}' already exists in '#{@config.glossary}'"
|
151
|
+
end
|
152
|
+
|
153
|
+
glossary.add(source_term, target_term, note)
|
154
|
+
rescue Logaling::CommandFailed, Logaling::TermError => e
|
155
|
+
say e.message
|
156
|
+
end
|
157
|
+
|
158
|
+
desc 'delete [SOURCE TERM] [TARGET TERM(optional)] [--force(optional)]', 'Delete term.'
|
159
|
+
method_option "force", type: :boolean, default: false
|
160
|
+
def delete(source_term, target_term=nil)
|
161
|
+
required_options = {
|
162
|
+
"glossary" => "input glossary name '-g <glossary name>'",
|
163
|
+
"source-language" => "input source-language code '-S <source-language code>'",
|
164
|
+
"target-language" => "input target-language code '-T <target-language code>'"
|
165
|
+
}
|
166
|
+
@config.check_required_option(required_options)
|
167
|
+
|
168
|
+
if target_term
|
169
|
+
glossary.delete(source_term, target_term)
|
170
|
+
else
|
171
|
+
glossary.delete_all(source_term, options["force"])
|
172
|
+
end
|
173
|
+
rescue Logaling::CommandFailed, Logaling::TermError => e
|
174
|
+
say e.message
|
175
|
+
rescue Logaling::GlossaryNotFound => e
|
176
|
+
say "Try 'loga new or register' first."
|
177
|
+
end
|
178
|
+
|
179
|
+
desc 'update [SOURCE TERM] [TARGET TERM] [NEW TARGET TERM] [NOTE(optional)]', 'Update term.'
|
180
|
+
def update(source_term, target_term, new_target_term, note='')
|
181
|
+
required_options = {
|
182
|
+
"glossary" => "input glossary name '-g <glossary name>'",
|
183
|
+
"source-language" => "input source-language code '-S <source-language code>'",
|
184
|
+
"target-language" => "input target-language code '-T <target-language code>'"
|
185
|
+
}
|
186
|
+
@config.check_required_option(required_options)
|
187
|
+
@repository.index
|
188
|
+
|
189
|
+
if @repository.bilingual_pair_exists_and_has_same_note?(source_term, new_target_term, note, @config.glossary)
|
190
|
+
raise Logaling::TermError, "term '#{source_term}: #{new_target_term}' already exists in '#{@config.glossary}'"
|
191
|
+
end
|
192
|
+
|
193
|
+
glossary.update(source_term, target_term, new_target_term, note)
|
194
|
+
rescue Logaling::CommandFailed, Logaling::TermError => e
|
195
|
+
say e.message
|
196
|
+
rescue Logaling::GlossaryNotFound => e
|
197
|
+
say "Try 'loga new or register' first."
|
198
|
+
end
|
199
|
+
|
200
|
+
desc 'lookup [TERM]', 'Lookup terms.'
|
201
|
+
method_option "output", type: :string, default: "terminal"
|
202
|
+
method_option "no-pager", type: :boolean, default: false
|
203
|
+
method_option "no-color", type: :boolean, default: false
|
204
|
+
method_option "dictionary", type: :boolean, default: false, aliases: "--dict"
|
205
|
+
def lookup(source_term)
|
206
|
+
@repository.index
|
207
|
+
terms = @repository.lookup(source_term, glossary, options["dictionary"])
|
208
|
+
unless terms.empty?
|
209
|
+
max_str_size = terms.map{|term| term[:source_term].size}.sort.last
|
210
|
+
run_pager
|
211
|
+
terms.each_with_index do |term, i|
|
212
|
+
source_string = extract_keyword_and_coloring(term[:snipped_source_term], term[:source_term])
|
213
|
+
target_string = extract_keyword_and_coloring(term[:snipped_target_term], term[:target_term])
|
214
|
+
note = term[:note].to_s unless term[:note].empty?
|
215
|
+
glossary_name = ""
|
216
|
+
if @repository.glossary_counts > 1
|
217
|
+
glossary_name = term[:glossary_name]
|
218
|
+
if term[:glossary_name] == @config.glossary
|
219
|
+
glossary_name = glossary_name.foreground(:white).background(:green)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
printer(source_string, target_string, note,
|
223
|
+
glossary_name, max_str_size, i, terms.length)
|
224
|
+
end
|
225
|
+
else
|
226
|
+
"source-term <#{source_term}> not found"
|
227
|
+
end
|
228
|
+
rescue Logaling::CommandFailed, Logaling::TermError => e
|
229
|
+
say e.message
|
230
|
+
end
|
231
|
+
|
232
|
+
desc 'version', 'Show version.'
|
233
|
+
def version
|
234
|
+
say "logaling-command version #{Logaling::Command::VERSION}"
|
235
|
+
end
|
236
|
+
|
237
|
+
desc 'show', 'Show terms in glossary.'
|
238
|
+
method_option "no-pager", type: :boolean, default: false
|
239
|
+
def show
|
240
|
+
required_options = {
|
241
|
+
"glossary" => "input glossary name '-g <glossary name>'",
|
242
|
+
"source-language" => "input source-language code '-S <source-language code>'",
|
243
|
+
"target-language" => "input target-language code '-T <target-language code>'"
|
244
|
+
}
|
245
|
+
@config.check_required_option(required_options)
|
246
|
+
@repository.index
|
247
|
+
terms = @repository.show_glossary(glossary)
|
248
|
+
unless terms.empty?
|
249
|
+
run_pager
|
250
|
+
max_str_size = terms.map{|term| term[:source_term].size}.sort.last
|
251
|
+
terms.each do |term|
|
252
|
+
target_string = "#{term[:target_term]}"
|
253
|
+
target_string << "\t# #{term[:note]}" unless term[:note].empty?
|
254
|
+
printf(" %-#{max_str_size+10}s %s\n", term[:source_term], target_string)
|
255
|
+
end
|
256
|
+
else
|
257
|
+
"glossary <#{@config.glossary}> not found"
|
258
|
+
end
|
259
|
+
|
260
|
+
rescue Logaling::CommandFailed, Logaling::GlossaryDBNotFound => e
|
261
|
+
say e.message
|
262
|
+
end
|
263
|
+
|
264
|
+
desc 'list', 'Show glossary list.'
|
265
|
+
method_option "no-pager", type: :boolean, default: false
|
266
|
+
def list
|
267
|
+
@repository.index
|
268
|
+
glossaries = @repository.list
|
269
|
+
unless glossaries.empty?
|
270
|
+
run_pager
|
271
|
+
glossaries.each do |glossary|
|
272
|
+
printf(" %s\n", glossary)
|
273
|
+
end
|
274
|
+
else
|
275
|
+
"There is no registered glossary."
|
276
|
+
end
|
277
|
+
|
278
|
+
rescue Logaling::CommandFailed, Logaling::GlossaryDBNotFound => e
|
279
|
+
say e.message
|
280
|
+
end
|
281
|
+
|
282
|
+
private
|
283
|
+
def glossary
|
284
|
+
@glossary ||= Logaling::Glossary.new(@config.glossary, @config.source_language, @config.target_language, @logaling_home)
|
285
|
+
end
|
286
|
+
|
287
|
+
def error(msg)
|
288
|
+
STDERR.puts(msg)
|
289
|
+
exit 1
|
290
|
+
end
|
291
|
+
|
292
|
+
def find_dotfile
|
293
|
+
dir = Dir.pwd
|
294
|
+
searched_path = []
|
295
|
+
while(dir) do
|
296
|
+
path = File.join(dir, '.logaling')
|
297
|
+
if File.exist?(path)
|
298
|
+
return path
|
299
|
+
else
|
300
|
+
if dir != "/"
|
301
|
+
searched_path << dir
|
302
|
+
dir = File.dirname(dir)
|
303
|
+
else
|
304
|
+
raise(Logaling::CommandFailed, "Can't found .logaling in #{searched_path}")
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def logaling_config_path
|
311
|
+
if options["logaling-config"]
|
312
|
+
options["logaling-config"]
|
313
|
+
else
|
314
|
+
File.join(Dir.pwd, LOGALING_CONFIG)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
# http://nex-3.com/posts/73-git-style-automatic-paging-in-ruby
|
319
|
+
def run_pager
|
320
|
+
return if options["no-pager"]
|
321
|
+
return if ::RUBY_PLATFORM =~ /win32/
|
322
|
+
return unless STDOUT.tty?
|
323
|
+
|
324
|
+
read, write = IO.pipe
|
325
|
+
|
326
|
+
unless Kernel.fork # Child process
|
327
|
+
STDOUT.reopen(write)
|
328
|
+
STDERR.reopen(write) if STDERR.tty?
|
329
|
+
read.close
|
330
|
+
write.close
|
331
|
+
return
|
332
|
+
end
|
333
|
+
|
334
|
+
# Parent process, become pager
|
335
|
+
STDIN.reopen(read)
|
336
|
+
read.close
|
337
|
+
write.close
|
338
|
+
|
339
|
+
ENV['LESS'] = 'FSRX' # Don't page if the input is short enough
|
340
|
+
|
341
|
+
# wait until we have input before we start the pager
|
342
|
+
Kernel.select [STDIN]
|
343
|
+
pager = ENV['PAGER'] || 'less'
|
344
|
+
exec pager rescue exec "/bin/sh", "-c", pager
|
345
|
+
end
|
346
|
+
|
347
|
+
def extract_keyword_and_coloring(snipped_term, term)
|
348
|
+
return term if snipped_term.empty? || options["no-color"]
|
349
|
+
display_string = snipped_term.map do |word|
|
350
|
+
word.is_a?(Hash) ? word[:keyword].bright : word
|
351
|
+
end
|
352
|
+
display_string = display_string.join
|
353
|
+
display_string
|
354
|
+
end
|
355
|
+
|
356
|
+
def printer(source_string, target_string, note=nil,
|
357
|
+
glossary_name, max_str_size, i, last)
|
358
|
+
case options["output"]
|
359
|
+
when "terminal"
|
360
|
+
unless note
|
361
|
+
format = target_string + "\t" + glossary_name
|
362
|
+
else
|
363
|
+
format = target_string + "\t# " + note + "\t" + glossary_name
|
364
|
+
end
|
365
|
+
printf(" %-#{max_str_size+10}s %s\n", source_string, format)
|
366
|
+
when "csv"
|
367
|
+
items = [source_string, target_string, note,
|
368
|
+
@config.source_language, @config.target_language]
|
369
|
+
print(CSV.generate {|csv| csv << items})
|
370
|
+
when "json"
|
371
|
+
puts("[") if i == 0
|
372
|
+
puts(",") if i > 0
|
373
|
+
record = {
|
374
|
+
:source => source_string, :target => target_string, :note => note,
|
375
|
+
:source_language => @config.source_language,
|
376
|
+
:target_language => @config.target_language
|
377
|
+
}
|
378
|
+
print JSON.pretty_generate(record)
|
379
|
+
puts("\n]") if i == last-1
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011 Miho SUZUKI
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
module Logaling
|
19
|
+
module Command
|
20
|
+
VERSION = "0.1.3"
|
21
|
+
end
|
22
|
+
end
|