anpo 0.1.0 → 1.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fdf7af205951539c90a16f0cec615f1acfc247c8a573ed5e65fbb904cecd1470
4
- data.tar.gz: a07c13bf87ed7199242ee4b6bafc4144dbe8f87a13e95e956900f1fa2c62def4
3
+ metadata.gz: b86255b8dcc794f8aa38dbd06a8b24840baae8308192061673a1b4c024d2887d
4
+ data.tar.gz: 91ae182af6f1f8f398b0ee0c61821ebd829398f486f3001ef10c9f4197c74aa8
5
5
  SHA512:
6
- metadata.gz: 0b4fe961b9a28bbcbe6353939b0bd5a4ead8e06cbf90720c949971c0cdfb93383808b675a4d3ad29cf6364ec0bb83655a6c76e963efba838176d95c7cf11f6ed
7
- data.tar.gz: 2fe2ae8f06f395272eb715993a3fcbb7d3d942ed88270117f4d25e81adc282b90a26cc19a5d8b86079235bd2b61c3ad6cb33f04e07b5d56253e358cccf1e7833
6
+ metadata.gz: cfce5c4b50344e28e1ba6a86226206bd1c6c7a80bca17bc45ac42a4cd776db882dc63dc5d09278bf76f5871abb9529734f8da50b2e67e8c65e21845c6157905d
7
+ data.tar.gz: aaf924b90685cad5da37f685426548bdd254f6cd13e89ae91c5f943fe7e77ad85cafa6f70adddbbbdb6fc862e798663f9d7e8317e42db2f2cc48d1171d3f9589
data/Rakefile CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
  require "rubocop/rake_task"
5
+ require "rspec/core/rake_task"
5
6
 
6
7
  RuboCop::RakeTask.new
7
8
 
8
- task default: :rubocop
9
+ RSpec::Core::RakeTask.new("spec")
10
+
11
+ task default: :spec
data/anpo.gemspec CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
14
14
  spec.license = "MIT"
15
15
  spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
16
16
 
17
- #spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
17
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
18
18
 
19
19
  spec.metadata["homepage_uri"] = spec.homepage
20
20
  spec.metadata["source_code_uri"] = "https://github.com/soburi/anpo"
@@ -29,8 +29,9 @@ Gem::Specification.new do |spec|
29
29
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
30
  spec.require_paths = ["lib"]
31
31
 
32
- # Uncomment to register a new dependency of your gem
33
- # spec.add_dependency "example-gem", "~> 1.0"
32
+ spec.add_development_dependency "bundler", "~> 2.0"
33
+ spec.add_development_dependency "rake", "~> 10.0"
34
+ spec.add_development_dependency "rspec", "~> 3.0"
34
35
 
35
36
  # For more information and examples about making a new gem, checkout our
36
37
  # guide at: https://bundler.io/guides/creating_gem.html
