berkeley_library-av-core 0.4.0

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.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +391 -0
  3. data/.github/workflows/build.yml +30 -0
  4. data/.gitignore +388 -0
  5. data/.idea/av_core.iml +146 -0
  6. data/.idea/codeStyles/Project.xml +12 -0
  7. data/.idea/codeStyles/codeStyleConfig.xml +5 -0
  8. data/.idea/go.imports.xml +6 -0
  9. data/.idea/inspectionProfiles/Project_Default.xml +37 -0
  10. data/.idea/misc.xml +6 -0
  11. data/.idea/modules.xml +8 -0
  12. data/.idea/vcs.xml +6 -0
  13. data/.rubocop.yml +241 -0
  14. data/.ruby-version +1 -0
  15. data/.simplecov +8 -0
  16. data/CHANGES.md +38 -0
  17. data/Gemfile +3 -0
  18. data/Jenkinsfile +16 -0
  19. data/LICENSE.md +21 -0
  20. data/README.md +20 -0
  21. data/Rakefile +20 -0
  22. data/av_core.gemspec +49 -0
  23. data/lib/berkeley_library/av/config.rb +238 -0
  24. data/lib/berkeley_library/av/constants.rb +30 -0
  25. data/lib/berkeley_library/av/core/module_info.rb +18 -0
  26. data/lib/berkeley_library/av/core.rb +7 -0
  27. data/lib/berkeley_library/av/marc/util.rb +114 -0
  28. data/lib/berkeley_library/av/marc.rb +52 -0
  29. data/lib/berkeley_library/av/metadata/README.md +5 -0
  30. data/lib/berkeley_library/av/metadata/field.rb +110 -0
  31. data/lib/berkeley_library/av/metadata/fields.rb +130 -0
  32. data/lib/berkeley_library/av/metadata/link.rb +28 -0
  33. data/lib/berkeley_library/av/metadata/readers/alma.rb +54 -0
  34. data/lib/berkeley_library/av/metadata/readers/base.rb +53 -0
  35. data/lib/berkeley_library/av/metadata/readers/tind.rb +52 -0
  36. data/lib/berkeley_library/av/metadata/readers.rb +2 -0
  37. data/lib/berkeley_library/av/metadata/source.rb +93 -0
  38. data/lib/berkeley_library/av/metadata/tind_html_metadata_da.json +2076 -0
  39. data/lib/berkeley_library/av/metadata/value.rb +121 -0
  40. data/lib/berkeley_library/av/metadata.rb +103 -0
  41. data/lib/berkeley_library/av/record.rb +86 -0
  42. data/lib/berkeley_library/av/record_id.rb +121 -0
  43. data/lib/berkeley_library/av/record_not_found.rb +7 -0
  44. data/lib/berkeley_library/av/restrictions.rb +36 -0
  45. data/lib/berkeley_library/av/track.rb +132 -0
  46. data/lib/berkeley_library/av/types/duration.rb +67 -0
  47. data/lib/berkeley_library/av/types/file_type.rb +84 -0
  48. data/lib/berkeley_library/av/util.rb +65 -0
  49. data/rakelib/bundle.rake +8 -0
  50. data/rakelib/coverage.rake +11 -0
  51. data/rakelib/gem.rake +54 -0
  52. data/rakelib/rubocop.rake +18 -0
  53. data/rakelib/spec.rake +12 -0
  54. data/spec/.rubocop.yml +116 -0
  55. data/spec/data/10.23.19.JessieLaCavalier.02.mrc +3 -0
  56. data/spec/data/alma/991005939359706532-sru.xml +123 -0
  57. data/spec/data/alma/991034756419706532-sru.xml +162 -0
  58. data/spec/data/alma/991047179369706532-sru.xml +210 -0
  59. data/spec/data/alma/991054360089706532-sru.xml +186 -0
  60. data/spec/data/alma/b11082434-sru.xml +165 -0
  61. data/spec/data/alma/b18538031-sru.xml +123 -0
  62. data/spec/data/alma/b20786580-sru.xml +123 -0
  63. data/spec/data/alma/b22139647-sru.xml +171 -0
  64. data/spec/data/alma/b22139658-sru.xml +282 -0
  65. data/spec/data/alma/b23161018-sru.xml +182 -0
  66. data/spec/data/alma/b23305522-sru.xml +144 -0
  67. data/spec/data/alma/b24071548-sru.xml +136 -0
  68. data/spec/data/alma/b24659129-sru.xml +210 -0
  69. data/spec/data/alma/b25207857-sru.xml +217 -0
  70. data/spec/data/alma/b25716973-sru.xml +186 -0
  71. data/spec/data/alma/b25742488-sru.xml +246 -0
  72. data/spec/data/record-(cityarts)00002.xml +78 -0
  73. data/spec/data/record-(cityarts)00773.xml +94 -0
  74. data/spec/data/record-(clir)00020.xml +153 -0
  75. data/spec/data/record-(miscmat)00615.xml +45 -0
  76. data/spec/data/record-(pacradio)00107.xml +85 -0
  77. data/spec/data/record-(pacradio)01469.xml +82 -0
  78. data/spec/data/record-empty-result.xml +4 -0
  79. data/spec/data/record-multiple-998s-disordered.xml +178 -0
  80. data/spec/data/record-multiple-998s.xml +178 -0
  81. data/spec/data/record-physcolloquia-bk00169017b.xml +78 -0
  82. data/spec/data/record-ragged-998-subfields.xml +122 -0
  83. data/spec/data/record-ragged-998s-multiple-fields.xml +160 -0
  84. data/spec/data/record-redirect-to-login.html +288 -0
  85. data/spec/data/record_id/bibs_with_check_digits.txt +151 -0
  86. data/spec/data/search-1993.xml +158 -0
  87. data/spec/data/search-b23305516.xml +81 -0
  88. data/spec/lib/berkeley_library/av/av_spec.rb +12 -0
  89. data/spec/lib/berkeley_library/av/config_spec.rb +250 -0
  90. data/spec/lib/berkeley_library/av/marc/util_spec.rb +150 -0
  91. data/spec/lib/berkeley_library/av/marc_spec.rb +62 -0
  92. data/spec/lib/berkeley_library/av/metadata/field_spec.rb +81 -0
  93. data/spec/lib/berkeley_library/av/metadata/fields_spec.rb +180 -0
  94. data/spec/lib/berkeley_library/av/metadata/metadata_spec.rb +274 -0
  95. data/spec/lib/berkeley_library/av/metadata/source_spec.rb +261 -0
  96. data/spec/lib/berkeley_library/av/metadata/value_spec.rb +29 -0
  97. data/spec/lib/berkeley_library/av/record_id_spec.rb +72 -0
  98. data/spec/lib/berkeley_library/av/record_spec.rb +284 -0
  99. data/spec/lib/berkeley_library/av/track_spec.rb +335 -0
  100. data/spec/lib/berkeley_library/av/types/duration_spec.rb +91 -0
  101. data/spec/lib/berkeley_library/av/types/file_type_spec.rb +98 -0
  102. data/spec/lib/berkeley_library/av/util_spec.rb +30 -0
  103. data/spec/spec_helper.rb +63 -0
  104. metadata +499 -0
