libis-tools 0.9.9 → 0.9.10
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 +4 -4
- data/.travis.yml +4 -2
- data/README.md +19 -0
- data/lib/libis/tools.rb +1 -0
- data/lib/libis/tools/config_file.rb +1 -1
- data/lib/libis/tools/extend/kernel.rb +16 -0
- data/lib/libis/tools/metadata.rb +20 -0
- data/lib/libis/tools/metadata/dublin_core_record.rb +1 -1
- data/lib/libis/tools/metadata/{field_spec.rb → field_format.rb} +7 -7
- data/lib/libis/tools/metadata/fix_field.rb +6 -1
- data/lib/libis/tools/metadata/mapper.rb +80 -0
- data/lib/libis/tools/metadata/mappers/flandrica.rb +69 -0
- data/lib/libis/tools/metadata/mappers/kuleuven.rb +1702 -0
- data/lib/libis/tools/metadata/marc21_record.rb +5 -4
- data/lib/libis/tools/metadata/marc_record.rb +96 -37
- data/lib/libis/tools/metadata/parser/basic_parser.rb +118 -0
- data/lib/libis/tools/metadata/parser/dublin_core_parser.rb +36 -0
- data/lib/libis/tools/metadata/parser/marc21_parser.rb +206 -0
- data/lib/libis/tools/metadata/parser/marc_format_parser.rb +52 -0
- data/lib/libis/tools/metadata/parser/marc_rules.rb +35 -0
- data/lib/libis/tools/metadata/parser/marc_select_parser.rb +25 -0
- data/lib/libis/tools/metadata/parser/patch.rb +21 -0
- data/lib/libis/tools/metadata/parser/subfield_criteria_parser.rb +71 -0
- data/lib/libis/tools/metadata/parsers.rb +12 -0
- data/lib/libis/tools/metadata/var_field.rb +57 -47
- data/lib/libis/tools/parameter.rb +12 -2
- data/lib/libis/tools/version.rb +1 -1
- data/libis-tools.gemspec +4 -3
- data/spec/config_spec.rb +3 -1
- data/spec/data/MetadataMapping.xlsx +0 -0
- data/spec/metadata/8389207.marc +96 -0
- data/spec/metadata/dublin_core_parser_spec.rb +48 -0
- data/spec/metadata/marc21_parser_data.rb +382 -0
- data/spec/metadata/marc21_parser_spec.rb +67 -0
- data/spec/metadata/marc21_spec.rb +116 -0
- data/spec/metadata/metadata_mapper_spec.rb +23 -0
- data/spec/spec_helper.rb +13 -0
- data/test.rb +61 -0
- metadata +77 -7
- data/lib/libis/tools/dc_record.rb +0 -47
@@ -14,7 +14,7 @@ module Libis
|
|
14
14
|
|
15
15
|
def get_all_records
|
16
16
|
|
17
|
-
@all_records
|
17
|
+
@all_records.clear
|
18
18
|
|
19
19
|
@node.xpath('.//leader').each { |f|
|
20
20
|
@all_records['LDR'] << FixField.new('LDR', f.content)
|
@@ -31,10 +31,11 @@ module Libis
|
|
31
31
|
tag = v['tag']
|
32
32
|
tag = '%03d' % tag.to_i if tag.size < 3
|
33
33
|
|
34
|
-
|
35
|
-
v.xpath('.//subfield').each { |s| subfields[s['code']] << CGI::escapeHTML(s.content) }
|
34
|
+
varfield = VarField.new(tag, v['ind1'].to_s, v['ind2'].to_s)
|
36
35
|
|
37
|
-
|
36
|
+
v.xpath('.//subfield').each { |s| varfield.add_subfield(s['code'], CGI::escapeHTML(s.content)) }
|
37
|
+
|
38
|
+
@all_records[tag] << varfield
|
38
39
|
|
39
40
|
}
|
40
41
|
|
@@ -8,58 +8,105 @@ require 'libis/tools/assert'
|
|
8
8
|
|
9
9
|
require_relative 'fix_field'
|
10
10
|
require_relative 'var_field'
|
11
|
-
require_relative '
|
11
|
+
require_relative 'field_format'
|
12
12
|
|
13
13
|
module Libis
|
14
14
|
module Tools
|
15
15
|
module Metadata
|
16
16
|
|
17
17
|
# noinspection RubyTooManyMethodsInspection
|
18
|
+
|
19
|
+
# Base class for reading MARC based records.
|
20
|
+
#
|
21
|
+
# For indicator selection: '#' or '' (empty) is wildcard; '_' or ' ' (space) is blank.
|
18
22
|
class MarcRecord
|
19
23
|
|
24
|
+
# Create a new MarcRecord object
|
25
|
+
#
|
26
|
+
# @param [XML node] xml_node XML node from Nokogiri or XmlDocument that contains child nodes with the data for
|
27
|
+
# one MARC record.
|
20
28
|
def initialize(xml_node)
|
21
29
|
@node = xml_node
|
30
|
+
@node.document.remove_namespaces!
|
31
|
+
@all_records = Hash.new { |h, k| h[k] = Array.new }
|
22
32
|
end
|
23
33
|
|
34
|
+
# Access to the XML node that was supplied to the constructor
|
35
|
+
# @return [XML node]
|
24
36
|
def to_raw
|
25
37
|
@node
|
26
38
|
end
|
27
39
|
|
40
|
+
# Returns the internal data structure (a Hash) with all the MARC data.
|
41
|
+
#
|
42
|
+
# The internal structure is a Hash with the tag as key and as value an Array of either FixField or VarField
|
43
|
+
# instances.
|
44
|
+
#
|
45
|
+
# @return [Hash] internal data structure
|
28
46
|
def all
|
29
|
-
|
30
|
-
@all_records
|
47
|
+
return @all_records unless @all_records.empty?
|
48
|
+
@all_records = get_all_records
|
31
49
|
end
|
32
50
|
|
51
|
+
# Iterates over all the MARC fields.
|
52
|
+
#
|
53
|
+
# If a block is supplied it will be called for each field in the MARC record. The supplied argument will be the
|
54
|
+
# FixField or VarField instance for each field.
|
55
|
+
#
|
56
|
+
# @return [Array] The list of the field data or return values for each block call.
|
33
57
|
def each
|
34
|
-
all.
|
35
|
-
yield
|
58
|
+
all.map { |_, field_array| field_array }.flatten.map do |field|
|
59
|
+
block_given? ? yield(field) : field
|
36
60
|
end
|
37
61
|
end
|
38
62
|
|
39
|
-
|
40
|
-
|
41
|
-
|
63
|
+
# Get all fields matching search criteria.
|
64
|
+
#
|
65
|
+
# A block with one parameter can be supplied when calling this method. Each time a match is found, the block
|
66
|
+
# will be called with the field data as argument and the return value of the block will be added to the method's
|
67
|
+
# return value. This could for example be used to narrow the selection of the fields:
|
68
|
+
#
|
69
|
+
# # Only select 700 tags where $4 subfield contains 'abc', 'def' or 'xyz'
|
70
|
+
# record.all_tags('700') { |v| v.subfield['4'] =~ /^(abc|def|xyz)$/ ? v : nil }.compact
|
71
|
+
#
|
72
|
+
# @param [String] tag Tag selection string. Tag name with indicators, '#' for wildcard, '_' for blank. If an
|
73
|
+
# extra subfield name is added, a result will be created for each instance found of that subfield.
|
74
|
+
# @param [String] subfields Subfield specification. See FieldFormat class for more info; ignored for controlfields.
|
75
|
+
# @param [Proc] select_block block that will be executed once for each field found. The block takes one argument
|
76
|
+
# (the field) and should return true or false. True selects the field, false rejects it.
|
77
|
+
# @return [Array] If a block was supplied to the method call, the array will contain the result of the block
|
78
|
+
# for each tag found. Otherwise the array will just contain the data for each matching tag.
|
79
|
+
def all_tags(tag, subfields = '', select_block = Proc.new { |_| true})
|
80
|
+
t, ind1, ind2, subfield = tag =~ /^\d{3}/ ? [tag[0..2], tag[3], tag[4], tag[5]] : [tag, nil, nil, nil]
|
81
|
+
result = get_records(t, ind1, ind2, subfield, subfields, &select_block)
|
42
82
|
return result unless block_given?
|
43
83
|
result.map { |record| yield record }
|
44
|
-
result.size > 0
|
45
84
|
end
|
46
85
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
yield result
|
52
|
-
true
|
86
|
+
alias_method :each_tag, :all_tags
|
87
|
+
|
88
|
+
def select_fields(tag, select_block = nil, &block)
|
89
|
+
all_tags(tag, nil, select_block, &block)
|
53
90
|
end
|
54
91
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
92
|
+
# Find the first field matching the criteria.
|
93
|
+
#
|
94
|
+
# If a block is supplied, it will be called with the found field data. The return value will be whatever the
|
95
|
+
# block returns. If no block is supplied, the field data will be returned. If nothing was found, the return
|
96
|
+
# value is nil.
|
97
|
+
#
|
98
|
+
# @param [String] tag Tag selection string. Tag name with indicators, '#' for wildcard, '_' for blank.
|
99
|
+
# @param [String] subfields Subfield specification. See FieldFormat class for more info; ignored for controlfields.
|
100
|
+
# @return [Object] nil if nothing found; field data or whatever block returns.
|
101
|
+
def first_tag(tag, subfields = '')
|
102
|
+
result = all_tags(tag, subfields).first
|
103
|
+
return nil unless result
|
104
|
+
return result unless block_given?
|
105
|
+
yield result
|
59
106
|
end
|
60
107
|
|
61
|
-
def all_fields(
|
62
|
-
r = all_tags(
|
108
|
+
def all_fields(tag, subfields)
|
109
|
+
r = all_tags(tag, subfields).collect { |tag| tag.subfields_array(subfields) }.flatten.compact
|
63
110
|
return r unless block_given?
|
64
111
|
r.map { |field| yield field }
|
65
112
|
r.size > 0
|
@@ -116,11 +163,11 @@ module Libis
|
|
116
163
|
record = ''
|
117
164
|
doc_number = tag('001').datas
|
118
165
|
|
119
|
-
all.select { |t| t.is_a? FixField }.each { |t| record += "#{format('%09s', doc_number)} #{t.tag} L #{t.datas}\n" }
|
120
|
-
all.select { |t| t.is_a? VarField }.each { |t|
|
166
|
+
all.select { |t| t.is_a? Libis::Tools::Metadata::FixField }.each { |t| record += "#{format('%09s', doc_number)} #{t.tag} L #{t.datas}\n" }
|
167
|
+
all.select { |t| t.is_a? Libis::Tools::Metadata::VarField }.each { |t|
|
121
168
|
record += "#{format('%09s', doc_number)} #{t.tag}#{t.ind1}#{t.ind2} L "
|
122
169
|
t.keys.each { |k|
|
123
|
-
t.
|
170
|
+
t.subfield_array(k).each { |f|
|
124
171
|
record += "$$#{k}#{CGI::unescapeHTML(f)}"
|
125
172
|
}
|
126
173
|
}
|
@@ -134,37 +181,37 @@ module Libis
|
|
134
181
|
|
135
182
|
def element(*parts)
|
136
183
|
opts = options parts
|
137
|
-
|
184
|
+
field_format(opts, *parts)
|
138
185
|
end
|
139
186
|
|
140
187
|
def list_s(*parts)
|
141
188
|
opts = options parts, join: ' '
|
142
|
-
|
189
|
+
field_format(opts, *parts)
|
143
190
|
end
|
144
191
|
|
145
192
|
def list_c(*parts)
|
146
193
|
opts = options parts, join: ', '
|
147
|
-
|
194
|
+
field_format(opts, *parts)
|
148
195
|
end
|
149
196
|
|
150
197
|
def list_d(*parts)
|
151
198
|
opts = options parts, join: ' - '
|
152
|
-
|
199
|
+
field_format(opts, *parts)
|
153
200
|
end
|
154
201
|
|
155
202
|
def repeat(*parts)
|
156
203
|
opts = options parts, join: '; '
|
157
|
-
|
204
|
+
field_format(opts, *parts)
|
158
205
|
end
|
159
206
|
|
160
207
|
def opt_r(*parts)
|
161
208
|
opts = options parts, fix: '()'
|
162
|
-
|
209
|
+
field_format(opts, *parts)
|
163
210
|
end
|
164
211
|
|
165
212
|
def opt_s(*parts)
|
166
213
|
opts = options parts, fix: '[]'
|
167
|
-
|
214
|
+
field_format(opts, *parts)
|
168
215
|
end
|
169
216
|
|
170
217
|
def odis_link(group, id, label)
|
@@ -177,11 +224,11 @@ module Libis
|
|
177
224
|
default.merge(args.last.is_a?(::Hash) ? args.pop : {})
|
178
225
|
end
|
179
226
|
|
180
|
-
def
|
181
|
-
|
227
|
+
def field_format(default_options, *parts)
|
228
|
+
Libis::Tools::Metadata::FieldFormat.new(*parts).add_default_options(default_options).to_s
|
182
229
|
end
|
183
230
|
|
184
|
-
def get_records(tag, ind1 = '', ind2 = '', subfields = '')
|
231
|
+
def get_records(tag, ind1 = '', ind2 = '', subfield = nil, subfields = '', &block)
|
185
232
|
|
186
233
|
ind1 ||= ''
|
187
234
|
ind2 ||= ''
|
@@ -193,14 +240,26 @@ module Libis
|
|
193
240
|
ind2.tr!('_', ' ')
|
194
241
|
ind2.tr!('#', '')
|
195
242
|
|
196
|
-
all[tag].select do |v|
|
197
|
-
v.is_a?(FixField) ||
|
243
|
+
found = all[tag].select do |v|
|
244
|
+
result = v.is_a?(Libis::Tools::Metadata::FixField) ||
|
198
245
|
((ind1.empty? or v.ind1 == ind1) &&
|
199
246
|
(ind2.empty? or v.ind2 == ind2) &&
|
200
|
-
v.
|
247
|
+
v.match(subfields)
|
201
248
|
)
|
249
|
+
result &&= block.call(v) if block
|
250
|
+
result
|
202
251
|
end
|
203
252
|
|
253
|
+
return found unless subfield
|
254
|
+
|
255
|
+
# duplicate tags for subfield instances
|
256
|
+
found.map do |field|
|
257
|
+
next unless field.is_a? Libis::Tools::Metadata::FixField
|
258
|
+
field.subfield_data[subfield].map do |sfield|
|
259
|
+
field.dup.subfield_data[subfield] = [sfield]
|
260
|
+
end
|
261
|
+
end.compact.flatten
|
262
|
+
|
204
263
|
end
|
205
264
|
|
206
265
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'parslet'
|
4
|
+
require 'parslet/convenience'
|
5
|
+
|
6
|
+
module Libis
|
7
|
+
module Tools
|
8
|
+
module Metadata
|
9
|
+
# noinspection RubyResolve
|
10
|
+
class BasicParser < Parslet::Parser
|
11
|
+
# space
|
12
|
+
rule(:space) { match('\s') }
|
13
|
+
rule(:space?) { space.maybe }
|
14
|
+
rule(:spaces) { space.repeat(1) }
|
15
|
+
rule(:spaces?) { space.repeat }
|
16
|
+
|
17
|
+
# numbers
|
18
|
+
rule(:number) { match('[0-9]') }
|
19
|
+
rule(:number?) { number.maybe }
|
20
|
+
rule(:integer) { number.repeat(1) }
|
21
|
+
|
22
|
+
# chars
|
23
|
+
rule(:character) { match(/[a-z]/i) }
|
24
|
+
rule(:character?) { character.maybe }
|
25
|
+
rule(:characters) { character.repeat(1) }
|
26
|
+
|
27
|
+
# word
|
28
|
+
rule(:wordchar) { match('\w') }
|
29
|
+
|
30
|
+
# name
|
31
|
+
rule(:name_string) { ((character | underscore) >> wordchar.repeat).repeat(1) }
|
32
|
+
|
33
|
+
# text
|
34
|
+
rule(:other) { not_paren }
|
35
|
+
rule(:text) { other.repeat(1) }
|
36
|
+
rule(:text?) { text.maybe }
|
37
|
+
|
38
|
+
# special chars
|
39
|
+
rule(:minus) { str('-') }
|
40
|
+
rule(:colon) { str(':') }
|
41
|
+
rule(:semicolon) { str(';') }
|
42
|
+
rule(:underscore) { str('_') }
|
43
|
+
rule(:hashtag) { str('#') }
|
44
|
+
rule(:dollar) { str('$') }
|
45
|
+
rule(:star) { str('*') }
|
46
|
+
|
47
|
+
# grouping
|
48
|
+
rule(:paren) { lparen | rparen }
|
49
|
+
rule(:lparen) { lrparen | lsparen | lcparen | squote | dquote }
|
50
|
+
rule(:rparen) { rrparen | rsparen | rcparen | squote | dquote }
|
51
|
+
|
52
|
+
rule(:not_paren) { paren.absent? >> any }
|
53
|
+
rule(:not_lparen) { lrparen.absent? >> lsparen.absent? >> lcparen.absent? >> squote.absent? >> dquote.absent? >> any }
|
54
|
+
rule(:not_rparen) { rrparen.absent? >> rsparen.absent? >> rcparen.absent? >> squote.absent? >> dquote.absent? >> any }
|
55
|
+
|
56
|
+
rule(:lrparen) { str('(') }
|
57
|
+
rule(:lsparen) { str('[') }
|
58
|
+
rule(:lcparen) { str('{') }
|
59
|
+
rule(:rrparen) { str(')') }
|
60
|
+
rule(:rsparen) { str(']') }
|
61
|
+
rule(:rcparen) { str('}') }
|
62
|
+
|
63
|
+
rule(:squote) { str("'") }
|
64
|
+
rule(:dquote) { str('"') }
|
65
|
+
rule(:quote) { squote | dquote }
|
66
|
+
|
67
|
+
rule(:not_squote) { squote.absent? >> any }
|
68
|
+
rule(:not_dquote) { dquote.absent? >> any }
|
69
|
+
rule(:not_quote) { quote.absent? >> any }
|
70
|
+
|
71
|
+
def complement(char)
|
72
|
+
case char
|
73
|
+
when '('
|
74
|
+
')'
|
75
|
+
when '{'
|
76
|
+
'}'
|
77
|
+
when '['
|
78
|
+
']'
|
79
|
+
else
|
80
|
+
char
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def grouped(foo, left_paren = lparen)
|
85
|
+
scope {
|
86
|
+
left_paren.capture(:paren).as(:lparen) >>
|
87
|
+
foo >>
|
88
|
+
dynamic { |_, c| str(complement(c.captures[:paren])) }.as(:rparen)
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def grouped_anonymous(foo, left_paren = lparen)
|
93
|
+
scope {
|
94
|
+
left_paren.capture(:paren) >>
|
95
|
+
foo >>
|
96
|
+
dynamic { |_, c| str(complement(c.captures[:paren])) }
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
def any_quoted(key = :text)
|
101
|
+
scope {
|
102
|
+
quote.capture(:quote) >>
|
103
|
+
dynamic { |_, c| (str(c.captures[:quote]).absent? >> any).repeat(1) }.maybe.as(key) >>
|
104
|
+
dynamic { |_, c| str(c.captures[:quote]) }
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def transformer
|
109
|
+
self.class::Transformer.new rescue nil
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'parslet'
|
4
|
+
|
5
|
+
require_relative 'basic_parser'
|
6
|
+
|
7
|
+
module Libis
|
8
|
+
module Tools
|
9
|
+
module Metadata
|
10
|
+
|
11
|
+
# noinspection RubyResolve
|
12
|
+
class DublinCoreParser < Libis::Tools::Metadata::BasicParser
|
13
|
+
rule(:namespace) { match('[^:]').repeat(1).as(:namespace) >> str(':') }
|
14
|
+
rule(:namespace?) { namespace.maybe }
|
15
|
+
|
16
|
+
rule(:attribute) { namespace? >> name_string.as(:name) >> str('=') >> str('"') >> match('[^"]').repeat(1).as(:value) >> str('"') }
|
17
|
+
rule(:attributes) { attribute >> (spaces >> attribute).repeat }
|
18
|
+
rule(:attributes?) { attributes.maybe }
|
19
|
+
rule(:element) { name_string.as(:element) }
|
20
|
+
rule(:DC) { namespace >> element >> (spaces >> attributes.as(:attributes)).maybe }
|
21
|
+
|
22
|
+
root(:DC)
|
23
|
+
|
24
|
+
def to_target(tree)
|
25
|
+
tree = tree[:DC]
|
26
|
+
return nil unless tree
|
27
|
+
result = "xml['#{tree[:namespace]}'].#{tree[:element]}("
|
28
|
+
tree[:attributes].each { |attribute| result += "'#{attribute[:name]}' => '#{attribute[:value]}'" }
|
29
|
+
result + ').text'
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'parslet'
|
4
|
+
|
5
|
+
require_relative 'basic_parser'
|
6
|
+
require_relative 'marc_rules'
|
7
|
+
|
8
|
+
module Libis
|
9
|
+
module Tools
|
10
|
+
module Metadata
|
11
|
+
|
12
|
+
# noinspection RubyResolve
|
13
|
+
class Marc21Parser < Libis::Tools::Metadata::BasicParser
|
14
|
+
|
15
|
+
root(:marc21)
|
16
|
+
rule(:marc21) { select.as(:select) | format.as(:format) }
|
17
|
+
|
18
|
+
# select syntax
|
19
|
+
rule(:select) do
|
20
|
+
str('MARC') >>
|
21
|
+
spaces? >> tag.as(:tag) >>
|
22
|
+
spaces? >> indicator.maybe.as(:ind1) >> indicator.maybe.as(:ind2) >>
|
23
|
+
spaces? >> subfield.maybe.as(:subfield) >>
|
24
|
+
spaces? >> condition.maybe.as(:condition)
|
25
|
+
end
|
26
|
+
rule(:condition) { grouped_anonymous(cond_format.as(:cond_format)) }
|
27
|
+
rule(:cond_format) { cond_entry.repeat(1).maybe.as(:entry) >> postfix.maybe.as(:postfix) }
|
28
|
+
rule(:cond_entry) { sf_reference | method_call | cond_group }
|
29
|
+
rule(:cond_group) { (prefix.maybe.as(:prefix) >> grouped(cond_format)).as(:cond_group) }
|
30
|
+
|
31
|
+
# Formatting syntax
|
32
|
+
rule(:format) { entry.repeat(1).maybe.as(:entry) >> postfix.maybe.as(:postfix) }
|
33
|
+
|
34
|
+
rule(:entry) { sf_reference | method_call | group }
|
35
|
+
# noinspection RubyArgCount
|
36
|
+
rule(:group) { (prefix.maybe.as(:prefix) >> grouped(format)).as(:group) }
|
37
|
+
# noinspection RubyArgCount
|
38
|
+
rule(:method_call) { (prefix.maybe.as(:prefix) >> sf_indicator >> grouped_anonymous(format, lrparen)).as(:method_call) }
|
39
|
+
|
40
|
+
# pre- and postfix
|
41
|
+
rule(:prefix) { other.repeat(1) }
|
42
|
+
rule(:prefix) { text }
|
43
|
+
rule(:postfix) { other.repeat(1) }
|
44
|
+
rule(:postfix) { text }
|
45
|
+
|
46
|
+
# subfield reference
|
47
|
+
rule(:sf_reference) { sf_variable.as(:subfield) | sf_fixed.as(:fixfield) }
|
48
|
+
|
49
|
+
rule(:sf_variable) { prefix.maybe.as(:prefix) >> sf_indicator >> sf_repeat.maybe.as(:repeat) >> sf_name }
|
50
|
+
rule(:sf_repeat) { star >> any_quoted(:separator).maybe }
|
51
|
+
|
52
|
+
rule(:sf_fixed) { prefix.maybe.as(:prefix) >> sf_indicator >> lsparen >> (sf_range | sf_position | sf_star) >> rsparen }
|
53
|
+
rule(:sf_star) { star.as(:all) }
|
54
|
+
rule(:sf_position) { integer.as(:position) }
|
55
|
+
rule(:sf_range) { integer.as(:first) >> minus >> integer.as(:last) }
|
56
|
+
|
57
|
+
rule(:other) { paren.absent? >> dollar.absent? >> any | litteral_dollar }
|
58
|
+
|
59
|
+
# tag
|
60
|
+
rule(:tag) { tag_numeric | tag_alpha }
|
61
|
+
rule(:tag_numeric) { number.repeat(3, 3) }
|
62
|
+
rule(:tag_alpha) { character.repeat(3, 3) }
|
63
|
+
|
64
|
+
# indicator
|
65
|
+
rule(:indicator) { hashtag | underscore | number | character }
|
66
|
+
|
67
|
+
# subfield
|
68
|
+
rule(:sf_indicator) { dollar }
|
69
|
+
rule(:sf_name) { (character | number).as(:name) }
|
70
|
+
rule(:sf_names) { (character | number).repeat(1).as(:names) }
|
71
|
+
rule(:subfield) { sf_indicator >> sf_name }
|
72
|
+
rule(:litteral_dollar) { dollar >> dollar }
|
73
|
+
|
74
|
+
# noinspection RubyResolve
|
75
|
+
class Transformer < Parslet::Transform
|
76
|
+
rule(name: simple(:name)) { "#{name}" }
|
77
|
+
# select transformation rules
|
78
|
+
rule(cond_group: {
|
79
|
+
prefix: simple(:prefix),
|
80
|
+
lparen: simple(:lparen),
|
81
|
+
entry: simple(:entry),
|
82
|
+
postfix: simple(:postfix),
|
83
|
+
rparen: simple(:rparen)}) {
|
84
|
+
"#{prefix}#{lparen}#{entry}#{postfix}#{rparen}"
|
85
|
+
}
|
86
|
+
rule(cond_group: {
|
87
|
+
prefix: simple(:prefix),
|
88
|
+
lparen: simple(:lparen),
|
89
|
+
entry: sequence(:entry),
|
90
|
+
postfix: simple(:postfix),
|
91
|
+
rparen: simple(:rparen)}) {
|
92
|
+
"#{prefix}#{lparen}#{entry.join}#{postfix}#{rparen}"
|
93
|
+
}
|
94
|
+
rule(cond_format: {
|
95
|
+
entry: sequence(:entry),
|
96
|
+
postfix: simple(:postfix)
|
97
|
+
}) { ", Proc.new { |f| #{entry.join}#{postfix} }" }
|
98
|
+
rule(select: {
|
99
|
+
tag: simple(:tag),
|
100
|
+
ind1: simple(:ind1),
|
101
|
+
ind2: simple(:ind2),
|
102
|
+
subfield: simple(:subfield),
|
103
|
+
condition: simple(:condition)
|
104
|
+
}) { "record.select_fields('#{tag}#{ind1 || '#'}#{ind2 || '#'}#{subfield}'#{condition || ''})" }
|
105
|
+
# format transformation rules
|
106
|
+
rule(format: {
|
107
|
+
entry: sequence(:entries),
|
108
|
+
postfix: simple(:postfix)
|
109
|
+
}) do
|
110
|
+
if entries.size == 1 && postfix.nil?
|
111
|
+
entries.first
|
112
|
+
else
|
113
|
+
"field_format(#{entries.join(',')}#{", postfix: '#{postfix}'" if postfix}).to_s"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
rule(group: {
|
117
|
+
prefix: simple(:prefix),
|
118
|
+
lparen: simple(:lparen),
|
119
|
+
entry: nil,
|
120
|
+
postfix: simple(:postfix),
|
121
|
+
rparen: simple(:rparen)}) {
|
122
|
+
"#{prefix}#{lparen}#{postfix}#{rparen}"
|
123
|
+
}
|
124
|
+
rule(group: {
|
125
|
+
prefix: simple(:prefix),
|
126
|
+
lparen: simple(:lparen),
|
127
|
+
entry: '',
|
128
|
+
postfix: simple(:postfix),
|
129
|
+
rparen: simple(:rparen)}) {
|
130
|
+
"#{prefix}#{lparen}#{postfix}#{rparen}"
|
131
|
+
}
|
132
|
+
rule(group: {
|
133
|
+
prefix: simple(:prefix),
|
134
|
+
lparen: simple(:lparen),
|
135
|
+
entry: simple(:entry),
|
136
|
+
postfix: simple(:postfix),
|
137
|
+
rparen: simple(:rparen)}) {
|
138
|
+
"field_format(#{entry}#{", prefix: '#{prefix}#{lparen}'" if prefix || lparen}#{", postfix: '#{postfix}#{rparen}'" if postfix || rparen}).to_s"
|
139
|
+
}
|
140
|
+
rule(group: {
|
141
|
+
prefix: simple(:prefix),
|
142
|
+
lparen: simple(:lparen),
|
143
|
+
entry: sequence(:entries),
|
144
|
+
postfix: simple(:postfix),
|
145
|
+
rparen: simple(:rparen)}) {
|
146
|
+
"field_format(#{entries.join(',')}#{", prefix: '#{prefix}#{lparen}'" if prefix || lparen}#{", postfix: '#{postfix}#{rparen}'" if postfix || rparen}).to_s"
|
147
|
+
}
|
148
|
+
rule(fixfield: {
|
149
|
+
prefix: nil,
|
150
|
+
all: '*'
|
151
|
+
}) { 'f[]' }
|
152
|
+
rule(fixfield: {
|
153
|
+
prefix: simple(:prefix),
|
154
|
+
all: '*'
|
155
|
+
}) { "field_format(f[], prefix: '#{prefix}').to_s" }
|
156
|
+
rule(fixfield: {
|
157
|
+
prefix: nil,
|
158
|
+
position: simple(:position)
|
159
|
+
}) { "f[#{position}]" }
|
160
|
+
rule(fixfield: {
|
161
|
+
prefix: simple(:prefix),
|
162
|
+
position: simple(:position)
|
163
|
+
}) do
|
164
|
+
if prefix
|
165
|
+
"field_format(f[#{position}], prefix: '#{prefix}').to_s"
|
166
|
+
else
|
167
|
+
"f[#{position}]"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
rule(fixfield: {
|
171
|
+
prefix: nil,
|
172
|
+
first: simple(:from),
|
173
|
+
last: simple(:to)
|
174
|
+
}) { "f[#{from},#{to}]" }
|
175
|
+
rule(fixfield: {
|
176
|
+
prefix: simple(:prefix),
|
177
|
+
first: simple(:from),
|
178
|
+
last: simple(:to)
|
179
|
+
}) { "field_format(f[#{from},#{to}], prefix: '#{prefix}').to_s" }
|
180
|
+
rule(subfield: {
|
181
|
+
prefix: simple(:prefix),
|
182
|
+
repeat: nil,
|
183
|
+
name: simple(:name),
|
184
|
+
}) { "field_format(f.subfield('#{name}'), prefix: '#{prefix}').to_s" }
|
185
|
+
rule(subfield: {
|
186
|
+
prefix: simple(:prefix),
|
187
|
+
repeat: {separator: simple(:separator)},
|
188
|
+
name: simple(:name),
|
189
|
+
}) { "field_format(f.subfield_array('#{name}')#{", prefix: '#{prefix}'" if prefix}, join: '#{separator}').to_s" }
|
190
|
+
rule(subfield: {
|
191
|
+
prefix: simple(:prefix),
|
192
|
+
repeat: '*',
|
193
|
+
name: simple(:name),
|
194
|
+
}) { "field_format(f.subfield_array('#{name}')#{", prefix: '#{prefix}'" if prefix}, join: ';').to_s" }
|
195
|
+
rule(subfield: {
|
196
|
+
prefix: nil,
|
197
|
+
repeat: nil,
|
198
|
+
name: simple(:name),
|
199
|
+
}) { "f.subfield('#{name}')" }
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|