data/bin/anpofilter ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "anpo"
5
+ require "optparse"
6
+
7
+ def ep(x)
8
+ $stderr.print(x.inspect)
9
+ $stderr.print("\n")
10
+ end
11
+
12
+ opt = OptionParser.new
13
+
14
+ exclude_not_fuzzy = false
15
+ exclude_not_translated = false
16
+ exclude_translated = false
17
+ exclude_fuzzy = false
18
+ matchstr = false
19
+
20
+ subtract = false
21
+ intersect = false
22
+
23
+ argfile = nil
24
+
25
+ opt.on("--str") { matchstr = true }
26
+
27
+ opt.on("--intersect") { |_f| intersect = true }
28
+ opt.on("--and") { |_f| intersect = true }
29
+ opt.on("-A") { |_f| intersect = true }
30
+ opt.on("--subtract") { |_f| subtract = true }
31
+ opt.on("-S") { |_f| subtract = true }
32
+
33
+ opt.on("-f") { |_x| exclude_not_fuzzy = true }
34
+ opt.on("-F") { |_x| exclude_fuzzy = true }
35
+ opt.on("-t") { |_x| exclude_not_translated = true }
36
+ opt.on("-T") { |_x| exclude_translated = true }
37
+
38
+ argv = opt.parse!(ARGV)
39
+
40
+ if intersect || subtract
41
+
42
+ inputs = []
43
+ inputs = if argv.length == 1
44
+ [argv[0], $stdin]
45
+ else
46
+ [argv[1], argv[0]]
47
+ end
48
+
49
+ File.open(inputs[0]) do |file|
50
+ Anpo::PO.parse(file) do |cmpfile|
51
+ infile = Anpo::PO.parse(inputs[1])
52
+
53
+ cmpfile.entries.delete_if(&:is_fuzzy?) if exclude_fuzzy
54
+ cmpfile.entries.delete_if { |e| !e.is_translated? } if exclude_not_translated
55
+
56
+ ids = []
57
+ infile.each do |ent|
58
+ ids.push(ent.msgid) if !cmpfile.msg[ent.msgid] || ((cmpfile.msg[ent.msgid] != ent.msgstr) && matchstr)
59
+ end
60
+
61
+ if subtract
62
+ infile.filter_by_ids(ids)
63
+ elsif intersect
64
+ infile.delete_by_ids(ids)
65
+ end
66
+
67
+ print infile
68
+ end
69
+ end
70
+
71
+ else
72
+
73
+ Anpo::PO.parse(argv.empty? ? $stdin : ARGV[0]) do |po|
74
+ po.entries.delete_if { |e| !e.is_fuzzy? } if exclude_not_fuzzy
75
+ po.entries.delete_if(&:is_fuzzy?) if exclude_fuzzy
76
+ po.entries.delete_if { |e| !e.is_translated? } if exclude_translated
77
+ po.entries.delete_if(&:is_translated?) if exclude_not_translated
78
+ print po
79
+ end
80
+
81
+ end
data/lib/anpo.rb CHANGED
@@ -1,37 +1,63 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "anpo/version"
4
+ require "csv"
5
+ require "stringio"
4
6
 
5
7
  module Anpo
6
8
  class Error < StandardError; end
7
9
 
8
10
  class POEntry
9
- attr_accessor :msgid
10
- attr_accessor :msgstr
11
11
  attr_accessor :comments
12
- attr_accessor :is_header
13
12
 
14
- def initialize(lines, header=false)
15
- state = ''
16
- @is_header = header
13
+ def on_changed(&proc)
14
+ @change_listener.push(proc)
15
+ end
16
+
17
+ def msgid
18
+ @msgid.clone
19
+ end
20
+
21
+ def msgid=(id)
22
+ @change_listener.each do |prc|
23
+ prc.call(self, id, @msgstr)
24
+ end
25
+ @msgid = id
26
+ end
27
+
28
+ def msgstr
29
+ @msgstr.clone
30
+ end
31
+
32
+ def msgstr=(str)
33
+ @change_listener.each do |prc|
34
+ prc.call(self, @msgid, str)
35
+ end
36
+ @msgstr = str
37
+ end
38
+
39
+ def initialize(lines = nil)
40
+ state = ""
41
+ @change_listener = []
17
42
  @msgid = nil
18
43
  @msgstr = nil
19
44
  @comments = []
