ruby-marc-spec 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build.yml +18 -0
  3. data/.gitignore +388 -0
  4. data/.gitmodules +3 -0
  5. data/.idea/codeStyles/codeStyleConfig.xml +5 -0
  6. data/.idea/go.imports.xml +6 -0
  7. data/.idea/inspectionProfiles/Project_Default.xml +23 -0
  8. data/.idea/marc_spec.iml +102 -0
  9. data/.idea/misc.xml +6 -0
  10. data/.idea/modules.xml +8 -0
  11. data/.idea/templateLanguages.xml +6 -0
  12. data/.idea/vcs.xml +7 -0
  13. data/.rubocop.yml +269 -0
  14. data/.ruby-version +1 -0
  15. data/.simplecov +8 -0
  16. data/CHANGES.md +3 -0
  17. data/Gemfile +6 -0
  18. data/LICENSE.md +21 -0
  19. data/README.md +172 -0
  20. data/Rakefile +20 -0
  21. data/lib/.rubocop.yml +5 -0
  22. data/lib/marc/spec/module_info.rb +14 -0
  23. data/lib/marc/spec/parsing/closed_int_range.rb +28 -0
  24. data/lib/marc/spec/parsing/closed_lc_alpha_range.rb +28 -0
  25. data/lib/marc/spec/parsing/parser.rb +213 -0
  26. data/lib/marc/spec/parsing.rb +1 -0
  27. data/lib/marc/spec/queries/al_num_range.rb +105 -0
  28. data/lib/marc/spec/queries/applicable.rb +18 -0
  29. data/lib/marc/spec/queries/character_spec.rb +81 -0
  30. data/lib/marc/spec/queries/comparison_string.rb +45 -0
  31. data/lib/marc/spec/queries/condition.rb +133 -0
  32. data/lib/marc/spec/queries/condition_context.rb +49 -0
  33. data/lib/marc/spec/queries/dsl.rb +80 -0
  34. data/lib/marc/spec/queries/indicator_value.rb +77 -0
  35. data/lib/marc/spec/queries/operator.rb +129 -0
  36. data/lib/marc/spec/queries/part.rb +63 -0
  37. data/lib/marc/spec/queries/position.rb +59 -0
  38. data/lib/marc/spec/queries/position_or_range.rb +27 -0
  39. data/lib/marc/spec/queries/query.rb +94 -0
  40. data/lib/marc/spec/queries/query_executor.rb +52 -0
  41. data/lib/marc/spec/queries/selector.rb +12 -0
  42. data/lib/marc/spec/queries/subfield.rb +88 -0
  43. data/lib/marc/spec/queries/subfield_value.rb +63 -0
  44. data/lib/marc/spec/queries/tag.rb +107 -0
  45. data/lib/marc/spec/queries/transform.rb +154 -0
  46. data/lib/marc/spec/queries.rb +1 -0
  47. data/lib/marc/spec.rb +32 -0
  48. data/rakelib/.rubocop.yml +19 -0
  49. data/rakelib/bundle.rake +8 -0
  50. data/rakelib/coverage.rake +11 -0
  51. data/rakelib/gem.rake +54 -0
  52. data/rakelib/parser_specs/formatter.rb +31 -0
  53. data/rakelib/parser_specs/parser_specs.rb.txt.erb +35 -0
  54. data/rakelib/parser_specs/rule.rb +95 -0
  55. data/rakelib/parser_specs/suite.rb +91 -0
  56. data/rakelib/parser_specs/test.rb +97 -0
  57. data/rakelib/parser_specs.rb +1 -0
  58. data/rakelib/rubocop.rake +18 -0
  59. data/rakelib/spec.rake +27 -0
  60. data/ruby-marc-spec.gemspec +42 -0
  61. data/spec/.rubocop.yml +46 -0
  62. data/spec/README.md +16 -0
  63. data/spec/data/b23161018-sru.xml +182 -0
  64. data/spec/data/sandburg.xml +82 -0
  65. data/spec/generated/char_indicator_spec.rb +174 -0
  66. data/spec/generated/char_spec.rb +113 -0
  67. data/spec/generated/comparison_string_spec.rb +74 -0
  68. data/spec/generated/field_tag_spec.rb +156 -0
  69. data/spec/generated/index_char_spec.rb +669 -0
  70. data/spec/generated/index_indicator_spec.rb +174 -0
  71. data/spec/generated/index_spec.rb +113 -0
  72. data/spec/generated/index_sub_spec_spec.rb +1087 -0
  73. data/spec/generated/indicators_spec.rb +75 -0
  74. data/spec/generated/position_or_range_spec.rb +110 -0
  75. data/spec/generated/sub_spec_spec.rb +208 -0
  76. data/spec/generated/sub_spec_sub_spec_spec.rb +1829 -0
  77. data/spec/generated/subfield_char_spec.rb +405 -0
  78. data/spec/generated/subfield_range_range_spec.rb +48 -0
  79. data/spec/generated/subfield_range_spec.rb +87 -0
  80. data/spec/generated/subfield_range_sub_spec_spec.rb +214 -0
  81. data/spec/generated/subfield_tag_range_spec.rb +477 -0
  82. data/spec/generated/subfield_tag_sub_spec_spec.rb +3216 -0
  83. data/spec/generated/subfield_tag_tag_spec.rb +5592 -0
  84. data/spec/marc/spec/parsing/closed_int_range_spec.rb +49 -0
  85. data/spec/marc/spec/parsing/closed_lc_alpha_range_spec.rb +49 -0
  86. data/spec/marc/spec/parsing/parser_spec.rb +545 -0
  87. data/spec/marc/spec/queries/al_num_range_spec.rb +114 -0
  88. data/spec/marc/spec/queries/character_spec_spec.rb +28 -0
  89. data/spec/marc/spec/queries/comparison_string_spec.rb +28 -0
  90. data/spec/marc/spec/queries/indicator_value_spec.rb +28 -0
  91. data/spec/marc/spec/queries/query_spec.rb +200 -0
  92. data/spec/marc/spec/queries/subfield_spec.rb +92 -0
  93. data/spec/marc/spec/queries/subfield_value_spec.rb +31 -0
  94. data/spec/marc/spec/queries/tag_spec.rb +144 -0
  95. data/spec/marc/spec/queries/transform_spec.rb +459 -0
  96. data/spec/marc_spec_spec.rb +247 -0
  97. data/spec/scratch_spec.rb +112 -0
  98. data/spec/spec_helper.rb +23 -0
  99. metadata +341 -0
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'parslet/rig/rspec'
3
+
4
+ module MARC::Spec
5
+ module Parsing
6
+ describe ClosedIntRange do
7
+ attr_reader :range
8
+
9
+ before(:each) do
10
+ @range = ClosedIntRange.new
11
+ end
12
+
13
+ it 'parses a valid range' do
14
+ valid_ranges = %w[
15
+ 0-1
16
+ 1-2
17
+ 12-345
18
+ 67890-123456
19
+ ]
20
+ aggregate_failures 'valid ranges' do
21
+ valid_ranges.each do |r|
22
+ expect(range).to parse(r)
23
+ end
24
+ end
25
+ end
26
+
27
+ it 'does not parse an invalid range' do
28
+ invalid_ranges = %w[
29
+ 1-0
30
+ 2-1
31
+ 345-12
32
+ 123456-67890
33
+ ]
34
+ aggregate_failures 'invalid ranges' do
35
+ invalid_ranges.each do |r|
36
+ expect(range).not_to parse(r)
37
+ end
38
+ end
39
+ end
40
+
41
+ describe :to_s do
42
+ it 'accepts a precedece argument' do
43
+ prec = Parslet::Atoms::Precedence::OUTER
44
+ expect(range.to_s(prec)).to eq('ClosedIntRange')
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'parslet/rig/rspec'
3
+
4
+ module MARC::Spec
5
+ module Parsing
6
+ describe ClosedLcAlphaRange do
7
+ attr_reader :range
8
+
9
+ before(:each) do
10
+ @range = ClosedLcAlphaRange.new
11
+ end
12
+
13
+ it 'parses a valid range' do
14
+ valid_ranges = %w[
15
+ a-b
16
+ c-d
17
+ e-f
18
+ g-z
19
+ ]
20
+ aggregate_failures 'valid ranges' do
21
+ valid_ranges.each do |r|
22
+ expect(range).to parse(r)
23
+ end
24
+ end
25
+ end
26
+
27
+ it 'does not parse an invalid range' do
28
+ invalid_ranges = %w[
29
+ z-a
30
+ b-a
31
+ d-c
32
+ x-f
33
+ ]
34
+ aggregate_failures 'invalid ranges' do
35
+ invalid_ranges.each do |r|
36
+ expect(range).not_to parse(r)
37
+ end
38
+ end
39
+ end
40
+
41
+ describe :to_s do
42
+ it 'accepts a precedece argument' do
43
+ prec = Parslet::Atoms::Precedence::OUTER
44
+ expect(range.to_s(prec)).to eq('ClosedLcAlphaRange')
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,545 @@
1
+ require 'spec_helper'
2
+ require 'parslet/rig/rspec'
3
+
4
+ # noinspection RubyResolve
5
+ module MARC::Spec
6
+ module Parsing
7
+ include MARC::Spec::Queries::Part
8
+
9
+ describe Parser do
10
+ let(:parser) { Parser.new }
11
+ let(:tags) { %w[245 2.. 24. .45 ..5 ... LDR] }
12
+ let(:positions) { %w[0 3 5] }
13
+ let(:pos_ranges) { [%w[1 3], %w[1 #], %w[# 1], %w[2 4], %w[# #]] }
14
+ let(:codes) { %w[b 2 $ \\ { }] }
15
+ let(:code_ranges) { [%w[a c], %w[1 3]] }
16
+ let(:inds) { %w[1 2] }
17
+
18
+ let(:r) { Parslet::ErrorReporter::Deepest.new }
19
+
20
+ # TODO: clean this up
21
+ # before(:all) do
22
+ # @all_parses = {}
23
+ # end
24
+ #
25
+ # after(:all) do
26
+ # parse_list = @all_parses.map do |val, parse_tree|
27
+ # [val.inspect, parse_tree.inspect].join(' => ')
28
+ # end
29
+ # parse_list.sort!
30
+ # parse_list.uniq!
31
+ # puts "{\n#{parse_list.join(",\n")}\n}"
32
+ # end
33
+
34
+ def parse_with(rule, value)
35
+ rule.parse(value).tap do |result|
36
+ # @all_parses[value] = result
37
+ end
38
+ end
39
+
40
+ describe :field_tag do
41
+ let(:rule) { parser.field_tag }
42
+
43
+ it 'parses tags' do
44
+ aggregate_failures do
45
+ tags.each do |tag|
46
+ expect(rule).to parse(tag)
47
+ result = parse_with(rule, tag)
48
+ expect(result).to eq(tag)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ describe :position do
55
+ let(:rule) { parser.position }
56
+
57
+ it 'parses positions' do
58
+ aggregate_failures do
59
+ %w[0 7 17 317].each do |pos|
60
+ expect(rule).to parse(pos, reporter: r)
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ describe :position_or_range do
67
+ let(:rule) { parser.position_or_range }
68
+
69
+ it 'parses positions' do
70
+ aggregate_failures do
71
+ positions.each do |pos|
72
+ expect(rule).to parse(pos, reporter: r)
73
+ result = parse_with(rule, pos)
74
+ expect(result[:pos]).to eq(pos)
75
+ end
76
+ end
77
+ end
78
+
79
+ it 'parses ranges' do
80
+ aggregate_failures do
81
+ pos_ranges.each do |from, to|
82
+ val = "#{from}-#{to}"
83
+ expect(rule).to parse(val, reporter: r)
84
+ result = parse_with(rule, val)
85
+ expect(result[:from]).to eq(from == '#' ? nil : from)
86
+ expect(result[:to]).to eq(to == '#' ? nil : to)
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ describe :character_spec do
93
+ let(:rule) { parser.character_spec }
94
+
95
+ it 'parses positions' do
96
+ aggregate_failures do
97
+ positions.each do |pos|
98
+ val = "/#{pos}"
99
+ expect(rule).to parse(val, reporter: r)
100
+ result = parse_with(rule, val)
101
+ character_spec = result[:character_spec]
102
+ expect(character_spec[:pos]).to eq(pos)
103
+ end
104
+ end
105
+ end
106
+
107
+ it 'parses ranges' do
108
+ aggregate_failures do
109
+ pos_ranges.each do |from, to|
110
+ val = "/#{from}-#{to}"
111
+ expect(rule).to parse(val, reporter: r)
112
+ result = parse_with(rule, val)
113
+ character_spec = result[:character_spec]
114
+ expect(character_spec[:from]).to eq(from == '#' ? nil : from)
115
+ expect(character_spec[:to]).to eq(to == '#' ? nil : to)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ describe :field_spec do
122
+ let(:rule) { parser.field_spec }
123
+
124
+ it 'parses tags' do
125
+ aggregate_failures do
126
+ tags.each do |tag|
127
+ expect(rule).to parse(tag)
128
+ result = parse_with(rule, tag)
129
+ expect(result[:tag]).to eq(tag)
130
+ end
131
+ end
132
+ end
133
+
134
+ describe 'with substrings' do
135
+ it 'parses positions' do
136
+ aggregate_failures do
137
+ tags.each do |tag|
138
+ positions.each do |pos|
139
+ val = "#{tag}/#{pos}"
140
+ expect(rule).to parse(val, reporter: r)
141
+ result = parse_with(rule, val)
142
+ expect(result[:tag]).to eq(tag)
143
+ selector = result[:selector]
144
+ character_spec = selector[:character_spec]
145
+ expect(character_spec[:pos]).to eq(pos)
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ it 'parses ranges' do
152
+ aggregate_failures do
153
+ tags.each do |tag|
154
+ pos_ranges.each do |from, to|
155
+ val = "#{tag}/#{from}-#{to}"
156
+ expect(rule).to parse(val, reporter: r)
157
+ result = parse_with(rule, val)
158
+ expect(result[:tag]).to eq(tag)
159
+ selector = result[:selector]
160
+ character_spec = selector[:character_spec]
161
+ expect(character_spec[:from]).to eq(from == '#' ? nil : from)
162
+ expect(character_spec[:to]).to eq(to == '#' ? nil : to)
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ describe 'with indices' do
170
+ it 'parses positions' do
171
+ aggregate_failures do
172
+ tags.each do |tag|
173
+ positions.each do |pos_index|
174
+ val = "#{tag}[#{pos_index}]"
175
+ expect(rule).to parse(val, reporter: r)
176
+ result = parse_with(rule, val)
177
+ expect(result[:tag]).to eq(tag)
178
+ index = result[:index]
179
+ expect(index[:pos]).to eq(pos_index)
180
+ end
181
+ end
182
+ end
183
+ end
184
+
185
+ it 'parses ranges' do
186
+ aggregate_failures do
187
+ tags.each do |tag|
188
+ pos_ranges.each do |from_index, to_index|
189
+ val = "#{tag}[#{from_index}-#{to_index}]"
190
+ expect(rule).to parse(val, reporter: r)
191
+ result = parse_with(rule, val)
192
+ expect(result[:tag]).to eq(tag)
193
+ index = result[:index]
194
+ expect(index[:from]).to eq(from_index == '#' ? nil : from_index)
195
+ expect(index[:to]).to eq(to_index == '#' ? nil : to_index)
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ describe :subfield_code do
204
+ let(:rule) { parser.subfield_code }
205
+
206
+ it 'parses codes' do
207
+ aggregate_failures do
208
+ codes.each do |code|
209
+ val = "$#{code}"
210
+ expect(rule).to parse(val, reporter: r)
211
+ expect(parse_with(rule, val)).to eq(code)
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ describe :subfield_spec do
218
+ let(:rule) { parser.subfield_spec }
219
+
220
+ it 'parses codes' do
221
+ aggregate_failures do
222
+ tags.each do |tag|
223
+ codes.each do |code|
224
+ val = "#{tag}$#{code}"
225
+ expect(rule).to parse(val, reporter: r)
226
+ result = parse_with(rule, val)
227
+ expect(result[:tag]).to eq(tag)
228
+ selector = result[:selector]
229
+ expect(selector).to eq({ code: code })
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ it 'parses code ranges' do
236
+ aggregate_failures do
237
+ tags.each do |tag|
238
+ code_ranges.each do |from, to|
239
+ val = "#{tag}$#{from}-#{to}"
240
+ expect(rule).to parse(val, reporter: r)
241
+ result = parse_with(rule, val)
242
+ expect(result[:tag]).to eq(tag)
243
+
244
+ selector = result[:selector]
245
+ expect(selector).not_to be_nil
246
+
247
+ code_range = selector[:code]
248
+ expect(code_range[:from]).to eq(from)
249
+ expect(code_range[:to]).to eq(to)
250
+ end
251
+ end
252
+ end
253
+ end
254
+
255
+ it 'parses codes with indices' do
256
+ aggregate_failures do
257
+ tags.each do |tag|
258
+ positions.each do |pos_index|
259
+ codes.each do |code|
260
+ val = "#{tag}[#{pos_index}]$#{code}"
261
+ expect(rule).to parse(val, reporter: r)
262
+ result = parse_with(rule, val)
263
+ expect(result[:tag]).to eq(tag)
264
+
265
+ index = result[:index]
266
+ expect(index[:pos]).to eq(pos_index)
267
+
268
+ selector = result[:selector]
269
+ expect(selector).not_to be_nil
270
+ expect(selector[:code]).to eq(code)
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ it 'parses code ranges with indices' do
278
+ pos_ranges.each do |from_index, to_index|
279
+ aggregate_failures do
280
+ tags.each do |tag|
281
+ code_ranges.each do |from, to|
282
+ val = "#{tag}[#{from_index}-#{to_index}]$#{from}-#{to}"
283
+ expect(rule).to parse(val, reporter: r)
284
+ result = parse_with(rule, val)
285
+ expect(result[:tag]).to eq(tag)
286
+
287
+ index = result[:index]
288
+ expect(index[:from]).to eq(from_index == '#' ? nil : from_index)
289
+ expect(index[:to]).to eq(to_index == '#' ? nil : to_index)
290
+
291
+ selector = result[:selector]
292
+ expect(selector).not_to be_nil
293
+
294
+ code_range = selector[:code]
295
+ expect(code_range[:from]).to eq(from)
296
+ expect(code_range[:to]).to eq(to)
297
+ end
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
303
+
304
+ describe :indicator_spec do
305
+ let(:rule) { parser.indicator_spec }
306
+
307
+ it 'parses indicators with plain tags' do
308
+ aggregate_failures do
309
+ tags.each do |tag|
310
+ inds.each do |ind|
311
+ val = "#{tag}^#{ind}"
312
+ expect(rule).to parse(val)
313
+ result = parse_with(rule, val)
314
+ expect(result[:tag]).to eq(tag)
315
+ selector = result[:selector]
316
+ expect(selector[:ind]).to eq(ind)
317
+ end
318
+ end
319
+ end
320
+ end
321
+
322
+ it 'parses indicators with position-indexed tags' do
323
+ aggregate_failures do
324
+ tags.each do |tag|
325
+ positions.each do |pos_index|
326
+ inds.each do |ind|
327
+ val = "#{tag}[#{pos_index}]^#{ind}"
328
+ result = parse_with(rule, val)
329
+ expect(result[:tag]).to eq(tag)
330
+ selector = result[:selector]
331
+ expect(selector[:ind]).to eq(ind)
332
+ index = result[:index]
333
+ expect(index[:pos]).to eq(pos_index)
334
+ end
335
+ end
336
+ end
337
+ end
338
+ end
339
+
340
+ it 'parses indicators with range-indexed tags' do
341
+ aggregate_failures do
342
+ tags.each do |tag|
343
+ pos_ranges.each do |from_index, to_index|
344
+ inds.each do |ind|
345
+ val = "#{tag}[#{from_index}-#{to_index}]^#{ind}"
346
+ result = parse_with(rule, val)
347
+ expect(result[:tag]).to eq(tag)
348
+ selector = result[:selector]
349
+ expect(selector[:ind]).to eq(ind)
350
+ index = result[:index]
351
+ expect(index[:from]).to eq(from_index == '#' ? nil : from_index)
352
+ expect(index[:to]).to eq(to_index == '#' ? nil : to_index)
353
+ end
354
+ end
355
+ end
356
+ end
357
+ end
358
+
359
+ end
360
+
361
+ describe :comparison_string do
362
+ let(:rule) { parser._comparison_string }
363
+
364
+ it 'parses a comparison string' do
365
+ expecteds = {
366
+ '\\svalue' => '\\svalue',
367
+ '\\\\svalue' => '\\svalue',
368
+ '\\value' => 'value',
369
+ '\\value\\!' => 'value\\!',
370
+ '\\!value' => '!value',
371
+ '\\\\!value' => '\\!value',
372
+ '\\help\\sI\\sam\\strapped\\sin\\sa\\sunit\\stest\\!\\sso\\sam\\sI' =>
373
+ 'help\\sI\\sam\\strapped\\sin\\sa\\sunit\\stest\\!\\sso\\sam\\sI',
374
+ '\\a\\{b\\}\\$1\\\\23\\=\\~\\|\\?' => 'a\\{b\\}\\$1\\\\23\\=\\~\\|\\?'
375
+ }
376
+ aggregate_failures do
377
+ expecteds.each do |val, expected|
378
+ expect(rule).to parse(val, reporter: r)
379
+ result = parse_with(rule, val)
380
+ expect(result).to be_a(Hash)
381
+ next unless result.is_a?(Hash)
382
+
383
+ cstr = result[:comparison_string]
384
+ expect(cstr).to eq(expected)
385
+ end
386
+ end
387
+ end
388
+ end
389
+
390
+ describe :sub_spec do
391
+ let(:rule) { parser.sub_spec }
392
+
393
+ it 'parses a condition' do
394
+ expecteds = {
395
+ '{/#=\\/}' => {
396
+ left: {
397
+ selector: {
398
+ character_spec: {
399
+ pos: '#'
400
+ }
401
+ }
402
+ },
403
+ operator: '=',
404
+ right: {
405
+ comparison_string: '/'
406
+ }
407
+ }
408
+ }
409
+ aggregate_failures do
410
+ expecteds.each do |val, expected|
411
+ expect(rule).to parse(val, reporter: r)
412
+ result = parse_with(rule, val)
413
+ expect(result).to eq(expected)
414
+ end
415
+ end
416
+ end
417
+ end
418
+
419
+ describe 'with multiple subfields' do
420
+ it 'parses subfield code lists' do
421
+ aggregate_failures do
422
+ tags.each do |tag|
423
+ code_list = codes.map { |c| "$#{c}" }.join
424
+ val = "#{tag}#{code_list}"
425
+ expect(parser).to parse(val, reporter: r)
426
+ result = parse_with(parser, val)
427
+
428
+ expect(result[:tag]).to eq(tag)
429
+
430
+ subqueries = result[:subqueries]
431
+ expect(subqueries.size).to eq(codes.size)
432
+ subqueries.each_with_index do |subquery, i|
433
+ selector = subquery[:selector]
434
+ subfield = selector
435
+ expect(subfield[:code]).to eq(codes[i])
436
+ end
437
+ end
438
+ end
439
+ end
440
+
441
+ it 'handles indices' do
442
+ aggregate_failures do
443
+ tags.each do |tag|
444
+ pos_ranges.each do |from_index, to_index|
445
+ code_list = codes.map { |c| "$#{c}" }.join
446
+ val = "#{tag}[#{from_index}-#{to_index}]#{code_list}"
447
+ expect(parser).to parse(val, reporter: r)
448
+ result = parse_with(parser, val)
449
+
450
+ expect(result[:tag]).to eq(tag)
451
+ index = result[:index]
452
+ expect(index[:from]).to eq(from_index == '#' ? nil : from_index)
453
+ expect(index[:to]).to eq(to_index == '#' ? nil : to_index)
454
+
455
+ subqueries = result[:subqueries]
456
+ expect(subqueries.size).to eq(codes.size)
457
+ subqueries.each_with_index do |subquery, i|
458
+ selector = subquery[:selector]
459
+ subfield = selector
460
+ expect(subfield[:code]).to eq(codes[i])
461
+ end
462
+ end
463
+ end
464
+ end
465
+ end
466
+
467
+ it 'parses subfield code lists with conditions' do
468
+ aggregate_failures do
469
+ tags.each do |tag|
470
+ pos_ranges.each do |from_index, to_index|
471
+ code_list = codes.map.with_index { |c, i| "$#{c}{~\\ok#{i}}" }.join
472
+ val = "#{tag}[#{from_index}-#{to_index}]#{code_list}"
473
+ expect(parser).to parse(val, reporter: r)
474
+ result = parse_with(parser, val)
475
+
476
+ expect(result[:tag]).to eq(tag)
477
+ index = result[:index]
478
+ expect(index[:from]).to eq(from_index == '#' ? nil : from_index)
479
+ expect(index[:to]).to eq(to_index == '#' ? nil : to_index)
480
+
481
+ subqueries = result[:subqueries]
482
+ expect(subqueries.size).to eq(codes.size)
483
+ subqueries.each_with_index do |subquery, i|
484
+ selector = subquery[:selector]
485
+ subfield = selector
486
+ expect(subfield[:code]).to eq(codes[i])
487
+ condition = subquery[:condition]
488
+ expect(condition[:operator]).to eq('~')
489
+ right_operand = condition[:right]
490
+ expect(right_operand[:comparison_string]).to eq("ok#{i}")
491
+ end
492
+ end
493
+ end
494
+ end
495
+ end
496
+
497
+ it 'handles complex combinations of subfields and subspecs' do
498
+ val = '880$a{?$f}$b$c$e{$f=\\q}'
499
+
500
+ expect(parser).to parse(val, reporter: r)
501
+ result = parse_with(parser, val)
502
+ expect(result[:tag]).to eq('880')
503
+
504
+ subqueries = result[:subqueries]
505
+ expect(subqueries.size).to eq(4)
506
+
507
+ expect(subqueries[0][:selector]).to eq({ code: 'a' })
508
+ condition0 = subqueries[0][:condition]
509
+ expect(condition0[:operator]).to eq('?')
510
+ expect(condition0[:right][:selector]).to eq({ code: 'f' })
511
+
512
+ expect(subqueries[1][:selector]).to eq({ code: 'b' })
513
+
514
+ expect(subqueries[2][:selector]).to eq({ code: 'c' })
515
+
516
+ expect(subqueries[3][:selector]).to eq({ code: 'e' })
517
+ condition3 = subqueries[3][:condition]
518
+ expect(condition3[:left][:selector]).to eq({ code: 'f' })
519
+ expect(condition3[:operator]).to eq('=')
520
+ expect(condition3[:right]).to eq({ comparison_string: 'q' })
521
+ end
522
+ end
523
+
524
+ describe 'indicators with conditions' do
525
+ it 'handles plain tags' do
526
+ aggregate_failures do
527
+ tags.each do |tag|
528
+ inds.each do |ind|
529
+ val = "#{tag}^#{ind}{=\\a}"
530
+ expect(parser).to parse(val)
531
+ result = parse_with(parser, val)
532
+ expect(result[:tag]).to eq(tag)
533
+ selector = result[:selector]
534
+ expect(selector[:ind]).to eq(ind)
535
+ condition = result[:condition]
536
+ expect(condition[:operator]).to eq('=')
537
+ expect(condition[:right][:comparison_string]).to eq('a')
538
+ end
539
+ end
540
+ end
541
+ end
542
+ end
543
+ end
544
+ end
545
+ end