marc 1.1.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
- data/.github/workflows/ruby.yml +24 -0
- data/.gitignore +17 -0
- data/.standard.yml +1 -0
- data/{Changes → CHANGELOG.md} +116 -30
- data/Gemfile +5 -0
- data/README.md +239 -46
- data/Rakefile +14 -14
- data/bin/marc +14 -0
- data/bin/marc2xml +17 -0
- data/examples/xml2marc.rb +10 -0
- data/lib/marc/constants.rb +3 -3
- data/lib/marc/controlfield.rb +35 -23
- data/lib/marc/datafield.rb +70 -63
- data/lib/marc/dublincore.rb +59 -41
- data/lib/marc/exception.rb +9 -1
- data/lib/marc/jsonl_reader.rb +33 -0
- data/lib/marc/jsonl_writer.rb +44 -0
- data/lib/marc/marc8/map_to_unicode.rb +16417 -16420
- data/lib/marc/marc8/to_unicode.rb +80 -87
- data/lib/marc/reader.rb +116 -124
- data/lib/marc/record.rb +72 -62
- data/lib/marc/subfield.rb +12 -10
- data/lib/marc/unsafe_xmlwriter.rb +93 -0
- data/lib/marc/version.rb +1 -1
- data/lib/marc/writer.rb +27 -30
- data/lib/marc/xml_parsers.rb +222 -197
- data/lib/marc/xmlreader.rb +131 -114
- data/lib/marc/xmlwriter.rb +93 -82
- data/lib/marc.rb +20 -18
- data/marc.gemspec +28 -0
- data/test/marc8/tc_marc8_mapping.rb +3 -3
- data/test/marc8/tc_to_unicode.rb +28 -34
- data/test/messed_up_leader.xml +9 -0
- data/test/tc_controlfield.rb +37 -34
- data/test/tc_datafield.rb +65 -60
- data/test/tc_dublincore.rb +9 -11
- data/test/tc_hash.rb +10 -13
- data/test/tc_jsonl.rb +19 -0
- data/test/tc_marchash.rb +17 -21
- data/test/tc_parsers.rb +108 -144
- data/test/tc_reader.rb +35 -36
- data/test/tc_reader_char_encodings.rb +149 -169
- data/test/tc_record.rb +143 -148
- data/test/tc_subfield.rb +14 -13
- data/test/tc_unsafe_xml.rb +95 -0
- data/test/tc_writer.rb +101 -108
- data/test/tc_xml.rb +101 -94
- data/test/tc_xml_error_handling.rb +7 -8
- data/test/ts_marc.rb +8 -8
- metadata +129 -22
data/lib/marc/record.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module MARC
|
2
|
-
|
3
2
|
# The FieldMap is an Array of DataFields and Controlfields.
|
4
3
|
# It also contains a Hash representation
|
5
4
|
# of the fields for faster lookups (under certain conditions)
|
@@ -8,7 +7,7 @@ module MARC
|
|
8
7
|
attr_accessor :clean
|
9
8
|
|
10
9
|
def initialize
|
11
|
-
@tags
|
10
|
+
@tags = {}
|
12
11
|
@clean = true
|
13
12
|
end
|
14
13
|
|
@@ -16,7 +15,7 @@ module MARC
|
|
16
15
|
# values of the fields Array
|
17
16
|
def reindex
|
18
17
|
@tags = {}
|
19
|
-
|
18
|
+
each_with_index do |field, i|
|
20
19
|
@tags[field.tag] ||= []
|
21
20
|
@tags[field.tag] << i
|
22
21
|
end
|
@@ -45,7 +44,7 @@ module MARC
|
|
45
44
|
indices.compact!
|
46
45
|
return [] if indices.empty?
|
47
46
|
|
48
|
-
|
47
|
+
# Sort it, so we get the fields back in the order they appear in the record
|
49
48
|
indices.sort!
|
50
49
|
|
51
50
|
indices.each do |tag|
|
@@ -53,13 +52,11 @@ module MARC
|
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
56
|
-
|
57
|
-
|
58
55
|
# Freeze for immutability, first reindexing if needed.
|
59
56
|
# A frozen FieldMap is safe for concurrent access, and also
|
60
57
|
# can more easily avoid accidental reindexing on even read-only use.
|
61
58
|
def freeze
|
62
|
-
|
59
|
+
reindex unless @clean
|
63
60
|
super
|
64
61
|
end
|
65
62
|
end
|
@@ -110,19 +107,29 @@ module MARC
|
|
110
107
|
include Enumerable
|
111
108
|
|
112
109
|
# the record fields
|
113
|
-
#attr_reader :fields
|
110
|
+
# attr_reader :fields
|
114
111
|
|
115
112
|
# the record leader
|
116
113
|
attr_accessor :leader
|
117
114
|
|
118
115
|
def initialize
|
119
|
-
@fields
|
116
|
+
@fields = FieldMap.new
|
120
117
|
# leader is 24 bytes
|
121
|
-
@leader
|
118
|
+
@leader = " " * 24
|
122
119
|
# leader defaults:
|
123
120
|
# http://www.loc.gov/marc/bibliographic/ecbdldrd.html
|
124
|
-
@leader[10..11] =
|
125
|
-
@leader[20..23] =
|
121
|
+
@leader[10..11] = "22"
|
122
|
+
@leader[20..23] = "4500"
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns true if there are no error messages associated with the record
|
126
|
+
def valid?
|
127
|
+
errors.none?
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns an array of validation errors for all fields in the record
|
131
|
+
def errors
|
132
|
+
@fields.flat_map(&:errors)
|
126
133
|
end
|
127
134
|
|
128
135
|
# add a field to the record
|
@@ -152,7 +159,7 @@ module MARC
|
|
152
159
|
# subjects = record.find_all {|f| ('600'..'699') === f.tag}
|
153
160
|
|
154
161
|
def each
|
155
|
-
|
162
|
+
@fields.each do |field|
|
156
163
|
yield field
|
157
164
|
end
|
158
165
|
end
|
@@ -167,14 +174,14 @@ module MARC
|
|
167
174
|
# title = record['245']
|
168
175
|
|
169
176
|
def [](tag)
|
170
|
-
|
177
|
+
find { |f| f.tag == tag }
|
171
178
|
end
|
172
179
|
|
173
180
|
# Provides a backwards compatible means to access the FieldMap.
|
174
181
|
# No argument returns the FieldMap array in entirety. Providing
|
175
182
|
# a string, array or range of tags will return an array of fields
|
176
183
|
# in the order they appear in the record.
|
177
|
-
def fields(filter=nil)
|
184
|
+
def fields(filter = nil)
|
178
185
|
unless filter
|
179
186
|
# Since we're returning the FieldMap object, which the caller
|
180
187
|
# may mutate, we precautionarily mark dirty -- unless it's frozen
|
@@ -198,7 +205,7 @@ module MARC
|
|
198
205
|
|
199
206
|
# Returns an array of all of the tags that appear in the record (not necessarily in the order they appear).
|
200
207
|
def tags
|
201
|
-
|
208
|
+
@fields.tag_list
|
202
209
|
end
|
203
210
|
|
204
211
|
# Factory method for creating a MARC::Record from MARC21 in
|
@@ -213,18 +220,17 @@ module MARC
|
|
213
220
|
#
|
214
221
|
# record = MARC::Record.new_from_marc(marc21, :forgiving => true)
|
215
222
|
|
216
|
-
def self.new_from_marc(raw, params={})
|
217
|
-
|
223
|
+
def self.new_from_marc(raw, params = {})
|
224
|
+
MARC::Reader.decode(raw, params)
|
218
225
|
end
|
219
226
|
|
220
|
-
|
221
227
|
# Returns a record in MARC21 transmission format (ANSI Z39.2).
|
222
228
|
# Really this is just a wrapper around MARC::MARC21::encode
|
223
229
|
#
|
224
230
|
# marc = record.to_marc()
|
225
231
|
|
226
232
|
def to_marc
|
227
|
-
|
233
|
+
MARC::Writer.encode(self)
|
228
234
|
end
|
229
235
|
|
230
236
|
# Handy method for returning the MARCXML serialization for a
|
@@ -232,9 +238,21 @@ module MARC
|
|
232
238
|
# Really this is just a wrapper around MARC::XMLWriter::encode
|
233
239
|
#
|
234
240
|
# xml_doc = record.to_xml()
|
241
|
+
def to_xml(include_namespace: true)
|
242
|
+
MARC::XMLWriter.encode(self, include_namespace: include_namespace)
|
243
|
+
end
|
235
244
|
|
236
|
-
|
237
|
-
|
245
|
+
# Create the actual XML string (as opposed to #to_xml which, for historic reasons,
|
246
|
+
# returns an REXML document)
|
247
|
+
# @param [Boolean] fast_but_unsafe Use the fast MARC::UnsafeXMLWriter code
|
248
|
+
# @param [Boolean] include_namespace Include namespaces on the <record> tag?
|
249
|
+
# @return [String] MARC-XML encoding of the record
|
250
|
+
def to_xml_string(fast_but_unsafe: false, include_namespace: true)
|
251
|
+
if fast_but_unsafe
|
252
|
+
MARC::UnsafeXMLWriter.encode(self, include_namespace: include_namespace)
|
253
|
+
else
|
254
|
+
MARC::XMLWriter.encode(self, include_namespace: include_namespace).to_s
|
255
|
+
end
|
238
256
|
end
|
239
257
|
|
240
258
|
# Handy method for returning a hash mapping this records values
|
@@ -244,95 +262,87 @@ module MARC
|
|
244
262
|
# print dc['title']
|
245
263
|
|
246
264
|
def to_dublin_core
|
247
|
-
|
265
|
+
MARC::DublinCore.map(self)
|
248
266
|
end
|
249
267
|
|
250
268
|
# Return a marc-hash version of the record
|
251
269
|
def to_marchash
|
252
|
-
|
253
|
-
'type' => 'marc-hash',
|
254
|
-
'version' => [MARCHASH_MAJOR_VERSION, MARCHASH_MINOR_VERSION],
|
255
|
-
'leader' => self.leader,
|
256
|
-
'fields' => self.map { |f| f.to_marchash }
|
257
|
-
}
|
270
|
+
{"type" => "marc-hash", "version" => [MARCHASH_MAJOR_VERSION, MARCHASH_MINOR_VERSION], "leader" => leader, "fields" => map { |f| f.to_marchash }}
|
258
271
|
end
|
259
272
|
|
260
|
-
#to_hash
|
261
|
-
|
262
273
|
# Factory method for creating a new MARC::Record from
|
263
274
|
# a marchash object
|
264
275
|
#
|
265
276
|
# record = MARC::Record->new_from_marchash(mh)
|
266
277
|
|
267
278
|
def self.new_from_marchash(mh)
|
268
|
-
r
|
269
|
-
r.leader = mh[
|
270
|
-
mh[
|
271
|
-
if
|
279
|
+
r = new
|
280
|
+
r.leader = mh["leader"]
|
281
|
+
mh["fields"].each do |f|
|
282
|
+
if f.length == 2
|
272
283
|
r << MARC::ControlField.new(f[0], f[1])
|
273
284
|
elsif r << MARC::DataField.new(f[0], f[1], f[2], *f[3])
|
274
285
|
end
|
275
286
|
end
|
276
|
-
|
287
|
+
r
|
277
288
|
end
|
278
289
|
|
279
|
-
|
280
290
|
# Returns a (roundtrippable) hash representation for MARC-in-JSON
|
281
291
|
def to_hash
|
282
|
-
record_hash = {
|
292
|
+
record_hash = {"leader" => @leader, "fields" => []}
|
283
293
|
@fields.each do |field|
|
284
|
-
record_hash[
|
294
|
+
record_hash["fields"] << field.to_hash
|
285
295
|
end
|
286
296
|
record_hash
|
287
297
|
end
|
288
298
|
|
299
|
+
# Return an actual json-encoded string.
|
300
|
+
def to_json_string
|
301
|
+
MARC::JSONLWriter.encode(self)
|
302
|
+
end
|
303
|
+
|
289
304
|
def self.new_from_hash(h)
|
290
|
-
r
|
291
|
-
r.leader = h[
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
f.append MARC::Subfield.new(code, value)
|
300
|
-
end
|
305
|
+
r = new
|
306
|
+
r.leader = h["leader"]
|
307
|
+
h["fields"]&.each do |position|
|
308
|
+
position.each_pair do |tag, field|
|
309
|
+
if field.is_a?(Hash)
|
310
|
+
f = MARC::DataField.new(tag, field["ind1"], field["ind2"])
|
311
|
+
field["subfields"].each do |pos|
|
312
|
+
pos.each_pair do |code, value|
|
313
|
+
f.append MARC::Subfield.new(code, value)
|
301
314
|
end
|
302
|
-
r << f
|
303
|
-
else
|
304
|
-
r << MARC::ControlField.new(tag, field)
|
305
315
|
end
|
316
|
+
r << f
|
317
|
+
else
|
318
|
+
r << MARC::ControlField.new(tag, field)
|
306
319
|
end
|
307
320
|
end
|
308
321
|
end
|
309
|
-
|
322
|
+
r
|
310
323
|
end
|
311
324
|
|
312
325
|
# Returns a string version of the record, suitable for printing
|
313
326
|
|
314
327
|
def to_s
|
315
328
|
str = "LEADER #{leader}\n"
|
316
|
-
|
317
|
-
str += field.to_s
|
329
|
+
each do |field|
|
330
|
+
str += field.to_s + "\n"
|
318
331
|
end
|
319
|
-
|
332
|
+
str
|
320
333
|
end
|
321
334
|
|
322
|
-
|
323
335
|
# For testing if two records can be considered equal.
|
324
336
|
|
325
337
|
def ==(other)
|
326
|
-
|
338
|
+
to_s == other.to_s
|
327
339
|
end
|
328
340
|
|
329
|
-
|
330
341
|
# Handy for using a record in a regex:
|
331
342
|
# if record =~ /Gravity's Rainbow/ then print "Slothrop" end
|
332
343
|
|
333
344
|
def =~(regex)
|
334
|
-
|
345
|
+
to_s =~ regex
|
335
346
|
end
|
336
|
-
|
337
347
|
end
|
338
348
|
end
|
data/lib/marc/subfield.rb
CHANGED
@@ -1,31 +1,33 @@
|
|
1
1
|
module MARC
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
# not be set to nil.
|
2
|
+
# A class that represents an individual subfield within a DataField.
|
3
|
+
# Accessor attributes include: code (letter subfield code) and value
|
4
|
+
# (the content of the subfield). Both can be empty string, but should
|
5
|
+
# not be set to nil.
|
7
6
|
|
8
7
|
class Subfield
|
9
8
|
attr_accessor :code, :value
|
10
9
|
|
11
|
-
def initialize(code=
|
10
|
+
def initialize(code = "", value = "")
|
12
11
|
# can't allow code of value to be nil
|
13
12
|
# or else it'll screw us up later on
|
14
|
-
@code = code
|
15
|
-
@value = value
|
13
|
+
@code = code.nil? ? "" : code
|
14
|
+
@value = value.nil? ? "" : value
|
16
15
|
end
|
17
16
|
|
18
17
|
def ==(other)
|
18
|
+
if !other.is_a?(Subfield)
|
19
|
+
return false
|
20
|
+
end
|
19
21
|
if @code != other.code
|
20
22
|
return false
|
21
23
|
elsif @value != other.value
|
22
24
|
return false
|
23
25
|
end
|
24
|
-
|
26
|
+
true
|
25
27
|
end
|
26
28
|
|
27
29
|
def to_s
|
28
|
-
|
30
|
+
"$#{code} #{value} "
|
29
31
|
end
|
30
32
|
end
|
31
33
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require "marc/xmlwriter"
|
2
|
+
|
3
|
+
module MARC
|
4
|
+
# UnsafeXMLWriter bypasses real xml handlers like REXML or Nokogiri and just concatenates strings
|
5
|
+
# to produce the XML document. This has no guarantees of validity if the MARC record you're encoding
|
6
|
+
# isn't valid and won't do things like entity expansion, but it does escape using ruby's
|
7
|
+
# String#encode(xml: :text) and it's much, much faster -- 4-5 times faster than using Nokogiri,
|
8
|
+
# and 15-20 times faster than the REXML version.
|
9
|
+
class UnsafeXMLWriter < MARC::XMLWriter
|
10
|
+
XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>'
|
11
|
+
NS_ATTRS = %(xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.loc.gov/MARC21/slim" xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd")
|
12
|
+
|
13
|
+
NS_COLLECTION = "<collection #{NS_ATTRS}>".freeze
|
14
|
+
COLLECTION = "<collection>".freeze
|
15
|
+
NS_RECORD = "<record #{NS_ATTRS}>".freeze
|
16
|
+
RECORD = "<record>".freeze
|
17
|
+
|
18
|
+
# Write the record to the target
|
19
|
+
# @param [MARC::Record] record
|
20
|
+
def write(record)
|
21
|
+
@fh.write(self.class.encode(record))
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
# Open `collection` tag, w or w/o namespace
|
26
|
+
def open_collection(include_namespace: true)
|
27
|
+
if include_namespace
|
28
|
+
NS_COLLECTION
|
29
|
+
else
|
30
|
+
COLLECTION
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def open_record(include_namespace: true)
|
35
|
+
if include_namespace
|
36
|
+
NS_RECORD
|
37
|
+
else
|
38
|
+
RECORD
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Produce an XML string with a single document in a collection
|
43
|
+
# @param [MARC::Record] record
|
44
|
+
# @param [Boolean] include_namespace Whether to namespace the resulting XML
|
45
|
+
def single_record_document(record, include_namespace: true)
|
46
|
+
xml = XML_HEADER.dup
|
47
|
+
xml << open_collection(include_namespace: include_namespace)
|
48
|
+
xml << encode(record, include_namespace: false)
|
49
|
+
xml << "</collection>".freeze
|
50
|
+
xml
|
51
|
+
end
|
52
|
+
|
53
|
+
# Take a record and turn it into a valid MARC-XML string. Note that
|
54
|
+
# this is an XML _snippet_, without an XML header or <collection>
|
55
|
+
# enclosure.
|
56
|
+
# @param [MARC::Record] record The record to encode to XML
|
57
|
+
# @return [String] The XML snippet of the record in MARC-XML
|
58
|
+
def encode(record, include_namespace: true)
|
59
|
+
xml = open_record(include_namespace: include_namespace).dup
|
60
|
+
|
61
|
+
# MARCXML only allows alphanumerics or spaces in the leader
|
62
|
+
lead = fix_leader(record.leader)
|
63
|
+
|
64
|
+
xml << "<leader>" << lead.encode(xml: :text) << "</leader>"
|
65
|
+
record.each do |f|
|
66
|
+
if f.instance_of?(MARC::DataField)
|
67
|
+
xml << open_datafield(f.tag, f.indicator1, f.indicator2)
|
68
|
+
f.each do |sf|
|
69
|
+
xml << open_subfield(sf.code) << sf.value.encode(xml: :text) << "</subfield>"
|
70
|
+
end
|
71
|
+
xml << "</datafield>"
|
72
|
+
elsif f.instance_of?(MARC::ControlField)
|
73
|
+
xml << open_controlfield(f.tag) << f.value.encode(xml: :text) << "</controlfield>"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
xml << "</record>"
|
77
|
+
xml.force_encoding("utf-8")
|
78
|
+
end
|
79
|
+
|
80
|
+
def open_datafield(tag, ind1, ind2)
|
81
|
+
"<datafield tag=\"#{tag}\" ind1=\"#{ind1}\" ind2=\"#{ind2}\">"
|
82
|
+
end
|
83
|
+
|
84
|
+
def open_subfield(code)
|
85
|
+
"<subfield code=\"#{code}\">"
|
86
|
+
end
|
87
|
+
|
88
|
+
def open_controlfield(tag)
|
89
|
+
"<controlfield tag=\"#{tag}\">"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/marc/version.rb
CHANGED
data/lib/marc/writer.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module MARC
|
2
|
-
|
3
2
|
# A class for writing MARC records as binary MARC (ISO 2709)
|
4
3
|
#
|
5
4
|
# == Too-long records
|
@@ -29,65 +28,65 @@ module MARC
|
|
29
28
|
# the constructor which you must pass a file path
|
30
29
|
# or an object that responds to a write message
|
31
30
|
|
32
|
-
def initialize(file)
|
33
|
-
if file.
|
34
|
-
@fh = File.new(file,"w")
|
35
|
-
elsif file.respond_to?(
|
31
|
+
def initialize(file, &blk)
|
32
|
+
if file.instance_of?(String)
|
33
|
+
@fh = File.new(file, "w")
|
34
|
+
elsif file.respond_to?(:write)
|
36
35
|
@fh = file
|
37
36
|
else
|
38
37
|
raise ArgumentError, "must pass in file name or handle"
|
39
38
|
end
|
40
39
|
self.allow_oversized = false
|
41
|
-
end
|
42
40
|
|
41
|
+
if block_given?
|
42
|
+
blk.call(self)
|
43
|
+
self.close
|
44
|
+
end
|
45
|
+
end
|
43
46
|
|
44
47
|
# write a record to the file or handle
|
45
48
|
|
46
49
|
def write(record)
|
47
|
-
@fh.write(MARC::Writer.encode(record,
|
50
|
+
@fh.write(MARC::Writer.encode(record, allow_oversized))
|
48
51
|
end
|
49
52
|
|
50
|
-
|
51
53
|
# close underlying filehandle
|
52
54
|
|
53
55
|
def close
|
54
56
|
@fh.close
|
55
57
|
end
|
56
58
|
|
57
|
-
|
58
59
|
# a static method that accepts a MARC::Record object
|
59
60
|
# and returns the record encoded as MARC21 in transmission format
|
60
61
|
#
|
61
62
|
# Second arg allow_oversized, default false, set to true
|
62
|
-
# to raise on MARC record that can't fit into ISO 2709.
|
63
|
+
# to raise on MARC record that can't fit into ISO 2709.
|
63
64
|
def self.encode(record, allow_oversized = false)
|
64
|
-
directory =
|
65
|
-
fields =
|
65
|
+
directory = ""
|
66
|
+
fields = ""
|
66
67
|
offset = 0
|
67
68
|
record.each do |field|
|
68
|
-
|
69
69
|
# encode the field
|
70
|
-
field_data =
|
71
|
-
if field.
|
70
|
+
field_data = ""
|
71
|
+
if field.instance_of?(MARC::DataField)
|
72
72
|
warn("Warn: Missing indicator") unless field.indicator1 && field.indicator2
|
73
73
|
field_data = (field.indicator1 || " ") + (field.indicator2 || " ")
|
74
|
-
|
74
|
+
field.subfields.each do |s|
|
75
75
|
field_data += SUBFIELD_INDICATOR + s.code + s.value
|
76
76
|
end
|
77
|
-
elsif field.
|
77
|
+
elsif field.instance_of?(MARC::ControlField)
|
78
78
|
field_data = field.value
|
79
79
|
end
|
80
80
|
field_data += END_OF_FIELD
|
81
81
|
|
82
82
|
# calculate directory entry for the field
|
83
83
|
field_length = (field_data.respond_to?(:bytesize) ?
|
84
|
-
|
85
|
-
|
84
|
+
field_data.bytesize :
|
85
|
+
field_data.length)
|
86
86
|
directory += sprintf("%03s", field.tag) + format_byte_count(field_length, allow_oversized, 4) + format_byte_count(offset, allow_oversized)
|
87
87
|
|
88
|
-
|
89
88
|
# add field to data for other fields
|
90
|
-
fields += field_data
|
89
|
+
fields += field_data
|
91
90
|
|
92
91
|
# update offset for next field
|
93
92
|
offset += field_length
|
@@ -100,19 +99,18 @@ module MARC
|
|
100
99
|
marc = base + fields + END_OF_RECORD
|
101
100
|
|
102
101
|
# update leader with the byte offest to the end of the directory
|
103
|
-
bytesize = base.respond_to?(:bytesize) ? base.bytesize() : base.length
|
102
|
+
bytesize = base.respond_to?(:bytesize) ? base.bytesize() : base.length
|
104
103
|
marc[12..16] = format_byte_count(bytesize, allow_oversized)
|
105
|
-
|
106
104
|
|
107
105
|
# update the record length
|
108
|
-
bytesize = marc.respond_to?(:bytesize) ? marc.bytesize() : marc.length
|
109
|
-
marc[0..4] = format_byte_count(bytesize, allow_oversized)
|
106
|
+
bytesize = marc.respond_to?(:bytesize) ? marc.bytesize() : marc.length
|
107
|
+
marc[0..4] = format_byte_count(bytesize, allow_oversized)
|
110
108
|
|
111
109
|
# store updated leader in the record that was passed in
|
112
|
-
record.leader = marc[0..LEADER_LENGTH-1]
|
110
|
+
record.leader = marc[0..LEADER_LENGTH - 1]
|
113
111
|
|
114
112
|
# return encoded marc
|
115
|
-
|
113
|
+
marc
|
116
114
|
end
|
117
115
|
|
118
116
|
# Formats numbers for insertion into marc binary slots.
|
@@ -123,7 +121,7 @@ module MARC
|
|
123
121
|
#
|
124
122
|
# first arg is number, second is boolean whether to allow oversized,
|
125
123
|
# third is max digits (default 5)
|
126
|
-
def self.format_byte_count(number, allow_oversized, num_digits=5)
|
124
|
+
def self.format_byte_count(number, allow_oversized, num_digits = 5)
|
127
125
|
formatted = sprintf("%0#{num_digits}i", number)
|
128
126
|
if formatted.length > num_digits
|
129
127
|
# uh, oh, we've exceeded our max. Either zero out
|
@@ -134,8 +132,7 @@ module MARC
|
|
134
132
|
raise MARC::Exception.new("Can't write MARC record in binary format, as a length/offset value of #{number} is too long for a #{num_digits}-byte slot.")
|
135
133
|
end
|
136
134
|
end
|
137
|
-
|
135
|
+
formatted
|
138
136
|
end
|
139
|
-
|
140
137
|
end
|
141
138
|
end
|