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,335 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module AV
5
+ describe Track do
6
+
7
+ describe :file_type do
8
+ it 'extrapolates from path' do
9
+ track = Track.new(sort_order: 2, title: 'Part 2', path: 'frost-read2.mp3')
10
+ expect(track.file_type).to eq(AV::Types::FileType::MP3)
11
+
12
+ track = Track.new(sort_order: 1, path: 'mrc/6927.mp4')
13
+ expect(track.file_type).to eq(AV::Types::FileType::MP4)
14
+ end
15
+ end
16
+
17
+ describe :inspect do
18
+ it 'does something useful' do
19
+ track = Track.new(sort_order: 0, title: 'Part 1', path: 'MRCAudio/frost-read1.mp3', duration: AV::Types::Duration.from_string('01:02:29'))
20
+ result = track.inspect
21
+ expect(result).to include(Track.name)
22
+ expect(result).to include(track.to_s)
23
+ end
24
+ end
25
+
26
+ describe :<=> do
27
+ it 'treats object as equal to itself' do
28
+ track = Track.new(sort_order: 2, title: 'Part 2', path: 'frost-read2.mp3')
29
+ expect(track == track).to eq(true)
30
+ end
31
+
32
+ it 'treats object as equal to identical object' do
33
+ t1 = Track.new(sort_order: 2, title: 'Part 2', path: 'frost-read2.mp3')
34
+ t2 = Track.new(sort_order: 2, title: 'Part 2', path: 'frost-read2.mp3')
35
+ expect(t1 == t2).to eq(true)
36
+ end
37
+
38
+ it 'sorts by sort order' do
39
+ t1 = Track.new(sort_order: 3, title: 'Part 2', path: 'frost-read2.mp3')
40
+ t2 = Track.new(sort_order: 2, title: 'Part 2', path: 'frost-read2.mp3')
41
+ expect(t1 > t2).to eq(true)
42
+ expect(t2 < t1).to eq(true)
43
+ end
44
+
45
+ it 'sorts by title' do
46
+ t1 = Track.new(sort_order: 2, title: 'Part 3', path: 'frost-read2.mp3')
47
+ t2 = Track.new(sort_order: 2, title: 'Part 2', path: 'frost-read2.mp3')
48
+ expect(t1 > t2).to eq(true)
49
+ expect(t2 < t1).to eq(true)
50
+ end
51
+
52
+ it 'sorts by path' do
53
+ t1 = Track.new(sort_order: 2, title: 'Part 2', path: 'frost-read2.mp4')
54
+ t2 = Track.new(sort_order: 2, title: 'Part 2', path: 'frost-read2.mp3')
55
+ expect(t1 > t2).to eq(true)
56
+ expect(t2 < t1).to eq(true)
57
+ end
58
+
59
+ it 'returns nil for nil' do
60
+ track = Track.new(sort_order: 1, path: 'mrc/6927.mp4')
61
+ expect(track <=> nil).to be_nil
62
+ end
63
+
64
+ it 'returns nil for things that are not Tracks' do
65
+ track = Track.new(sort_order: 1, path: 'mrc/6927.mp4')
66
+ expect(track <=> track.to_s).to be_nil
67
+ end
68
+ end
69
+
70
+ describe :to_s do
71
+ it 'includes all relevant info' do
72
+ track_params = {
73
+ sort_order: 19,
74
+ title: 'reel 20, part 1',
75
+ path: 'MRCAudio/C040791201_1.mp3',
76
+ duration: AV::Types::Duration.from_string('00:40:12')
77
+ }
78
+ track_str = Track.new(**track_params).to_s
79
+ track_params.each_value do |v|
80
+ expect(track_str).to include(v.to_s)
81
+ end
82
+ end
83
+ end
84
+
85
+ describe :to_marc_subfields do
86
+ it 'returns the MARC subfields' do
87
+ track = Track.new(
88
+ sort_order: 0,
89
+ title: 'reel 1, part 1',
90
+ path: 'MRCAudio/C040790863_1.mp3',
91
+ duration: AV::Types::Duration.from_string('00:47:49')
92
+ )
93
+ marc_subfields = track.to_marc_subfields
94
+ expect(marc_subfields.size).to eq(3)
95
+
96
+ duration_subfield = marc_subfields[0]
97
+ expect(duration_subfield.code).to eq(AV::Constants::SUBFIELD_CODE_DURATION)
98
+ expect(duration_subfield.value).to eq(track.duration.to_s)
99
+
100
+ title_subfield = marc_subfields[1]
101
+ expect(title_subfield.code).to eq(AV::Constants::SUBFIELD_CODE_TITLE)
102
+ expect(title_subfield.value).to eq(track.title)
103
+
104
+ path_subfield = marc_subfields[2]
105
+ expect(path_subfield.code).to eq(AV::Constants::SUBFIELD_CODE_PATH)
106
+ expect(path_subfield.value).to eq(track.path)
107
+ end
108
+ end
109
+
110
+ describe :tracks_from do
111
+ it 'reads the tracks' do
112
+ expected_tracks = [
113
+ Track.new(sort_order: 0, title: 'reel 1, part 1', path: 'MRCAudio/C040790863_1.mp3', duration: AV::Types::Duration.from_string('00:47:49')),
114
+ Track.new(sort_order: 1, title: 'reel 2, part 1', path: 'MRCAudio/C040790960_1.mp3', duration: AV::Types::Duration.from_string('00:23:29')),
115
+ Track.new(sort_order: 2, title: 'reel 3, part 1', path: 'MRCAudio/C040791061_1.mp3', duration: AV::Types::Duration.from_string('00:22:56')),
116
+ Track.new(sort_order: 3, title: 'reel 4, part 1', path: 'MRCAudio/C040791168_1.mp3', duration: AV::Types::Duration.from_string('00:21:46')),
117
+ Track.new(sort_order: 4, title: 'reel 5, part 1', path: 'MRCAudio/C040790872_1.mp3', duration: AV::Types::Duration.from_string('00:24:19')),
118
+ Track.new(sort_order: 5, title: 'reel 6, part 1', path: 'MRCAudio/C040790979_1.mp3', duration: AV::Types::Duration.from_string('00:24:18')),
119
+ Track.new(sort_order: 6, title: 'reel 7, part 1', path: 'MRCAudio/C040791070_1.mp3', duration: AV::Types::Duration.from_string('00:24:18')),
120
+ Track.new(sort_order: 7, title: 'reel 8, part 1', path: 'MRCAudio/C040791177_1.mp3', duration: AV::Types::Duration.from_string('00:23:23')),
121
+ Track.new(sort_order: 8, title: 'reel 9, part 1', path: 'MRCAudio/C040790881_1.mp3', duration: AV::Types::Duration.from_string('00:20:01')),
122
+ Track.new(sort_order: 9, title: 'reel 10', path: 'MRCAudio/C040790988.mp3', duration: AV::Types::Duration.from_string('00:22:38')),
123
+ Track.new(sort_order: 10, title: 'reel 11', path: 'MRCAudio/C040791089.mp3', duration: AV::Types::Duration.from_string('00:23:44')),
124
+ Track.new(sort_order: 11, title: 'reel 12', path: 'MRCAudio/C040791186.mp3', duration: AV::Types::Duration.from_string('00:22:27')),
125
+ Track.new(sort_order: 12, title: 'reel 13, part 1', path: 'MRCAudio/C040790890_1.mp3',
126
+ duration: AV::Types::Duration.from_string('00:21:57')),
127
+ Track.new(sort_order: 13, title: 'reel 14, part 1', path: 'MRCAudio/C040790997_1.mp3',
128
+ duration: AV::Types::Duration.from_string('00:44:28')),
129
+ Track.new(sort_order: 14, title: 'reel 15, part 1', path: 'MRCAudio/C040791098_1.mp3',
130
+ duration: AV::Types::Duration.from_string('00:44:33')),
131
+ Track.new(sort_order: 15, title: 'reel 16, part 1', path: 'MRCAudio/C040790906_1.mp3',
132
+ duration: AV::Types::Duration.from_string('00:44:01')),
133
+ Track.new(sort_order: 16, title: 'reel 17, part 1', path: 'MRCAudio/C040791007_1.mp3',
134
+ duration: AV::Types::Duration.from_string('00:46:58')),
135
+ Track.new(sort_order: 17, title: 'reel 18, part 1', path: 'MRCAudio/C040791104_1.mp3',
136
+ duration: AV::Types::Duration.from_string('00:42:49')),
137
+ Track.new(sort_order: 18, title: 'reel 19, part 1', path: 'MRCAudio/C040791195_1.mp3',
138
+ duration: AV::Types::Duration.from_string('00:44:35')),
139
+ Track.new(sort_order: 19, title: 'reel 20, part 1', path: 'MRCAudio/C040791201_1.mp3',
140
+ duration: AV::Types::Duration.from_string('00:40:12'))
141
+ ]
142
+
143
+ bib_number = 'b23161018'
144
+ marc_record = alma_marc_record_for(bib_number)
145
+ tracks = Track.tracks_from(marc_record, collection: 'MRCAudio')
146
+ expect(tracks.size).to eq(expected_tracks.size)
147
+ aggregate_failures 'tracks' do
148
+ tracks.each_with_index do |track, i|
149
+ expect(track).to eq(expected_tracks[i])
150
+ end
151
+ end
152
+ end
153
+
154
+ it 'reads tracks from multiple 998s' do
155
+ expected_tracks = [
156
+ Track.new(sort_order: 0, title: 'reel 1, part 1', path: 'MRCAudio/C040790863_1.mp3', duration: AV::Types::Duration.from_string('00:47:49')),
157
+ Track.new(sort_order: 1, title: 'reel 2, part 1', path: 'MRCAudio/C040790960_1.mp3', duration: AV::Types::Duration.from_string('00:23:29')),
158
+ Track.new(sort_order: 2, title: 'reel 3, part 1', path: 'MRCAudio/C040791061_1.mp3', duration: AV::Types::Duration.from_string('00:22:56')),
159
+ Track.new(sort_order: 3, title: 'reel 4, part 1', path: 'MRCAudio/C040791168_1.mp3', duration: AV::Types::Duration.from_string('00:21:46')),
160
+ Track.new(sort_order: 4, title: 'reel 5, part 1', path: 'MRCAudio/C040790872_1.mp3', duration: AV::Types::Duration.from_string('00:24:19')),
161
+ Track.new(sort_order: 5, title: 'reel 6, part 1', path: 'MRCAudio/C040790979_1.mp3', duration: AV::Types::Duration.from_string('00:24:18')),
162
+ Track.new(sort_order: 6, title: 'reel 7, part 1', path: 'MRCAudio/C040791070_1.mp3', duration: AV::Types::Duration.from_string('00:24:18')),
163
+ Track.new(sort_order: 7, title: 'reel 8, part 1', path: 'MRCAudio/C040791177_1.mp3', duration: AV::Types::Duration.from_string('00:23:23')),
164
+ Track.new(sort_order: 8, title: 'reel 9, part 1', path: 'MRCAudio/C040790881_1.mp3', duration: AV::Types::Duration.from_string('00:20:01')),
165
+ Track.new(sort_order: 9, title: 'reel 10', path: 'MRCAudio/C040790988.mp3', duration: AV::Types::Duration.from_string('00:22:38')),
166
+ Track.new(sort_order: 10, title: 'reel 11', path: 'MRCAudio/C040791089.mp3', duration: AV::Types::Duration.from_string('00:23:44')),
167
+ Track.new(sort_order: 11, title: 'reel 12', path: 'MRCAudio/C040791186.mp3', duration: AV::Types::Duration.from_string('00:22:27')),
168
+ Track.new(sort_order: 12, title: 'reel 13, part 1', path: 'MRCAudio/C040790890_1.mp3',
169
+ duration: AV::Types::Duration.from_string('00:21:57')),
170
+ Track.new(sort_order: 13, title: 'reel 14, part 1', path: 'MRCAudio/C040790997_1.mp3',
171
+ duration: AV::Types::Duration.from_string('00:44:28')),
172
+ Track.new(sort_order: 14, title: 'reel 15, part 1', path: 'MRCAudio/C040791098_1.mp3',
173
+ duration: AV::Types::Duration.from_string('00:44:33')),
174
+ Track.new(sort_order: 15, title: 'reel 16, part 1', path: 'MRCAudio/C040790906_1.mp3',
175
+ duration: AV::Types::Duration.from_string('00:44:01')),
176
+ Track.new(sort_order: 16, title: 'reel 17, part 1', path: 'MRCAudio/C040791007_1.mp3',
177
+ duration: AV::Types::Duration.from_string('00:46:58')),
178
+ Track.new(sort_order: 17, title: 'reel 18, part 1', path: 'MRCAudio/C040791104_1.mp3',
179
+ duration: AV::Types::Duration.from_string('00:42:49')),
180
+ Track.new(sort_order: 18, title: 'reel 19, part 1', path: 'MRCAudio/C040791195_1.mp3',
181
+ duration: AV::Types::Duration.from_string('00:44:35')),
182
+ Track.new(sort_order: 19, title: 'reel 20, part 1', path: 'MRCAudio/C040791201_1.mp3',
183
+ duration: AV::Types::Duration.from_string('00:40:12'))
184
+ ]
185
+
186
+ marc_record = AV::Marc.from_xml(File.read('spec/data/record-multiple-998s.xml'))
187
+ tracks = Track.tracks_from(marc_record, collection: 'MRCAudio')
188
+ expect(tracks.size).to eq(expected_tracks.size)
189
+ aggregate_failures 'tracks' do
190
+ tracks.each_with_index do |track, i|
191
+ expect(track).to eq(expected_tracks[i])
192
+ end
193
+ end
194
+ end
195
+
196
+ it 'accepts disordered fields in multiple 998s' do
197
+ expected_tracks = [
198
+ Track.new(sort_order: 0, title: 'reel 1, part 1', path: 'MRCAudio/C040790863_1.mp3', duration: AV::Types::Duration.from_string('00:47:49')),
199
+ Track.new(sort_order: 1, title: 'reel 2, part 1', path: 'MRCAudio/C040790960_1.mp3', duration: AV::Types::Duration.from_string('00:23:29')),
200
+ Track.new(sort_order: 2, title: 'reel 3, part 1', path: 'MRCAudio/C040791061_1.mp3', duration: AV::Types::Duration.from_string('00:22:56')),
201
+ Track.new(sort_order: 3, title: 'reel 4, part 1', path: 'MRCAudio/C040791168_1.mp3', duration: AV::Types::Duration.from_string('00:21:46')),
202
+ Track.new(sort_order: 4, title: 'reel 5, part 1', path: 'MRCAudio/C040790872_1.mp3', duration: AV::Types::Duration.from_string('00:24:19')),
203
+ Track.new(sort_order: 5, title: 'reel 6, part 1', path: 'MRCAudio/C040790979_1.mp3', duration: AV::Types::Duration.from_string('00:24:18')),
204
+ Track.new(sort_order: 6, title: 'reel 7, part 1', path: 'MRCAudio/C040791070_1.mp3', duration: AV::Types::Duration.from_string('00:24:18')),
205
+ Track.new(sort_order: 7, title: 'reel 8, part 1', path: 'MRCAudio/C040791177_1.mp3', duration: AV::Types::Duration.from_string('00:23:23')),
206
+ Track.new(sort_order: 8, title: 'reel 9, part 1', path: 'MRCAudio/C040790881_1.mp3', duration: AV::Types::Duration.from_string('00:20:01')),
207
+ Track.new(sort_order: 9, title: 'reel 10', path: 'MRCAudio/C040790988.mp3', duration: AV::Types::Duration.from_string('00:22:38')),
208
+ Track.new(sort_order: 10, title: 'reel 11', path: 'MRCAudio/C040791089.mp3', duration: AV::Types::Duration.from_string('00:23:44')),
209
+ Track.new(sort_order: 11, title: 'reel 12', path: 'MRCAudio/C040791186.mp3', duration: AV::Types::Duration.from_string('00:22:27')),
210
+ Track.new(sort_order: 12, title: 'reel 13, part 1', path: 'MRCAudio/C040790890_1.mp3',
211
+ duration: AV::Types::Duration.from_string('00:21:57')),
212
+ Track.new(sort_order: 13, title: 'reel 14, part 1', path: 'MRCAudio/C040790997_1.mp3',
213
+ duration: AV::Types::Duration.from_string('00:44:28')),
214
+ Track.new(sort_order: 14, title: 'reel 15, part 1', path: 'MRCAudio/C040791098_1.mp3',
215
+ duration: AV::Types::Duration.from_string('00:44:33')),
216
+ Track.new(sort_order: 15, title: 'reel 16, part 1', path: 'MRCAudio/C040790906_1.mp3',
217
+ duration: AV::Types::Duration.from_string('00:44:01')),
218
+ Track.new(sort_order: 16, title: 'reel 17, part 1', path: 'MRCAudio/C040791007_1.mp3',
219
+ duration: AV::Types::Duration.from_string('00:46:58')),
220
+ Track.new(sort_order: 17, title: 'reel 18, part 1', path: 'MRCAudio/C040791104_1.mp3',
221
+ duration: AV::Types::Duration.from_string('00:42:49')),
222
+ Track.new(sort_order: 18, title: 'reel 19, part 1', path: 'MRCAudio/C040791195_1.mp3',
223
+ duration: AV::Types::Duration.from_string('00:44:35')),
224
+ Track.new(sort_order: 19, title: 'reel 20, part 1', path: 'MRCAudio/C040791201_1.mp3',
225
+ duration: AV::Types::Duration.from_string('00:40:12'))
226
+ ]
227
+
228
+ marc_record = AV::Marc.from_xml(File.read('spec/data/record-multiple-998s-disordered.xml'))
229
+ tracks = Track.tracks_from(marc_record, collection: 'MRCAudio')
230
+ expect(tracks.size).to eq(expected_tracks.size)
231
+ aggregate_failures 'tracks' do
232
+ tracks.each_with_index do |track, i|
233
+ expect(track).to eq(expected_tracks[i])
234
+ end
235
+ end
236
+ end
237
+
238
+ it 'accepts ragged subfields in multiple 998s' do
239
+ expected_tracks = [
240
+ Track.new(sort_order: 0, path: 'MRCAudio/C040790863_1.mp3', duration: AV::Types::Duration.from_string('00:47:49')),
241
+ Track.new(sort_order: 1, path: 'MRCAudio/C040790960_1.mp3', duration: AV::Types::Duration.from_string('00:23:29')),
242
+ Track.new(sort_order: 2, path: 'MRCAudio/C040791061_1.mp3', duration: AV::Types::Duration.from_string('00:22:56')),
243
+ Track.new(sort_order: 3, path: 'MRCAudio/C040791168_1.mp3', duration: AV::Types::Duration.from_string('00:21:46')),
244
+ Track.new(sort_order: 4, path: 'MRCAudio/C040790872_1.mp3', duration: AV::Types::Duration.from_string('00:24:19')),
245
+ Track.new(sort_order: 5, path: 'MRCAudio/C040790979_1.mp3', duration: AV::Types::Duration.from_string('00:24:18')),
246
+ Track.new(sort_order: 6, path: 'MRCAudio/C040791070_1.mp3', duration: AV::Types::Duration.from_string('00:24:18')),
247
+ Track.new(sort_order: 7, path: 'MRCAudio/C040791177_1.mp3', duration: AV::Types::Duration.from_string('00:23:23')),
248
+ Track.new(sort_order: 8, path: 'MRCAudio/C040790881_1.mp3', duration: AV::Types::Duration.from_string('00:20:01')),
249
+ Track.new(sort_order: 9, path: 'MRCAudio/C040790988.mp3', duration: AV::Types::Duration.from_string('00:22:38')),
250
+ Track.new(sort_order: 10, title: 'reel 11', path: 'MRCAudio/C040791089.mp3'),
251
+ Track.new(sort_order: 11, title: 'reel 12', path: 'MRCAudio/C040791186.mp3'),
252
+ Track.new(sort_order: 12, title: 'reel 13, part 1', path: 'MRCAudio/C040790890_1.mp3'),
253
+ Track.new(sort_order: 13, title: 'reel 14, part 1', path: 'MRCAudio/C040790997_1.mp3'),
254
+ Track.new(sort_order: 14, title: 'reel 15, part 1', path: 'MRCAudio/C040791098_1.mp3'),
255
+ Track.new(sort_order: 15, title: 'reel 16, part 1', path: 'MRCAudio/C040790906_1.mp3'),
256
+ Track.new(sort_order: 16, title: 'reel 17, part 1', path: 'MRCAudio/C040791007_1.mp3'),
257
+ Track.new(sort_order: 17, title: 'reel 18, part 1', path: 'MRCAudio/C040791104_1.mp3'),
258
+ Track.new(sort_order: 18, title: 'reel 19, part 1', path: 'MRCAudio/C040791195_1.mp3'),
259
+ Track.new(sort_order: 19, title: 'reel 20, part 1', path: 'MRCAudio/C040791201_1.mp3')
260
+ ]
261
+
262
+ marc_record = AV::Marc.from_xml(File.read('spec/data/record-ragged-998s-multiple-fields.xml'))
263
+ tracks = Track.tracks_from(marc_record, collection: 'MRCAudio')
264
+ expect(tracks.size).to eq(expected_tracks.size)
265
+ aggregate_failures 'tracks' do
266
+ tracks.each_with_index do |track, i|
267
+ expect(track).to eq(expected_tracks[i])
268
+ end
269
+ end
270
+ end
271
+
272
+ it 'accepts ragged subfields in a single 998 if ordered correctly' do
273
+ expected_tracks = [
274
+ Track.new(sort_order: 0, path: 'MRCAudio/C040790863_1.mp3', duration: AV::Types::Duration.from_string('00:47:49')),
275
+ Track.new(sort_order: 1, path: 'MRCAudio/C040790960_1.mp3', duration: AV::Types::Duration.from_string('00:23:29')),
276
+ Track.new(sort_order: 2, path: 'MRCAudio/C040791061_1.mp3', duration: AV::Types::Duration.from_string('00:22:56')),
277
+ Track.new(sort_order: 3, path: 'MRCAudio/C040791168_1.mp3', duration: AV::Types::Duration.from_string('00:21:46')),
278
+ Track.new(sort_order: 4, path: 'MRCAudio/C040790872_1.mp3', duration: AV::Types::Duration.from_string('00:24:19')),
279
+ Track.new(sort_order: 5, path: 'MRCAudio/C040790979_1.mp3', duration: AV::Types::Duration.from_string('00:24:18')),
280
+ Track.new(sort_order: 6, path: 'MRCAudio/C040791070_1.mp3', duration: AV::Types::Duration.from_string('00:24:18')),
281
+ Track.new(sort_order: 7, path: 'MRCAudio/C040791177_1.mp3', duration: AV::Types::Duration.from_string('00:23:23')),
282
+ Track.new(sort_order: 8, path: 'MRCAudio/C040790881_1.mp3', duration: AV::Types::Duration.from_string('00:20:01')),
283
+ Track.new(sort_order: 9, path: 'MRCAudio/C040790988.mp3', duration: AV::Types::Duration.from_string('00:22:38')),
284
+ Track.new(sort_order: 10, title: 'reel 11', path: 'MRCAudio/C040791089.mp3'),
285
+ Track.new(sort_order: 11, title: 'reel 12', path: 'MRCAudio/C040791186.mp3'),
286
+ Track.new(sort_order: 12, title: 'reel 13, part 1', path: 'MRCAudio/C040790890_1.mp3'),
287
+ Track.new(sort_order: 13, title: 'reel 14, part 1', path: 'MRCAudio/C040790997_1.mp3'),
288
+ Track.new(sort_order: 14, title: 'reel 15, part 1', path: 'MRCAudio/C040791098_1.mp3'),
289
+ Track.new(sort_order: 15, title: 'reel 16, part 1', path: 'MRCAudio/C040790906_1.mp3'),
290
+ Track.new(sort_order: 16, title: 'reel 17, part 1', path: 'MRCAudio/C040791007_1.mp3'),
291
+ Track.new(sort_order: 17, title: 'reel 18, part 1', path: 'MRCAudio/C040791104_1.mp3'),
292
+ Track.new(sort_order: 18, title: 'reel 19, part 1', path: 'MRCAudio/C040791195_1.mp3'),
293
+ Track.new(sort_order: 19, title: 'reel 20, part 1', path: 'MRCAudio/C040791201_1.mp3')
294
+ ]
295
+
296
+ marc_record = AV::Marc.from_xml(File.read('spec/data/record-ragged-998-subfields.xml'))
297
+ tracks = Track.tracks_from(marc_record, collection: 'MRCAudio')
298
+ expect(tracks.size).to eq(expected_tracks.size)
299
+ aggregate_failures 'tracks' do
300
+ tracks.each_with_index do |track, i|
301
+ expect(track).to eq(expected_tracks[i])
302
+ end
303
+ end
304
+ end
305
+
306
+ it 'handles records with no tracks' do
307
+ bib_number = 'b23161018'
308
+ marc_record = alma_marc_record_for(bib_number)
309
+ marc_record.fields.reject! { |df| df.tag == AV::Constants::TAG_TRACK_FIELD }
310
+
311
+ tracks = Track.tracks_from(marc_record, collection: 'MRCAudio')
312
+ expect(tracks).to eq([])
313
+ end
314
+
315
+ it 'handles tracks with spaces' do
316
+ expected_tracks = [
317
+ Track.new(sort_order: 0, title: 'Part 1', path: 'MRCAudio/frost-read1.mp3', duration: AV::Types::Duration.from_string('01:02:29')),
318
+ Track.new(sort_order: 1, title: 'Part 2', path: 'MRCAudio/frost-read2.mp3', duration: AV::Types::Duration.from_string('00:28:58'))
319
+ ]
320
+
321
+ bib_number = 'b11082434'
322
+ marc_record = alma_marc_record_for(bib_number)
323
+
324
+ tracks = Track.tracks_from(marc_record, collection: 'MRCAudio')
325
+ expect(tracks.size).to eq(expected_tracks.size)
326
+ aggregate_failures 'tracks' do
327
+ tracks.each_with_index do |track, i|
328
+ expect(track).to eq(expected_tracks[i])
329
+ end
330
+ end
331
+ end
332
+ end
333
+ end
334
+ end
335
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module AV
5
+ module Types
6
+ describe Duration do
7
+ describe :from_string do
8
+ it 'parses HH:MM:SS' do
9
+ duration = Duration.from_string('12:34:56')
10
+ expect(duration).not_to be_nil
11
+ expect(duration.hours).to eq(12)
12
+ expect(duration.minutes).to eq(34)
13
+ expect(duration.seconds).to eq(56)
14
+ end
15
+
16
+ it 'parses HHMMSS' do
17
+ duration = Duration.from_string('123456')
18
+ expect(duration).not_to be_nil
19
+ expect(duration.hours).to eq(12)
20
+ expect(duration.minutes).to eq(34)
21
+ expect(duration.seconds).to eq(56)
22
+ end
23
+
24
+ it 'parses H:MM:SS' do
25
+ duration = Duration.from_string('2:34:56')
26
+ expect(duration).not_to be_nil
27
+ expect(duration.hours).to eq(2)
28
+ expect(duration.minutes).to eq(34)
29
+ expect(duration.seconds).to eq(56)
30
+ end
31
+
32
+ it 'parses HMMSS' do
33
+ duration = Duration.from_string('23456')
34
+ expect(duration).not_to be_nil
35
+ expect(duration.hours).to eq(2)
36
+ expect(duration.minutes).to eq(34)
37
+ expect(duration.seconds).to eq(56)
38
+ end
39
+
40
+ it 'parses MM:SS' do
41
+ duration = Duration.from_string('34:56')
42
+ expect(duration).not_to be_nil
43
+ expect(duration.hours).to eq(0)
44
+ expect(duration.minutes).to eq(34)
45
+ expect(duration.seconds).to eq(56)
46
+ end
47
+
48
+ it 'parses MMSS' do
49
+ duration = Duration.from_string('3456')
50
+ expect(duration).not_to be_nil
51
+ expect(duration.hours).to eq(0)
52
+ expect(duration.minutes).to eq(34)
53
+ expect(duration.seconds).to eq(56)
54
+ end
55
+
56
+ it 'returns nil for garbage' do
57
+ duration = Duration.from_string('Not a duration')
58
+ expect(duration).to be_nil
59
+ end
60
+
61
+ it 'handles whitespace' do
62
+ duration = Duration.from_string('00: 23:44')
63
+ expect(duration).not_to be_nil
64
+ expect(duration.hours).to eq(0)
65
+ expect(duration.minutes).to eq(23)
66
+ expect(duration.seconds).to eq(44)
67
+ end
68
+ end
69
+
70
+ describe :<=> do
71
+ it 'returns 0 for equivalent durations' do
72
+ d1 = Duration.from_string('00:34:56')
73
+ d2 = Duration.new(hours: 0, minutes: 34, seconds: 56)
74
+ expect(d1 <=> d2).to eq(0)
75
+ expect(d2 <=> d1).to eq(0)
76
+ end
77
+
78
+ it 'returns nil for nil' do
79
+ d = Duration.from_string('00:34:56')
80
+ expect(d <=> nil).to be_nil
81
+ end
82
+
83
+ it 'returs nil for things that are not durations' do
84
+ d = Duration.from_string('00:34:56')
85
+ expect(d <=> d.to_s).to be_nil
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module AV
5
+ module Types
6
+ describe FileType do
7
+ describe :mime_type do
8
+ it 'is correct' do
9
+ expected = {
10
+ FileType::MP3 => 'application/x-mpegURL',
11
+ FileType::MP4 => 'video/mp4',
12
+ FileType::UNKNOWN => 'application/octet-stream'
13
+ }
14
+ expected.each do |t, mt_expected|
15
+ expect(t.mime_type).to eq(mt_expected)
16
+ end
17
+ end
18
+ end
19
+
20
+ describe :player_tag do
21
+ it 'is correct' do
22
+ expected = {
23
+ FileType::MP3 => 'audio',
24
+ FileType::MP4 => 'video',
25
+ FileType::UNKNOWN => 'object'
26
+ }
27
+ expected.each do |t, pt_expected|
28
+ expect(t.player_tag).to eq(pt_expected)
29
+ end
30
+ end
31
+ end
32
+
33
+ describe :prefix do
34
+ it 'is correct' do
35
+ expected = {
36
+ FileType::MP3 => 'mp3',
37
+ FileType::MP4 => 'mp4',
38
+ FileType::UNKNOWN => 'unknown'
39
+ }
40
+ expected.each do |t, prefix_expected|
41
+ expect(t.prefix).to eq(prefix_expected)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe :label do
47
+ it 'is correct' do
48
+ expected = {
49
+ FileType::MP3 => 'Audio',
50
+ FileType::MP4 => 'Video',
51
+ FileType::UNKNOWN => 'Unknown'
52
+ }
53
+ expected.each do |t, pt_expected|
54
+ expect(t.label).to eq(pt_expected)
55
+ end
56
+ end
57
+ end
58
+
59
+ describe :to_s do
60
+ it 'returns the value' do
61
+ FileType.each do |t|
62
+ expect(t.to_s).to eq(t.value.to_s)
63
+ end
64
+ end
65
+ end
66
+
67
+ describe :for_path do
68
+ it 'identifies an MP3' do
69
+ expect(FileType.for_path('foo.mp3')).to eq(AV::Types::FileType::MP3)
70
+ end
71
+
72
+ it 'identifies an MP4' do
73
+ %w[.mp4 .f4v .mov .m4a .m4v .mp4a .mp4v .3gp .3g2].each do |ext|
74
+ expect(FileType.for_path("foo#{ext}")).to eq(AV::Types::FileType::MP4)
75
+ end
76
+ end
77
+
78
+ it 'returns UNKNOWN for nil' do
79
+ # noinspection RubyYardParamTypeMatch
80
+ expect(FileType.for_path(nil)).to eq(AV::Types::FileType::UNKNOWN)
81
+ end
82
+
83
+ it 'returns UNKNOWN for unknown types' do
84
+ expect(FileType.for_path('foo.txt')).to eq(AV::Types::FileType::UNKNOWN)
85
+ end
86
+ end
87
+
88
+ describe :new do
89
+ it "doesn't allow duplicate extensions" do
90
+ expect { FileType.send(:new, :QT, 'video', 'video/qt', extensions: ['.mov']) }
91
+ .to raise_error(ArgumentError, /.mov/)
92
+ expect(defined? FileType::QT).to be_nil
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module AV
5
+ describe Util do
6
+ describe :do_get do
7
+
8
+ it 'sends a custom user-agent header' do
9
+ expected_ua = Util::DEFAULT_USER_AGENT
10
+
11
+ bib_number = 'b11082434'
12
+ url = alma_sru_url_for(bib_number)
13
+ data_path = alma_sru_data_path_for(bib_number)
14
+ body = File.read(data_path)
15
+ stub_request(:get, url).with(headers: { 'User-Agent' => expected_ua }).to_return(status: 200, body:)
16
+
17
+ result = AV::Util.do_get(url)
18
+ expect(result).to eq(body.scrub)
19
+ end
20
+
21
+ it 'ignores errors if ignore_errors is set to true' do
22
+ url = alma_sru_url_for('b11082434')
23
+ stub_request(:get, url).to_return(status: 404)
24
+ result = AV::Util.do_get(url, ignore_errors: true)
25
+ expect(result).to be_nil
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,63 @@
1
+ # ------------------------------------------------------------
2
+ # Simplecov
3
+
4
+ if ENV['COVERAGE']
5
+ require 'colorize'
6
+ require 'simplecov'
7
+ end
8
+
9
+ # ------------------------------------------------------------
10
+ # RSpec
11
+
12
+ require 'webmock/rspec'
13
+
14
+ RSpec.configure do |config|
15
+ config.color = true
16
+ config.tty = true
17
+ config.formatter = :documentation
18
+ config.before { WebMock.disable_net_connect!(allow_localhost: true) }
19
+ config.after { WebMock.allow_net_connect! }
20
+ config.mock_with :rspec do |mocks|
21
+ mocks.verify_partial_doubles = true
22
+ end
23
+ config.shared_context_metadata_behavior = :apply_to_host_groups
24
+ end
25
+
26
+ # ------------------------------------------------------------
27
+ # Code under test
28
+
29
+ require 'berkeley_library/av/core'
30
+
31
+ # ------------------------------------------------------------
32
+ # Utility methods
33
+
34
+ def sru_url_base
35
+ 'https://berkeley.alma.exlibrisgroup.com/view/sru/01UCS_BER?version=1.2&operation=searchRetrieve&query='
36
+ end
37
+
38
+ def permalink_base
39
+ 'https://search.library.berkeley.edu/permalink/01UCS_BER/iqob43/alma'
40
+ end
41
+
42
+ def alma_sru_url_for(record_id)
43
+ return "#{sru_url_base}alma.mms_id%3D#{record_id}" unless BerkeleyLibrary::AV::RecordId::Type.for_id(record_id) == BerkeleyLibrary::AV::RecordId::Type::MILLENNIUM
44
+
45
+ full_bib = BerkeleyLibrary::AV::RecordId.ensure_check_digit(record_id)
46
+ "#{sru_url_base}alma.other_system_number%3DUCB-#{full_bib}-01ucs_ber"
47
+ end
48
+
49
+ def alma_sru_data_path_for(record_id)
50
+ "spec/data/alma/#{record_id}-sru.xml"
51
+ end
52
+
53
+ def stub_sru_request(record_id)
54
+ sru_url = alma_sru_url_for(record_id)
55
+ marc_xml_path = alma_sru_data_path_for(record_id)
56
+
57
+ stub_request(:get, sru_url).to_return(status: 200, body: File.read(marc_xml_path))
58
+ end
59
+
60
+ def alma_marc_record_for(record_id)
61
+ marc_xml_path = alma_sru_data_path_for(record_id)
62
+ MARC::XMLReader.new(marc_xml_path).first
63
+ end