text_alignment 0.6.3 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6bed1eba72da626227ab727ce22129d226539bcfae5ca22006ac26258b184d8c
4
- data.tar.gz: d2c121ea072186fd25fd61fb90c5ffacb886c1d109b82c044a1666220b8f7d8b
3
+ metadata.gz: '0788bdb6d161499f5a5258757b9f61faee96b60246b422b57b17ba953b4a2c87'
4
+ data.tar.gz: 4564fd15e1e1d673932438206989dc706aa67f2467698f16946e2635c562ec90
5
5
  SHA512:
6
- metadata.gz: 6e526995325e79fdde8ecd729c04e2e6a21e13f0166acc39b341133055275a1bbd5a3318f78dd5af4a72237c140fa8eb06270441a16e2426e58a57183b91ca6a
7
- data.tar.gz: ec423d59036b1ee5595141428fe320f0e9ca16b8b2660d46a0f59f376c3845ad70196d006c2f83390ac12f98b35ff14a1098fcd24cda0ee1c6534f36915def81
6
+ metadata.gz: c93882cc28e8bfdbdeed325b282b63fd3f3644d1739ed979ef92a7d8a133e26f4a8ffd0bda22da0fc2e0a31c77c6d49ce89f8f0fae802c9ae2041a7db60a2a4e
7
+ data.tar.gz: 238ed44ac0c0a178a64743846550639c626d3a300a79f05fedb016e71a890f11025b52d260ce2f46f00d68b1c4b9c13c48a8be8712f737bb2b57b0274b174b8b
@@ -105,9 +105,7 @@ lost_annotations = []
105
105
  target_annotations = if source_annotations.class == Array
106
106
  align_mdoc(source_annotations, {text: target_text})
107
107
  else
108
- alignment = TextAlignment::TextAlignment.new(source_annotations[:text], target_text)
109
-
110
- # pp alignment
108
+ alignment = TextAlignment::TextAlignment.new(source_annotations[:text], target_text, source_annotations[:denotations])
111
109
 
112
110
  # verification
113
111
  # source_text = source_annotations[:text]
@@ -142,22 +140,7 @@ else
142
140
  puts "====="
143
141
  # exit
144
142
 
145
- # verification of source denotations
146
- puts "[Invalid source denotations]"
147
- source_annotations[:denotations] do |d|
148
- p d unless d[:span][:begin] && d[:span][:end] && d[:span][:begin] < d[:span][:end] && d[:span][:begin] >= 0 && d[:span][:end] < source_text.length
149
- end
150
- puts "====="
151
- puts
152
-
153
143
  denotations = alignment.transform_hdenotations(source_annotations[:denotations])
154
- puts "[Invalid transformation]"
155
- denotations.each do |d|
156
- p d unless d[:span][:begin] && d[:span][:end] && d[:span][:begin] < d[:span][:end] && d[:span][:begin] >= 0 && d[:span][:end] < target_text.length
157
- end
158
- puts "====="
159
- puts
160
-
161
144
  lost_annotations += alignment.lost_annotations if alignment.lost_annotations
162
145
 
163
146
  source_annotations.merge({text:target_text, denotations:denotations})
@@ -194,7 +177,9 @@ warn "denotations:\t#{target_annotations[:denotations].nil? ? 0 : target_annotat
194
177
 
195
178
  if lost_annotations
196
179
  warn "\n[lost annotations]"
197
- warn "#{lost_annotations.length}"
180
+ lost_annotations.each do |a|
181
+ p a
182
+ end
198
183
  end
199
184
 
200
185
  #puts target_annotations.to_json
@@ -1,7 +1,7 @@
1
1
  module TextAlignment; end unless defined? TextAlignment
2
2
 
3
3
  TextAlignment::SIZE_NGRAM = 8 unless defined? TextAlignment::SIZE_NGRAM
4
- TextAlignment::SIZE_WINDOW = 60 unless defined? TextAlignment::SIZE_WINDOW
4
+ TextAlignment::SIZE_WINDOW = 30 unless defined? TextAlignment::SIZE_WINDOW
5
5
  TextAlignment::BUFFER_RATE = 0.1 unless defined? TextAlignment::BUFFER_RATE
