logaling-command 0.1.2 → 0.1.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.
- 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
|