Class: MARC::Record

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/marc/record.rb

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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeRecord

Returns a new instance of Record.



118
119
120
121
122
123
124
125
126
# File 'lib/marc/record.rb', line 118

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

#leaderObject

the record leader



116
117
118
# File 'lib/marc/record.rb', line 116

def leader
  @leader
end

Class Method Details

.new_from_hash(h) ⇒ Object



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/marc/record.rb', line 289

def self.new_from_hash(h)
  r        = self.new
  r.leader = h['leader']
  if h['fields']
    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
  end
  return 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)


216
217
218
# File 'lib/marc/record.rb', line 216

def self.new_from_marc(raw, params={})
  return 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)



267
268
269
270
271
272
273
274
275
276
277
# File 'lib/marc/record.rb', line 267

def self.new_from_marchash(mh)
  r        = self.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
  return r
end

Instance Method Details

#<<(field) ⇒ Object

alias to append



138
139
140
# File 'lib/marc/record.rb', line 138

def <<(field)
  append(field)
end

#==(other) ⇒ Object

For testing if two records can be considered equal.



325
326
327
# File 'lib/marc/record.rb', line 325

def ==(other)
  return self.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


333
334
335
# File 'lib/marc/record.rb', line 333

def =~(regex)
  return self.to_s =~ regex
end

#[](tag) ⇒ Object

You can lookup fields using this shorthand:

title = record['245']


169
170
171
# File 'lib/marc/record.rb', line 169

def [](tag)
  return self.find { |f| f.tag == tag }
end

#append(field) ⇒ Object

add a field to the record

record.append(MARC::DataField.new( '100', '2', '0', ['a', 'Fred']))


131
132
133
134
# File 'lib/marc/record.rb', line 131

def append(field)
  @fields.push(field)
  @fields.clean = false
end

#eachObject

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}


154
155
156
157
158
# File 'lib/marc/record.rb', line 154

def each
  for field in @fields
    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.



162
163
164
# File 'lib/marc/record.rb', line 162

def each_by_tag(filter)
  @fields.each_by_tag(filter) { |tag| yield tag }
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.



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/marc/record.rb', line 177

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.tags[filter]
    @fields.tags[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

#tagsObject

Returns an array of all of the tags that appear in the record (not necessarily in the order they appear).



200
201
202
# File 'lib/marc/record.rb', line 200

def tags
  return @fields.tag_list
end

#to_dublin_coreObject

Handy method for returning a hash mapping this records values to the Dublin Core.

dc = record.to_dublin_core()
print dc['title']


246
247
248
# File 'lib/marc/record.rb', line 246

def to_dublin_core
  return MARC::DublinCore.map(self)
end

#to_hashObject

Returns a (roundtrippable) hash representation for MARC-in-JSON



281
282
283
284
285
286
287
# File 'lib/marc/record.rb', line 281

def to_hash
  record_hash = {'leader' => @leader, 'fields' => []}
  @fields.each do |field|
    record_hash['fields'] << field.to_hash
  end
  record_hash
end

#to_marcObject

Returns a record in MARC21 transmission format (ANSI Z39.2). Really this is just a wrapper around MARC::MARC21::encode

marc = record.to_marc()


226
227
228
# File 'lib/marc/record.rb', line 226

def to_marc
  return MARC::Writer.encode(self)
end

#to_marchashObject

Return a marc-hash version of the record



251
252
253
254
255
256
257
258
# File 'lib/marc/record.rb', line 251

def to_marchash
  return {
      'type'    => 'marc-hash',
      'version' => [MARCHASH_MAJOR_VERSION, MARCHASH_MINOR_VERSION],
      'leader'  => self.leader,
      'fields'  => self.map { |f| f.to_marchash }
  }
end

#to_sObject

Returns a string version of the record, suitable for printing



314
315
316
317
318
319
320
# File 'lib/marc/record.rb', line 314

def to_s
  str = "LEADER #{leader}\n"
  self.each do |field|
    str += field.to_s() + "\n"
  end
  return str
end

#to_xmlObject

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()


236
237
238
# File 'lib/marc/record.rb', line 236

def to_xml
  return MARC::XMLWriter.encode(self, :include_namespace => true)
end