berkeley_library-av-core 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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