@@ -0,0 +1,81 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!-- Search-Engine-Total-Number-Of-Results: 1 -->
3
+ <collection xmlns="http://www.loc.gov/MARC21/slim">
4
+ <record>
5
+ <controlfield tag="001">19816</controlfield>
6
+ <controlfield tag="005">20190703231952.0</controlfield>
7
+ <datafield tag="035" ind1=" " ind2=" ">
8
+ <subfield code="a">(pacradio)00107</subfield>
9
+ </datafield>
10
+ <datafield tag="041" ind1=" " ind2=" ">
11
+ <subfield code="a">eng</subfield>
12
+ </datafield>
13
+ <datafield tag="245" ind1=" " ind2=" ">
14
+ <subfield code="a">Author Alice Walker reads the short story, Roselily</subfield>
15
+ </datafield>
16
+ <datafield tag="260" ind1=" " ind2=" ">
17
+ <subfield code="a">Los Angeles </subfield>
18
+ <subfield code="b">Pacifica Radio Archives</subfield>
19
+ <subfield code="c">1994.</subfield>
20
+ </datafield>
21
+ <datafield tag="269" ind1=" " ind2=" ">
22
+ <subfield code="a">1994</subfield>
23
+ </datafield>
24
+ <datafield tag="300" ind1=" " ind2=" ">
25
+ <subfield code="a">1 online resource.</subfield>
26
+ </datafield>
27
+ <datafield tag="336" ind1=" " ind2=" ">
28
+ <subfield code="a">Audio</subfield>
29
+ </datafield>
30
+ <datafield tag="520" ind1=" " ind2=" ">
31
+ <subfield code="a">Pulitzer Prize-winning author Alice Walker (1944 - ) reads her short story "Roselily" (written ca. 1967). Walker then talks with KPFA's Ginny Z. Berson about the story, how she wrote it about her first marriage to a civil rights attorney and how it questions the dominance of Christianity over all religions in the US. The story was dropped from a 10th grade English test by California state educators based on complaints from Christian conservatives in early 1994. The Traditional Values Coalition applied pressure based on what they read as "anti-religious" elements in the story.</subfield>
32
+ </datafield>
33
+ <datafield tag="536" ind1=" " ind2=" ">
34
+ <subfield code="a">Sponsored by the National Historical Publications and Records Commission at the National Archives and Records Administration as part of Pacifica's American Women Making History and Culture: 1963-1982 grant preservation project.</subfield>
35
+ </datafield>
36
+ <datafield tag="540" ind1=" " ind2=" ">
37
+ <subfield code="a">RESTRICTED. Permissions, licensing requests, and all other inquiries should be directed in writing to: Director of the Archives, Pacifica Radio Archives, 3729 Cahuenga Blvd. West, North Hollywood, CA 91604, 800-735-0230 x 263, fax 818-506-1084, info@pacificaradioarchives.org, http://www.pacificaradioarchives.org</subfield>
38
+ </datafield>
39
+ <datafield tag="700" ind1="1" ind2=" ">
40
+ <subfield code="a">Berson, Ginny Z.</subfield>
41
+ <subfield code="e">interviewer.</subfield>
42
+ </datafield>
43
+ <datafield tag="700" ind1="1" ind2=" ">
44
+ <subfield code="a">Walker, Alice.</subfield>
45
+ <subfield code="e">interviewee.</subfield>
46
+ </datafield>
47
+ <datafield tag="710" ind1="2" ind2=" ">
48
+ <subfield code="a">Pacifica Radio Archive.</subfield>
49
+ </datafield>
50
+ <datafield tag="710" ind1="2" ind2=" ">
51
+ <subfield code="a">KPFA (Radio station : Berkeley, Calif.).</subfield>
52
+ </datafield>
53
+ <datafield tag="852" ind1=" " ind2=" ">
54
+ <subfield code="c">The Library</subfield>
55
+ </datafield>
56
+ <datafield tag="856" ind1="4" ind2="1">
57
+ <subfield code="u">http://oskicat.berkeley.edu/record=b23305516</subfield>
58
+ <subfield code="y">View library catalog record.</subfield>
59
+ </datafield>
60
+ <datafield tag="856" ind1="4" ind2="2">
61
+ <subfield code="u">http://servlet1.lib.berkeley.edu:8080/audio/stream.play.logic?coll=pacifica&amp;group=b23305516</subfield>
62
+ <subfield code="y">Play Audio for American Women Making History and Culture. Freely available for streaming.</subfield>
63
+ </datafield>
64
+ <datafield tag="901" ind1=" " ind2=" ">
65
+ <subfield code="m">b23305516</subfield>
66
+ <subfield code="o">947286769</subfield>
67
+ </datafield>
68
+ <datafield tag="903" ind1=" " ind2=" ">
69
+ <subfield code="b">m</subfield>
70
+ </datafield>
71
+ <datafield tag="980" ind1=" " ind2=" ">
72
+ <subfield code="a">pacradio</subfield>
73
+ </datafield>
74
+ <datafield tag="982" ind1=" " ind2=" ">
75
+ <subfield code="a">Pacifica Radio Archives</subfield>
76
+ <subfield code="b">Pacifica Radio Archives Social Activism Sound Recording Project</subfield>
77
+ </datafield>
78
+ </record>
79
+
80
+
81
+ </collection>
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ describe AV do
5
+ describe :configure do
6
+ it 'exposes the configuration' do
7
+ conf = AV.configure { config }
8
+ expect(conf).to eq(AV::Config)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,250 @@
1
+ require 'spec_helper'
2
+ require 'uri'
3
+
4
+ module BerkeleyLibrary
5
+ module AV
6
+ describe Config do
7
+
8
+ after do
9
+ Config.send(:clear!)
10
+ end
11
+
12
+ describe(:configured?) do
13
+ it 'defaults to false' do
14
+ expect(AV.configured?).to eq(false)
15
+ end
16
+
17
+ it 'returns true if and only if all values are configured' do
18
+ settings = {
19
+ avplayer_base_uri: 'http://avplayer.example.edu',
20
+ alma_sru_host: 'berkeley.alma.exlibrisgroup.com',
21
+ alma_primo_host: 'search.library.berkeley.edu',
22
+ alma_institution_code: '01UCS_BER',
23
+ alma_permalink_key: 'iqob43',
24
+ tind_base_uri: 'http://tind.example.edu',
25
+ wowza_base_uri: 'http://wowza.example.edu'
26
+ }
27
+ settings.each { |setting, value| Config.send("#{setting}=", value) }
28
+ expect(AV.configured?).to eq(true)
29
+
30
+ aggregate_failures do
31
+ settings.each do |setting, value|
32
+ Config.instance_variable_set("@#{setting}".to_sym, nil)
33
+ expect(AV.configured?).to eq(false), "Clearing #{setting} did not set configured? to false"
34
+ Config.send("#{setting}=", value)
35
+ end
36
+ end
37
+ end
38
+
39
+ it 'reads values from a Rails config if present' do
40
+ settings = {
41
+ avplayer_base_uri: URI.parse('http://avplayer.example.edu'),
42
+ alma_sru_host: 'berkeley.alma.exlibrisgroup.com',
43
+ alma_primo_host: 'search.library.berkeley.edu',
44
+ alma_institution_code: '01UCS_BER',
45
+ alma_permalink_key: 'iqob43',
46
+ tind_base_uri: URI.parse('http://tind.example.edu'),
47
+ wowza_base_uri: URI.parse('http://wowza.example.edu')
48
+ }
49
+ rails_config = Struct.new(*settings.keys, keyword_init: true).new(**settings)
50
+
51
+ # Mock Rails config
52
+ expect(defined?(Rails)).to be_nil # just to be sure
53
+ Object.send(:const_set, 'Rails', Struct.new(:application).new)
54
+ Rails.application = Struct.new(:config).new
55
+ Rails.application.config = rails_config
56
+
57
+ aggregate_failures do
58
+ settings.each do |setting, v|
59
+ expect(Config.send(setting)).to eq(v)
60
+ end
61
+ end
62
+
63
+ expect(AV.configured?).to eq(true)
64
+ ensure
65
+ Object.send(:remove_const, 'Rails')
66
+ end
67
+ end
68
+
69
+ describe(:missing) do
70
+ it 'defaults to all settings' do
71
+ expect(Config.missing).to eq(Config::REQUIRED_SETTINGS)
72
+ end
73
+
74
+ it 'returns an empty array if nothing is missing' do
75
+ settings = {
76
+ avplayer_base_uri: 'http://avplayer.example.edu',
77
+ alma_sru_host: 'berkeley.alma.exlibrisgroup.com',
78
+ alma_primo_host: 'search.library.berkeley.edu',
79
+ alma_institution_code: '01UCS_BER',
80
+ alma_permalink_key: 'iqob43',
81
+ tind_base_uri: 'http://tind.example.edu',
82
+ wowza_base_uri: 'http://wowza.example.edu'
83
+ }
84
+ settings.each { |setting, value| Config.send("#{setting}=", value) }
85
+
86
+ expect(Config.missing).to eq([])
87
+ end
88
+
89
+ it 'returns the missing settings' do
90
+ settings = {
91
+ avplayer_base_uri: 'http://avplayer.example.edu',
92
+ alma_sru_host: 'berkeley.alma.exlibrisgroup.com',
93
+ alma_primo_host: 'search.library.berkeley.edu',
94
+ alma_institution_code: '01UCS_BER',
95
+ alma_permalink_key: 'iqob43',
96
+ tind_base_uri: 'http://tind.example.edu',
97
+ wowza_base_uri: 'http://wowza.example.edu'
98
+ }
99
+ settings.each { |setting, value| Config.send("#{setting}=", value) }
100
+
101
+ expected = []
102
+ settings.each_key do |setting|
103
+ Config.instance_variable_set("@#{setting}".to_sym, nil)
104
+ expected << setting
105
+ expect(Config.missing).to eq(expected)
106
+ end
107
+ end
108
+ end
109
+
110
+ describe :avplayer_base_uri= do
111
+ it 'converts strings to URIs' do
112
+ expected_uri = URI.parse('http://avplayer.example.edu')
113
+ Config.avplayer_base_uri = expected_uri.to_s
114
+ expect(Config.avplayer_base_uri).to eq(expected_uri)
115
+ end
116
+
117
+ it 'strips trailing slashes' do
118
+ expected_uri = URI.parse('http://avplayer.example.edu')
119
+ Config.avplayer_base_uri = "#{expected_uri}/"
120
+ expect(Config.avplayer_base_uri).to eq(expected_uri)
121
+ end
122
+ end
123
+
124
+ describe :alma_sru_host= do
125
+ it 'sets the hostname' do
126
+ expected_host = 'alma.example.org'
127
+ Config.alma_sru_host = expected_host
128
+ expect(Config.alma_sru_host).to eq(expected_host)
129
+ end
130
+ end
131
+
132
+ describe :alma_institution_code= do
133
+ it 'sets the Alma institution code' do
134
+ expected_code = '01UCS_BER'
135
+ Config.alma_institution_code = expected_code
136
+ expect(Config.alma_institution_code).to eq(expected_code)
137
+ end
138
+ end
139
+
140
+ describe :alma_sru_base_uri do
141
+ it 'generates the base URI from the SRU host and institution code' do
142
+ expected_host = 'alma.example.org'
143
+ Config.alma_sru_host = expected_host
144
+
145
+ expected_code = '01UCS_BER'
146
+ Config.alma_institution_code = expected_code
147
+
148
+ base_uri = Config.alma_sru_base_uri
149
+ expect(base_uri.host).to eq(expected_host)
150
+ expect(base_uri.path).to end_with("/#{expected_code}")
151
+ end
152
+
153
+ it 'requires both the SRU host and institution code to be set' do
154
+ expect { Config.alma_sru_base_uri }.to raise_error(ArgumentError)
155
+
156
+ Config.alma_sru_host = 'alma.example.org'
157
+ Config.alma_institution_code = '01UCS_BER'
158
+
159
+ Config.alma_sru_base_uri
160
+ end
161
+ end
162
+
163
+ describe :wowza_base_uri= do
164
+ it 'converts strings to URIs' do
165
+ expected_uri = URI.parse('http://wowza.example.edu')
166
+ Config.wowza_base_uri = expected_uri.to_s
167
+ expect(Config.wowza_base_uri).to eq(expected_uri)
168
+ end
169
+
170
+ it 'strips trailing slashes' do
171
+ expected_uri = URI.parse('http://wowza.example.edu')
172
+ Config.wowza_base_uri = "#{expected_uri}/"
173
+ expect(Config.wowza_base_uri).to eq(expected_uri)
174
+ end
175
+ end
176
+
177
+ describe :tind_base_uri= do
178
+ it 'converts strings to URIs' do
179
+ expected_uri = URI.parse('http://tind.example.edu')
180
+ Config.tind_base_uri = expected_uri.to_s
181
+ expect(Config.tind_base_uri).to eq(expected_uri)
182
+ end
183
+
184
+ it 'strips trailing slashes' do
185
+ expected_uri = URI.parse('http://tind.example.edu')
186
+ Config.tind_base_uri = "#{expected_uri}/"
187
+ expect(Config.tind_base_uri).to eq(expected_uri)
188
+ end
189
+ end
190
+
191
+ describe 'with Rails config' do
192
+ attr_reader :config
193
+
194
+ before do
195
+ @config = double(Config)
196
+
197
+ application = double(Object)
198
+ allow(application).to receive(:config).and_return(config)
199
+
200
+ rails = double(Object)
201
+ allow(rails).to receive(:application).and_return(application)
202
+
203
+ Object.const_set(:Rails, rails)
204
+ end
205
+
206
+ after do
207
+ Object.send(:remove_const, :Rails)
208
+ end
209
+
210
+ describe :avplayer_base_uri do
211
+ attr_reader :avplayer_base_uri
212
+
213
+ before do
214
+ @avplayer_base_uri = URI.parse('http://avplayer.example.edu')
215
+ allow(config).to receive(:avplayer_base_uri).and_return(avplayer_base_uri.to_s)
216
+ end
217
+
218
+ it 'falls back to the Rails config, if available' do
219
+ expect(Config.avplayer_base_uri).to eq(avplayer_base_uri)
220
+ end
221
+
222
+ it 'prefers the explicitly configured URI' do
223
+ expected_avplayer_uri = URI.parse('https://avplayer-other.example.edu')
224
+ Config.avplayer_base_uri = expected_avplayer_uri
225
+ expect(Config.avplayer_base_uri).to eq(expected_avplayer_uri)
226
+ end
227
+ end
228
+
229
+ describe :tind_base_uri do
230
+ attr_reader :tind_base_uri
231
+
232
+ before do
233
+ @tind_base_uri = URI.parse('http://tind.example.edu')
234
+ allow(config).to receive(:tind_base_uri).and_return(tind_base_uri.to_s)
235
+ end
236
+
237
+ it 'falls back to the Rails config, if available' do
238
+ expect(Config.tind_base_uri).to eq(tind_base_uri)
239
+ end
240
+
241
+ it 'prefers the explicitly configured URI' do
242
+ expected_tind_uri = URI.parse('https://tind-other.example.edu')
243
+ Config.tind_base_uri = expected_tind_uri
244
+ expect(Config.tind_base_uri).to eq(expected_tind_uri)
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+ require 'marc'
3
+
4
+ module BerkeleyLibrary
5
+ module AV
6
+ module Marc
7
+ module Util
8
+ describe :grouped_subfield_values do
9
+ let(:df) { MARC::DataField.new('999') }
10
+ let(:original_groups) do
11
+ [
12
+ { b: '2', a: '1', c: '7' },
13
+ { b: '4', a: '3', c: '9' },
14
+ { b: '6', a: '5', c: '10' }
15
+ ]
16
+ end
17
+
18
+ let(:order) { %i[c b a] }
19
+ let(:ordered_groups) do
20
+ [
21
+ { c: '7', b: '2', a: '1' },
22
+ { c: '9', b: '4', a: '3' },
23
+ { c: '10', b: '6', a: '5' }
24
+ ]
25
+ end
26
+
27
+ describe 'pre-grouped subfields' do
28
+ before do
29
+ original_groups.each do |g|
30
+ g.each { |c, v| df.subfields << MARC::Subfield.new(c, v) }
31
+ end
32
+ end
33
+
34
+ it 'returns the groups' do
35
+ groups = Util.group_subfield_values(df)
36
+ expect(groups).to eq(original_groups)
37
+ end
38
+
39
+ it 'reorders the groups' do
40
+ reordered = Util.group_subfield_values(df, order:)
41
+ # Hash equality doesn't check order, so we do it by hand
42
+ reordered.each_with_index do |actual, i|
43
+ expected = ordered_groups[i]
44
+ expect(actual.keys).to eq(expected.keys), "#{i}: expected: #{expected}, got: #{actual}"
45
+ expect(actual.values).to eq(expected.values), "#{i}: expected: #{expected}, got: #{actual}"
46
+ end
47
+ end
48
+ end
49
+
50
+ describe 'pre-grouped by code' do
51
+ before do
52
+ {
53
+ b: %w[2 4 6],
54
+ a: %w[1 3 5],
55
+ c: %w[7 9 10]
56
+ }.each do |c, vv|
57
+ vv.each { |v| df.subfields << MARC::Subfield.new(c, v) }
58
+ end
59
+ end
60
+
61
+ it 'groups the values' do
62
+ groups = Util.group_subfield_values(df)
63
+ expect(groups).to eq(original_groups)
64
+ end
65
+
66
+ it 'reorders the groups' do
67
+ reordered = Util.group_subfield_values(df, order:)
68
+ # Hash equality doesn't check order, so we do it by hand
69
+ reordered.each_with_index do |actual, i|
70
+ expected = ordered_groups[i]
71
+ expect(actual.keys).to eq(expected.keys), "#{i}: expected: #{expected}, got: #{actual}"
72
+ expect(actual.values).to eq(expected.values), "#{i}: expected: #{expected}, got: #{actual}"
73
+ end
74
+ end
75
+ end
76
+
77
+ describe 'inconsistent order' do
78
+ let(:inconsistent_groups) do
79
+ [
80
+ { a: '1', b: '2', c: '7' },
81
+ { b: '4', a: '3', c: '9' },
82
+ { c: '10', a: '5', b: '6' }
83
+ ]
84
+ end
85
+
86
+ before do
87
+ inconsistent_groups.each do |g|
88
+ g.each { |c, v| df.subfields << MARC::Subfield.new(c, v) }
89
+ end
90
+ end
91
+
92
+ it 'groups the values' do
93
+ groups = Util.group_subfield_values(df)
94
+ expect(groups).to eq(inconsistent_groups)
95
+ end
96
+
97
+ it 'reorders the groups' do
98
+ reordered = Util.group_subfield_values(df, order:)
99
+ # Hash equality doesn't check order, so we do it by hand
100
+ reordered.each_with_index do |actual, i|
101
+ expected = ordered_groups[i]
102
+ expect(actual.keys).to eq(expected.keys), "#{i}: expected: #{expected}, got: #{actual}"
103
+ expect(actual.values).to eq(expected.values), "#{i}: expected: #{expected}, got: #{actual}"
104
+ end
105
+ end
106
+ end
107
+
108
+ describe 'ragged groups' do
109
+ let(:ragged_groups) do
110
+ [{ a: '1' }, { b: '2' }, { c: '7' }, { b: '4' }, { c: '9' }, { a: '5' }, { b: '6' }]
111
+ end
112
+ let(:ragged_reordered) do
113
+ [
114
+ { c: '7', b: '2', a: '1' },
115
+ { c: '9', b: '4', a: '5' },
116
+ { b: '6' }
117
+ ]
118
+ end
119
+
120
+ before do
121
+ ragged_groups.each do |g|
122
+ g.each { |c, v| df.subfields << MARC::Subfield.new(c, v) }
123
+ end
124
+ end
125
+
126
+ it 'groups the values' do
127
+ expected = [
128
+ { a: '1', b: '2', c: '7' },
129
+ { a: '5', b: '4', c: '9' },
130
+ { b: '6' }
131
+ ]
132
+ groups = Util.group_subfield_values(df)
133
+ expect(groups).to eq(expected)
134
+ end
135
+
136
+ it 'reorders the values' do
137
+ reordered = Util.group_subfield_values(df, order:)
138
+ # Hash equality doesn't check order, so we do it by hand
139
+ reordered.each_with_index do |actual, i|
140
+ expected = ragged_reordered[i]
141
+ expect(actual.keys).to eq(expected.keys), "#{i}: expected: #{expected}, got: #{actual}"
142
+ expect(actual.values).to eq(expected.values), "#{i}: expected: #{expected}, got: #{actual}"
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module AV
5
+ describe Marc do
6
+ describe :from_xml do
7
+ it 'returns a single record' do
8
+ marc_xml = File.read('spec/data/search-1993.xml')
9
+ record = Marc.from_xml(marc_xml)
10
+ expect(record).to be_a(MARC::Record)
11
+ end
12
+ end
13
+
14
+ describe :all_from_xml do
15
+ it 'returns multiple records' do
16
+ marc_xml = File.read('spec/data/search-1993.xml')
17
+ reader = Marc.all_from_xml(marc_xml)
18
+ records = reader.to_a
19
+ expect(records.size).to eq(2)
20
+ records.each do |record|
21
+ expect(record).to be_a(MARC::Record)
22
+ end
23
+ end
24
+ end
25
+
26
+ describe :reader_for do
27
+ it 'returns a reader for XML' do
28
+ marc_path = 'spec/data/record-(cityarts)00002.xml'
29
+ reader = Marc.reader_for(marc_path)
30
+ record = reader.first
31
+ expect(record['245'].value).to eq('826 Spelling Bee For Cheaters, February 17, 2011')
32
+ end
33
+
34
+ it 'returns a reader for binary MARC' do
35
+ marc_path = 'spec/data/10.23.19.JessieLaCavalier.02.mrc'
36
+ reader = Marc.reader_for(marc_path)
37
+ record = reader.first
38
+ expect(record['100'].value).to eq('LeCavalier, Jesse.')
39
+ end
40
+
41
+ it 'raises an error for non-MARC files' do
42
+ marc_path = 'I am not a MARC file'
43
+ expect { Marc.reader_for(marc_path) }.to raise_error(ArgumentError)
44
+ end
45
+ end
46
+
47
+ describe :read do
48
+ it 'reads XML' do
49
+ marc_path = 'spec/data/record-(cityarts)00002.xml'
50
+ record = Marc.read(marc_path)
51
+ expect(record['245'].value).to eq('826 Spelling Bee For Cheaters, February 17, 2011')
52
+ end
53
+
54
+ it 'reads binary MARC' do
55
+ marc_path = 'spec/data/10.23.19.JessieLaCavalier.02.mrc'
56
+ record = Marc.read(marc_path)
57
+ expect(record['100'].value).to eq('LeCavalier, Jesse.')
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+ require 'marc'
3
+
4
+ module BerkeleyLibrary
5
+ module AV
6
+ class Metadata
7
+ describe Field do
8
+ attr_reader :marc_record
9
+
10
+ before do
11
+ @marc_record = MARC::XMLReader.new('spec/data/record-(pacradio)01469.xml').first
12
+ end
13
+
14
+ it 'extracts the text from whole datafields' do
15
+ field = Field.new(order: 2, label: 'Creator', spec: '700')
16
+ value = field.value_from(marc_record)
17
+ expect(value).to be_a(Value)
18
+
19
+ expected_entries = [
20
+ 'Coleman, Wanda. interviewee.',
21
+ 'Adisa, Opal Palmer. interviewer.'
22
+ ]
23
+ expect(value.entries).to eq(expected_entries)
24
+ end
25
+
26
+ it 'extracts the text from subfields without groups' do
27
+ field = Field.new(order: 85, label: 'Usage Statement', spec: '540$a')
28
+ value = field.value_from(marc_record)
29
+ expect(value).to be_a(Value)
30
+
31
+ expected_entries = ['RESTRICTED. Permissions, licensing requests, and all other inquiries should be directed in writing to: Director of the Archives, Pacifica Radio Archives, 3729 Cahuenga Blvd. West, North Hollywood, CA 91604, 800-735-0230 x 263, fax 818-506-1084, info@pacificaradioarchives.org, http://www.pacificaradioarchives.org']
32
+ expect(value.entries).to eq(expected_entries)
33
+ end
34
+
35
+ # TODO: test multiple groups
36
+ it 'extracts the text from grouped subfields' do
37
+ field = Field.new(order: 66, label: 'Grant Information', spec: '536', subfield_order: %w[a o m n])
38
+ value = field.value_from(marc_record)
39
+ expect(value).to be_a(Value)
40
+ expected_entries = ["Sponsored by the National Historical Publications and Records Commission at the National Archives and Records Administration as part of Pacifica's American Women Making History and Culture: 1963-1982 grant preservation project."]
41
+ expect(value.entries).to eq(expected_entries)
42
+ end
43
+
44
+ context 'links' do
45
+ it 'extracts links from TIND records' do
46
+ field = Field.new(order: 9999, label: 'Link to Content', spec: '856{^1=\4}{^2=\2}', subfield_order: %w[u y])
47
+ value = field.value_from(marc_record)
48
+ expect(value).to be_a(Value)
49
+ expected_link = AV::Metadata::Link.new(
50
+ url: 'https://avplayer.lib.berkeley.edu/Pacifica/b23305522',
51
+ body: 'Play Audio for American Women Making History and Culture. Freely available for streaming.'
52
+ )
53
+ expect(value.entries).to contain_exactly(expected_link)
54
+ end
55
+
56
+ it 'extracts links from Alma records' do
57
+ marc_record = MARC::XMLReader.new('spec/data/alma/991054360089706532-sru.xml').first
58
+ field = Field.new(order: 9999, label: 'Link to Content', spec: '956{^1=\4}{^2=\0}', subfield_order: %w[u z])
59
+ value = field.value_from(marc_record)
60
+ expect(value).to be_a(Value)
61
+ expected_link = AV::Metadata::Link.new(
62
+ url: 'https://avplayer.lib.berkeley.edu/Video-UCBOnly-MRC/b25716973',
63
+ body: 'UCB Access.'
64
+ )
65
+ expect(value.entries).to contain_exactly(expected_link)
66
+ end
67
+ end
68
+
69
+ describe :hash do
70
+ it 'returns the same hash for identical Fields' do
71
+ f1 = Field.new(order: 2, label: 'Description', spec: '520$a')
72
+ f2 = Field.new(order: 2, label: 'Description', spec: '520$a')
73
+ expect(f1).to eq(f2) # just to be sure
74
+
75
+ expect(f1.hash).to eq(f2.hash)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end