Class: MARC::Record
Overview
A class that represents an individual MARC record. Every record is made up of a collection of MARC::DataField objects.
MARC::Record mixes in Enumerable to enable access to constituent DataFields. For example, to return a list of all subject DataFields:
record.find_all {|field| field.tag =~ /^6../}
The accessor ‘fields’ is also an Array of MARC::DataField objects which the client can modify if neccesary.
record.fields.delete(field)
Other accessor attribute: ‘leader’ for record leader as String
High-performance lookup by tag
A frequent use case is looking up fields in a MARC record by tag, such as ‘all the 500 fields’. Certain methods can use a hash keyed by tag name for higher performance lookup by tag. The hash is lazily created on first access – there is some cost of creating the hash, testing shows you get a performance advantage to using the hash-based methods if you are doing at least a dozen lookups.
record.fields("500") # returns an array
record.each_by_tag("500") {|field| ... }
record.fields(['100', '700']) # can also use an array in both methods
record.each_by_tag( 600..699 ) # or a range
Freezing for thread-safety and high performance
MARC::Record is not generally safe for sharing between threads. Even if you think you are just acccessing it read-only, you may accidentally trigger a reindex of the by-tag cache (see above).
However, after you are done constructing a Record, you can mark the ‘fields` array as immutable. This makes a Record safe for sharing between threads for read-only use, and also helps you avoid accidentally triggering a reindex, as accidental reindexes can harm by-tag lookup performance.
record.fields.freeze
Instance Attribute Summary collapse
-
#leader ⇒ Object
the record leader.
Class Method Summary collapse
- .new_from_hash(h) ⇒ Object
-
.new_from_marc(raw, params = {}) ⇒ Object
Factory method for creating a MARC::Record from MARC21 in transmission format.
-
.new_from_marchash(mh) ⇒ Object
Factory method for creating a new MARC::Record from a marchash object.
Instance Method Summary collapse
-
#<<(field) ⇒ Object
alias to append.
-
#==(other) ⇒ Object
For testing if two records can be considered equal.
-
#=~(regex) ⇒ Object
Handy for using a record in a regex: if record =~ /Gravity’s Rainbow/ then print “Slothrop” end.
-
#[](tag) ⇒ Object
You can lookup fields using this shorthand: title = record.
-
#append(field) ⇒ Object
add a field to the record record.append(MARC::DataField.new( ‘100’, ‘2’, ‘0’, [‘a’, ‘Fred’])).
-
#each ⇒ Object
each() is here to support iterating and searching since MARC::Record mixes in Enumerable.
-
#each_by_tag(filter) ⇒ Object
A more convenient way to iterate over each field with a given tag.
-
#errors ⇒ Object
Returns an array of validation errors for all fields in the record.
-
#fields(filter = nil) ⇒ Object
Provides a backwards compatible means to access the FieldMap.
-
#initialize ⇒ Record
constructor
A new instance of Record.
-
#tags ⇒ Object
Returns an array of all of the tags that appear in the record (not necessarily in the order they appear).
-
#to_dublin_core ⇒ Object
Handy method for returning a hash mapping this records values to the Dublin Core.
-
#to_hash ⇒ Object
Returns a (roundtrippable) hash representation for MARC-in-JSON.
-
#to_json_string ⇒ Object
Return an actual json-encoded string.
-
#to_marc ⇒ Object
Returns a record in MARC21 transmission format (ANSI Z39.2).
-
#to_marchash ⇒ Object
Return a marc-hash version of the record.
-
#to_s ⇒ Object
Returns a string version of the record, suitable for printing.
-
#to_xml(include_namespace: true) ⇒ Object
Handy method for returning the MARCXML serialization for a MARC::Record object.
-
#to_xml_string(fast_but_unsafe: false, include_namespace: true) ⇒ String
Create the actual XML string (as opposed to #to_xml which, for historic reasons, returns an REXML document).
-
#valid? ⇒ Boolean
Returns true if there are no error messages associated with the record.
Constructor Details
#initialize ⇒ Record
Returns a new instance of Record.
115 116 117 118 119 120 121 122 123 |
# File 'lib/marc/record.rb', line 115 def initialize @fields = FieldMap.new # leader is 24 bytes @leader = " " * 24 # leader defaults: # http://www.loc.gov/marc/bibliographic/ecbdldrd.html @leader[10..11] = "22" @leader[20..23] = "4500" end |
Instance Attribute Details
#leader ⇒ Object
the record leader
113 114 115 |
# File 'lib/marc/record.rb', line 113 def leader @leader end |
Class Method Details
.new_from_hash(h) ⇒ Object
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/marc/record.rb', line 304 def self.new_from_hash(h) r = new r.leader = h["leader"] h["fields"]&.each do |position| position.each_pair do |tag, field| if field.is_a?(Hash) f = MARC::DataField.new(tag, field["ind1"], field["ind2"]) field["subfields"].each do |pos| pos.each_pair do |code, value| f.append MARC::Subfield.new(code, value) end end r << f else r << MARC::ControlField.new(tag, field) end end end r end |
.new_from_marc(raw, params = {}) ⇒ Object
Factory method for creating a MARC::Record from MARC21 in transmission format.
record = MARC::Record.new_from_marc(marc21)
in cases where you might be working with somewhat flawed MARC data you may want to use the :forgiving parameter which will bypass using field byte offsets and simply look for the end of field byte to figure out the end of fields.
record = MARC::Record.new_from_marc(marc21, :forgiving => true)
223 224 225 |
# File 'lib/marc/record.rb', line 223 def self.new_from_marc(raw, params = {}) MARC::Reader.decode(raw, params) end |
.new_from_marchash(mh) ⇒ Object
Factory method for creating a new MARC::Record from a marchash object
record = MARC::Record->new_from_marchash(mh)
278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/marc/record.rb', line 278 def self.new_from_marchash(mh) r = new r.leader = mh["leader"] mh["fields"].each do |f| if f.length == 2 r << MARC::ControlField.new(f[0], f[1]) elsif r << MARC::DataField.new(f[0], f[1], f[2], *f[3]) end end r end |
Instance Method Details
#<<(field) ⇒ Object
alias to append
145 146 147 |
# File 'lib/marc/record.rb', line 145 def <<(field) append(field) end |
#==(other) ⇒ Object
For testing if two records can be considered equal.
337 338 339 |
# File 'lib/marc/record.rb', line 337 def ==(other) to_s == other.to_s end |
#=~(regex) ⇒ Object
Handy for using a record in a regex:
if record =~ /Gravity's Rainbow/ then print "Slothrop" end
344 345 346 |
# File 'lib/marc/record.rb', line 344 def =~(regex) to_s =~ regex end |
#[](tag) ⇒ Object
You can lookup fields using this shorthand:
title = record['245']
176 177 178 |
# File 'lib/marc/record.rb', line 176 def [](tag) find { |f| f.tag == tag } end |
#append(field) ⇒ Object
138 139 140 141 |
# File 'lib/marc/record.rb', line 138 def append(field) @fields.push(field) @fields.clean = false end |
#each ⇒ Object
each() is here to support iterating and searching since MARC::Record mixes in Enumerable
iterating through the fields in a record:
record.each { |f| print f }
getting the 245
title = record.find {|f| f.tag == '245'}
getting all subjects
subjects = record.find_all {|f| ('600'..'699') === f.tag}
161 162 163 164 165 |
# File 'lib/marc/record.rb', line 161 def each @fields.each do |field| yield field end end |
#each_by_tag(filter) ⇒ Object
A more convenient way to iterate over each field with a given tag. The filter argument can be a string, array or range.
169 170 171 |
# File 'lib/marc/record.rb', line 169 def each_by_tag(filter) @fields.each_by_tag(filter) { |tag| yield tag } end |
#errors ⇒ Object
Returns an array of validation errors for all fields in the record
131 132 133 |
# File 'lib/marc/record.rb', line 131 def errors @fields.flat_map(&:errors) end |
#fields(filter = nil) ⇒ Object
Provides a backwards compatible means to access the FieldMap. No argument returns the FieldMap array in entirety. Providing a string, array or range of tags will return an array of fields in the order they appear in the record.
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/marc/record.rb', line 184 def fields(filter = nil) unless filter # Since we're returning the FieldMap object, which the caller # may mutate, we precautionarily mark dirty -- unless it's frozen # immutable. @fields.clean = false unless @fields.frozen? return @fields end @fields.reindex unless @fields.clean flds = [] if filter.is_a?(String) && @fields.[filter] @fields.[filter].each do |idx| flds << @fields[idx] end elsif filter.is_a?(Array) || filter.is_a?(Range) @fields.each_by_tag(filter) do |tag| flds << tag end end flds end |
#tags ⇒ Object
Returns an array of all of the tags that appear in the record (not necessarily in the order they appear).
207 208 209 |
# File 'lib/marc/record.rb', line 207 def @fields.tag_list end |
#to_dublin_core ⇒ Object
Handy method for returning a hash mapping this records values to the Dublin Core.
dc = record.to_dublin_core()
print dc['title']
264 265 266 |
# File 'lib/marc/record.rb', line 264 def to_dublin_core MARC::DublinCore.map(self) end |
#to_hash ⇒ Object
Returns a (roundtrippable) hash representation for MARC-in-JSON
291 292 293 294 295 296 297 |
# File 'lib/marc/record.rb', line 291 def to_hash record_hash = {"leader" => @leader, "fields" => []} @fields.each do |field| record_hash["fields"] << field.to_hash end record_hash end |
#to_json_string ⇒ Object
Return an actual json-encoded string.
300 301 302 |
# File 'lib/marc/record.rb', line 300 def to_json_string MARC::JSONLWriter.encode(self) end |
#to_marc ⇒ Object
Returns a record in MARC21 transmission format (ANSI Z39.2). Really this is just a wrapper around MARC::MARC21::encode
marc = record.to_marc()
232 233 234 |
# File 'lib/marc/record.rb', line 232 def to_marc MARC::Writer.encode(self) end |
#to_marchash ⇒ Object
Return a marc-hash version of the record
269 270 271 |
# File 'lib/marc/record.rb', line 269 def to_marchash {"type" => "marc-hash", "version" => [MARCHASH_MAJOR_VERSION, MARCHASH_MINOR_VERSION], "leader" => leader, "fields" => map { |f| f.to_marchash }} end |
#to_s ⇒ Object
Returns a string version of the record, suitable for printing
327 328 329 330 331 332 333 |
# File 'lib/marc/record.rb', line 327 def to_s str = "LEADER #{leader}\n" each do |field| str += field.to_s + "\n" end str end |
#to_xml(include_namespace: true) ⇒ Object
Handy method for returning the MARCXML serialization for a MARC::Record object. You’ll get back a REXML::Document object. Really this is just a wrapper around MARC::XMLWriter::encode
xml_doc = record.to_xml()
241 242 243 |
# File 'lib/marc/record.rb', line 241 def to_xml(include_namespace: true) MARC::XMLWriter.encode(self, include_namespace: include_namespace) end |
#to_xml_string(fast_but_unsafe: false, include_namespace: true) ⇒ String
Create the actual XML string (as opposed to #to_xml which, for historic reasons, returns an REXML document)
250 251 252 253 254 255 256 |
# File 'lib/marc/record.rb', line 250 def to_xml_string(fast_but_unsafe: false, include_namespace: true) if fast_but_unsafe MARC::UnsafeXMLWriter.encode(self, include_namespace: include_namespace) else MARC::XMLWriter.encode(self, include_namespace: include_namespace).to_s end end |
#valid? ⇒ Boolean
Returns true if there are no error messages associated with the record
126 127 128 |
# File 'lib/marc/record.rb', line 126 def valid? errors.none? end |