berkeley_library-alma 0.0.2 → 0.0.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f75f54a66128a500d0bc512b7077ac20f1287d44a41ef418ad63bc20e1ec970
4
- data.tar.gz: 321aac3f7709f68098d0014165114536856281645a02773766744b08f6ba1f60
3
+ metadata.gz: '0805d8fb305dd644ede89efb0c71845c479df3cc8b69334bbfc4e747312c229d'
4
+ data.tar.gz: 80b1576c6dc7d5bbfd5bd5a0a53985167c4c3929668d505dae92ffcac196988e
5
5
  SHA512:
6
- metadata.gz: 603cdb1c13e5a2a169690ccf1ca2f99af82c5dcd2e2f6b99e09fa9dc897a57643b0957f22e88b35eb48aa52eb048f8f0b93e2f721f5ecac27351777a713a713d
7
- data.tar.gz: f239d7f6626d4e57c455c6dc2a32dffbbe1b32c1aea8eaf958409f096bf5350181df993f8eb45b2f2fd8b400a59d8b719d59b9dc144f7ab164f8470b110c710f
6
+ metadata.gz: 879cb4ef386caa8543bf883ac3b62888c4ca810ef431562618ad456c9d560ee81f6dada5b9ae850ca15523cd6f58d5487919c3e782c817313deaff346912c257
7
+ data.tar.gz: b10240d0571733f28f90ab099da879647cdd670da05c5af5aa396f0fcd8c9ae026d2ec9b4c1246764a6b919bc594c665ff0096140b400cb2626af893bdcd3b21
data/.idea/alma.iml CHANGED
@@ -8,12 +8,13 @@
8
8
  <sourceFolder url="file://$MODULE_DIR$/features" isTestSource="true" />
9
9
  <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
10
10
  <excludeFolder url="file://$MODULE_DIR$/artifacts" />
11
+ <excludeFolder url="file://$MODULE_DIR$/doc" />
11
12
  </content>
12
13
  <orderEntry type="jdk" jdkName="RVM: ruby-2.7.5" jdkType="RUBY_SDK" />
13
14
  <orderEntry type="sourceFolder" forTests="false" />
14
- <orderEntry type="library" scope="PROVIDED" name="actionpack (v6.1.4.4, RVM: ruby-2.7.5) [gem]" level="application" />
15
- <orderEntry type="library" scope="PROVIDED" name="actionview (v6.1.4.4, RVM: ruby-2.7.5) [gem]" level="application" />
16
- <orderEntry type="library" scope="PROVIDED" name="activesupport (v6.1.4.4, RVM: ruby-2.7.5) [gem]" level="application" />
15
+ <orderEntry type="library" scope="PROVIDED" name="actionpack (v6.1.4.6, RVM: ruby-2.7.5) [gem]" level="application" />
16
+ <orderEntry type="library" scope="PROVIDED" name="actionview (v6.1.4.6, RVM: ruby-2.7.5) [gem]" level="application" />
17
+ <orderEntry type="library" scope="PROVIDED" name="activesupport (v6.1.4.6, RVM: ruby-2.7.5) [gem]" level="application" />
17
18
  <orderEntry type="library" scope="PROVIDED" name="addressable (v2.8.0, RVM: ruby-2.7.5) [gem]" level="application" />
18
19
  <orderEntry type="library" scope="PROVIDED" name="amazing_print (v1.4.0, RVM: ruby-2.7.5) [gem]" level="application" />
19
20
  <orderEntry type="library" scope="PROVIDED" name="ast (v2.4.2, RVM: ruby-2.7.5) [gem]" level="application" />
@@ -22,7 +23,7 @@
22
23
  <orderEntry type="library" scope="PROVIDED" name="berkeley_library-util (v0.1.1, RVM: ruby-2.7.5) [gem]" level="application" />
23
24
  <orderEntry type="library" scope="PROVIDED" name="builder (v3.2.4, RVM: ruby-2.7.5) [gem]" level="application" />
24
25
  <orderEntry type="library" scope="PROVIDED" name="bundle-audit (v0.1.0, RVM: ruby-2.7.5) [gem]" level="application" />
25
- <orderEntry type="library" scope="PROVIDED" name="bundler (v2.2.31, RVM: ruby-2.7.5) [gem]" level="application" />
26
+ <orderEntry type="library" scope="PROVIDED" name="bundler (v2.1.4, RVM: ruby-2.7.5) [gem]" level="application" />
26
27
  <orderEntry type="library" scope="PROVIDED" name="bundler-audit (v0.9.0.1, RVM: ruby-2.7.5) [gem]" level="application" />
27
28
  <orderEntry type="library" scope="PROVIDED" name="ci_reporter (v2.0.0, RVM: ruby-2.7.5) [gem]" level="application" />
28
29
  <orderEntry type="library" scope="PROVIDED" name="ci_reporter_rspec (v1.0.0, RVM: ruby-2.7.5) [gem]" level="application" />
@@ -37,13 +38,14 @@
37
38
  <orderEntry type="library" scope="PROVIDED" name="hashdiff (v1.0.1, RVM: ruby-2.7.5) [gem]" level="application" />
38
39
  <orderEntry type="library" scope="PROVIDED" name="http-accept (v1.7.0, RVM: ruby-2.7.5) [gem]" level="application" />
39
40
  <orderEntry type="library" scope="PROVIDED" name="http-cookie (v1.0.4, RVM: ruby-2.7.5) [gem]" level="application" />
40
- <orderEntry type="library" scope="PROVIDED" name="i18n (v1.9.0, RVM: ruby-2.7.5) [gem]" level="application" />
41
+ <orderEntry type="library" scope="PROVIDED" name="i18n (v1.10.0, RVM: ruby-2.7.5) [gem]" level="application" />
41
42
  <orderEntry type="library" scope="PROVIDED" name="lograge (v0.11.2, RVM: ruby-2.7.5) [gem]" level="application" />
42
- <orderEntry type="library" scope="PROVIDED" name="loofah (v2.13.0, RVM: ruby-2.7.5) [gem]" level="application" />
43
+ <orderEntry type="library" scope="PROVIDED" name="loofah (v2.14.0, RVM: ruby-2.7.5) [gem]" level="application" />
43
44
  <orderEntry type="library" scope="PROVIDED" name="marc (v1.1.1, RVM: ruby-2.7.5) [gem]" level="application" />
44
45
  <orderEntry type="library" scope="PROVIDED" name="method_source (v1.0.0, RVM: ruby-2.7.5) [gem]" level="application" />
45
46
  <orderEntry type="library" scope="PROVIDED" name="mime-types (v3.4.1, RVM: ruby-2.7.5) [gem]" level="application" />
46
47
  <orderEntry type="library" scope="PROVIDED" name="mime-types-data (v3.2022.0105, RVM: ruby-2.7.5) [gem]" level="application" />
48
+ <orderEntry type="library" scope="PROVIDED" name="mini_portile2 (v2.7.1, RVM: ruby-2.7.5) [gem]" level="application" />
47
49
  <orderEntry type="library" scope="PROVIDED" name="minitest (v5.15.0, RVM: ruby-2.7.5) [gem]" level="application" />
48
50
  <orderEntry type="library" scope="PROVIDED" name="netrc (v0.11.0, RVM: ruby-2.7.5) [gem]" level="application" />
49
51
  <orderEntry type="library" scope="PROVIDED" name="nokogiri (v1.13.1, RVM: ruby-2.7.5) [gem]" level="application" />
@@ -58,23 +60,23 @@
58
60
  <orderEntry type="library" scope="PROVIDED" name="rack-test (v1.1.0, RVM: ruby-2.7.5) [gem]" level="application" />
59
61
  <orderEntry type="library" scope="PROVIDED" name="rails-dom-testing (v2.0.3, RVM: ruby-2.7.5) [gem]" level="application" />
60
62
  <orderEntry type="library" scope="PROVIDED" name="rails-html-sanitizer (v1.4.2, RVM: ruby-2.7.5) [gem]" level="application" />
61
- <orderEntry type="library" scope="PROVIDED" name="railties (v6.1.4.4, RVM: ruby-2.7.5) [gem]" level="application" />
63
+ <orderEntry type="library" scope="PROVIDED" name="railties (v6.1.4.6, RVM: ruby-2.7.5) [gem]" level="application" />
62
64
  <orderEntry type="library" scope="PROVIDED" name="rainbow (v3.1.1, RVM: ruby-2.7.5) [gem]" level="application" />
63
65
  <orderEntry type="library" scope="PROVIDED" name="rake (v13.0.6, RVM: ruby-2.7.5) [gem]" level="application" />
64
66
  <orderEntry type="library" scope="PROVIDED" name="regexp_parser (v2.2.0, RVM: ruby-2.7.5) [gem]" level="application" />
65
67
  <orderEntry type="library" scope="PROVIDED" name="request_store (v1.5.1, RVM: ruby-2.7.5) [gem]" level="application" />
66
68
  <orderEntry type="library" scope="PROVIDED" name="rest-client (v2.1.0, RVM: ruby-2.7.5) [gem]" level="application" />
67
69
  <orderEntry type="library" scope="PROVIDED" name="rexml (v3.2.5, RVM: ruby-2.7.5) [gem]" level="application" />
68
- <orderEntry type="library" scope="PROVIDED" name="rspec (v3.10.0, RVM: ruby-2.7.5) [gem]" level="application" />
69
- <orderEntry type="library" scope="PROVIDED" name="rspec-core (v3.10.1, RVM: ruby-2.7.5) [gem]" level="application" />
70
- <orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.10.2, RVM: ruby-2.7.5) [gem]" level="application" />
71
- <orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.10.2, RVM: ruby-2.7.5) [gem]" level="application" />
72
- <orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.10.3, RVM: ruby-2.7.5) [gem]" level="application" />
70
+ <orderEntry type="library" scope="PROVIDED" name="rspec (v3.11.0, RVM: ruby-2.7.5) [gem]" level="application" />
71
+ <orderEntry type="library" scope="PROVIDED" name="rspec-core (v3.11.0, RVM: ruby-2.7.5) [gem]" level="application" />
72
+ <orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.11.0, RVM: ruby-2.7.5) [gem]" level="application" />
73
+ <orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.11.0, RVM: ruby-2.7.5) [gem]" level="application" />
74
+ <orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.11.0, RVM: ruby-2.7.5) [gem]" level="application" />
73
75
  <orderEntry type="library" scope="PROVIDED" name="rubocop (v1.11.0, RVM: ruby-2.7.5) [gem]" level="application" />
74
76
  <orderEntry type="library" scope="PROVIDED" name="rubocop-ast (v1.15.1, RVM: ruby-2.7.5) [gem]" level="application" />
75
77
  <orderEntry type="library" scope="PROVIDED" name="rubocop-rake (v0.6.0, RVM: ruby-2.7.5) [gem]" level="application" />
76
78
  <orderEntry type="library" scope="PROVIDED" name="rubocop-rspec (v2.4.0, RVM: ruby-2.7.5) [gem]" level="application" />
77
- <orderEntry type="library" scope="PROVIDED" name="ruby-marc-spec (v0.1.1, RVM: ruby-2.7.5) [gem]" level="application" />
79
+ <orderEntry type="library" scope="PROVIDED" name="ruby-marc-spec (v0.1.3, RVM: ruby-2.7.5) [gem]" level="application" />
78
80
  <orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.11.0, RVM: ruby-2.7.5) [gem]" level="application" />
79
81
  <orderEntry type="library" scope="PROVIDED" name="scrub_rb (v1.0.1, RVM: ruby-2.7.5) [gem]" level="application" />
80
82
  <orderEntry type="library" scope="PROVIDED" name="simplecov (v0.21.2, RVM: ruby-2.7.5) [gem]" level="application" />
@@ -90,7 +92,7 @@
90
92
  <orderEntry type="library" scope="PROVIDED" name="webmock (v3.14.0, RVM: ruby-2.7.5) [gem]" level="application" />
91
93
  <orderEntry type="library" scope="PROVIDED" name="webrick (v1.7.0, RVM: ruby-2.7.5) [gem]" level="application" />
92
94
  <orderEntry type="library" scope="PROVIDED" name="yard (v0.9.27, RVM: ruby-2.7.5) [gem]" level="application" />
93
- <orderEntry type="library" scope="PROVIDED" name="zeitwerk (v2.5.3, RVM: ruby-2.7.5) [gem]" level="application" />
95
+ <orderEntry type="library" scope="PROVIDED" name="zeitwerk (v2.5.4, RVM: ruby-2.7.5) [gem]" level="application" />
94
96
  </component>
95
97
  <component name="RModuleSettingsStorage">
96
98
  <LOAD_PATH number="2" string0="$MODULE_DIR$/lib" string1="$MODULE_DIR$/spec" />
@@ -107,7 +109,7 @@
107
109
  </RakeTaskImpl>
108
110
  <RakeTaskImpl description="Run all specs in spec directory, with coverage" fullCommand="coverage" id="coverage" />
109
111
  <RakeTaskImpl description="Run tests, check test coverage, check code style, check for vulnerabilities, build gem" fullCommand="default" id="default" />
110
- <RakeTaskImpl description="Build berkeley_library-alma.gemspec as berkeley_library-alma-0.0.2.gem" fullCommand="gem" id="gem" />
112
+ <RakeTaskImpl description="Build berkeley_library-alma.gemspec as berkeley_library-alma-0.0.3.gem" fullCommand="gem" id="gem" />
111
113
  <RakeTaskImpl description="Run RuboCop with auto-correct, and output results to console" fullCommand="ra" id="ra" />
112
114
  <RakeTaskImpl description="Run rubocop with HTML output" fullCommand="rubocop" id="rubocop" />
113
115
  <RakeTaskImpl id="rubocop">
data/CHANGES.md CHANGED
@@ -1,3 +1,21 @@
1
+ # 0.0.5 (4 May 2022)
2
+
3
+ - extracts `SRU` module for performing SRU queries.
4
+ - MARCXML parsing now uses `MARC::NokogiriReader` rather than the `REXML` default.
5
+
6
+ # 0.0.4 (15 February 2022)
7
+
8
+ - adds `BarCode` subclass of `RecordId`, to look up bibliographic records by
9
+ item barcode. Note that since barcodes have no consistent format, `BarCode`
10
+ objects need to be constructed deliberately with `BarCode#new`, not with
11
+ `RecordId#parse`.
12
+
13
+ # 0.0.3 (3 February 2022)
14
+
15
+ - use `local_field_996` instead of `other_system_number` when making SRU queries
16
+ for Millennium records
17
+ - accept leading upper-case B in bib numbers
18
+
1
19
  # 0.0.2 (26 January 2022)
2
20
 
3
21
  - update to [berkeley_library-marc](https://github.com/BerkeleyLibrary/marc) 0.3.1
data/README.md CHANGED
@@ -41,10 +41,34 @@ options to either:
41
41
 
42
42
  ## Retrieving Alma records
43
43
 
44
- The [`RecordId`](lib/berkeley_library/alma/record_id.rb) class encapsulates
45
- an ID that can be used to look up records in Alma via
46
- [SRU](https://developers.exlibrisgroup.com/alma/integrations/sru/).
47
- This can be either an Alma MMS ID:
44
+ ### Via `SRU`
45
+
46
+ The [`SRU`](lib/berkeley_library/alma/sru.rb) module encapsulates Alma
47
+ [SRU](https://developers.exlibrisgroup.com/alma/integrations/sru/) queries.
48
+
49
+ Retrieving MARC records:
50
+
51
+ ```ruby
52
+ reader = BerkeleyLibrary::Alma::SRU.get_marc_records('991005668209706532', '991005668359706532')
53
+ # => #<MARC::XMLReader:0x0000000135b940e8 @freeze=false, @handle=#<StringIO:0x0000000135b94160>...
54
+ ```
55
+
56
+ Making arbitrary SRU queries:
57
+
58
+ ```ruby
59
+ BerkeleyLibrary::Alma::SRU.make_sru_query('alma.other_system_number=UCB-b123230470-01ucs_ber')
60
+ # => "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><searchRetrieveResponse xmlns=...
61
+ ```
62
+
63
+ ### Via `RecordId`
64
+
65
+ In addition, individual records can be retrieved from an instance of the
66
+ [`RecordId`](lib/berkeley_library/alma/record_id.rb) class, which encapsulates
67
+ an Alma MMS ID, Millennium bib number, or item barcode.
68
+
69
+ #### Initializing `RecordID` objects
70
+
71
+ Alma MMS ID:
48
72
 
49
73
  ```ruby
50
74
  mms_id_str = '991054360089706532'
@@ -52,7 +76,7 @@ record_id = BerkeleyLibrary::Alma::RecordId.parse(mms_id_str)
52
76
  # => #<BerkeleyLibrary::Alma::MMSID:0x0000000138949830 @institution="6532", @mms_id="991054360089706532", @type_prefix="99", @unique_part="105436008970">
53
77
  ```
54
78
 
55
- Or a Millennium bib number:
79
+ Millennium bib number:
56
80
 
57
81
  ```ruby
58
82
  bib_number_str = 'b11082434'
@@ -60,6 +84,20 @@ record_id = BerkeleyLibrary::Alma::RecordId.parse(bib_number_str)
60
84
  # => #<BerkeleyLibrary::Alma::BibNumber:0x0000000118815038 @check_str="9", @digit_str="11082434">
61
85
  ```
62
86
 
87
+ Item barcode:
88
+
89
+ ```ruby
90
+ barcode_str = 'C084093187'
91
+ barcode = BerkeleyLibrary::Alma::BarCode.new(barcode_str)
92
+ # => #<BerkeleyLibrary::Alma::BarCode:0x000000013fac4c08 @barcode="C084093187">
93
+ ```
94
+
95
+ ⚠️ Note that because of the free-form nature of barcodes, they cannot be auto-detected,
96
+ and hence are **not** supported by the `RecordId#parse` method; they must be instantiated
97
+ directly.
98
+
99
+ #### Using `RecordId` objects to make SRU queries
100
+
63
101
  Given a `RecordId` object, you can get the SRU query URI for the corresponding MARC record:
64
102
 
65
103
  ```ruby
data/bin/alma-mms-lookup CHANGED
@@ -25,9 +25,7 @@ require 'berkeley_library/alma'
25
25
  # Configuration
26
26
 
27
27
  # Configure Alma URLs etc.
28
- BerkeleyLibrary::Alma.configure do
29
- Config.default!
30
- end
28
+ BerkeleyLibrary::Alma::Config.default!
31
29
 
32
30
  # Set log level
33
31
  BerkeleyLibrary::Logging.logger.level = Logger::Severity::WARN
@@ -0,0 +1,33 @@
1
+ require 'berkeley_library/alma/record_id'
2
+
3
+ module BerkeleyLibrary
4
+ module Alma
5
+ # {RecordId} subclass representing an item barcode.
6
+ class BarCode
7
+ include RecordId
8
+
9
+ attr_reader :barcode
10
+
11
+ # Initialize a barcode. Since we purchase barcodes of varied formats and accept vendor
12
+ # barcodes as well we are only validating whether it's a string or not.
13
+ def initialize(barcode)
14
+ string?(barcode)
15
+ @barcode = barcode
16
+ end
17
+
18
+ # Returns the SRU query value for this Barcode.
19
+ #
20
+ # @return [String] the Barcode query value
21
+ def sru_query_value
22
+ "alma.barcode=#{@barcode}"
23
+ end
24
+
25
+ private
26
+
27
+ def string?(barcode)
28
+ raise ArgumentError, "Barcode must be a string: #{barcode.inspect}" unless barcode.is_a?(String)
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -18,7 +18,8 @@ module BerkeleyLibrary
18
18
  # ------------------------------------------------------------
19
19
  # Initializer
20
20
 
21
- # Initializes a new {BibNumber} from the specified string.
21
+ # Initializes a new {BibNumber} from the specified string. Note that a bib number
22
+ # may start with an upper-case B, but it will be converted to lower case.
22
23
  #
23
24
  # @param [String] bib_number The bib number, with or without check digit
24
25
  # @raise [ArgumentError] if the specified string is not an 8- or 9-digit bib number,
@@ -51,9 +52,7 @@ module BerkeleyLibrary
51
52
  #
52
53
  # @return [String] the SRU query value
53
54
  def sru_query_value
54
- # TODO: stop hard-coding `UCB-`
55
- other_system_number = "UCB-#{self}-#{Config.alma_institution_code.downcase}"
56
- "alma.other_system_number=#{other_system_number}"
55
+ "alma.local_field_996=#{full_bib}"
57
56
  end
58
57
 
59
58
  # ------------------------------------------------------------
@@ -4,7 +4,7 @@ module BerkeleyLibrary
4
4
  module Alma
5
5
  module Constants
6
6
  # 'x' represents a calculated check digit of 10; 'a' is a wildcard
7
- MILLENNIUM_RECORD_RE = /^b(?<digits>[0-9]{8})(?<check>[0-9ax])?$/.freeze
7
+ MILLENNIUM_RECORD_RE = /^[Bb](?<digits>[0-9]{8})(?<check>[0-9ax])?$/.freeze
8
8
 
9
9
  # '99' is the Alma prefix for a Metadata Management System ID
10
10
  # see https://knowledge.exlibrisgroup.com/Alma/Product_Documentation/010Alma_Online_Help_(English)/Metadata_Management/005Introduction_to_Metadata_Management/020Record_Numbers
@@ -7,7 +7,7 @@ module BerkeleyLibrary
7
7
  SUMMARY = 'Alma/Primo utilities for the UC Berkeley Library'.freeze
8
8
  DESCRIPTION = 'A gem providing Alma/Primo-related utility code for the UC Berkeley Library'.freeze
9
9
  LICENSE = 'MIT'.freeze
10
- VERSION = '0.0.2'.freeze
10
+ VERSION = '0.0.5'.freeze
11
11
  HOMEPAGE = 'https://github.com/BerkeleyLibrary/alma'.freeze
12
12
  end
13
13
  end
@@ -2,6 +2,7 @@ require 'berkeley_library/logging'
2
2
  require 'berkeley_library/marc'
3
3
  require 'berkeley_library/util/uris'
4
4
  require 'berkeley_library/alma/constants'
5
+ require 'berkeley_library/alma/sru'
5
6
 
6
7
  module BerkeleyLibrary
7
8
  module Alma
@@ -22,6 +23,9 @@ module BerkeleyLibrary
22
23
  # also accepts a {RecordId} and simply returns it, so it can be used in
23
24
  # situations where it may not be clear whether the ID has already been parsed.
24
25
  #
26
+ # **Note:** Use the {BarCode} class for barcodes, which don't have a consistent
27
+ # format and hence can't be auto-detected.
28
+ #
25
29
  # @param id [String, RecordId] the ID to parse
26
30
  # @return [RecordId, nil] an {MMSID} or {BibNumber}, depending on the type of ID,
27
31
  # or `nil` if the specified `id` is neither an MMS ID nor a bib number
@@ -44,13 +48,7 @@ module BerkeleyLibrary
44
48
  #
45
49
  # @return [URI] the MARC URI
46
50
  def marc_uri
47
- query_string = URI.encode_www_form(
48
- 'version' => '1.2',
49
- 'operation' => 'searchRetrieve',
50
- 'query' => sru_query_value
51
- )
52
-
53
- URIs.append(Config.alma_sru_base_uri, '?', query_string)
51
+ SRU.sru_query_uri(sru_query_value)
54
52
  end
55
53
 
56
54
  # Makes an SRU query for this record and returns a MARC record, or nil if the
@@ -62,8 +60,12 @@ module BerkeleyLibrary
62
60
  # @return [MARC::Record, nil] the MARC record
63
61
  # rubocop:disable Naming/AccessorMethodName
64
62
  def get_marc_record
65
- marc_xml = get_marc_xml
66
- logger.warn("GET #{marc_uri} did not return a MARC record") unless (marc_record = parse_marc_xml(marc_xml))
63
+ unless (marc_reader = SRU.marc_records_for(sru_query_value))
64
+ logger.warn("GET #{marc_uri} did not return a MARC record")
65
+ return nil
66
+ end
67
+
68
+ logger.warn("GET #{marc_uri} did not return a MARC record") unless (marc_record = marc_reader.first)
67
69
  marc_record
68
70
  end
69
71
  # rubocop:enable Naming/AccessorMethodName
@@ -74,10 +76,7 @@ module BerkeleyLibrary
74
76
  # @return [String, nil] the SRU query response body, or nil in the event of an error.
75
77
  # rubocop:disable Naming/AccessorMethodName
76
78
  def get_marc_xml
77
- URIs.get(marc_uri, headers: { user_agent: DEFAULT_USER_AGENT })
78
- rescue RestClient::Exception => e
79
- logger.warn("GET #{marc_uri} failed", e)
80
- nil
79
+ SRU.make_sru_query(sru_query_value)
81
80
  end
82
81
  # rubocop:enable Naming/AccessorMethodName
83
82
 
@@ -95,19 +94,6 @@ module BerkeleyLibrary
95
94
 
96
95
  to_s <=> other.to_s
97
96
  end
98
-
99
- # ------------------------------------------------------------
100
- # Private methods
101
-
102
- private
103
-
104
- def parse_marc_xml(xml)
105
- return unless xml
106
-
107
- input = StringIO.new(xml.scrub)
108
- reader = MARC::XMLReader.new(input)
109
- reader.first
110
- end
111
97
  end
112
98
  end
113
99
  end
@@ -0,0 +1,79 @@
1
+ require 'uri'
2
+ require 'berkeley_library/alma/config'
3
+ require 'berkeley_library/alma/constants'
4
+ require 'berkeley_library/alma/record_id'
5
+ require 'berkeley_library/util/uris'
6
+
7
+ module BerkeleyLibrary
8
+ module Alma
9
+ module SRU
10
+ include BerkeleyLibrary::Logging
11
+ include Constants
12
+
13
+ class << self
14
+ include SRU
15
+ end
16
+
17
+ # Given a list of record IDs, returns the MARC records for each ID (if found).
18
+ #
19
+ # @param record_ids [Array<String, RecordId>] the record IDs to look up
20
+ # @return [MARC::XMLReader, nil] a reader for the MARC records, or nil if
21
+ # the records could not be read
22
+ def get_marc_records(*record_ids)
23
+ # noinspection RubyMismatchedReturnType
24
+ parsed_ids = record_ids.filter_map { |id| RecordId.parse(id) }
25
+ raise ArgumentError, "Argument #{record_ids.inspect} contain no valid record IDs" if parsed_ids.empty?
26
+
27
+ sru_query_value = parsed_ids.map(&:sru_query_value).join(' or ')
28
+ SRU.marc_records_for(sru_query_value)
29
+ end
30
+
31
+ # Returns a URI for retrieving records for the specified query
32
+ # via SRU. Requires {Config#alma_sru_base_uri} to be set.
33
+ #
34
+ # @return [URI] the MARC URI
35
+ def sru_query_uri(sru_query_value)
36
+ query_string = URI.encode_www_form(
37
+ 'version' => '1.2',
38
+ 'operation' => 'searchRetrieve',
39
+ 'query' => sru_query_value
40
+ )
41
+
42
+ Util::URIs.append(Config.alma_sru_base_uri, '?', query_string)
43
+ end
44
+
45
+ # Makes an SRU query for the specified query value and returns the query response
46
+ # as a string.
47
+ #
48
+ # @param query_value [String] the value of the SRU query parameter
49
+ # @return [String, nil] the SRU query response body, or nil in the event of an error.
50
+ def make_sru_query(query_value)
51
+ uri = sru_query_uri(query_value)
52
+ do_get(uri)
53
+ end
54
+
55
+ # Makes an SRU query for the specified query value and returns the query response
56
+ # as MARC records.
57
+ #
58
+ # @param query_value [String] the value of the SRU query parameter
59
+ # @return [MARC::XMLReader, nil] a reader for the MARC records, or nil if
60
+ # the records could not be read
61
+ def marc_records_for(query_value)
62
+ return unless (xml = make_sru_query(query_value))
63
+
64
+ input = StringIO.new(xml.scrub)
65
+ MARC::XMLReader.new(input, parser: 'nokogiri')
66
+ end
67
+
68
+ private
69
+
70
+ def do_get(uri)
71
+ # TODO: can we get the XML as an IO rather than as a string?
72
+ Util::URIs.get(uri, headers: { user_agent: DEFAULT_USER_AGENT })
73
+ rescue RestClient::Exception => e
74
+ logger.warn("GET #{uri} failed", e)
75
+ nil
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,299 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?><searchRetrieveResponse xmlns="http://www.loc.gov/zing/srw/">
2
+ <version>1.2</version>
3
+ <numberOfRecords>1</numberOfRecords>
4
+ <records>
5
+ <record>
6
+ <recordSchema>marcxml</recordSchema>
7
+ <recordPacking>xml</recordPacking>
8
+ <recordData>
9
+ <record xmlns="http://www.loc.gov/MARC21/slim">
10
+ <leader>03480cgm a2200781Mi 4500</leader>
11
+ <controlfield tag="001">991039354509706532</controlfield>
12
+ <controlfield tag="005">20210615124013.0</controlfield>
13
+ <controlfield tag="007">vd cvaizs</controlfield>
14
+ <controlfield tag="008">050601p19971996quc135 vleng d</controlfield>
15
+ <datafield ind1="1" ind2=" " tag="024">
16
+ <subfield code="a">774212007033</subfield>
17
+ </datafield>
18
+ <datafield ind1="4" ind2="2" tag="028">
19
+ <subfield code="a">20070</subfield>
20
+ <subfield code="b">Seville Pictures</subfield>
21
+ </datafield>
22
+ <datafield ind1=" " ind2=" " tag="035">
23
+ <subfield code="a">(OCoLC)613118288</subfield>
24
+ <subfield code="z">(OCoLC)800045975</subfield>
25
+ <subfield code="z">(OCoLC)811619021</subfield>
26
+ </datafield>
27
+ <datafield ind1=" " ind2=" " tag="035">
28
+ <subfield code="a">(CLU)8266514-ucladb-Voyager</subfield>
29
+ </datafield>
30
+ <datafield ind1=" " ind2=" " tag="035">
31
+ <subfield code="a">(EXLNZ-01UCS_NETWORK)9912724001006531</subfield>
32
+ </datafield>
33
+ <datafield ind1=" " ind2=" " tag="040">
34
+ <subfield code="a">W2U</subfield>
35
+ <subfield code="b">eng</subfield>
36
+ <subfield code="c">W2U</subfield>
37
+ <subfield code="d">OCLCF</subfield>
38
+ <subfield code="d">OCLCO</subfield>
39
+ <subfield code="d">TOH</subfield>
40
+ <subfield code="d">GK8</subfield>
41
+ <subfield code="d">OCL</subfield>
42
+ <subfield code="d">OCLCQ</subfield>
43
+ <subfield code="d">OCLCO</subfield>
44
+ <subfield code="e">rda</subfield>
45
+ <subfield code="d">CDS</subfield>
46
+ <subfield code="d">OCLCA</subfield>
47
+ </datafield>
48
+ <datafield ind1="1" ind2=" " tag="041">
49
+ <subfield code="a">eng</subfield>
50
+ <subfield code="a">fre</subfield>
51
+ </datafield>
52
+ <datafield ind1=" " ind2=" " tag="043">
53
+ <subfield code="a">n-us---</subfield>
54
+ </datafield>
55
+ <datafield ind1=" " ind2=" " tag="044">
56
+ <subfield code="a">quc</subfield>
57
+ <subfield code="a">xxu</subfield>
58
+ <subfield code="a">fr</subfield>
59
+ </datafield>
60
+ <datafield ind1=" " ind2=" " tag="046">
61
+ <subfield code="k">1996</subfield>
62
+ </datafield>
63
+ <datafield ind1="1" ind2="4" tag="050">
64
+ <subfield code="a">PN1997</subfield>
65
+ <subfield code="b">.L735 1997</subfield>
66
+ </datafield>
67
+ <datafield ind1=" " ind2=" " tag="090">
68
+ <subfield code="b">DVD 3916</subfield>
69
+ <subfield code="9">LOCAL</subfield>
70
+ </datafield>
71
+ <datafield ind1="0" ind2="0" tag="245">
72
+ <subfield code="a">Lost highway /</subfield>
73
+ <subfield code="c">October Films presents a CIBY 2000/Asymmetrical production ; a David Lynch film ; produced by Deepak Nayar, Tom Sternberg, Mahy Sweeney ; written by David Lynch &amp; Barry Gifford ; directed by David Lynch.</subfield>
74
+ </datafield>
75
+ <datafield ind1=" " ind2=" " tag="257">
76
+ <subfield code="a">United States</subfield>
77
+ <subfield code="a">France</subfield>
78
+ <subfield code="2">naf</subfield>
79
+ </datafield>
80
+ <datafield ind1=" " ind2="1" tag="264">
81
+ <subfield code="a">[Montreal] :</subfield>
82
+ <subfield code="b">Seville Pictures,</subfield>
83
+ <subfield code="c">1997.</subfield>
84
+ </datafield>
85
+ <datafield ind1=" " ind2=" " tag="300">
86
+ <subfield code="a">1 videodisc (135 min.) :</subfield>
87
+ <subfield code="b">sound, color ;</subfield>
88
+ <subfield code="c">4 3/4 in.</subfield>
89
+ </datafield>
90
+ <datafield ind1=" " ind2=" " tag="336">
91
+ <subfield code="a">two-dimensional moving image</subfield>
92
+ <subfield code="b">tdi</subfield>
93
+ <subfield code="2">rdacontent</subfield>
94
+ </datafield>
95
+ <datafield ind1=" " ind2=" " tag="337">
96
+ <subfield code="a">video</subfield>
97
+ <subfield code="b">v</subfield>
98
+ <subfield code="2">rdamedia</subfield>
99
+ </datafield>
100
+ <datafield ind1=" " ind2=" " tag="338">
101
+ <subfield code="a">videodisc</subfield>
102
+ <subfield code="b">vd</subfield>
103
+ <subfield code="2">rdacarrier</subfield>
104
+ </datafield>
105
+ <datafield ind1=" " ind2=" " tag="344">
106
+ <subfield code="a">digital</subfield>
107
+ <subfield code="b">optical</subfield>
108
+ <subfield code="g">stereo</subfield>
109
+ </datafield>
110
+ <datafield ind1=" " ind2=" " tag="346">
111
+ <subfield code="b">NTSC</subfield>
112
+ </datafield>
113
+ <datafield ind1=" " ind2=" " tag="347">
114
+ <subfield code="a">video file</subfield>
115
+ <subfield code="b">DVD video</subfield>
116
+ <subfield code="e">Region 1</subfield>
117
+ <subfield code="2">rda</subfield>
118
+ </datafield>
119
+ <datafield ind1=" " ind2=" " tag="380">
120
+ <subfield code="a">Motion picture</subfield>
121
+ </datafield>
122
+ <datafield ind1=" " ind2=" " tag="500">
123
+ <subfield code="a">Pan and scan version.</subfield>
124
+ </datafield>
125
+ <datafield ind1=" " ind2=" " tag="500">
126
+ <subfield code="a">Special features include: theatrical trailer; interactive menus; scene selection.</subfield>
127
+ </datafield>
128
+ <datafield ind1=" " ind2=" " tag="500">
129
+ <subfield code="a">Originally released as a motion picture in 1996.</subfield>
130
+ </datafield>
131
+ <datafield ind1=" " ind2=" " tag="508">
132
+ <subfield code="a">Music, Angelo Badalamenti ; editor, Mary Sweeney ; director of photography, Peter Deming.</subfield>
133
+ </datafield>
134
+ <datafield ind1="1" ind2=" " tag="511">
135
+ <subfield code="a">Bill Pullman, Patricia Arquette, Balthazar Getty, Robert Blake, Natasha Gregson Wagner, Gary Busey, Robert Loggia.</subfield>
136
+ </datafield>
137
+ <datafield ind1=" " ind2=" " tag="520">
138
+ <subfield code="a">A successful jazz musician whose marriage is on the rocks, a man in black who threatens to expose him, a young mechanic with links to a mobster, and the mobster's moll are all riders on the lost highway, trapped in their worlds of desire, destiny and unknown destination where the truth is always a short way further down the road.</subfield>
139
+ </datafield>
140
+ <datafield ind1="8" ind2=" " tag="521">
141
+ <subfield code="a">Canadian home video rating: R.</subfield>
142
+ </datafield>
143
+ <datafield ind1=" " ind2=" " tag="538">
144
+ <subfield code="a">DVD, Dolby stereo; Region 1; NTSC.</subfield>
145
+ </datafield>
146
+ <datafield ind1=" " ind2=" " tag="546">
147
+ <subfield code="a">In English or dubbed French.</subfield>
148
+ </datafield>
149
+ <datafield ind1=" " ind2="0" tag="650">
150
+ <subfield code="a">Jazz musicians</subfield>
151
+ <subfield code="z">United States</subfield>
152
+ <subfield code="v">Drama.</subfield>
153
+ </datafield>
154
+ <datafield ind1=" " ind2="7" tag="650">
155
+ <subfield code="a">Jazz musicians.</subfield>
156
+ <subfield code="2">fast</subfield>
157
+ <subfield code="0">(OCoLC)fst00982205</subfield>
158
+ </datafield>
159
+ <datafield ind1=" " ind2="7" tag="651">
160
+ <subfield code="a">United States.</subfield>
161
+ <subfield code="2">fast</subfield>
162
+ <subfield code="0">(OCoLC)fst01204155</subfield>
163
+ </datafield>
164
+ <datafield ind1=" " ind2="7" tag="655">
165
+ <subfield code="a">Drama.</subfield>
166
+ <subfield code="2">fast</subfield>
167
+ <subfield code="0">(OCoLC)fst01423879</subfield>
168
+ </datafield>
169
+ <datafield ind1=" " ind2="7" tag="655">
170
+ <subfield code="a">Feature films.</subfield>
171
+ <subfield code="2">fast</subfield>
172
+ <subfield code="0">(OCoLC)fst01710384</subfield>
173
+ </datafield>
174
+ <datafield ind1=" " ind2="7" tag="655">
175
+ <subfield code="a">Fiction films.</subfield>
176
+ <subfield code="2">fast</subfield>
177
+ <subfield code="0">(OCoLC)fst01710264</subfield>
178
+ </datafield>
179
+ <datafield ind1=" " ind2="7" tag="655">
180
+ <subfield code="a">Feature films.</subfield>
181
+ <subfield code="2">lcgft</subfield>
182
+ </datafield>
183
+ <datafield ind1=" " ind2="7" tag="655">
184
+ <subfield code="a">Fiction films.</subfield>
185
+ <subfield code="2">lcgft</subfield>
186
+ </datafield>
187
+ <datafield ind1="1" ind2=" " tag="700">
188
+ <subfield code="a">Pullman, Bill,</subfield>
189
+ <subfield code="e">actor.</subfield>
190
+ </datafield>
191
+ <datafield ind1="1" ind2=" " tag="700">
192
+ <subfield code="a">Arquette, Patricia,</subfield>
193
+ <subfield code="d">1968-</subfield>
194
+ <subfield code="e">actor.</subfield>
195
+ </datafield>
196
+ <datafield ind1="1" ind2=" " tag="700">
197
+ <subfield code="a">Getty, Balthazar,</subfield>
198
+ <subfield code="e">actor.</subfield>
199
+ </datafield>
200
+ <datafield ind1="1" ind2=" " tag="700">
201
+ <subfield code="a">Blake, Robert,</subfield>
202
+ <subfield code="d">1933-</subfield>
203
+ <subfield code="e">actor.</subfield>
204
+ </datafield>
205
+ <datafield ind1="1" ind2=" " tag="700">
206
+ <subfield code="a">Wagner, Natasha Gregson,</subfield>
207
+ <subfield code="d">1970-</subfield>
208
+ <subfield code="e">actor.</subfield>
209
+ </datafield>
210
+ <datafield ind1="1" ind2=" " tag="700">
211
+ <subfield code="a">Busey, Gary,</subfield>
212
+ <subfield code="e">actor.</subfield>
213
+ </datafield>
214
+ <datafield ind1="1" ind2=" " tag="700">
215
+ <subfield code="a">Loggia, Robert,</subfield>
216
+ <subfield code="e">actor.</subfield>
217
+ </datafield>
218
+ <datafield ind1="1" ind2=" " tag="700">
219
+ <subfield code="a">Nayar, Deepak.</subfield>
220
+ <subfield code="e">film producer.</subfield>
221
+ </datafield>
222
+ <datafield ind1="1" ind2=" " tag="700">
223
+ <subfield code="a">Sternberg, Tom.</subfield>
224
+ <subfield code="e">film producer.</subfield>
225
+ </datafield>
226
+ <datafield ind1="1" ind2=" " tag="700">
227
+ <subfield code="a">Sweeney, Mahy.</subfield>
228
+ <subfield code="e">film produer</subfield>
229
+ </datafield>
230
+ <datafield ind1="1" ind2=" " tag="700">
231
+ <subfield code="a">Lynch, David,</subfield>
232
+ <subfield code="d">1946-</subfield>
233
+ <subfield code="e">screenwriter,</subfield>
234
+ <subfield code="e">director.</subfield>
235
+ </datafield>
236
+ <datafield ind1="1" ind2=" " tag="700">
237
+ <subfield code="a">Gifford, Barry,</subfield>
238
+ <subfield code="d">1946-</subfield>
239
+ <subfield code="e">screenwriter.</subfield>
240
+ </datafield>
241
+ <datafield ind1="2" ind2=" " tag="710">
242
+ <subfield code="a">Séville Pictures.</subfield>
243
+ </datafield>
244
+ <datafield ind1="2" ind2=" " tag="710">
245
+ <subfield code="a">Asymmetrical (Firm)</subfield>
246
+ </datafield>
247
+ <datafield ind1="2" ind2=" " tag="710">
248
+ <subfield code="a">CIBY 2000 (Firm)</subfield>
249
+ </datafield>
250
+ <datafield ind1="2" ind2=" " tag="710">
251
+ <subfield code="a">October Films.</subfield>
252
+ </datafield>
253
+ <datafield ind1="4" ind2="2" tag="956">
254
+ <subfield code="z">Credits from Internet Movie Database</subfield>
255
+ <subfield code="u">http://www.imdb.com/title/tt0116922/</subfield>
256
+ <subfield code="9">LOCAL</subfield>
257
+ </datafield>
258
+ <datafield ind1=" " ind2=" " tag="968">
259
+ <subfield code="a">Reclvl: f Addate: 050601 Addid: SMI Moddate: 090214 Modid: MRC</subfield>
260
+ <subfield code="9">LOCAL</subfield>
261
+ </datafield>
262
+ <datafield ind1=" " ind2=" " tag="996">
263
+ <subfield code="e">GLADN151847889</subfield>
264
+ <subfield code="9">LOCAL</subfield>
265
+ </datafield>
266
+ <datafield ind1=" " ind2=" " tag="996">
267
+ <subfield code="a">b111084751</subfield>
268
+ <subfield code="b">Updated: 06-14-21</subfield>
269
+ <subfield code="c">Created: 03-17-09</subfield>
270
+ <subfield code="d">CatDate: 06-01-05</subfield>
271
+ <subfield code="9">LOCAL</subfield>
272
+ </datafield>
273
+ <datafield ind1=" " ind2=" " tag="AVA">
274
+ <subfield code="0">991039354509706532</subfield>
275
+ <subfield code="8">22725145850006532</subfield>
276
+ <subfield code="a">01UCS_BER</subfield>
277
+ <subfield code="b">MRC</subfield>
278
+ <subfield code="c">Media Resources Center</subfield>
279
+ <subfield code="d">DVD 3916</subfield>
280
+ <subfield code="e">available</subfield>
281
+ <subfield code="f">1</subfield>
282
+ <subfield code="g">0</subfield>
283
+ <subfield code="i">UCB_Campus</subfield>
284
+ <subfield code="j">mc</subfield>
285
+ <subfield code="k">8</subfield>
286
+ <subfield code="p">1</subfield>
287
+ <subfield code="q">Media Resources Center</subfield>
288
+ </datafield>
289
+ </record>
290
+ </recordData>
291
+ <recordIdentifier>991039354509706532</recordIdentifier>
292
+ <recordPosition>1</recordPosition>
293
+ </record>
294
+ </records>
295
+ <extraResponseData xmlns:xb="http://www.exlibris.com/repository/search/xmlbeans/">
296
+ <xb:exact>true</xb:exact>
297
+ <xb:responseDate>2022-02-10T12:40:32-0800</xb:responseDate>
298
+ </extraResponseData>
299
+ </searchRetrieveResponse>
@@ -0,0 +1,109 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?><searchRetrieveResponse xmlns="http://www.loc.gov/zing/srw/">
2
+ <version>1.2</version>
3
+ <numberOfRecords>2</numberOfRecords>
4
+ <records>
5
+ <record>
6
+ <recordSchema>marcxml</recordSchema>
7
+ <recordPacking>xml</recordPacking>
8
+ <recordData>
9
+ <record xmlns="http://www.loc.gov/MARC21/slim">
10
+ <leader>00307nam a2200085 a 4500</leader>
11
+ <controlfield tag="001">991005668359706532</controlfield>
12
+ <controlfield tag="008">^^</controlfield>
13
+ <datafield ind1=" " ind2=" " tag="035">
14
+ <subfield code="a">(CUY)UCB-b207766733-01ucs_ber</subfield>
15
+ </datafield>
16
+ <datafield ind1="1" ind2=" " tag="100">
17
+ <subfield code="a">Fukazawa, Yukio,</subfield>
18
+ <subfield code="d">1924-2017.</subfield>
19
+ </datafield>
20
+ <datafield ind1="1" ind2="0" tag="245">
21
+ <subfield code="a">Blue Doors</subfield>
22
+ <subfield code="h">[graphic]</subfield>
23
+ </datafield>
24
+ <datafield ind1=" " ind2=" " tag="300">
25
+ <subfield code="a">1 relief etching and aquatint :</subfield>
26
+ <subfield code="b">col.</subfield>
27
+ </datafield>
28
+ <datafield ind1=" " ind2=" " tag="996">
29
+ <subfield code="a">b207766733</subfield>
30
+ <subfield code="b">Updated: 01-09-19</subfield>
31
+ <subfield code="c">Created: 11-01-13</subfield>
32
+ <subfield code="d">CatDate: - -</subfield>
33
+ <subfield code="9">LOCAL</subfield>
34
+ </datafield>
35
+ <datafield ind1=" " ind2=" " tag="AVA">
36
+ <subfield code="0">991005668359706532</subfield>
37
+ <subfield code="8">22928381510006532</subfield>
38
+ <subfield code="a">01UCS_BER</subfield>
39
+ <subfield code="b">MORR</subfield>
40
+ <subfield code="c">Morrison</subfield>
41
+ <subfield code="e">unavailable</subfield>
42
+ <subfield code="f">1</subfield>
43
+ <subfield code="g">1</subfield>
44
+ <subfield code="i">UCB_Campus</subfield>
45
+ <subfield code="j">mo</subfield>
46
+ <subfield code="k">#</subfield>
47
+ <subfield code="p">1</subfield>
48
+ <subfield code="q">Morrison Library</subfield>
49
+ </datafield>
50
+ </record>
51
+ </recordData>
52
+ <recordIdentifier>991005668359706532</recordIdentifier>
53
+ <recordPosition>1</recordPosition>
54
+ </record>
55
+ <record>
56
+ <recordSchema>marcxml</recordSchema>
57
+ <recordPacking>xml</recordPacking>
58
+ <recordData>
59
+ <record xmlns="http://www.loc.gov/MARC21/slim">
60
+ <leader>00273nam a2200085 a 4500</leader>
61
+ <controlfield tag="001">991005668209706532</controlfield>
62
+ <controlfield tag="008">^^</controlfield>
63
+ <datafield ind1=" " ind2=" " tag="035">
64
+ <subfield code="a">(CUY)UCB-b207766708-01ucs_ber</subfield>
65
+ </datafield>
66
+ <datafield ind1="1" ind2=" " tag="100">
67
+ <subfield code="a">Beutlich, Tadek.</subfield>
68
+ </datafield>
69
+ <datafield ind1="1" ind2="0" tag="245">
70
+ <subfield code="a">River</subfield>
71
+ <subfield code="h">[graphic]</subfield>
72
+ </datafield>
73
+ <datafield ind1=" " ind2=" " tag="300">
74
+ <subfield code="a">1 lithograph :</subfield>
75
+ <subfield code="b">col.</subfield>
76
+ </datafield>
77
+ <datafield ind1=" " ind2=" " tag="996">
78
+ <subfield code="a">b207766708</subfield>
79
+ <subfield code="b">Updated: 11-01-13</subfield>
80
+ <subfield code="c">Created: 11-01-13</subfield>
81
+ <subfield code="d">CatDate: - -</subfield>
82
+ <subfield code="9">LOCAL</subfield>
83
+ </datafield>
84
+ <datafield ind1=" " ind2=" " tag="AVA">
85
+ <subfield code="0">991005668209706532</subfield>
86
+ <subfield code="8">22928389410006532</subfield>
87
+ <subfield code="a">01UCS_BER</subfield>
88
+ <subfield code="b">MORR</subfield>
89
+ <subfield code="c">Morrison</subfield>
90
+ <subfield code="e">available</subfield>
91
+ <subfield code="f">1</subfield>
92
+ <subfield code="g">0</subfield>
93
+ <subfield code="i">UCB_Campus</subfield>
94
+ <subfield code="j">mo</subfield>
95
+ <subfield code="k">#</subfield>
96
+ <subfield code="p">1</subfield>
97
+ <subfield code="q">Morrison Library</subfield>
98
+ </datafield>
99
+ </record>
100
+ </recordData>
101
+ <recordIdentifier>991005668209706532</recordIdentifier>
102
+ <recordPosition>2</recordPosition>
103
+ </record>
104
+ </records>
105
+ <extraResponseData xmlns:xb="http://www.exlibris.com/repository/search/xmlbeans/">
106
+ <xb:exact>true</xb:exact>
107
+ <xb:responseDate>2022-05-04T14:31:01-0700</xb:responseDate>
108
+ </extraResponseData>
109
+ </searchRetrieveResponse>
File without changes
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module Alma
5
+ describe BarCode do
6
+ before(:each) do
7
+ BerkeleyLibrary::Alma.configure do
8
+ Config.alma_sru_host = 'berkeley.alma.exlibrisgroup.com'
9
+ Config.alma_institution_code = '01UCS_BER'
10
+ Config.alma_primo_host = 'search.library.berkeley.edu'
11
+ Config.alma_permalink_key = 'iqob43'
12
+ end
13
+ end
14
+
15
+ after(:each) do
16
+ BerkeleyLibrary::Alma::Config.send(:clear!)
17
+ end
18
+
19
+ describe :new do
20
+ bad_barcode = 1234
21
+ it "raises an error for input that's not an string" do
22
+ expect { BarCode.new(bad_barcode) }.to raise_error(ArgumentError), "#{bad_barcode}: No error raised for #{bad_barcode}"
23
+ end
24
+ end
25
+
26
+ describe :marc_record do
27
+ it 'returns the MARC record' do
28
+ barcode_id = 'C084093187'
29
+ stub_sru_request('alma.barcode', barcode_id)
30
+
31
+ barcode = BerkeleyLibrary::Alma::BarCode.new(barcode_id)
32
+ marc_record = barcode.get_marc_record
33
+ expect(marc_record).not_to be_nil
34
+ end
35
+
36
+ it 'returns nil for an empty response' do
37
+ empty_sru_response = <<~XML.strip
38
+ <?xml version="1.0" encoding="UTF-8"?>
39
+ <searchRetrieveResponse>
40
+ <version>1.2</version>
41
+ <numberOfRecords>0</numberOfRecords>
42
+ <records/>
43
+ </searchRetrieveResponse>
44
+ XML
45
+
46
+ barcode_id = '991054360089706532doesntexist'
47
+ sru_url = sru_url_for('alma.barcode', barcode_id)
48
+ stub_request(:get, sru_url).to_return(status: 200, body: empty_sru_response)
49
+
50
+ barcode = BarCode.new(barcode_id)
51
+ marc_record = barcode.get_marc_record
52
+ expect(marc_record).to be_nil
53
+ end
54
+
55
+ it 'returns nil for an HTTP error response' do
56
+ barcode_id = '991054360089706532934j3h'
57
+ sru_url = sru_url_for('alma.barcode', barcode_id)
58
+ stub_request(:get, sru_url).to_return(status: 404)
59
+
60
+ barcode = BarCode.new(barcode_id)
61
+ marc_record = barcode.get_marc_record
62
+ expect(marc_record).to be_nil
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -40,6 +40,14 @@ module BerkeleyLibrary
40
40
  end
41
41
  end
42
42
 
43
+ it 'accepts an upper-case B, but treats it as lower case' do
44
+ raw_bib = 'B11082434'
45
+ expected_bib = 'b110824349'
46
+ bib_number = BibNumber.new(raw_bib)
47
+ expect(bib_number.full_bib).to eq(expected_bib)
48
+ expect(bib_number.to_s).to eq(expected_bib)
49
+ end
50
+
43
51
  it 'produces the expected check digit' do
44
52
  aggregate_failures do
45
53
  expected_check_digits_by_bib.each do |bib_expected, cd_expected|
@@ -82,7 +90,8 @@ module BerkeleyLibrary
82
90
  describe :marc_record do
83
91
  it 'returns the MARC record' do
84
92
  raw_bib_number = 'b11082434'
85
- stub_sru_request(raw_bib_number)
93
+ full_bib_number = 'b110824349'
94
+ stub_sru_request('alma.local_field_996', full_bib_number)
86
95
 
87
96
  bib_number = BibNumber.new(raw_bib_number)
88
97
  marc_record = bib_number.get_marc_record
@@ -58,7 +58,7 @@ module BerkeleyLibrary
58
58
  describe :marc_record do
59
59
  it 'returns the MARC record' do
60
60
  raw_mms_id = '991054360089706532'
61
- stub_sru_request(raw_mms_id)
61
+ stub_sru_request('alma.mms_id', raw_mms_id)
62
62
 
63
63
  mms_id = MMSID.new(raw_mms_id)
64
64
  marc_record = mms_id.get_marc_record
@@ -77,7 +77,7 @@ module BerkeleyLibrary
77
77
  XML
78
78
 
79
79
  raw_mms_id = '991054360089706532'
80
- sru_url = sru_url_for(raw_mms_id)
80
+ sru_url = sru_url_for('alma.mms_id', raw_mms_id)
81
81
  stub_request(:get, sru_url).to_return(status: 200, body: empty_sru_response)
82
82
 
83
83
  mms_id = MMSID.new(raw_mms_id)
@@ -87,7 +87,7 @@ module BerkeleyLibrary
87
87
 
88
88
  it 'returns nil for an HTTP error response' do
89
89
  raw_mms_id = '991054360089706532'
90
- sru_url = sru_url_for(raw_mms_id)
90
+ sru_url = sru_url_for('alma.mms_id', raw_mms_id)
91
91
  stub_request(:get, sru_url).to_return(status: 404)
92
92
 
93
93
  mms_id = MMSID.new(raw_mms_id)
@@ -36,6 +36,26 @@ module BerkeleyLibrary
36
36
  end
37
37
  end
38
38
  end
39
+
40
+ describe 'SRU methods' do
41
+ before { Config.default! }
42
+ after { Config.send(:clear!) }
43
+
44
+ describe :get_marc_xml do
45
+ let(:record_id) { RecordId.parse('991054360089706532') }
46
+ let(:expected_body) { File.read('spec/data/991054360089706532-sru.xml') }
47
+
48
+ before do
49
+ sru_query_uri = record_id.marc_uri
50
+ stub_request(:get, sru_query_uri).to_return(body: expected_body)
51
+ end
52
+
53
+ it 'returns the MARC XML' do
54
+ marc_xml = record_id.get_marc_xml
55
+ expect(marc_xml).to eq(expected_body)
56
+ end
57
+ end
58
+ end
39
59
  end
40
60
  end
41
61
  end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module Alma
5
+ describe SRU do
6
+ before { Config.default! }
7
+ after { Config.send(:clear!) }
8
+
9
+ describe :get_marc_records do
10
+ let(:mms_ids) { %w[991005668209706532 991005668359706532] }
11
+
12
+ before do
13
+ sru_query_value = mms_ids.map { |id| RecordId.parse(id) }.map(&:sru_query_value).join(' or ')
14
+ sru_query_uri = SRU.sru_query_uri(sru_query_value)
15
+ stub_request(:get, sru_query_uri).to_return(body: File.read('spec/data/availability-sru.xml'))
16
+ end
17
+
18
+ it 'returns the MARC records' do
19
+ reader = SRU.get_marc_records(*mms_ids)
20
+ expect(reader).to be_a(MARC::XMLReader)
21
+
22
+ marc_records = reader.to_a
23
+ expect(marc_records.size).to eq(mms_ids.size)
24
+ expect(marc_records.map(&:record_id)).to contain_exactly(*mms_ids)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
data/spec/spec_helper.rb CHANGED
@@ -31,26 +31,17 @@ require 'berkeley_library/alma'
31
31
  # ------------------------------------------------------------
32
32
  # Utility methods
33
33
 
34
- def sru_url_for(record_id)
35
- sru_url_base = 'https://berkeley.alma.exlibrisgroup.com/view/sru/01UCS_BER?version=1.2&operation=searchRetrieve&query='
36
-
37
- if BerkeleyLibrary::Alma::Constants::ALMA_RECORD_RE =~ record_id
38
- "#{sru_url_base}alma.mms_id%3D#{record_id}"
39
- elsif BerkeleyLibrary::Alma::Constants::MILLENNIUM_RECORD_RE =~ record_id
40
- full_bib_number = BerkeleyLibrary::Alma::BibNumber.new(record_id).to_s
41
- "#{sru_url_base}alma.other_system_number%3DUCB-#{full_bib_number}-01ucs_ber"
42
- else
43
- raise ArgumentError, "Unknown record ID type: #{record_id}"
44
- end
34
+ def sru_url_for(index, value)
35
+ "https://berkeley.alma.exlibrisgroup.com/view/sru/01UCS_BER?version=1.2&operation=searchRetrieve&query=#{index}%3D#{value}"
45
36
  end
46
37
 
47
38
  def sru_data_path_for(record_id)
48
39
  "spec/data/#{record_id}-sru.xml"
49
40
  end
50
41
 
51
- def stub_sru_request(record_id)
52
- sru_url = sru_url_for(record_id)
53
- marc_xml_path = sru_data_path_for(record_id)
42
+ def stub_sru_request(index, value)
43
+ sru_url_ = sru_url_for(index, value)
44
+ marc_xml_path = sru_data_path_for(value)
54
45
 
55
- stub_request(:get, sru_url).to_return(status: 200, body: File.read(marc_xml_path))
46
+ stub_request(:get, sru_url_).to_return(status: 200, body: File.read(marc_xml_path))
56
47
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: berkeley_library-alma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Moles
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-03 00:00:00.000000000 Z
11
+ date: 2022-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: berkeley_library-logging
@@ -269,12 +269,14 @@ files:
269
269
  - bin/alma-mms-lookup
270
270
  - docker-compose.yml
271
271
  - lib/berkeley_library/alma.rb
272
+ - lib/berkeley_library/alma/barcode.rb
272
273
  - lib/berkeley_library/alma/bib_number.rb
273
274
  - lib/berkeley_library/alma/config.rb
274
275
  - lib/berkeley_library/alma/constants.rb
275
276
  - lib/berkeley_library/alma/mms_id.rb
276
277
  - lib/berkeley_library/alma/module_info.rb
277
278
  - lib/berkeley_library/alma/record_id.rb
279
+ - lib/berkeley_library/alma/sru.rb
278
280
  - rakelib/bundle.rake
279
281
  - rakelib/coverage.rake
280
282
  - rakelib/gem.rake
@@ -282,12 +284,16 @@ files:
282
284
  - rakelib/spec.rake
283
285
  - spec/.rubocop.yml
284
286
  - spec/data/991054360089706532-sru.xml
285
- - spec/data/b11082434-sru.xml
287
+ - spec/data/C084093187-sru.xml
288
+ - spec/data/availability-sru.xml
289
+ - spec/data/b110824349-sru.xml
286
290
  - spec/data/bibs_with_check_digits.txt
291
+ - spec/lib/berkeley_library/alma/barcode_spec.rb
287
292
  - spec/lib/berkeley_library/alma/bib_number_spec.rb
288
293
  - spec/lib/berkeley_library/alma/config_spec.rb
289
294
  - spec/lib/berkeley_library/alma/mms_id_spec.rb
290
295
  - spec/lib/berkeley_library/alma/record_id_spec.rb
296
+ - spec/lib/berkeley_library/alma/sru_spec.rb
291
297
  - spec/spec_helper.rb
292
298
  homepage: https://github.com/BerkeleyLibrary/alma
293
299
  licenses:
@@ -315,10 +321,14 @@ summary: Alma/Primo utilities for the UC Berkeley Library
315
321
  test_files:
316
322
  - spec/.rubocop.yml
317
323
  - spec/data/991054360089706532-sru.xml
318
- - spec/data/b11082434-sru.xml
324
+ - spec/data/C084093187-sru.xml
325
+ - spec/data/availability-sru.xml
326
+ - spec/data/b110824349-sru.xml
319
327
  - spec/data/bibs_with_check_digits.txt
328
+ - spec/lib/berkeley_library/alma/barcode_spec.rb
320
329
  - spec/lib/berkeley_library/alma/bib_number_spec.rb
321
330
  - spec/lib/berkeley_library/alma/config_spec.rb
322
331
  - spec/lib/berkeley_library/alma/mms_id_spec.rb
323
332
  - spec/lib/berkeley_library/alma/record_id_spec.rb
333
+ - spec/lib/berkeley_library/alma/sru_spec.rb
324
334
  - spec/spec_helper.rb