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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -2
  3. data/README.md +19 -0
  4. data/lib/libis/tools.rb +1 -0
  5. data/lib/libis/tools/config_file.rb +1 -1
  6. data/lib/libis/tools/extend/kernel.rb +16 -0
  7. data/lib/libis/tools/metadata.rb +20 -0
  8. data/lib/libis/tools/metadata/dublin_core_record.rb +1 -1
  9. data/lib/libis/tools/metadata/{field_spec.rb → field_format.rb} +7 -7
  10. data/lib/libis/tools/metadata/fix_field.rb +6 -1
  11. data/lib/libis/tools/metadata/mapper.rb +80 -0
  12. data/lib/libis/tools/metadata/mappers/flandrica.rb +69 -0
  13. data/lib/libis/tools/metadata/mappers/kuleuven.rb +1702 -0
  14. data/lib/libis/tools/metadata/marc21_record.rb +5 -4
  15. data/lib/libis/tools/metadata/marc_record.rb +96 -37
  16. data/lib/libis/tools/metadata/parser/basic_parser.rb +118 -0
  17. data/lib/libis/tools/metadata/parser/dublin_core_parser.rb +36 -0
  18. data/lib/libis/tools/metadata/parser/marc21_parser.rb +206 -0
  19. data/lib/libis/tools/metadata/parser/marc_format_parser.rb +52 -0
  20. data/lib/libis/tools/metadata/parser/marc_rules.rb +35 -0
  21. data/lib/libis/tools/metadata/parser/marc_select_parser.rb +25 -0
  22. data/lib/libis/tools/metadata/parser/patch.rb +21 -0
  23. data/lib/libis/tools/metadata/parser/subfield_criteria_parser.rb +71 -0
  24. data/lib/libis/tools/metadata/parsers.rb +12 -0
  25. data/lib/libis/tools/metadata/var_field.rb +57 -47
  26. data/lib/libis/tools/parameter.rb +12 -2
  27. data/lib/libis/tools/version.rb +1 -1
  28. data/libis-tools.gemspec +4 -3
  29. data/spec/config_spec.rb +3 -1
  30. data/spec/data/MetadataMapping.xlsx +0 -0
  31. data/spec/metadata/8389207.marc +96 -0
  32. data/spec/metadata/dublin_core_parser_spec.rb +48 -0
  33. data/spec/metadata/marc21_parser_data.rb +382 -0
  34. data/spec/metadata/marc21_parser_spec.rb +67 -0
  35. data/spec/metadata/marc21_spec.rb +116 -0
  36. data/spec/metadata/metadata_mapper_spec.rb +23 -0
  37. data/spec/spec_helper.rb +13 -0
  38. data/test.rb +61 -0
  39. metadata +77 -7
  40. 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 = Hash.new { |h, k| h[k] = [] }
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
- subfields = Hash.new { |h, k| h[k] = [] }
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
- @all_records[tag] << VarField.new(tag, v['ind1'].to_s, v['ind2'].to_s, subfields)
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 'field_spec'
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
- # noinspection RubyResolve
30
- @all_records ||= get_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.each do |k, v|
35
- yield k, v
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
- def all_tags(tag, subfields = '')
40
- tag_, ind1, ind2 = tag =~ /^\d{3}/ ? [tag[0..2], tag[3], tag[4]] : [tag, nil, nil]
41
- result = get_records(tag_, ind1, ind2, subfields)
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
- def first_tag(t, subfields = '')
48
- result = all_tags(t, subfields).first
49
- return result unless block_given?
50
- return false unless result
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
- def each_tag(t, s = '')
56
- all_tags(t, s).each do |record|
57
- yield record
58
- end
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(t, s)
62
- r = all_tags(t, s).collect { |tag| tag.fields_array(s) }.flatten.compact
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.field_array(k).each { |f|
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
- field_spec(opts, *parts)
184
+ field_format(opts, *parts)
138
185
  end
139
186
 
140
187
  def list_s(*parts)
141
188
  opts = options parts, join: ' '
142
- field_spec(opts, *parts)
189
+ field_format(opts, *parts)
143
190
  end
144
191
 
145
192
  def list_c(*parts)
146
193
  opts = options parts, join: ', '
147
- field_spec(opts, *parts)
194
+ field_format(opts, *parts)
148
195
  end
149
196
 
150
197
  def list_d(*parts)
151
198
  opts = options parts, join: ' - '
152
- field_spec(opts, *parts)
199
+ field_format(opts, *parts)
153
200
  end
154
201
 
155
202
  def repeat(*parts)
156
203
  opts = options parts, join: '; '
157
- field_spec(opts, *parts)
204
+ field_format(opts, *parts)
158
205
  end
159
206
 
160
207
  def opt_r(*parts)
161
208
  opts = options parts, fix: '()'
162
- field_spec(opts, *parts)
209
+ field_format(opts, *parts)
163
210
  end
164
211
 
165
212
  def opt_s(*parts)
166
213
  opts = options parts, fix: '[]'
167
- field_spec(opts, *parts)
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 field_spec(default_options, *parts)
181
- FieldSpec.new(*parts).add_default_options(default_options).to_s
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.match_fieldspec?(subfields)
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