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.



103
104
105
106
107
108
109
110
111
# File 'lib/marc/record.rb', line 103

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



101
102
103
# File 'lib/marc/record.rb', line 101

def leader
  @leader
end

Class Method Details

.new_from_hash(h) ⇒ Object



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/marc/record.rb', line 274

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)


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

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)



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

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



123
124
125
# File 'lib/marc/record.rb', line 123

def <<(field)
  append(field)      
end

#==(other) ⇒ Object

For testing if two records can be considered equal.



309
310
311
# File 'lib/marc/record.rb', line 309

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


317
318
319
# File 'lib/marc/record.rb', line 317

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

#[](tag) ⇒ Object

You can lookup fields using this shorthand:

title = record['245']


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

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']))


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

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}


139
140
141
142
143
# File 'lib/marc/record.rb', line 139

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.



147
148
149
# File 'lib/marc/record.rb', line 147

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.



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/marc/record.rb', line 162

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



185
186
187
# File 'lib/marc/record.rb', line 185

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']


231
232
233
# File 'lib/marc/record.rb', line 231

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

#to_hashObject

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



266
267
268
269
270
271
272
# File 'lib/marc/record.rb', line 266

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


211
212
213
# File 'lib/marc/record.rb', line 211

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

#to_marchashObject

Return a marc-hash version of the record



236
237
238
239
240
241
242
243
# File 'lib/marc/record.rb', line 236

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



298
299
300
301
302
303
304
# File 'lib/marc/record.rb', line 298

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


221
222
223
# File 'lib/marc/record.rb', line 221

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