marc 0.7.0 → 0.7.1
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.
- data/lib/marc/record.rb +41 -3
- data/lib/marc/version.rb +1 -1
- data/lib/marc/writer.rb +1 -1
- data/test/tc_record.rb +35 -0
- metadata +15 -14
data/lib/marc/record.rb
CHANGED
@@ -39,6 +39,14 @@ module MARC
|
|
39
39
|
yield tag
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
# Freeze for immutability, first reindexing if needed.
|
44
|
+
# A frozen FieldMap is safe for concurrent access, and also
|
45
|
+
# can more easily avoid accidental reindexing on even read-only use.
|
46
|
+
def freeze
|
47
|
+
self.reindex unless @clean
|
48
|
+
super
|
49
|
+
end
|
42
50
|
end
|
43
51
|
|
44
52
|
# A class that represents an individual MARC record. Every record
|
@@ -50,12 +58,39 @@ module MARC
|
|
50
58
|
# record.find_all {|field| field.tag =~ /^6../}
|
51
59
|
#
|
52
60
|
# The accessor 'fields' is also an Array of MARC::DataField objects which
|
53
|
-
# the client can access or
|
61
|
+
# the client can access or modify if neccesary.
|
54
62
|
#
|
55
63
|
# record.fields.delete(field)
|
56
64
|
#
|
57
65
|
# Other accessor attribute: 'leader' for record leader as String
|
58
|
-
|
66
|
+
#
|
67
|
+
# == High-performance lookup by tag
|
68
|
+
#
|
69
|
+
# A frequent use case is looking up fields in a MARC record by tag, such
|
70
|
+
# as 'all the 500 fields'. Certain methods can use a hash keyed by
|
71
|
+
# tag name for higher performance lookup by tag. The hash is lazily
|
72
|
+
# created on first access -- there is some cost of creating the hash,
|
73
|
+
# testing shows you get a performance advantage to using the hash-based
|
74
|
+
# methods if you are doing at least a dozen lookups.
|
75
|
+
#
|
76
|
+
# record.fields("500") # returns an array
|
77
|
+
# record.each_by_tag("500") {|field| ... }
|
78
|
+
# record.fields(['100', '700']) # can also use an array in both methods
|
79
|
+
# record.each_by_tag( 600..699 ) # or a range
|
80
|
+
#
|
81
|
+
# == Freezing for thread-safety and high performance
|
82
|
+
#
|
83
|
+
# MARC::Record is not generally safe for sharing between threads.
|
84
|
+
# Even if you think you are just acccessing it read-only,
|
85
|
+
# you may accidentally trigger a reindex of the by-tag cache (see above).
|
86
|
+
#
|
87
|
+
# However, after you are done constructing a Record, you can mark
|
88
|
+
# the `fields` array as immutable. This makes a Record safe for sharing
|
89
|
+
# between threads for read-only use, and also helps you avoid accidentally
|
90
|
+
# triggering a reindex, as accidental reindexes can harm by-tag
|
91
|
+
# lookup performance.
|
92
|
+
#
|
93
|
+
# record.fields.freeze
|
59
94
|
class Record
|
60
95
|
include Enumerable
|
61
96
|
|
@@ -126,7 +161,10 @@ module MARC
|
|
126
161
|
# in the order they appear in the record.
|
127
162
|
def fields(filter=nil)
|
128
163
|
unless filter
|
129
|
-
|
164
|
+
# Since we're returning the FieldMap object, which the caller
|
165
|
+
# may mutate, we precautionarily mark dirty -- unless it's frozen
|
166
|
+
# immutable.
|
167
|
+
@fields.clean = false unless @fields.frozen?
|
130
168
|
return @fields
|
131
169
|
end
|
132
170
|
@fields.reindex unless @fields.clean
|
data/lib/marc/version.rb
CHANGED
data/lib/marc/writer.rb
CHANGED
@@ -131,7 +131,7 @@ module MARC
|
|
131
131
|
if allow_oversized
|
132
132
|
formatted = sprintf("%0#{num_digits}i", 0)
|
133
133
|
else
|
134
|
-
raise MARC::Exception.new("Can't write MARC record, as length/offset value of #{number} is too long for
|
134
|
+
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
135
|
end
|
136
136
|
end
|
137
137
|
return formatted
|
data/test/tc_record.rb
CHANGED
@@ -119,4 +119,39 @@ class TestRecord < Test::Unit::TestCase
|
|
119
119
|
assert_equal(five_hundreds.last['a'], 'Composer and program notes in container.')
|
120
120
|
end
|
121
121
|
|
122
|
+
|
123
|
+
# Some tests for the internal FieldMap hash, normally
|
124
|
+
# an implementation detail, but things get tricky and we need
|
125
|
+
# tests to make sure we're good. Some of these you might
|
126
|
+
# change if you change FieldMap caching implementation or contract/API.
|
127
|
+
def test_direct_change_dirties_fieldmap
|
128
|
+
# if we ask for #fields directly, and mutate it
|
129
|
+
# with it's own methods, does any cache update?
|
130
|
+
r = MARC::Record.new
|
131
|
+
assert r.fields('500').empty?
|
132
|
+
r.fields.push MARC::DataField.new('500', ' ', ' ', ['a', 'notes'])
|
133
|
+
assert ! r.fields('500').empty?, "New 505 directly added to #fields is picked up"
|
134
|
+
|
135
|
+
# Do it again, make sure #[] works too
|
136
|
+
r = MARC::Record.new
|
137
|
+
assert r['500'].nil?
|
138
|
+
r.fields.push MARC::DataField.new('500', ' ', ' ', ['a', 'notes'])
|
139
|
+
assert r['500'], "New 505 directly added to #fields is picked up"
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_frozen_fieldmap
|
143
|
+
r = MARC::Record.new
|
144
|
+
r.fields.push MARC::DataField.new('500', ' ', ' ', ['a', 'notes'])
|
145
|
+
|
146
|
+
r.fields.freeze
|
147
|
+
|
148
|
+
r.fields.inspect
|
149
|
+
r.fields
|
150
|
+
assert ! r.fields('500').empty?
|
151
|
+
|
152
|
+
assert r.fields.instance_variable_get("@clean"), "FieldMap still marked clean"
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
|
122
157
|
end
|
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: marc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
4
|
+
prerelease:
|
5
|
+
version: 0.7.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Kevin Clarke
|
@@ -13,14 +13,15 @@ authors:
|
|
13
13
|
autorequire: marc
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2013-09-
|
16
|
+
date: 2013-09-09 00:00:00.000000000 Z
|
17
17
|
dependencies: []
|
18
|
-
description:
|
18
|
+
description:
|
19
19
|
email: ehs@pobox.com
|
20
20
|
executables: []
|
21
21
|
extensions: []
|
22
22
|
extra_rdoc_files: []
|
23
23
|
files:
|
24
|
+
- lib/marc.rb
|
24
25
|
- lib/marc/constants.rb
|
25
26
|
- lib/marc/controlfield.rb
|
26
27
|
- lib/marc/datafield.rb
|
@@ -35,7 +36,6 @@ files:
|
|
35
36
|
- lib/marc/xml_parsers.rb
|
36
37
|
- lib/marc/xmlreader.rb
|
37
38
|
- lib/marc/xmlwriter.rb
|
38
|
-
- lib/marc.rb
|
39
39
|
- test/batch.dat
|
40
40
|
- test/batch.xml
|
41
41
|
- test/cp866_multirecord.marc
|
@@ -71,27 +71,28 @@ files:
|
|
71
71
|
- Changes
|
72
72
|
- LICENSE
|
73
73
|
homepage: https://github.com/ruby-marc/ruby-marc/
|
74
|
-
licenses:
|
75
|
-
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
post_install_message:
|
76
77
|
rdoc_options: []
|
77
78
|
require_paths:
|
78
79
|
- lib
|
79
80
|
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
-
none: false
|
81
81
|
requirements:
|
82
|
-
- -
|
82
|
+
- - '>='
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: 1.8.6
|
85
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
85
|
none: false
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
|
-
- -
|
88
|
+
- - '>='
|
89
89
|
- !ruby/object:Gem::Version
|
90
90
|
version: '0'
|
91
|
+
none: false
|
91
92
|
requirements: []
|
92
|
-
rubyforge_project:
|
93
|
-
rubygems_version: 1.8.
|
94
|
-
signing_key:
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 1.8.24
|
95
|
+
signing_key:
|
95
96
|
specification_version: 3
|
96
97
|
summary: A ruby library for working with Machine Readable Cataloging
|
97
98
|
test_files:
|