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 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: