evergreen-ils 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d4a950c35117be9bb820cc29a6c51a7b6d617182cea54d5fc83449dd02654e13
4
+ data.tar.gz: 44124d0b23d86d2da8d60a5e169ead1d23ab98238cffd16e1be5dbb149350971
5
+ SHA512:
6
+ metadata.gz: 6085835b66450141c9a330cc7b98f8e8e9b61e6d31779bcb4f0e974864a0a8942c78c5dc493c42216f8b827ad6559ac35bf286b3cdfd0fab5568fe9b04a5a402
7
+ data.tar.gz: 5ce0821e80d4ce4dd4d08c39685311a98edbe98edca5859724e4d5d589f8f3cbcf27ba1f51fdee503426eb42c8d33abfd705c23ffd87220a73e06583a95d47e3
data/.reek.yml ADDED
@@ -0,0 +1,23 @@
1
+ # Auto generated by Reeks --todo flag
2
+ ---
3
+ detectors:
4
+ Attribute:
5
+ exclude:
6
+ - Evergreen::Configuration#default_password
7
+ - Evergreen::Configuration#default_username
8
+ - Evergreen::Configuration#host
9
+ - Evergreen::Configuration#read_only
10
+ FeatureEnvy:
11
+ exclude:
12
+ - Evergreen::Configuration#field_is_empty
13
+ LongParameterList:
14
+ exclude:
15
+ - Evergreen::IDL::IDLSaxHandler#start_element
16
+ NilCheck:
17
+ exclude:
18
+ - Evergreen::Configuration#field_is_empty
19
+ UtilityFunction:
20
+ exclude:
21
+ - Evergreen#initialize
22
+ exclude_paths:
23
+ - spec
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,12 @@
1
+ require:
2
+ - rubocop-performance
3
+ - rubocop-rspec
4
+ AllCops:
5
+ TargetRubyVersion: 3.0
6
+ NewCops: enable
7
+ Exclude:
8
+ - 'sig/**/*'
9
+ - 'vendor/**/*'
10
+ SuggestExtensions: false
11
+ RSpec/ExampleLength:
12
+ Max: 10
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.2.1
data/Gemfile ADDED
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in evergreen-ils.gemspec
6
+ gemspec
7
+
8
+ gem 'rake'
9
+
10
+ group :test do
11
+ gem 'rspec'
12
+ gem 'webmock'
13
+ end
14
+
15
+ group :check do
16
+ gem 'flay'
17
+ gem 'flog'
18
+ gem 'rbs'
19
+ gem 'reek'
20
+ gem 'rubocop', require: false
21
+ gem 'rubocop-performance', require: false
22
+ gem 'rubocop-rspec', require: false
23
+ gem 'steep'
24
+ end
25
+
26
+ group :development do
27
+ gem 'debug'
28
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,180 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ evergreen-ils (0.2.0)
5
+ marc (>= 1.0.0, < 2.0)
6
+ rexml (>= 3.0.0, < 4.0)
7
+ zeitwerk (>= 2.0.0, < 3.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (7.0.6)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 1.6, < 2)
15
+ minitest (>= 5.1)
16
+ tzinfo (~> 2.0)
17
+ addressable (2.8.5)
18
+ public_suffix (>= 2.0.2, < 6.0)
19
+ ast (2.4.2)
20
+ concurrent-ruby (1.2.2)
21
+ crack (0.4.5)
22
+ rexml
23
+ csv (3.2.7)
24
+ debug (1.8.0)
25
+ irb (>= 1.5.0)
26
+ reline (>= 0.3.1)
27
+ diff-lcs (1.5.0)
28
+ erubi (1.12.0)
29
+ ffi (1.15.5)
30
+ ffi (1.15.5-java)
31
+ fileutils (1.7.1)
32
+ flay (2.13.1)
33
+ erubi (~> 1.10)
34
+ path_expander (~> 1.0)
35
+ ruby_parser (~> 3.0)
36
+ sexp_processor (~> 4.0)
37
+ flog (4.7.0)
38
+ path_expander (~> 1.0)
39
+ ruby_parser (~> 3.1, > 3.1.0)
40
+ sexp_processor (~> 4.8)
41
+ hashdiff (1.0.1)
42
+ i18n (1.14.1)
43
+ concurrent-ruby (~> 1.0)
44
+ io-console (0.6.0)
45
+ io-console (0.6.0-java)
46
+ irb (1.7.4)
47
+ reline (>= 0.3.6)
48
+ json (2.6.3)
49
+ json (2.6.3-java)
50
+ kwalify (0.7.2)
51
+ language_server-protocol (3.17.0.3)
52
+ listen (3.8.0)
53
+ rb-fsevent (~> 0.10, >= 0.10.3)
54
+ rb-inotify (~> 0.9, >= 0.9.10)
55
+ logger (1.5.3)
56
+ marc (1.2.0)
57
+ rexml
58
+ scrub_rb (>= 1.0.1, < 2)
59
+ unf
60
+ minitest (5.19.0)
61
+ parallel (1.23.0)
62
+ parser (3.2.2.3)
63
+ ast (~> 2.4.1)
64
+ racc
65
+ path_expander (1.1.1)
66
+ public_suffix (5.0.3)
67
+ racc (1.7.1)
68
+ racc (1.7.1-java)
69
+ rainbow (3.1.1)
70
+ rake (13.0.6)
71
+ rb-fsevent (0.11.2)
72
+ rb-inotify (0.10.1)
73
+ ffi (~> 1.0)
74
+ rbs (3.1.3)
75
+ reek (6.1.4)
76
+ kwalify (~> 0.7.0)
77
+ parser (~> 3.2.0)
78
+ rainbow (>= 2.0, < 4.0)
79
+ regexp_parser (2.8.1)
80
+ reline (0.3.7)
81
+ io-console (~> 0.5)
82
+ rexml (3.2.6)
83
+ rspec (3.12.0)
84
+ rspec-core (~> 3.12.0)
85
+ rspec-expectations (~> 3.12.0)
86
+ rspec-mocks (~> 3.12.0)
87
+ rspec-core (3.12.2)
88
+ rspec-support (~> 3.12.0)
89
+ rspec-expectations (3.12.3)
90
+ diff-lcs (>= 1.2.0, < 2.0)
91
+ rspec-support (~> 3.12.0)
92
+ rspec-mocks (3.12.6)
93
+ diff-lcs (>= 1.2.0, < 2.0)
94
+ rspec-support (~> 3.12.0)
95
+ rspec-support (3.12.1)
96
+ rubocop (1.55.1)
97
+ json (~> 2.3)
98
+ language_server-protocol (>= 3.17.0)
99
+ parallel (~> 1.10)
100
+ parser (>= 3.2.2.3)
101
+ rainbow (>= 2.2.2, < 4.0)
102
+ regexp_parser (>= 1.8, < 3.0)
103
+ rexml (>= 3.2.5, < 4.0)
104
+ rubocop-ast (>= 1.28.1, < 2.0)
105
+ ruby-progressbar (~> 1.7)
106
+ unicode-display_width (>= 2.4.0, < 3.0)
107
+ rubocop-ast (1.29.0)
108
+ parser (>= 3.2.1.0)
109
+ rubocop-capybara (2.18.0)
110
+ rubocop (~> 1.41)
111
+ rubocop-factory_bot (2.23.1)
112
+ rubocop (~> 1.33)
113
+ rubocop-performance (1.18.0)
114
+ rubocop (>= 1.7.0, < 2.0)
115
+ rubocop-ast (>= 0.4.0)
116
+ rubocop-rspec (2.23.1)
117
+ rubocop (~> 1.33)
118
+ rubocop-capybara (~> 2.17)
119
+ rubocop-factory_bot (~> 2.22)
120
+ ruby-progressbar (1.13.0)
121
+ ruby_parser (3.20.3)
122
+ sexp_processor (~> 4.16)
123
+ scrub_rb (1.0.1)
124
+ securerandom (0.2.2)
125
+ sexp_processor (4.17.0)
126
+ steep (1.5.2)
127
+ activesupport (>= 5.1)
128
+ concurrent-ruby (>= 1.1.10)
129
+ csv (>= 3.0.9)
130
+ fileutils (>= 1.1.0)
131
+ json (>= 2.1.0)
132
+ language_server-protocol (>= 3.15, < 4.0)
133
+ listen (~> 3.0)
134
+ logger (>= 1.3.0)
135
+ parser (>= 3.1)
136
+ rainbow (>= 2.2.2, < 4.0)
137
+ rbs (>= 3.1.0)
138
+ securerandom (>= 0.1)
139
+ strscan (>= 1.0.0)
140
+ terminal-table (>= 2, < 4)
141
+ strscan (3.0.6)
142
+ strscan (3.0.6-java)
143
+ terminal-table (3.0.2)
144
+ unicode-display_width (>= 1.1.1, < 3)
145
+ tzinfo (2.0.6)
146
+ concurrent-ruby (~> 1.0)
147
+ unf (0.1.4)
148
+ unf_ext
149
+ unf (0.1.4-java)
150
+ unf_ext (0.0.8.2)
151
+ unicode-display_width (2.4.2)
152
+ webmock (3.18.1)
153
+ addressable (>= 2.8.0)
154
+ crack (>= 0.3.2)
155
+ hashdiff (>= 0.4.0, < 2.0.0)
156
+ zeitwerk (2.6.11)
157
+
158
+ PLATFORMS
159
+ arm64-darwin-21
160
+ arm64-darwin-22
161
+ universal-java-11
162
+ x86_64-linux
163
+
164
+ DEPENDENCIES
165
+ debug
166
+ evergreen-ils!
167
+ flay
168
+ flog
169
+ rake
170
+ rbs
171
+ reek
172
+ rspec
173
+ rubocop
174
+ rubocop-performance
175
+ rubocop-rspec
176
+ steep
177
+ webmock
178
+
179
+ BUNDLED WITH
180
+ 2.4.6
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # Evergreen
2
+
3
+ A gem for interacting with [Evergreen ILS](https://evergreen-ils.org)
4
+
5
+ ## Installation
6
+
7
+ Install the gem and add to the application's Gemfile by executing:
8
+
9
+ $ bundle add evergreen-ils
10
+
11
+ If bundler is not being used to manage dependencies, install the gem by executing:
12
+
13
+ $ gem install evergreen-ils
14
+
15
+ ## Usage
16
+
17
+ ### Configuration
18
+
19
+ Configure your `Evergreen` object with a hostname, and any other relevant
20
+ options. If using Rails, you might create an Evergreen service:
21
+
22
+ ```
23
+ class EvergreenService
24
+ attr_reader :evergreen
25
+ def initialize
26
+ @evergreen ||= Evergreen.new do |config|
27
+ config.host = 'my.evergreen.server'
28
+ config.default_username = 'user1'
29
+ config.default_username = ENV['my_pass']
30
+ config.read_only = false
31
+ end
32
+ end
33
+ end
34
+ ```
35
+
36
+ You can then access those configurations in your app at
37
+ `service.evergreen.configuration.host`.
38
+
39
+ ### Retrieving objects
40
+
41
+ Once you have an object:
42
+
43
+ ```
44
+ Evergreen.new(host: 'my.evergreen.server') do |evergreen|
45
+ bib = evergreen.get_bib_record(123)
46
+ bib.to_marc
47
+ evergreen.get_item(345)
48
+ evergreen.get_call_number(2345)
49
+ end
50
+ ```
51
+
52
+
53
+ ## Development
54
+
55
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
56
+
57
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
58
+
59
+ ## Goals
60
+
61
+ The priorities here are:
62
+ * Thread safety
63
+ * Good documentation
64
+ * Good tests
65
+
66
+ ## Contributing
67
+
68
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sandbergja/evergreen-rb.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/Steepfile ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # D = Steep::Diagnostic
4
+ #
5
+ target :lib do
6
+ signature 'sig'
7
+
8
+ check 'lib'
9
+
10
+ library 'json', 'net-http', 'uri'
11
+
12
+ ignore 'lib/evergreen.rb'
13
+ ignore 'lib/evergreen/bib_record.rb'
14
+ ignore 'lib/evergreen/connection.rb'
15
+ ignore 'lib/evergreen/idl.rb'
16
+ ignore 'lib/evergreen/mixins'
17
+ ignore 'lib/opensrf/http_translator_request.rb'
18
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/evergreen/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'evergreen-ils'
7
+ spec.version = Evergreen::VERSION
8
+ spec.authors = ['Jane Sandberg']
9
+ spec.email = ['sandbergja@gmail.com']
10
+
11
+ spec.summary = 'Evergreen ILS'
12
+ spec.description = 'A gem for interacting with the Evergreen Integrated Library System'
13
+ spec.homepage = 'https://github.com/sandbergja/evergreen-rb'
14
+ spec.required_ruby_version = '>= 3.0.0'
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://github.com/sandbergja/evergreen-rb'
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(__dir__) do
22
+ `git ls-files -z`.split("\x0").reject do |f|
23
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
24
+ end
25
+ end
26
+ spec.bindir = 'exe'
27
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ['lib']
29
+
30
+ # Uncomment to register a new dependency of your gem
31
+ spec.add_dependency 'marc', '>= 1.0.0', '< 2.0'
32
+ spec.add_dependency 'rexml', '>= 3.0.0', '< 4.0'
33
+ spec.add_dependency 'zeitwerk', '>= 2.0.0', '< 3.0'
34
+
35
+ # For more information and examples about making a new gem, check out our
36
+ # guide at: https://bundler.io/guides/creating_gem.html
37
+ spec.metadata['rubygems_mfa_required'] = 'true'
38
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'marc'
4
+ require 'stringio'
5
+
6
+ class Evergreen
7
+ # A bibliographic record (title)
8
+ class BibRecord < IDLObject
9
+ include Mixins::AnonymousPcrud
10
+ def initialize(id:, configuration:, idl:)
11
+ @id = id
12
+ @configuration = configuration
13
+ super(idl)
14
+ end
15
+
16
+ # rubocop:disable Naming/MemoizedInstanceVariableName
17
+ def to_marc
18
+ @marc ||= MARC::XMLReader.new(StringIO.new(get('marc'))).first
19
+ end
20
+ # rubocop:enable Naming/MemoizedInstanceVariableName
21
+
22
+ def holdings
23
+ payload = OpenSRF::ClassAndData.new(klass: 'osrfMessage', data: {
24
+ 'method' => 'open-ils.cat.asset.copy_tree.global.retrieve',
25
+ 'params' => ['', @id.to_s]
26
+ }).to_h
27
+ OpenSRF::HTTPTranslatorRequest.new(payload: payload, configuration: @configuration,
28
+ service: 'open-ils.cat').response
29
+ end
30
+
31
+ def tcn
32
+ get 'tcn_value'
33
+ end
34
+
35
+ private
36
+
37
+ def idl_class
38
+ 'bre'
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evergreen
4
+ # Configuration for an instance of Evergreen
5
+ class Configuration
6
+ attr_reader :host, :default_username, :default_password, :read_only
7
+
8
+ def initialize(config_hash)
9
+ @read_only = true
10
+ config_hash.each_pair do |key, value|
11
+ instance_variable_set("@#{key}", value)
12
+ end
13
+ configuration_complete
14
+ end
15
+
16
+ private
17
+
18
+ def configuration_complete
19
+ check_required_fields
20
+ freeze
21
+ end
22
+
23
+ def check_required_fields
24
+ raise ArgumentError, 'you must supply a host' if field_is_empty :host
25
+ return unless !@read_only && (field_is_empty(:default_username) || field_is_empty(:default_password))
26
+
27
+ raise ArgumentError, 'you must supply default credentials unless you are in read-only mode'
28
+ end
29
+
30
+ def field_is_empty(field_name)
31
+ field_value = instance_variable_get("@#{field_name}")
32
+ field_value.nil? || field_value.empty?
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evergreen
4
+ # This class provides an interface for making
5
+ # requests to the Evergreen server
6
+ class Connection
7
+ include Mixins::RetrievalMethods
8
+ attr_reader :configuration
9
+
10
+ def initialize(configuration:)
11
+ @configuration = configuration
12
+ end
13
+
14
+ def close; end
15
+
16
+ def idl
17
+ @idl ||= IDL.new(@configuration)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open-uri'
4
+ require 'rexml/parsers/sax2parser'
5
+ require 'rexml/sax2listener'
6
+
7
+ class Evergreen
8
+ # Evergreen's fieldmapper IDL
9
+ class IDL
10
+ attr_reader :fields
11
+
12
+ def initialize(configuration)
13
+ @configuration = configuration
14
+ @handler = IDLSaxHandler.new
15
+ fetch
16
+ freeze
17
+ end
18
+
19
+ def [](key)
20
+ fields[key]
21
+ end
22
+
23
+ private
24
+
25
+ def fetch
26
+ URI.open("https://#{@configuration.host}/reports/fm_IDL.xml") do |file|
27
+ @fields = IDLSaxParser.new(file).parse
28
+ end
29
+ rescue Errno::ECONNREFUSED, OpenURI::HTTPError
30
+ raise Evergreen::ConnectionError
31
+ end
32
+
33
+ # A wrapper around the SAX parser
34
+ class IDLSaxParser
35
+ def initialize(file)
36
+ @parser = REXML::Parsers::SAX2Parser.new(file)
37
+ @handler = IDLSaxHandler.new
38
+ end
39
+
40
+ def parse
41
+ @parser.listen(@handler)
42
+ @parser.parse
43
+ @handler.idl_fields
44
+ end
45
+ end
46
+
47
+ # A SAX parsing handler
48
+ class IDLSaxHandler
49
+ include REXML::SAX2Listener
50
+ attr_reader :idl_fields
51
+
52
+ def initialize
53
+ @idl_fields = {}
54
+ @current_class = nil
55
+ super
56
+ end
57
+
58
+ # Callback for when we hit an XML attribute
59
+ def start_element(_uri, _localname, _qname, attributes)
60
+ if attributes.key? 'id'
61
+ # We found a class ID!
62
+ @current_class = attributes['id']
63
+ @idl_fields[@current_class] = []
64
+ elsif attributes.key? 'name'
65
+ # We found the name of a field!
66
+ @idl_fields[@current_class].push(attributes['name'])
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evergreen
4
+ # A base class for any object represented in
5
+ # Evergreen's IDL
6
+ class IDLObject
7
+ def initialize(idl)
8
+ @idl = idl
9
+ end
10
+
11
+ def idl_fields
12
+ @idl[idl_class]
13
+ end
14
+
15
+ def get(field_name)
16
+ data[idl_fields.index(field_name)]
17
+ end
18
+
19
+ private
20
+
21
+ # This should be overriden by subclasses
22
+ def idl_class
23
+ 'acp'
24
+ end
25
+
26
+ def data; end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evergreen
4
+ module Mixins
5
+ # This read-only API is available
6
+ # without any credentials
7
+ module AnonymousPcrud
8
+ def data
9
+ return @data if @data
10
+ return unless @id && @configuration && idl_class && idl_fields
11
+
12
+ payload = OpenSRF::ClassAndData.new(klass: 'osrfMessage', data: {
13
+ 'method' => "open-ils.pcrud.retrieve.#{idl_class}",
14
+ 'params' => ['ANONYMOUS', @id.to_s]
15
+ }).to_h
16
+ response = OpenSRF::HTTPTranslatorRequest.new(payload: payload, configuration: @configuration,
17
+ service: 'open-ils.pcrud').response
18
+ @data = OpenSRF::ClassAndData.parse(response['content']).data
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evergreen
4
+ module Mixins
5
+ # Convenience methods to search for data
6
+ # and initialize objects based on the results
7
+ module RetrievalMethods
8
+ def get_bib_record(id)
9
+ Evergreen::BibRecord.new(id: id, configuration: configuration, idl: idl)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evergreen
4
+ VERSION = '0.2.0'
5
+ end
data/lib/evergreen.rb ADDED
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zeitwerk'
4
+ loader = Zeitwerk::Loader.for_gem
5
+ loader.inflector.inflect(
6
+ 'http_translator_request' => 'HTTPTranslatorRequest',
7
+ 'idl' => 'IDL',
8
+ 'idl_object' => 'IDLObject',
9
+ 'opensrf' => 'OpenSRF',
10
+ 'json' => 'JSON'
11
+ )
12
+ loader.setup
13
+
14
+ # The main class provided by this gem
15
+ class Evergreen
16
+ def initialize(config_hash)
17
+ connection = Connection.new(configuration: Configuration.new(config_hash))
18
+ yield connection
19
+ connection.close
20
+ end
21
+
22
+ # An error when we can't connect to the Evergreen server
23
+ class ConnectionError < StandardError; end
24
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenSRF
4
+ # OpenSRF requests and responses often
5
+ # take the following form
6
+ # {"__c": "clasname", "__p": ["all", "the", "data"]}
7
+ class ClassAndData
8
+ attr_reader :klass, :data
9
+
10
+ def initialize(klass:, data:)
11
+ @klass = klass
12
+ @data = data
13
+ end
14
+
15
+ def self.parse(hash, path = [])
16
+ small_json = new(klass: hash['__c'], data: hash['__p'])
17
+ return small_json if path.empty?
18
+
19
+ key = path.shift
20
+ parse(small_json.data[key], path)
21
+ end
22
+
23
+ def to_h
24
+ { '__c' => @klass,
25
+ '__p' => @data }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'uri'
6
+
7
+ module OpenSRF
8
+ # A stateful endpoint, described in
9
+ # this documentation:
10
+ # https://docs.evergreen-ils.org/eg/docs/latest/integrations/web_services.html#_http_translator
11
+ class HTTPTranslatorRequest
12
+ def initialize(configuration:, service:, payload:)
13
+ @configuration = configuration
14
+ @service = service
15
+ @payload = payload
16
+ end
17
+
18
+ def response
19
+ raw = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
20
+ http.request(request)
21
+ end
22
+ OpenSRF::ClassAndData.parse(::JSON.parse(raw.body).first, ['payload']).data
23
+ end
24
+
25
+ private
26
+
27
+ def request
28
+ request = Net::HTTP::Post.new(uri)
29
+ request['X-Opensrf-Service'] = @service
30
+ request.body = "osrf-msg=#{osrf_message.to_json}"
31
+ request
32
+ end
33
+
34
+ def osrf_message
35
+ [
36
+ OpenSRF::ClassAndData.new(klass: 'osrfMessage', data: {
37
+ 'threadTrace' => 0,
38
+ 'locale' => 'en-CA',
39
+ 'type' => 'REQUEST',
40
+ 'payload' => @payload
41
+ }).to_h
42
+ ]
43
+ end
44
+
45
+ def req_options
46
+ { use_ssl: true }
47
+ end
48
+
49
+ def uri
50
+ URI.parse("https://#{@configuration.host}/osrf-http-translator")
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,22 @@
1
+ class Evergreen
2
+ # Configuration for an instance of Evergreen
3
+ class Configuration
4
+ attr_accessor host: untyped
5
+
6
+ attr_accessor default_username: untyped
7
+
8
+ attr_accessor default_password: untyped
9
+
10
+ attr_accessor read_only: untyped
11
+
12
+ def initialize: (Hash[Symbol, (String | bool)]) -> void
13
+
14
+ def configuration_complete: () -> untyped
15
+
16
+ private
17
+
18
+ def check_required_fields: () -> (nil | untyped)
19
+
20
+ def field_is_empty: (untyped field_name) -> untyped
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ class Evergreen
2
+ # This class provides an interface for making
3
+ # requests to the Evergreen server
4
+ class Connection
5
+ attr_reader configuration: Evergreen::Configuration
6
+
7
+ def initialize: (configuration: untyped) -> void
8
+
9
+ def close: () -> nil
10
+
11
+ def idl: () -> Evergreen::IDL
12
+ end
13
+ end
@@ -0,0 +1,31 @@
1
+ class Evergreen
2
+ # Evergreen's fieldmapper IDL
3
+ class IDL
4
+ attr_reader fields: untyped
5
+
6
+ def initialize: (untyped configuration) -> void
7
+
8
+ def []: (untyped key) -> untyped
9
+
10
+ private
11
+
12
+ def fetch: () -> untyped
13
+
14
+ # A wrapper around the SAX parser
15
+ class IDLSaxParser
16
+ def initialize: (untyped file) -> void
17
+
18
+ def parse: () -> untyped
19
+ end
20
+
21
+ # A SAX parsing handler
22
+ class IDLSaxHandler
23
+ attr_reader idl_fields: untyped
24
+
25
+ def initialize: () -> void
26
+
27
+ # Callback for when we hit an XML attribute
28
+ def start_element: (untyped _uri, untyped _localname, untyped _qname, untyped attributes) -> (untyped | untyped | nil)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,10 @@
1
+ class Evergreen::IDLObject
2
+ @idl: untyped
3
+
4
+ def initialize: (untyped idl) -> void
5
+ def idl_fields: -> untyped
6
+
7
+ private
8
+ def idl_class: -> String
9
+ def data: -> untyped
10
+ end
@@ -0,0 +1,9 @@
1
+ class Evergreen
2
+ module Mixins
3
+ # Convenience methods to search for data
4
+ # and initialize objects based on the results
5
+ module RetrievalMethods
6
+ def get_bib_record: (untyped id) -> untyped
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ # TypeProf 0.21.2
2
+
3
+ # Classes
4
+ class Evergreen
5
+ VERSION: String
6
+ end
@@ -0,0 +1,12 @@
1
+ # TypeProf 0.21.2
2
+
3
+ # Classes
4
+ module OpenSRF
5
+ class ClassAndData
6
+ attr_reader klass: untyped
7
+ attr_reader data: untyped
8
+ def initialize: (klass: untyped, data: untyped) -> void
9
+ def self.parse: (untyped hash, ?Array[untyped] path) -> ClassAndData
10
+ def to_h: -> Hash[String, untyped]
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ module OpenSRF
2
+ # A stateful endpoint, described in
3
+ # this documentation:
4
+ # https://docs.evergreen-ils.org/eg/docs/latest/integrations/web_services.html#_http_translator
5
+ class HTTPTranslatorRequest
6
+ def initialize: (configuration: untyped, service: untyped, payload: untyped) -> void
7
+
8
+ def response: () -> untyped
9
+
10
+ private
11
+
12
+ def request: () -> untyped
13
+
14
+ def osrf_message: () -> ::Array[untyped]
15
+
16
+ def req_options: () -> { use_ssl: true }
17
+
18
+ def uri: () -> untyped
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: evergreen-ils
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Jane Sandberg
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-08-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: marc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: rexml
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 3.0.0
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '4.0'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 3.0.0
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '4.0'
53
+ - !ruby/object:Gem::Dependency
54
+ name: zeitwerk
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 2.0.0
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: '3.0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 2.0.0
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: '3.0'
73
+ description: A gem for interacting with the Evergreen Integrated Library System
74
+ email:
75
+ - sandbergja@gmail.com
76
+ executables: []
77
+ extensions: []
78
+ extra_rdoc_files: []
79
+ files:
80
+ - ".reek.yml"
81
+ - ".rspec"
82
+ - ".rubocop.yml"
83
+ - ".tool-versions"
84
+ - Gemfile
85
+ - Gemfile.lock
86
+ - README.md
87
+ - Rakefile
88
+ - Steepfile
89
+ - evergreen-ils.gemspec
90
+ - lib/evergreen.rb
91
+ - lib/evergreen/bib_record.rb
92
+ - lib/evergreen/configuration.rb
93
+ - lib/evergreen/connection.rb
94
+ - lib/evergreen/idl.rb
95
+ - lib/evergreen/idl_object.rb
96
+ - lib/evergreen/mixins/anonymous_pcrud.rb
97
+ - lib/evergreen/mixins/retrieval_methods.rb
98
+ - lib/evergreen/version.rb
99
+ - lib/opensrf/class_and_data.rb
100
+ - lib/opensrf/http_translator_request.rb
101
+ - sig/evergreen/configuration.rbs
102
+ - sig/evergreen/connection.rbs
103
+ - sig/evergreen/idl.rbs
104
+ - sig/evergreen/idl_object.rbs
105
+ - sig/evergreen/mixins/retrieval_methods.rb
106
+ - sig/evergreen/version.rbs
107
+ - sig/opensrf/class_and_data.rbs
108
+ - sig/opensrf/http_translator_request.rbs
109
+ homepage: https://github.com/sandbergja/evergreen-rb
110
+ licenses: []
111
+ metadata:
112
+ homepage_uri: https://github.com/sandbergja/evergreen-rb
113
+ source_code_uri: https://github.com/sandbergja/evergreen-rb
114
+ rubygems_mfa_required: 'true'
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: 3.0.0
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubygems_version: 3.4.6
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: Evergreen ILS
134
+ test_files: []