6
6
  TextAlignment::BUFFER_MIN = 20 unless defined? TextAlignment::BUFFER_MIN
7
7
  TextAlignment::TEXT_SIMILARITY_THRESHOLD = 0.9 unless defined? TextAlignment::TEXT_SIMILARITY_THRESHOLD
@@ -63,6 +63,7 @@ TextAlignment::MAPPINGS = [
63
63
  [" ", " "], #U+200A (hair space)
64
64
  [" ", " "], #U+00A0 (no-break space)
65
65
  [" ", " "], #U+3000 (ideographic space)
66
+ ["‑", "-"], #U+2211 (Non-Breaking Hyphen)
66
67
  ["−", "-"], #U+2212 (minus sign)
67
68
  ["–", "-"], #U+2013 (en dash)
68
69
  ["′", "'"], #U+2032 (prime)
@@ -12,43 +12,46 @@ class TextAlignment::TextAlignment
12
12
  attr_reader :similarity
13
13
  attr_reader :lost_annotations
14
14
 
15
- def initialize(str1, str2, _size_ngram = nil, _size_window = nil, _text_similiarity_threshold = nil)
15
+ def initialize(str1, str2, denotations = nil, _size_ngram = nil, _size_window = nil, _text_similiarity_threshold = nil)
16
16
  raise ArgumentError, "nil string" if str1.nil? || str2.nil?
17
17
 
18
18
  @block_alignment = {source_text:str1, target_text:str2}
19
+ @str1 = str1
20
+ @str2 = str2
19
21
 
20
- # try exact match
22
+ ## Block exact match
21
23
  block_begin = str2.index(str1)
22
24
  unless block_begin.nil?
23
25
  @block_alignment[:blocks] = [{source:{begin:0, end:str1.length}, target:{begin:block_begin, end:block_begin + str1.length}, delta:block_begin, alignment: :block}]
24
- return @block_alignment
26
+ return
25
27
  end
26
28
 
27
- # try exact match
28
29
  block_begin = str2.downcase.index(str1.downcase)
29
30
  unless block_begin.nil?
30
31
  @block_alignment[:blocks] = [{source:{begin:0, end:str1.length}, target:{begin:block_begin, end:block_begin + str1.length}, delta:block_begin, alignment: :block}]
31
- return @block_alignment
32
+ return
32
33
  end
33
34
 
35
+
36
+ ## to find block alignments
34
37
  anchor_finder = TextAlignment::AnchorFinder.new(str1, str2, _size_ngram, _size_window, _text_similiarity_threshold)
35
38
 
36
- # To collect matched blocks
37
- mblocks = []
38
- while anchor = anchor_finder.get_next_anchor
39
- last = mblocks.last
40
- if last && (anchor[:source][:begin] == last[:source][:end] + 1) && (anchor[:target][:begin] == last[:target][:end] + 1)
41
- last[:source][:end] = anchor[:source][:end]
42
- last[:target][:end] = anchor[:target][:end]
39
+ blocks = []
40
+ while block = anchor_finder.get_next_anchor
41
+ last = blocks.last
42
+ if last && (block[:source][:begin] == last[:source][:end] + 1) && (block[:target][:begin] == last[:target][:end] + 1)
43
+ last[:source][:end] = block[:source][:end]
44
+ last[:target][:end] = block[:target][:end]
43
45
  else
44
- mblocks << anchor
46
+ blocks << block.merge(alignment: :block, delta: block[:target][:begin] - block[:source][:begin])
45
47
  end
46
48
  end
47
49
 
48
- # pp mblocks
50
+ # pp blocks
49
51
  # puts "-----"
50
52
  # puts
51
- # mblocks.each do |b|
53
+ # exit
54
+ # blocks.each do |b|
52
55
  # p [b[:source], b[:target]]
53
56
  # puts "---"
54
57
  # puts str1[b[:source][:begin] ... b[:source][:end]]
@@ -60,114 +63,202 @@ class TextAlignment::TextAlignment
60
63
  # puts "-=-=-=-=-"
61
64
  # puts
62
65
 
63
- ## To find block alignments
64
- @block_alignment[:blocks] = []
65
- return if mblocks.empty?
66
-
67
- # Initial step
68
- if mblocks[0][:source][:begin] > 0
69
- e1 = mblocks[0][:source][:begin]
70
- e2 = mblocks[0][:target][:begin]
66
+ ## to fill the gaps
67
+ last_block = nil
68
+ blocks2 = blocks.inject([]) do |sum, block|
69
+ b1 = last_block ? last_block[:source][:end] : 0
70
+ e1 = block[:source][:begin]
71
71
 
72
- if mblocks[0][:target][:begin] == 0
73
- @block_alignment[:blocks] << {source:{begin:0, end:e1}, target:{begin:0, end:0}, alignment: :empty}
72
+ sum += if b1 == e1
73
+ [block]
74
74
  else
75
- _str1 = str1[0 ... e1]
76
- _str2 = str2[0 ... e2]
75
+ b2 = last_block ? last_block[:target][:end] : 0
76
+ e2 = block[:target][:begin]
77
+
78
+ if b2 == e2
79
+ [
80
+ {source:{begin:b1, end:e1}, target:{begin:b2, end:e2}, alignment: :empty},
81
+ block
82
+ ]
83
+ else
84
+ if b1 == 0 && b2 == 0
85
+ len_buffer = (e1 * (1 + TextAlignment::BUFFER_RATE)).to_i + TextAlignment::BUFFER_MIN
86
+ b2 = e2 - len_buffer if e2 > len_buffer
87
+ end
88
+
89
+ _str1 = str1[b1 ... e1]
90
+ _str2 = str2[b2 ... e2]
77
91
 
78
- unless _str1.strip.empty?
79
- if _str2.strip.empty?
80
- @block_alignment[:blocks] << {source:{begin:0, end:e1}, target:{begin:0, end:e2}, alignment: :empty}
92
+ if _str1.strip.empty? || _str2.strip.empty?
93
+ [
94
+ {source:{begin:b1, end:e1}, target:{begin:b2, end:e2}, alignment: :empty},
95
+ block
96
+ ]
81
97
  else
82
- len_min = [_str1.length, _str2.length].min
83
- len_buffer = (len_min * (1 + TextAlignment::BUFFER_RATE)).to_i + TextAlignment::BUFFER_MIN
84
- b1 = _str1.length < len_buffer ? 0 : e1 - len_buffer
85
- b2 = _str2.length < len_buffer ? 0 : e2 - len_buffer
86
-
87
- @block_alignment[:blocks] << {source:{begin:0, end:b1}, target:{begin:0, end:b2}, alignment: :empty} if b1 > 0
88
-
89
- _str1 = str1[b1 ... e1]
90
- _str2 = str2[b2 ... e2]
91
- alignment = TextAlignment::MixedAlignment.new(_str1.downcase, _str2.downcase)
92
- if alignment.similarity < 0.5
93
- @block_alignment[:blocks] << {source:{begin:b1, end:e1}, target:{begin:0, end:e2}, alignment: :empty, similarity: alignment.similarity}
94
- else
95
- @block_alignment[:blocks] << {source:{begin:b1, end:e1}, target:{begin:0, end:e2}, alignment:alignment, similarity: alignment.similarity}
96
- end
98
+ local_alignment_blocks(str1, b1, e1, str2, b2, e2, denotations) << block
97
99
  end
98
100
  end
99
101
  end
102
+
103
+ last_block = block
104
+ sum
100
105
  end
101
- @block_alignment[:blocks] << mblocks[0].merge(alignment: :block)
102
-
103
- (1 ... mblocks.length).each do |i|
104
- b1 = mblocks[i - 1][:source][:end]
105
- b2 = mblocks[i - 1][:target][:end]
106
- e1 = mblocks[i][:source][:begin]
107
- e2 = mblocks[i][:target][:begin]
108
- _str1 = str1[b1 ... e1]
109
- _str2 = str2[b2 ... e2]
110
- unless _str1.strip.empty?
111
- if _str2.strip.empty?
112
- @block_alignment[:blocks] << {source:{begin:b1, end:e1}, target:{begin:b2, end:e2}, alignment: :empty}
106
+
107
+ # the last step
108
+ blocks2 += if last_block.nil?
109
+ local_alignment_blocks(str1, 0, str1.length, str2, 0, str2.length, denotations)
110
+ else
111
+ b1 = last_block[:source][:end]
112
+ if b1 < str1.length
113
+ e1 = str1.length
114
+
115
+ b2 = last_block[:target][:end]
116
+ if b2 < str2.length
117
+ len_buffer = ((e1 - b1) * (1 + TextAlignment::BUFFER_RATE)).to_i + TextAlignment::BUFFER_MIN
118
+ e2 = (str2.length - b2) > len_buffer ? b2 + len_buffer : str2.length
119
+ local_alignment_blocks(str1, b1, e1, str2, b2, e2, denotations)
113
120
  else
114
- alignment = TextAlignment::MixedAlignment.new(_str1.downcase, _str2.downcase)
115
- if alignment.similarity < 0.5
116
- @block_alignment[:blocks] << {source:{begin:b1, end:e1}, target:{begin:b2, end:e2}, alignment: :empty, similarity: alignment.similarity}
117
- else
118
- @block_alignment[:blocks] << {source:{begin:b1, end:e1}, target:{begin:b2, end:e2}, alignment:alignment, similarity: alignment.similarity}
121
+ [{source:{begin:last_block[:source][:end], end:str1.length}, alignment: :empty}]
122
+ end
123
+ else
124
+ []
125
+ end
126
+ end
127
+
128
+ @block_alignment[:blocks] = blocks2
129
+ end
130
+
131
+ def local_alignment_blocks(str1, b1, e1, str2, b2, e2, denotations = nil)
132
+ block2 = str2[b2 ... e2]
133
+
134
+ ## term-based alignment
135
+ tblocks = if denotations
136
+ ds_in_scope = denotations.select{|d| d[:span][:begin] >= b1 && d[:span][:end] <= e1}.
137
+ sort{|d1, d2| d1[:span][:begin] <=> d2[:span][:begin] || d2[:span][:end] <=> d1[:span][:end] }.
138
+ map{|d| d.merge(lex:str1[d[:span][:begin] ... d[:span][:end]])}
139
+
140
+ position = 0
141
+ tblocks = ds_in_scope.map do |term|
142
+ lex = term[:lex]
143
+ r = block2.index(lex, position)
144
+ if r.nil?
145
+ position = nil
146
+ break
147
+ end
148
+ position = r + lex.length
149
+ {source:term[:span], target:{begin:r + b2, end:r + b2 + lex.length}, alignment: :term, delta: r - term[:span][:begin]}
150
+ end
151
+
152
+ # missing term found
153
+ tblocks = [] if position.nil?
154
+
155
+ # redundant matching found
156
+ unless position.nil?
157
+ ds_in_scope.each do |term|
158
+ lex = term[:lex]
159
+ look_forward = block2.index(lex, position)
160
+ unless look_forward.nil?
161
+ puts lex
162
+ tblocks = []
163
+ break
119
164
  end
120
165
  end
121
166
  end
122
- @block_alignment[:blocks] << mblocks[i].merge(alignment: :block)
167
+
168
+ tblocks
123
169
  end
124
170
 
125
- # Final step
126
- if mblocks[-1][:source][:end] < str1.length && mblocks[-1][:target][:end] < str2.length
127
- b1 = mblocks[-1][:source][:end]
128
- b2 = mblocks[-1][:target][:end]
129
- _str1 = str1[b1 ... str1.length]
130
- _str2 = str2[b2 ... str2.length]
171
+ if tblocks.empty?
172
+ if b1 == 0 && e1 == str1.length
173
+ if (e1 > 2000) || (e2 > 2000)
174
+ [{source:{begin:b1, end:e1}, target:{begin:b2, end:e2}, alignment: :empty}]
175
+ else
176
+ block1 = str1[b1 ... e1]
177
+ block2 = str2[b2 ... e2]
131
178
 
132
- unless _str1.strip.empty?
133
- if _str2.strip.empty?
134
- @block_alignment[:blocks] << {source:{begin:b1, end:str1.length}, target:{begin:b2, end:str2.length}, alignment: :empty}
179
+ ## character-based alignment
180
+ alignment = TextAlignment::MixedAlignment.new(block1.downcase, block2.downcase)
181
+ if alignment.sdiff.nil?
182
+ [{source:{begin:b1, end:e1}, target:{begin:b2, end:e2}, alignment: :empty}]
183
+ else
184
+ [{source:{begin:b1, end:e1}, target:{begin:b2, end:e2}, alignment: alignment, similarity: alignment.similarity}]
185
+ end
186
+ end
187
+ else
188
+ block1 = str1[b1 ... e1]
189
+ block2 = str2[b2 ... e2]
190
+
191
+ ## character-based alignment
192
+ alignment = TextAlignment::MixedAlignment.new(block1.downcase, block2.downcase)
193
+ if alignment.sdiff.nil?
194
+ [{source:{begin:b1, end:e1}, target:{begin:b2, end:e2}, alignment: :empty}]
135
195
  else
136
- len_min = [_str1.length, _str2.length].min
137
- len_buffer = (len_min * (1 + TextAlignment::BUFFER_RATE)).to_i + TextAlignment::BUFFER_MIN
138
- e1 = _str1.length < len_buffer ? str1.length : b1 + len_buffer
139
- e2 = _str2.length < len_buffer ? str2.length : b2 + len_buffer
140
- _str1 = str1[b1 ... e1]
141
- _str2 = str2[b2 ... e2]
196
+ [{source:{begin:b1, end:e1}, target:{begin:b2, end:e2}, alignment: alignment, similarity: alignment.similarity}]
197
+ end
198
+ end
199
+ else
200
+ last_tblock = nil
201
+ lblocks = tblocks.inject([]) do |sum, tblock|
202
+ tb1 = last_tblock ? last_tblock[:source][:end] : b1
203
+ te1 = tblock[:source][:begin]
142
204
 
143
- alignment = TextAlignment::MixedAlignment.new(_str1.downcase, _str2.downcase)
144
- if alignment.similarity < 0.5
145
- @block_alignment[:blocks] << {source:{begin:b1, end:e1}, target:{begin:b2, end:e2}, alignment: :empty, similarity: alignment.similarity}
205
+ sum += if te1 == tb1
206
+ [tblock]
207
+ else
208
+ tb2 = last_tblock ? last_tblock[:target][:end] : b2
209
+ te2 = tblock[:target][:begin]
210
+
211
+ if b2 == e2
212
+ [
213
+ {source:{begin:tb1, end:te1}, alignment: :empty},
214
+ tblock
215
+ ]
146
216
  else
147
- @block_alignment[:blocks] << {source:{begin:b1, end:e1}, target:{begin:b2, end:e2}, alignment:alignment, similarity: alignment.similarity}
217
+ [
218
+ {source:{begin:tb1, end:te1}, target:{begin:tb2, end:te2}, alignment: :empty},
219
+ tblock
220
+ ]
148
221
  end
222
+ end
149
223
 
150
- @block_alignment[:blocks] << {source:{begin:e1, end:-1}, target:{begin:e2, end:-1}, alignment: :empty} if e1 < str1.length
224
+ last_tblock = tblock
225
+ sum
226
+ end
227
+
228
+ if last_tblock[:source][:end] < e1
229
+ if last_tblock[:target][:end] < e2
230
+ lblocks << {source:{begin:last_tblock[:source][:end], end:e1}, target:{begin:last_tblock[:target][:end], end:e2}, alignment: :empty}
231
+ else
232
+ lblocks << {source:{begin:last_tblock[:source][:end], end:e1}, alignment: :empty}
151
233
  end
152
234
  end
153
- end
154
235
 
155
- @block_alignment[:blocks].each do |a|
156
- a[:delta] = a[:target][:begin] - a[:source][:begin]
236
+ lblocks
157
237
  end
158
238
  end
159
239
 
240
+
241
+ def indices(str, target)
242
+ position = 0
243
+ len = target.len
244
+ Enumerator.new do |yielder|
245
+ while idx = str.index(target, position)
246
+ yielder << idx
247
+ position = idx + len
248
+ end
249
+ end
250
+ end
251
+
160
252
  def transform_begin_position(begin_position)
161
253
  i = @block_alignment[:blocks].index{|b| b[:source][:end] > begin_position}
162
254
  block = @block_alignment[:blocks][i]
163
255
 
164
- b = if block[:alignment] == :block
256
+ b = if block[:alignment] == :block || block[:alignment] == :term
165
257
  begin_position + block[:delta]
166
258
  elsif block[:alignment] == :empty
167
259
  if begin_position == block[:source][:begin]
168
260
  block[:target][:begin]
169
261
  else
170
- # raise "lost annotation"
171
262
  nil
172
263
  end
173
264
  else
@@ -180,13 +271,12 @@ class TextAlignment::TextAlignment
180
271
  i = @block_alignment[:blocks].index{|b| b[:source][:end] >= end_position}
181
272
  block = @block_alignment[:blocks][i]
182
273
 
183
- e = if block[:alignment] == :block
274
+ e = if block[:alignment] == :block || block[:alignment] == :term
184
275
  end_position + block[:delta]
185
276
  elsif block[:alignment] == :empty
186
277
  if end_position == block[:source][:end]
187
278
  block[:target][:end]
188
279
  else
189
- # raise "lost annotation"
190
280
  nil
191
281
  end
192
282
  else
@@ -208,14 +298,14 @@ class TextAlignment::TextAlignment
208
298
  @lost_annotations = []
209
299
 
210
300
  denotations.each do |d|
211
- begin
212
- d.begin = transform_begin_position(d.begin);
213
- d.end = transform_end_position(d.end);
214
- rescue
215
- @lost_annotations << d
216
- d.begin = nil
217
- d.end = nil
218
- end
301
+ source = {begin:d.begin, end:d.end}
302
+ d.begin = transform_begin_position(d.begin);
303
+ d.end = transform_end_position(d.end);
304
+ raise "invalid transform" unless !d.begin.nil? && !d.end.nil? && d.begin >= 0 && d.end > d.begin && d.end <= @str2.length
305
+ rescue
306
+ @lost_annotations << {source: source, target:{begin:d.begin, end:d.end}}
307
+ d.begin = nil
308
+ d.end = nil
219
309
  end
220
310
 
221
311
  @lost_annotations
@@ -226,12 +316,12 @@ class TextAlignment::TextAlignment
226
316
  @lost_annotations = []
227
317
 
228
318
  r = hdenotations.collect do |d|
229
- new_d = begin
230
- d.dup.merge({span:transform_a_span(d[:span])})
231
- rescue
232
- @lost_annotations << d
233
- nil
234
- end
319
+ t = transform_a_span(d[:span])
320
+ raise "invalid transform" unless !t[:begin].nil? && !t[:end].nil? && t[:begin] >= 0 && t[:end] > t[:begin] && t[:end] <= @str2.length
321
+ new_d = d.dup.merge({span:t})
322
+ rescue
323
+ @lost_annotations << {source: d[:span], target:t}
324
+ nil
235
325
  end.compact
236
326
 
237
327
  r
@@ -245,7 +335,10 @@ class TextAlignment::TextAlignment
245
335
  @block_alignment[:blocks].each do |a|
246
336
  show += case a[:alignment]
247
337
  when :block
248
- "===== common ===== [#{a[:source][:begin]} - #{a[:source][:end]}] [#{a[:target][:begin]} - #{a[:target][:end]}]\n" +
338
+ "===== common (block) ===== [#{a[:source][:begin]} - #{a[:source][:end]}] [#{a[:target][:begin]} - #{a[:target][:end]}]\n" +
339
+ stext[a[:source][:begin] ... a[:source][:end]] + "\n\n"
340
+ when :term
341
+ "===== common (term) ===== [#{a[:source][:begin]} - #{a[:source][:end]}] [#{a[:target][:begin]} - #{a[:target][:end]}]\n" +
249
342
  stext[a[:source][:begin] ... a[:source][:end]] + "\n\n"
250
343
  when :empty
251
344
  "xxxxx disparate texts (similarity: #{a[:similarity]})\n" +
@@ -1,3 +1,3 @@
1
1
  class TextAlignment
2
- VERSION = '0.6.3'
2
+ VERSION = '0.7.3'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: text_alignment
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jin-Dong Kim
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-06 00:00:00.000000000 Z
11
+ date: 2020-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-dictionary