20
- lines.each do |l|
21
- if l.start_with?("msgid")
22
- state = "msgid"
23
- @msgid = l.gsub("msgid \"","").gsub(/\"\s*$/,"")
24
- elsif l.start_with?("msgstr")
25
- state = "msgstr"
26
- @msgstr = l.gsub("msgstr \"","").gsub(/\"\s*$/,"")
27
- elsif l.start_with?("#")
28
- state = "comment"
29
- @comments.push(l.gsub("\n",'') )
30
- else
31
- if state == "msgid"
32
- @msgid = @msgid + "\n" + l.gsub(/^\s*\"/,'').gsub(/\"\s*$/,'')
45
+
46
+ unless lines.nil?
47
+ lines.each do |l|
48
+ if l.start_with?("msgid")
49
+ state = "msgid"
50
+ @msgid = l.gsub("msgid \"", "").gsub(/"\s*$/, "")
51
+ elsif l.start_with?("msgstr")
52
+ state = "msgstr"
53
+ @msgstr = l.gsub("msgstr \"", "").gsub(/"\s*$/, "")
54
+ elsif l.start_with?("#")
55
+ state = "comment"
56
+ @comments.push(l.gsub("\n", ""))
57
+ elsif state == "msgid"
58
+ @msgid = @msgid + "\n" + l.gsub(/^\s*"/, "").gsub(/"\s*$/, "")
33
59
  elsif state == "msgstr"
34
- @msgstr = @msgstr + "\n" + l.gsub(/^\s*\"/,'').gsub(/\"\s*$/,'')
60
+ @msgstr = @msgstr + "\n" + l.gsub(/^\s*"/, "").gsub(/"\s*$/, "")
35
61
  end
36
62
  end
37
63
  end
@@ -42,26 +68,30 @@ module Anpo
42
68
  end
43
69
 
44
70
  def is_translated?
45
- @msgstr.empty? and not @msgid.empty?
71
+ (@msgid and !@msgid.empty?) and (@msgstr and !@msgstr.empty?)
72
+ end
73
+
74
+ def is_header?
75
+ @msgid and @msgid.empty? and !@msgstr.empty?
46
76
  end
47
77
 
48
78
  def msgid_to_s
49
- if @msgid == nil and @is_header
50
- "msgid \"\"\n"
51
- elsif @msgid == nil
79
+ if @msgid.nil?
52
80
  ""
81
+ elsif is_header?
82
+ "msgid \"\"\n"
53
83
  else
54
- "msgid " + @msgid.split("\n").collect{|x| "\"#{x}\""}.join("\n") + "\n"
84
+ "msgid " + @msgid.split("\n").collect { |x| "\"#{x}\"" }.join("\n") + "\n"
55
85
  end
56
86
  end
57
87
 
58
88
  def msgstr_to_s
59
- if @msgid == nil and not @is_header
89
+ if @msgid.nil?
60
90
  ""
61
91
  elsif @msgstr.empty?
62
92
  "msgstr \"\"\n"
63
93
  else
64
- "msgstr " + @msgstr.split("\n").collect{|x| "\"#{x}\""}.join("\n") + "\n"
94
+ "msgstr " + @msgstr.split("\n").collect { |x| "\"#{x}\"" }.join("\n") + "\n"
65
95
  end
66
96
  end
67
97
 
@@ -74,126 +104,285 @@ module Anpo
74
104
  end
75
105
 
76
106
  def to_s
77
- comments_to_s + msgid_to_s + msgstr_to_s
107
+ comments_to_s + msgid_to_s + msgstr_to_s
78
108
  end
79
109
  end
80
110
 
81
- class PO
82
-
83
- include Enumerable
111
+ class PO < Array
112
+ private
84
113
 
85
- def self.parse(file, &block)
86
- pofile = PO.new(file)
87
- if block
88
- block.call(pofile)
89
- else
90
- pofile
114
+ def set_on_changed(entry)
115
+ entry.on_changed do |_e, _newid, _newstr|
116
+ @dirty = true
91
117
  end
92
118
  end
93
119
 
94
- attr_accessor :entries
120
+ public
95
121
 
96
- def [](key)
97
- @hcache[key]
122
+ def <<(*args)
123
+ super
124
+ @dirty = true
98
125
  end
99
126
 
100
- def update_hashcache
101
- @hcache = {}
102
- @entries.each do |e|
103
- @hcache[e.msgid] = e.msgstr
104
- end
127
+ def append(*args)
128
+ super
129
+ @dirty = true
105
130
  end
106
131
 
107
- def entry(id)
108
- @entries.find {|e| e.msgid == id}
132
+ def clear(*args)
133
+ super
134
+ @dirty = true
109
135
  end
110
136
 
111
- def each(&block)
112
- @entries.each do |e|
113
- block.call(e)
114
- end
137
+ def collect!(*args)
138
+ super
139
+ @dirty = true
140
+ end
141
+
142
+ def compact!(*args)
143
+ super
144
+ @dirty = true
145
+ end
146
+
147
+ def concat(*args)
148
+ super
149
+ @dirty = true
150
+ end
151
+
152
+ def delete(*args)
153
+ super
154
+ @dirty = true
155
+ end
156
+
157
+ def delete_at(*args)
158
+ super
159
+ @dirty = true
160
+ end
161
+
162
+ def delete_if(*args)
163
+ super
164
+ @dirty = true
165
+ end
166
+
167
+ def fill(*args)
168
+ super
169
+ @dirty = true
170
+ end
171
+
172
+ def filter!(*args)
173
+ super
174
+ @dirty = true
175
+ end
176
+
177
+ def flatten!(*args)
178
+ super
179
+ @dirty = true
180
+ end
181
+
182
+ def insert(*args)
183
+ super
184
+ @dirty = true
185
+ end
186
+
187
+ def keep_if(*args)
188
+ super
189
+ @dirty = true
190
+ end
191
+
192
+ def map!(*args)
193
+ super
194
+ @dirty = true
195
+ end
196
+
197
+ def pop(*args)
198
+ super
199
+ @dirty = true
200
+ end
201
+
202
+ def prepend(*args)
203
+ super
204
+ @dirty = true
205
+ end
206
+
207
+ def push(*args)
208
+ super
209
+ @dirty = true
210
+ end
211
+
212
+ def reject!(*args)
213
+ super
214
+ @dirty = true
215
+ end
216
+
217
+ def replace(*args)
218
+ super
219
+ @dirty = true
220
+ end
221
+
222
+ def reverse!(*args)
223
+ super
224
+ @dirty = true
225
+ end
226
+
227
+ def rotate!(*args)
228
+ super
229
+ @dirty = true
230
+ end
231
+
232
+ def select!(*args)
233
+ super
234
+ @dirty = true
115
235
  end
116
236
 
117
- def keys()
118
- @hcache.keys
237
+ def shift(*args)
238
+ super
239
+ @dirty = true
119
240
  end
120
241
 
121
- def values()
122
- @hcache.values
242
+ def shuffle!(*args)
243
+ super
244
+ @dirty = true
123
245
  end
124
246
 
125
- def filter_by_ids(ids)
126
- filterids = @entries.collect {|e| e.msgid}
247
+ def slice!(*args)
248
+ super
249
+ @dirty = true
250
+ end
251
+
252
+ def sort!(*args)
253
+ super
254
+ @dirty = true
255
+ end
256
+
257
+ def sort_by!(*args)
258
+ super
259
+ @dirty = true
260
+ end
261
+
262
+ def uniq!(*args)
263
+ super
264
+ @dirty = true
265
+ end
127
266
 
128
- if not (ids - filterids).empty?
129
- raise
267
+ def unshift(*args)
268
+ super
269
+ @dirty = true
270
+ end
271
+
272
+ def msg
273
+ if @dirty
274
+ @msg.clear
275
+ each do |e|
276
+ @msg[e.msgid] = e.msgstr
277
+ end
278
+ @dirty = false
279
+ end
280
+ @msg
281
+ end
282
+
283
+ def self.parse(input, mode = "r", _opt = {}, &block)
284
+ po = nil
285
+ if input.is_a?(IO)
286
+ po = PO.new(input)
287
+ elsif FileTest.exists?(input.to_s)
288
+ File.open(input.to_s, mode) do |f|
289
+ po = PO.new(f)
290
+ end
130
291
  end
131
292
 
132
- filterids = filterids - ids
133
- @entries.delete_if {|e| filterids.include?(e.msgid)}
134
- update_hashcache
293
+ if block
294
+ block.call(po)
295
+ nil
296
+ else
297
+ po
298
+ end
299
+ end
300
+
301
+ def entry(id)
302
+ find { |e| e.msgid == id }
303
+ end
304
+
305
+ def new_entry(msgid, msgstr, comments = nil)
306
+ poe = POEntry.new
307
+ set_on_changed(poe)
308
+ poe.msgid = msgid
309
+ poe.msgstr = msgstr
310
+ poe.comments = comments || []
311
+ push(poe)
312
+ end
313
+
314
+ def filter_by_ids(keepids, _force = false)
315
+ ids = collect { |e| e.msgid }
316
+
317
+ raise unless (keepids - ids).empty?
318
+
319
+ keep_if { |e| keepids.include?(e.msgid) }
135
320
  end
136
321
 
137
322
  def delete_by_ids(deleteids)
138
- ids = @entries.collect {|e| e.msgid}
323
+ ids = collect { |e| e.msgid }
139
324
 
140
- if not (deleteids - ids).empty?
141
- raise
142
- end
325
+ raise unless (deleteids - ids).empty?
143
326
 
144
- @entries.delete_if {|e| deleteids.include?(e.msgid)}
145
- update_hashcache
327
+ delete_if { |e| deleteids.include?(e.msgid) }
146
328
  end
147
329
 
148
- def initialize(file)
330
+ def initialize(io = nil)
331
+ super()
149
332
  @caches = []
150
- @entries = []
151
333
  @header = nil
334
+ @msg = {}
152
335
 
153
- buffer = []
336
+ if io
337
+ _self = self
338
+ proc_new_entry = proc do |buf|
339
+ ent = POEntry.new(buf)
340
+ set_on_changed(ent)
154
341
 
155
- while l = file.gets
156
- if not @header
157
- if l == "\n"
158
- @header = POEntry.new(buffer, true)
159
- buffer = []
342
+ if ent.is_header?
343
+ if @header
344
+ STDERR.print("duplicate header\n" + ent.to_s)
345
+ else
346
+ @header = ent
347
+ end
348
+ elsif ent.msgid.nil?
349
+ @caches.push(ent)
160
350
  else
161
- buffer.push(l)
351
+ push(ent)
162
352
  end
163
- next
164
353
  end
165
354
 
166
- if l == "\n"
167
- ent = POEntry.new(buffer)
168
- if ent.msgid == nil
169
- @caches.push(ent)
355
+ buffer = []
356
+ while (l = io.gets)
357
+ if l == "\n"
358
+ proc_new_entry.call(buffer)
359
+ buffer = []
170
360
  else
171
- if ent.msgid != nil and ent.msgid.empty?
172
- STDERR.print("duplicate header\n" + ent.to_s)
173
- else
174
- @entries.push(ent)
175
- end
361
+ buffer.push(l)
176
362
  end
177
- buffer = []
178
- else
179
- buffer.push(l)
180
363
  end
181
- end
182
- ent = POEntry.new(buffer)
183
- if ent.msgid == nil
184
- @caches.push(ent)
364
+
365
+ proc_new_entry.call(buffer)
185
366
  else
186
- if not (ent.msgid != nil and ent.msgid.empty?)
187
- @entries.push(ent)
188
- end
367
+ @header = POEntry.new
368
+ set_on_changed(@header)
369
+ @header.msgid = ""
370
+ @header.msgstr = "\n"
189
371
  end
190
- update_hashcache
191
- self
192
372
  end
193
373
 
194
- def to_s(with_cache=true)
195
- ([@header] + @entries + (with_cache ? @caches : [])).collect{|e| e.to_s}.join("\n").to_s
374
+ def to_s(with_cache = true)
375
+ ([@header] + self + (with_cache ? @caches : [])).collect { |e| e.to_s }.join("\n").to_s
196
376
  end
197
377
 
378
+ def to_csv(opts = {})
379
+ # CSV.new(StringIO.new, opts)
380
+ csvstr = CSV.generate(opts) do |csv|
381
+ ([@header] + self + @caches).each do |e|
382
+ csv << [e.comments_to_s.chop, e.msgid, e.msgstr]
383
+ end
384
+ end
385
+ CSV.new(StringIO.new(csvstr))
386
+ end
198
387
  end
199
388
  end
data/lib/anpo/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anpo
4
- VERSION = "0.1.0"
4
+ VERSION = "1.0.0"
5
5
  end
metadata CHANGED
@@ -1,15 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anpo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - TOKITA, Hiroshi
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-13 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2021-03-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
13
55
  description: Simple and stupidly pofile parser.
14
56
  email:
15
57
  - tokita.hiroshi@gmail.com
@@ -27,6 +69,7 @@ files:
27
69
  - README.md
28
70
  - Rakefile
29
71
  - anpo.gemspec
72
+ - bin/anpofilter
30
73
  - bin/console
31
74
  - bin/setup
32
75
  - lib/anpo.rb
@@ -38,7 +81,7 @@ metadata:
38
81
  homepage_uri: https://github.com/soburi/anpo
39
82
  source_code_uri: https://github.com/soburi/anpo
40
83
  changelog_uri: https://github.com/soburi/anpo/CHANGELOG.md
41
- post_install_message:
84
+ post_install_message:
42
85
  rdoc_options: []
43
86
  require_paths:
44
87
  - lib
@@ -53,8 +96,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
96
  - !ruby/object:Gem::Version
54
97
  version: '0'
55
98
  requirements: []
56
- rubygems_version: 3.2.9
57
- signing_key:
99
+ rubygems_version: 3.2.3
100
+ signing_key:
58
101
  specification_version: 4
59
102
  summary: A nutty pofile parser
60
103
  test_files: []