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/lib/logaling/command.rb
CHANGED
@@ -15,360 +15,5 @@
|
|
15
15
|
# You should have received a copy of the GNU General Public License
|
16
16
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require "logaling/repository"
|
21
|
-
require "logaling/glossary"
|
22
|
-
|
23
|
-
class Logaling::Command < Thor
|
24
|
-
VERSION = "0.1.2"
|
25
|
-
LOGALING_CONFIG = '.logaling'
|
26
|
-
|
27
|
-
map '-a' => :add,
|
28
|
-
'-d' => :delete,
|
29
|
-
'-u' => :update,
|
30
|
-
'-l' => :lookup,
|
31
|
-
'-i' => :import,
|
32
|
-
'-n' => :new,
|
33
|
-
'-r' => :register,
|
34
|
-
'-U' => :unregister,
|
35
|
-
'-L' => :list,
|
36
|
-
'-s' => :show,
|
37
|
-
'-v' => :version
|
38
|
-
|
39
|
-
class_option "glossary", type: :string, aliases: "-g"
|
40
|
-
class_option "source-language", type: :string, aliases: "-S"
|
41
|
-
class_option "target-language", type: :string, aliases: "-T"
|
42
|
-
class_option "logaling-home", type: :string, aliases: "-h"
|
43
|
-
|
44
|
-
desc 'new [PROJECT NAME] [SOURCE LANGUAGE] [TARGET LANGUAGE(optional)]', 'Create .logaling'
|
45
|
-
method_option "no-register", type: :boolean, default: false
|
46
|
-
def new(project_name, source_language, target_language=nil)
|
47
|
-
unless File.exist?(LOGALING_CONFIG)
|
48
|
-
FileUtils.mkdir_p(File.join(LOGALING_CONFIG, "glossary"))
|
49
|
-
config = {"glossary" => project_name, "source-language" => source_language}
|
50
|
-
config["target-language"] = target_language if target_language
|
51
|
-
write_config(File.join(LOGALING_CONFIG, "config"), config)
|
52
|
-
|
53
|
-
register unless options["no-register"]
|
54
|
-
say "Successfully created #{LOGALING_CONFIG}"
|
55
|
-
else
|
56
|
-
say "#{LOGALING_CONFIG} already exists."
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
desc 'import', 'Import external glossary'
|
61
|
-
method_option "list", type: :boolean, default: false
|
62
|
-
def import(external_glossary=nil)
|
63
|
-
require "logaling/external_glossary"
|
64
|
-
Logaling::ExternalGlossary.load
|
65
|
-
if options["list"]
|
66
|
-
Logaling::ExternalGlossary.list.each {|glossary| say "#{glossary.name.bright} : #{glossary.description}" }
|
67
|
-
else
|
68
|
-
repository.import(Logaling::ExternalGlossary.get(external_glossary))
|
69
|
-
end
|
70
|
-
rescue Logaling::ExternalGlossaryNotFound
|
71
|
-
say "'#{external_glossary}' can't find in import list."
|
72
|
-
say "Try 'loga import --list' and confirm import list."
|
73
|
-
end
|
74
|
-
|
75
|
-
desc 'register', 'Register .logaling'
|
76
|
-
def register
|
77
|
-
logaling_path = find_dotfile
|
78
|
-
|
79
|
-
required_options = {"glossary" => "input glossary name '-g <glossary name>'"}
|
80
|
-
config = load_config_and_merge_options(required_options)
|
81
|
-
|
82
|
-
repository.register(logaling_path, config["glossary"])
|
83
|
-
say "#{config['glossary']} is now registered to logaling."
|
84
|
-
rescue Logaling::CommandFailed => e
|
85
|
-
say e.message
|
86
|
-
say "Try 'loga new' first."
|
87
|
-
rescue Logaling::GlossaryAlreadyRegistered => e
|
88
|
-
say "#{config['glossary']} is already registered."
|
89
|
-
end
|
90
|
-
|
91
|
-
desc 'unregister', 'Unregister .logaling'
|
92
|
-
def unregister
|
93
|
-
required_options = {"glossary" => "input glossary name '-g <glossary name>'"}
|
94
|
-
config = load_config_and_merge_options(required_options)
|
95
|
-
|
96
|
-
repository.unregister(config["glossary"])
|
97
|
-
say "#{config['glossary']} is now unregistered."
|
98
|
-
rescue Logaling::CommandFailed => e
|
99
|
-
say e.message
|
100
|
-
rescue Logaling::GlossaryNotFound => e
|
101
|
-
say "#{config['glossary']} is not yet registered."
|
102
|
-
end
|
103
|
-
|
104
|
-
desc 'config [KEY] [VALUE] [--global(optional)]', 'Set config.'
|
105
|
-
method_option "global", type: :boolean, default: false
|
106
|
-
def config(key, value)
|
107
|
-
support_keys = %w(glossary source-language target-language)
|
108
|
-
raise Logaling::CommandFailed, "#{key} is unsupported option" unless support_keys.include?(key)
|
109
|
-
|
110
|
-
config_path = options["global"] ? File.join(LOGALING_HOME, "config") : File.join(find_dotfile, "config")
|
111
|
-
FileUtils.touch(config_path) unless File.exist?(config_path)
|
112
|
-
|
113
|
-
config = load_config(config_path)
|
114
|
-
config = merge_options({key => value}, config)
|
115
|
-
write_config(config_path, config)
|
116
|
-
say "Successfully set config."
|
117
|
-
rescue Logaling::CommandFailed => e
|
118
|
-
say e.message
|
119
|
-
end
|
120
|
-
|
121
|
-
desc 'add [SOURCE TERM] [TARGET TERM] [NOTE(optional)]', 'Add term to glossary.'
|
122
|
-
def add(source_term, target_term, note='')
|
123
|
-
config = load_config_and_merge_options
|
124
|
-
repository.index
|
125
|
-
|
126
|
-
if repository.bilingual_pair_exists?(source_term, target_term, config["glossary"])
|
127
|
-
raise Logaling::TermError, "term '#{source_term}: #{target_term}' already exists in '#{config["glossary"]}'"
|
128
|
-
end
|
129
|
-
|
130
|
-
glossary.add(source_term, target_term, note)
|
131
|
-
rescue Logaling::CommandFailed, Logaling::TermError => e
|
132
|
-
say e.message
|
133
|
-
end
|
134
|
-
|
135
|
-
desc 'delete [SOURCE TERM] [TARGET TERM(optional)] [--force(optional)]', 'Delete term.'
|
136
|
-
method_option "force", type: :boolean, default: false
|
137
|
-
def delete(source_term, target_term=nil)
|
138
|
-
if target_term
|
139
|
-
glossary.delete(source_term, target_term)
|
140
|
-
else
|
141
|
-
glossary.delete_all(source_term, options["force"])
|
142
|
-
end
|
143
|
-
rescue Logaling::CommandFailed, Logaling::TermError => e
|
144
|
-
say e.message
|
145
|
-
rescue Logaling::GlossaryNotFound => e
|
146
|
-
say "Try 'loga new or register' first."
|
147
|
-
end
|
148
|
-
|
149
|
-
desc 'update [SOURCE TERM] [TARGET TERM] [NEW TARGET TERM], [NOTE(optional)]', 'Update term.'
|
150
|
-
def update(source_term, target_term, new_target_term, note='')
|
151
|
-
config = load_config_and_merge_options
|
152
|
-
repository.index
|
153
|
-
|
154
|
-
if repository.bilingual_pair_exists_and_has_same_note?(source_term, new_target_term, note, config["glossary"])
|
155
|
-
raise Logaling::TermError, "term '#{source_term}: #{new_target_term}' already exists in '#{config["glossary"]}'"
|
156
|
-
end
|
157
|
-
|
158
|
-
glossary.update(source_term, target_term, new_target_term, note)
|
159
|
-
rescue Logaling::CommandFailed, Logaling::TermError => e
|
160
|
-
say e.message
|
161
|
-
rescue Logaling::GlossaryNotFound => e
|
162
|
-
say "Try 'loga new or register' first."
|
163
|
-
end
|
164
|
-
|
165
|
-
desc 'lookup [TERM]', 'Lookup terms.'
|
166
|
-
def lookup(source_term)
|
167
|
-
config = load_config_and_merge_options
|
168
|
-
repository.index
|
169
|
-
terms = repository.lookup(source_term, config["source_language"], config["target_language"], config["glossary"])
|
170
|
-
|
171
|
-
unless terms.empty?
|
172
|
-
max_str_size = terms.map{|term| term[:source_term].size}.sort.last
|
173
|
-
run_pager
|
174
|
-
terms.each do |term|
|
175
|
-
target_string = "#{term[:target_term].bright}"
|
176
|
-
target_string << "\t# #{term[:note]}" unless term[:note].empty?
|
177
|
-
if repository.glossary_counts > 1
|
178
|
-
target_string << "\t"
|
179
|
-
glossary_name = "(#{term[:glossary_name]})"
|
180
|
-
if term[:glossary_name] == config["glossary"]
|
181
|
-
target_string << glossary_name.foreground(:white).background(:green)
|
182
|
-
else
|
183
|
-
target_string << glossary_name
|
184
|
-
end
|
185
|
-
end
|
186
|
-
source_string = term[:snipped_source_term].map{|word| word.is_a?(Hash) ? word[:keyword].bright : word }.join
|
187
|
-
printf(" %-#{max_str_size+10}s %s\n", source_string, target_string)
|
188
|
-
end
|
189
|
-
else
|
190
|
-
"source-term <#{source_term}> not found"
|
191
|
-
end
|
192
|
-
rescue Logaling::CommandFailed, Logaling::TermError => e
|
193
|
-
say e.message
|
194
|
-
end
|
195
|
-
|
196
|
-
desc 'version', 'Show version.'
|
197
|
-
def version
|
198
|
-
say "logaling-command version #{Logaling::Command::VERSION}"
|
199
|
-
end
|
200
|
-
|
201
|
-
desc 'show', 'Show terms in glossary.'
|
202
|
-
def show
|
203
|
-
required_options = {
|
204
|
-
"glossary" => "input glossary name '-g <glossary name>'",
|
205
|
-
"source-language" => "input source-language code '-S <source-language code>'",
|
206
|
-
"target-language" => "input target-language code '-T <target-language code>'"
|
207
|
-
}
|
208
|
-
config = load_config_and_merge_options(required_options)
|
209
|
-
repository.index
|
210
|
-
terms = repository.show_glossary(config["glossary"], config["source-language"], config["target-language"])
|
211
|
-
unless terms.empty?
|
212
|
-
run_pager
|
213
|
-
max_str_size = terms.map{|term| term[:source_term].size}.sort.last
|
214
|
-
terms.each do |term|
|
215
|
-
target_string = "#{term[:target_term]}"
|
216
|
-
target_string << "\t# #{term[:note]}" unless term[:note].empty?
|
217
|
-
printf(" %-#{max_str_size+10}s %s\n", term[:source_term], target_string)
|
218
|
-
end
|
219
|
-
else
|
220
|
-
"glossary <#{config['glossary']}> not found"
|
221
|
-
end
|
222
|
-
|
223
|
-
rescue Logaling::CommandFailed, Logaling::GlossaryDBNotFound => e
|
224
|
-
say e.message
|
225
|
-
end
|
226
|
-
|
227
|
-
desc 'list', 'Show glossary list.'
|
228
|
-
def list
|
229
|
-
repository.index
|
230
|
-
glossaries = repository.list
|
231
|
-
unless glossaries.empty?
|
232
|
-
run_pager
|
233
|
-
glossaries.each do |glossary|
|
234
|
-
printf(" %s\n", glossary)
|
235
|
-
end
|
236
|
-
else
|
237
|
-
"There is no registered glossary."
|
238
|
-
end
|
239
|
-
|
240
|
-
rescue Logaling::CommandFailed, Logaling::GlossaryDBNotFound => e
|
241
|
-
say e.message
|
242
|
-
end
|
243
|
-
|
244
|
-
private
|
245
|
-
def repository
|
246
|
-
@repository ||= Logaling::Repository.new(LOGALING_HOME)
|
247
|
-
end
|
248
|
-
|
249
|
-
def glossary
|
250
|
-
if @glossary
|
251
|
-
@glossary
|
252
|
-
else
|
253
|
-
required_options = {
|
254
|
-
"glossary" => "input glossary name '-g <glossary name>'",
|
255
|
-
"source-language" => "input source-language code '-S <source-language code>'",
|
256
|
-
"target-language" => "input target-language code '-T <target-language code>'"
|
257
|
-
}
|
258
|
-
config = load_config_and_merge_options(required_options)
|
259
|
-
@glossary = Logaling::Glossary.new(config["glossary"], config["source-language"], config["target-language"])
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
|
-
def error(msg)
|
264
|
-
STDERR.puts(msg)
|
265
|
-
exit 1
|
266
|
-
end
|
267
|
-
|
268
|
-
def load_config_and_merge_options(required={})
|
269
|
-
config_list ||= {}
|
270
|
-
find_config.each{|type, path| config_list[type] = load_config(path)}
|
271
|
-
global_config = config_list["global_config"] ? config_list["global_config"] : {}
|
272
|
-
project_config = config_list["project_config"] ? config_list["project_config"] : {}
|
273
|
-
|
274
|
-
config = merge_options(project_config, global_config)
|
275
|
-
config = merge_options(options, config)
|
276
|
-
|
277
|
-
required.each do |required_option, message|
|
278
|
-
raise(Logaling::CommandFailed, message) unless config[required_option]
|
279
|
-
end
|
280
|
-
|
281
|
-
config
|
282
|
-
end
|
283
|
-
|
284
|
-
def merge_options(options, secondary_options)
|
285
|
-
config ||={}
|
286
|
-
config["glossary"] = options["glossary"] ? options["glossary"] : secondary_options["glossary"]
|
287
|
-
config["source-language"] = options["source-language"] ? options["source-language"] : secondary_options["source-language"]
|
288
|
-
config["target-language"] = options["target-language"] ? options["target-language"] : secondary_options["target-language"]
|
289
|
-
config
|
290
|
-
end
|
291
|
-
|
292
|
-
def find_config
|
293
|
-
config ||= {}
|
294
|
-
config["project_config"] = File.join(find_dotfile, 'config')
|
295
|
-
config["global_config"] = global_config_path if global_config_path
|
296
|
-
config
|
297
|
-
rescue Logaling::CommandFailed
|
298
|
-
config ||= {}
|
299
|
-
config["project_config"] = repository.config_path if repository.config_path
|
300
|
-
config["global_config"] = global_config_path if global_config_path
|
301
|
-
config
|
302
|
-
end
|
303
|
-
|
304
|
-
def load_config(config_path=nil)
|
305
|
-
config ||= {}
|
306
|
-
if config_path
|
307
|
-
File.readlines(config_path).map{|l| l.chomp.split " "}.each do |option|
|
308
|
-
key = option[0].sub(/^[\-]{2}/, "")
|
309
|
-
value = option[1]
|
310
|
-
config[key] = value
|
311
|
-
end
|
312
|
-
end
|
313
|
-
config
|
314
|
-
end
|
315
|
-
|
316
|
-
def find_dotfile
|
317
|
-
dir = Dir.pwd
|
318
|
-
searched_path = []
|
319
|
-
while(dir) do
|
320
|
-
path = File.join(dir, '.logaling')
|
321
|
-
if File.exist?(path)
|
322
|
-
return path
|
323
|
-
else
|
324
|
-
if dir != "/"
|
325
|
-
searched_path << dir
|
326
|
-
dir = File.dirname(dir)
|
327
|
-
else
|
328
|
-
raise(Logaling::CommandFailed, "Can't found .logaling in #{searched_path}")
|
329
|
-
end
|
330
|
-
end
|
331
|
-
end
|
332
|
-
end
|
333
|
-
|
334
|
-
def global_config_path
|
335
|
-
path = File.join(LOGALING_HOME, "config")
|
336
|
-
File.exist?(path) ? path : nil
|
337
|
-
end
|
338
|
-
|
339
|
-
def write_config(config_path, config)
|
340
|
-
File.open(config_path, 'w') do |fp|
|
341
|
-
fp.puts "--glossary #{config['glossary']}" if config['glossary']
|
342
|
-
fp.puts "--source-language #{config['source-language']}" if config['source-language']
|
343
|
-
fp.puts "--target-language #{config['target-language']}" if config['target-language']
|
344
|
-
end
|
345
|
-
end
|
346
|
-
|
347
|
-
# http://nex-3.com/posts/73-git-style-automatic-paging-in-ruby
|
348
|
-
def run_pager
|
349
|
-
return if ::RUBY_PLATFORM =~ /win32/
|
350
|
-
return unless STDOUT.tty?
|
351
|
-
|
352
|
-
read, write = IO.pipe
|
353
|
-
|
354
|
-
unless Kernel.fork # Child process
|
355
|
-
STDOUT.reopen(write)
|
356
|
-
STDERR.reopen(write) if STDERR.tty?
|
357
|
-
read.close
|
358
|
-
write.close
|
359
|
-
return
|
360
|
-
end
|
361
|
-
|
362
|
-
# Parent process, become pager
|
363
|
-
STDIN.reopen(read)
|
364
|
-
read.close
|
365
|
-
write.close
|
366
|
-
|
367
|
-
ENV['LESS'] = 'FSRX' # Don't page if the input is short enough
|
368
|
-
|
369
|
-
# wait until we have input before we start the pager
|
370
|
-
Kernel.select [STDIN]
|
371
|
-
pager = ENV['PAGER'] || 'less'
|
372
|
-
exec pager rescue exec "/bin/sh", "-c", pager
|
373
|
-
end
|
374
|
-
end
|
18
|
+
require "logaling/command/version"
|
19
|
+
require "logaling/command/application"
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# Copyright (C) 2012 Koji SHIMADA <koji.shimada@enishi-tech.com>
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
module Logaling
|
17
|
+
class Config
|
18
|
+
class << self
|
19
|
+
def load(config_path)
|
20
|
+
config = new
|
21
|
+
config.load(config_path)
|
22
|
+
config
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(config={})
|
27
|
+
@config = config
|
28
|
+
end
|
29
|
+
|
30
|
+
def check_required_option(required={})
|
31
|
+
required.each do |required_option, message|
|
32
|
+
raise(Logaling::CommandFailed, message) unless @config[required_option]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def merge!(config)
|
37
|
+
keys.each do |key|
|
38
|
+
@config[key] = config[key] if config[key]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def load(config_path=nil)
|
43
|
+
if config_path && File.exist?(config_path)
|
44
|
+
File.readlines(config_path).map{|l| l.chomp.split " "}.each do |option|
|
45
|
+
key = option[0].sub(/^[\-]{2}/, "")
|
46
|
+
value = option[1]
|
47
|
+
@config[key] = value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def add(key, value)
|
53
|
+
raise Logaling::CommandFailed, "#{key} is unsupported option" unless support?(key)
|
54
|
+
merge!(key => value)
|
55
|
+
end
|
56
|
+
|
57
|
+
def save(config_path)
|
58
|
+
File.open(config_path, 'w') do |fp|
|
59
|
+
keys.each do |key|
|
60
|
+
fp.puts "--#{key} #{@config[key]}" if @config[key]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def glossary
|
66
|
+
@config["glossary"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def source_language
|
70
|
+
@config["source-language"]
|
71
|
+
end
|
72
|
+
|
73
|
+
def target_language
|
74
|
+
@config["target-language"]
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def keys
|
79
|
+
%w(glossary source-language target-language)
|
80
|
+
end
|
81
|
+
|
82
|
+
def support?(key)
|
83
|
+
keys.include?(key)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2012 Koji SHIMADA <koji.shimada@enishi-tech.com>
|
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 'open-uri'
|
19
|
+
|
20
|
+
module Logaling
|
21
|
+
class FreebsdJpman < ExternalGlossary
|
22
|
+
description 'FreeBSD jpman(http://www.jp.freebsd.org/man-jp/)'
|
23
|
+
source_language 'en'
|
24
|
+
target_language 'ja'
|
25
|
+
output_format 'csv'
|
26
|
+
|
27
|
+
private
|
28
|
+
def convert_to_csv(csv)
|
29
|
+
url = 'http://www.jp.freebsd.org/man-jp/docs/wordlist.txt'
|
30
|
+
open(url,'r:iso-2022-jp') do |f|
|
31
|
+
f.each_line.map{|l| l.encode("utf-8")}.each do |line|
|
32
|
+
next if line =~ /^(#|\t)/
|
33
|
+
csv << line.split(/\t+| {5}/, 2).map(&:strip)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|