marc 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
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 modifyi if neccesary.
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
- @fields.clean = false
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
@@ -1,3 +1,3 @@
1
1
  module MARC
2
- VERSION = "0.7.0"
2
+ VERSION = "0.7.1"
3
3
  end
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 the #{num_digits} slot in binary format.")
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
- version: 0.7.0
5
- prerelease:
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-03 00:00:00.000000000 Z
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
- post_install_message:
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.23
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: