berkeley_library-alma 0.0.2 → 0.0.5

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