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/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
|