aspire 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +59 -0
- data/.rbenv-gemsets +1 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Dockerfile +20 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +851 -0
- data/Rakefile +10 -0
- data/aspire.gemspec +40 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/entrypoint.sh +11 -0
- data/exe/build-cache +13 -0
- data/lib/aspire.rb +11 -0
- data/lib/aspire/api.rb +2 -0
- data/lib/aspire/api/base.rb +198 -0
- data/lib/aspire/api/json.rb +195 -0
- data/lib/aspire/api/linked_data.rb +214 -0
- data/lib/aspire/caching.rb +4 -0
- data/lib/aspire/caching/builder.rb +356 -0
- data/lib/aspire/caching/cache.rb +365 -0
- data/lib/aspire/caching/cache_entry.rb +296 -0
- data/lib/aspire/caching/cache_logger.rb +63 -0
- data/lib/aspire/caching/util.rb +210 -0
- data/lib/aspire/cli/cache_builder.rb +123 -0
- data/lib/aspire/cli/command.rb +20 -0
- data/lib/aspire/enumerator/base.rb +29 -0
- data/lib/aspire/enumerator/json_enumerator.rb +130 -0
- data/lib/aspire/enumerator/linked_data_uri_enumerator.rb +32 -0
- data/lib/aspire/enumerator/report_enumerator.rb +64 -0
- data/lib/aspire/exceptions.rb +36 -0
- data/lib/aspire/object.rb +7 -0
- data/lib/aspire/object/base.rb +155 -0
- data/lib/aspire/object/digitisation.rb +43 -0
- data/lib/aspire/object/factory.rb +87 -0
- data/lib/aspire/object/list.rb +590 -0
- data/lib/aspire/object/module.rb +36 -0
- data/lib/aspire/object/resource.rb +371 -0
- data/lib/aspire/object/time_period.rb +47 -0
- data/lib/aspire/object/user.rb +46 -0
- data/lib/aspire/properties.rb +20 -0
- data/lib/aspire/user_lookup.rb +103 -0
- data/lib/aspire/util.rb +185 -0
- data/lib/aspire/version.rb +3 -0
- data/lib/retry.rb +197 -0
- metadata +274 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 814a301b9b2e5cf3ebc1abb88696445a45cbe802
|
4
|
+
data.tar.gz: 6bf15e340d9f193d63919bce90fcad7683fae408
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0db90365108443a9b0dde28a0a548572bca5924fc45ac6a09e91ecab81240c9b87e47aafc9b2d801c98b2c238a46427ca9269f8f1564a2685825bc3505849ddd
|
7
|
+
data.tar.gz: f0b6c6683233251de2cf045b7fce0afd245226e507c59ca02a9b7448eab0a7874b2364ba54a48326e169de42da4b21cc80cfd28d4cb025b8d46527c86d39bcd4
|
data/.gitignore
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# IntelliJ files
|
2
|
+
.idea/
|
3
|
+
*.iml
|
4
|
+
*.iws
|
5
|
+
|
6
|
+
# Created by https://www.gitignore.io/api/ruby
|
7
|
+
|
8
|
+
### Ruby ###
|
9
|
+
*.gem
|
10
|
+
*.rbc
|
11
|
+
/.byebug_history
|
12
|
+
/.config
|
13
|
+
/coverage/
|
14
|
+
/InstalledFiles
|
15
|
+
/pkg/
|
16
|
+
/spec/reports/
|
17
|
+
/spec/examples.txt
|
18
|
+
/test/tmp/
|
19
|
+
/test/version_tmp/
|
20
|
+
/tmp/
|
21
|
+
|
22
|
+
# Used by dotenv library to load environment variables.
|
23
|
+
.env
|
24
|
+
|
25
|
+
## Specific to RubyMotion:
|
26
|
+
.dat*
|
27
|
+
.repl_history
|
28
|
+
build/
|
29
|
+
*.bridgesupport
|
30
|
+
build-iPhoneOS/
|
31
|
+
build-iPhoneSimulator/
|
32
|
+
|
33
|
+
## Specific to RubyMotion (use of CocoaPods):
|
34
|
+
#
|
35
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
36
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
37
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
38
|
+
#
|
39
|
+
# vendor/Pods/
|
40
|
+
|
41
|
+
## Documentation cache and generated files:
|
42
|
+
/.yardoc/
|
43
|
+
/_yardoc/
|
44
|
+
/doc/
|
45
|
+
/rdoc/
|
46
|
+
|
47
|
+
## Environment normalization:
|
48
|
+
/.bundle/
|
49
|
+
/vendor/bundle
|
50
|
+
/lib/bundler/man/
|
51
|
+
|
52
|
+
# for a library or gem, you might want to ignore these files since the code is
|
53
|
+
# intended to run in multiple environments; otherwise, check them in:
|
54
|
+
Gemfile.lock
|
55
|
+
.ruby-version
|
56
|
+
.ruby-gemset
|
57
|
+
|
58
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
59
|
+
.rvmrc
|
data/.rbenv-gemsets
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
aspire
|
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at Sh3d0fd00m. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Dockerfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
FROM ruby:2.4.1
|
2
|
+
|
3
|
+
#RUN groupadd -r pure && useradd -r -g pure pure
|
4
|
+
|
5
|
+
# RENAME TO LEGANTO TO ASPIRE
|
6
|
+
ENV DATA_ROOT /leganto-data
|
7
|
+
ENV APP_ROOT /leganto-extractor
|
8
|
+
RUN mkdir -p $DATA_ROOT
|
9
|
+
RUN mkdir -p $APP_ROOT
|
10
|
+
WORKDIR $APP_ROOT
|
11
|
+
|
12
|
+
ENV LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8
|
13
|
+
|
14
|
+
COPY pkg/aspire-0.1.0.gem aspire-0.1.0.gem
|
15
|
+
|
16
|
+
RUN gem install aspire-0.1.0.gem
|
17
|
+
|
18
|
+
COPY entrypoint.sh $APP_ROOT/entrypoint.sh
|
19
|
+
|
20
|
+
ENTRYPOINT ["./entrypoint.sh"]
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 lbaajh
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,851 @@
|
|
1
|
+
# Aspire
|
2
|
+
|
3
|
+
This gem provides tools for working with Talis Aspire APIs to manage reading
|
4
|
+
lists. It implements a data model for the common API objects (list, section,
|
5
|
+
item, resource etc.) and provides a mechanism for caching API data for offline
|
6
|
+
access.
|
7
|
+
|
8
|
+
## Contents
|
9
|
+
|
10
|
+
* [Installation](#installation)
|
11
|
+
* [Usage](#usage)
|
12
|
+
* [Overview](#usage-overview)
|
13
|
+
* [APIs](#usage-apis)
|
14
|
+
* [Linked Data API](#usage-apis-linked-data)
|
15
|
+
* [Authenticated (JSON) API](#usage-apis-json)
|
16
|
+
* [Caching](#caching)
|
17
|
+
* [Cache](#caching-cache)
|
18
|
+
* [Cache Builder](#caching-cache-builder)
|
19
|
+
* [Report Enumerator](#caching-cache-builder-enum)
|
20
|
+
* [Cache Builder](#caching-cache-builder-cache-builder)
|
21
|
+
* [Caveats](#caching-cache-builder-caveats)
|
22
|
+
* [Data Model](#model)
|
23
|
+
* [Overview](#model-overview)
|
24
|
+
* [User Profiles](#model-user-profiles)
|
25
|
+
* [Factory](#model-factory)
|
26
|
+
* [List](#model-list)
|
27
|
+
* [Iterating over lists and sections](#model-list-iter)
|
28
|
+
* [List Properties](#model-list-properties)
|
29
|
+
* [ListSection Properties](#model-list-section-properties)
|
30
|
+
* [ListItem Properties](#model-list-item-properties)
|
31
|
+
* [Resource](#model-resource)
|
32
|
+
* [Basic Properties](#model-resource-basic)
|
33
|
+
* [Linked Resource Properties](#model-resource-linked)
|
34
|
+
* [Digitisation](#model-digitisation)
|
35
|
+
* [Module](#model-module)
|
36
|
+
* [TimePeriod](#model-timeperiod)
|
37
|
+
* [User](#model-user)
|
38
|
+
* [Implementation Notes](#implementation)
|
39
|
+
* [Preserving List Structure](#implementation-structure)
|
40
|
+
* [Development](#development)
|
41
|
+
* [Contributing](#contributing)
|
42
|
+
* [License](#license)
|
43
|
+
|
44
|
+
## <a name="installation"></a>Installation
|
45
|
+
|
46
|
+
Add this line to your application's Gemfile:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
gem 'aspire'
|
50
|
+
```
|
51
|
+
|
52
|
+
And then execute:
|
53
|
+
|
54
|
+
$ bundle
|
55
|
+
|
56
|
+
Or install it yourself as:
|
57
|
+
|
58
|
+
$ gem install aspire
|
59
|
+
|
60
|
+
## <a name="usage"></a>Usage
|
61
|
+
|
62
|
+
### <a name="usage-overview"></a>Overview
|
63
|
+
|
64
|
+
This gem provides tools for working with the Talis Aspire
|
65
|
+
[Linked Data API](https://support.talis.com/hc/en-us/articles/205860451) and the
|
66
|
+
newer [Authenticated (JSON) APIs](http://docs.talisrl.apiary.io/).
|
67
|
+
|
68
|
+
To use the Authenticated (JSON) APIs, you will need to request an API key and
|
69
|
+
secret from Talis
|
70
|
+
([more](https://support.talis.com/hc/en-us/articles/208221125)).
|
71
|
+
|
72
|
+
### <a name="usage-apis"></a>APIs
|
73
|
+
Credentials, tenancy URLs and other client-specific details are encapsulated
|
74
|
+
by API objects which are passed to other classes.
|
75
|
+
|
76
|
+
#### <a name="usage-apis-linked-data"></a>Linked Data API
|
77
|
+
|
78
|
+
To create a Linked Data API instance:
|
79
|
+
```ruby
|
80
|
+
require 'aspire'
|
81
|
+
|
82
|
+
# Create and configure a logger if required, or pass nil to disable logging
|
83
|
+
require 'logger'
|
84
|
+
logger = Logger.new(STDOUT)
|
85
|
+
|
86
|
+
# Set the timeout in seconds for API calls, or 0 to disable (wait indefinitely).
|
87
|
+
# Large lists (several hundred items) may take up to 30 seconds so adjust this
|
88
|
+
# according to your requirements.
|
89
|
+
timeout = 10
|
90
|
+
|
91
|
+
# Tenancy configuration
|
92
|
+
# - these settings specify the base components of resource URIs; all are
|
93
|
+
# optional and default to values derived from the tenancy code
|
94
|
+
|
95
|
+
# linked_data_root is the root URI of URIs returned in linked data responses
|
96
|
+
# (defaults to https://<tenancy-code>.myreadinglists.org)
|
97
|
+
linked_data_root = 'https://myinstitution.myreadinglists.org'
|
98
|
+
|
99
|
+
# tenancy_host_aliases is a list of host name aliases which may appear in
|
100
|
+
# resource URIs
|
101
|
+
tenancy_host_aliases = ['resourcelists.myinstitution.ac.uk']
|
102
|
+
|
103
|
+
# tenancy_root is the canonical root URI of the tenancy
|
104
|
+
# (defaults to https://<tenancy_code>.rl.talis.com)
|
105
|
+
tenancy_root = 'https://myinstitution.rl.talis.com'
|
106
|
+
|
107
|
+
# Create the Linked Data API instance
|
108
|
+
# - replace 'tenancy_code' with the appropriate value for your Aspire tenancy,
|
109
|
+
# e.g. 'myinstitution'
|
110
|
+
ld_api = Aspire::API::LinkedData('tenancy_code',
|
111
|
+
linked_data_root: linked_data_root,
|
112
|
+
tenancy_host_aliases: tenancy_host_aliases,
|
113
|
+
tenancy_root: tenancy_root,
|
114
|
+
logger: logger, timeout: timeout)
|
115
|
+
```
|
116
|
+
|
117
|
+
#### <a name="usage-apis-json"></a>Authenticated (JSON) API
|
118
|
+
|
119
|
+
To create an Authenticated (JSON) API instance:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
require 'aspire'
|
123
|
+
|
124
|
+
# Set the logger and timeout as above
|
125
|
+
logger = ...
|
126
|
+
timeout = ...
|
127
|
+
|
128
|
+
# Create the JSON API instance
|
129
|
+
# - replace 'api_client_id', 'api_secret' and 'tenancy_code' with appropriate
|
130
|
+
# values for your Aspire tenancy
|
131
|
+
json_api = Aspire::API::JSON.new('api_client_id', 'api_secret', 'tenancy_code',
|
132
|
+
logger: logger, timeout: timeout)
|
133
|
+
|
134
|
+
# Call a JSON API endpoint
|
135
|
+
json_api.call()
|
136
|
+
|
137
|
+
```
|
138
|
+
|
139
|
+
### <a name="caching"></a>Caching
|
140
|
+
|
141
|
+
#### <a name="caching-cache"></a>Cache
|
142
|
+
|
143
|
+
The `Aspire::Caching::Cache` class mediates access to the Aspire APIs,
|
144
|
+
storing API responses on disk and returning the cached copies on subsequent
|
145
|
+
accesses. This is intended to serve as both a means of backing up Aspire data
|
146
|
+
locally and as a means of speeding up slow API calls.
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
require 'aspire'
|
150
|
+
|
151
|
+
# Create Linked Data and JSON API instances
|
152
|
+
ld_api = Aspire::API::LinkedData.new(...)
|
153
|
+
json_api = Aspire::API::JSON.new(...)
|
154
|
+
|
155
|
+
# Create the cache at the specified path
|
156
|
+
#
|
157
|
+
# The following optional keyword arguments are accepted:
|
158
|
+
# - clear: if true, remove and recreate the cache path
|
159
|
+
# (default: false)
|
160
|
+
# - logger: a Logger instance for logging, or nil to disable
|
161
|
+
# (default: nil)
|
162
|
+
# - mode: the octal file permissions for the cache directory
|
163
|
+
# (default: 0o0750)
|
164
|
+
#
|
165
|
+
cache = Aspire::Caching::Cache.new(ld_api, json_api, '/path/to/cache/root',
|
166
|
+
clear: false,
|
167
|
+
logger: Logger.new(STDOUT),
|
168
|
+
mode: 0o0700)
|
169
|
+
```
|
170
|
+
|
171
|
+
The cache is mainly intended for internal use by the data model but can be
|
172
|
+
used by clients of this gem if required.
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
# An Aspire linked data URI resource
|
176
|
+
uri = 'https://myinstitution.rl.talis.com/lists/FFCB71DE-EE3A-1D42-BAAE-9CA9CFF0EE72'
|
177
|
+
|
178
|
+
# Read an Aspire linked data URI, returning the parsed JSON data as a Hash
|
179
|
+
# - use_api: if true, when a URI is not already in the cache, read data from
|
180
|
+
# the Aspire API and write it to the cache
|
181
|
+
# (default: true)
|
182
|
+
# - use_cache: if true, read data from the cache before trying the Aspire API
|
183
|
+
# (default: true)
|
184
|
+
# - json: if true, read data from the JSON API, otherwise use the Linked
|
185
|
+
# Data API
|
186
|
+
# (default: false)
|
187
|
+
data = cache.read(uri, json: false, use_api: true, use_cache: true) \
|
188
|
+
do |data, entry, from_cache, json|
|
189
|
+
# The block is called only if data is read from the cache or API
|
190
|
+
# The parameters are:
|
191
|
+
# - data = the parsed JSON data for the resource
|
192
|
+
# - entry = an ```Aspire::Caching::CacheEntry``` instance
|
193
|
+
# representing the cached entry (for internal use)
|
194
|
+
# - from_cache = true if the data was retrieved from the cache,
|
195
|
+
# false if from the API
|
196
|
+
# - json = true if the data is from the JSON API,
|
197
|
+
# false if from the Linked Data API
|
198
|
+
end
|
199
|
+
|
200
|
+
# Remove a resource from the cache, returning the cached JSON data as a Hash
|
201
|
+
# - remove_children: if true, remove
|
202
|
+
data = cache.remove(uri, force: true, remove_children: true) \
|
203
|
+
do |data, entry|
|
204
|
+
# The block is called only if cached data exists
|
205
|
+
# The parameters are:
|
206
|
+
# - data = the parsed JSON data for the cached resource
|
207
|
+
# - entry = an ```Aspire::Caching::CacheEntry``` instance representing
|
208
|
+
# the cached entry (for internal use)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Delete the cache contents but not the root path
|
212
|
+
cache.clear
|
213
|
+
|
214
|
+
# Delete the cache root path and contents
|
215
|
+
cache.delete
|
216
|
+
|
217
|
+
# Return true if the cache root path is empty, false otherwise
|
218
|
+
cache.empty?
|
219
|
+
```
|
220
|
+
|
221
|
+
#### <a name="caching-cache-builder"></a>Cache Builder
|
222
|
+
|
223
|
+
The `Aspire::Caching::Builder` class constructs an offline cache of Linked Data
|
224
|
+
and JSON API data.
|
225
|
+
|
226
|
+
Given an enumerator of list IDs, the cache builder downloads the JSON and Linked
|
227
|
+
Data API data for each list and recursively follows all URIs in the linked data
|
228
|
+
until every entity has been retrieved.
|
229
|
+
|
230
|
+
The list enumerator is expected to be an instance of
|
231
|
+
`Aspire::Enumerator::ReportEnumerator`, which parses a file as a CSV and yields
|
232
|
+
the parsed row. When parsing an Aspire "All Lists" report, the parsed CSV row is
|
233
|
+
yielded as a Hash containing the list URI at key "List Link", so the cache
|
234
|
+
builder expects this from the enumerator. If you're using a custom enumerator
|
235
|
+
rather than the `ReportEnumerator`, you should yield the list URI as follows:
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
yielder << { 'List Link' => '<list-URI>' }
|
239
|
+
```
|
240
|
+
|
241
|
+
##### <a name="caching-cache-builder-enum"></a>Report Enumerator
|
242
|
+
|
243
|
+
A list enumerator is created from an Aspire "All Lists" report CSV as follows:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
|
247
|
+
# Optionally define one or more filters to control which lists are selected.
|
248
|
+
# Each filter is a Proc instance which accepts the Hash or Array from the
|
249
|
+
# CSV parser and returns true to include the list or false to ignore it.
|
250
|
+
# All filters must return true for the list to be included.
|
251
|
+
filters = [
|
252
|
+
proc { |row| row['Status'] == 'Published' },
|
253
|
+
proc { |row| row['Privacy'] == 'Public' },
|
254
|
+
proc { |row| row['Time Period'] == '2017-18' }
|
255
|
+
]
|
256
|
+
|
257
|
+
# The filename of a downloaded Aspire "All Lists" report in CSV format
|
258
|
+
filename = '/path/to/all_lists.csv'
|
259
|
+
|
260
|
+
# Create the report enumerator
|
261
|
+
lists = Aspire::Enumerator::ReportEnumerator(filename, filters)
|
262
|
+
```
|
263
|
+
|
264
|
+
##### <a name="caching-cache-builder-cache-builder"></a>Cache Builder
|
265
|
+
|
266
|
+
```ruby
|
267
|
+
# Create a Cache
|
268
|
+
cache = Aspire::Caching::Cache.new(...)
|
269
|
+
|
270
|
+
# Create a list enumerator
|
271
|
+
lists = Aspire::Enumerator::ReportEnumerator(...)
|
272
|
+
|
273
|
+
# Create a cache Builder
|
274
|
+
builder = Aspire::Caching::Builder.new(cache)
|
275
|
+
|
276
|
+
# Build the cache
|
277
|
+
# - clear: if true, clear the cache before building
|
278
|
+
builder.build(lists, clear: true)
|
279
|
+
```
|
280
|
+
|
281
|
+
##### <a name="caching-cache-builder-caveats"></a>Caveats
|
282
|
+
|
283
|
+
1. The current implementation is slow to run (partly due to the slow speed of
|
284
|
+
the JSON API for large lists) and memory-intensive (due to the recursive
|
285
|
+
processing of referenced resources, which can result in deeply-nested method
|
286
|
+
call stacks). A parallelised approach would help to improve this.
|
287
|
+
|
288
|
+
2. The current implementation doesn't reliably handle resuming an interrupted
|
289
|
+
build and may skip data. Because of this, it's **strongly recommended** to
|
290
|
+
always build a new cache with the `clear: true` flag.
|
291
|
+
|
292
|
+
3. Due to the previous two points (slow run speed and inability to resume an
|
293
|
+
interrupted build), and the possibility that network and other problems may
|
294
|
+
break a long-running build, it's recommended to build a number of small caches
|
295
|
+
rather than a single cache of everything. Filters passed to the
|
296
|
+
`Aspire::Enumerator::ReportEnumerator` can limit the size of the cache.
|
297
|
+
For example, you may want to build one cache per time period.
|
298
|
+
|
299
|
+
4. The cache builder can only download publicly-visible lists (private lists
|
300
|
+
require authentication by the owner), so you should always include a filter for
|
301
|
+
this:
|
302
|
+
|
303
|
+
```ruby
|
304
|
+
filters = [
|
305
|
+
proc { |row| row['Privacy'] == 'Public' },
|
306
|
+
# other filters
|
307
|
+
]
|
308
|
+
```
|
309
|
+
### <a name="model"></a>Data Model
|
310
|
+
|
311
|
+
#### <a name="model-overview"></a>Overview
|
312
|
+
|
313
|
+
The data model provides a set of classes representing common resources in the
|
314
|
+
Talis Aspire APIs, such as lists, list sections, list items and bibliographic
|
315
|
+
resources.
|
316
|
+
|
317
|
+
Model instances are retrieved through a factory which uses a combination of the
|
318
|
+
Linked Data API, Authenticated (JSON) API and the Aspire "All User Profiles"
|
319
|
+
report to construct the models.
|
320
|
+
|
321
|
+
#### <a name="model-user-profiles"></a>User Profiles
|
322
|
+
|
323
|
+
User profiles referenced by the Aspire Linked Data API URIs are not directly
|
324
|
+
available through the Linked Data or JSON APIs, so the `Aspire::Object::Factory`
|
325
|
+
class accepts a Hash of user data of the form:
|
326
|
+
|
327
|
+
```ruby
|
328
|
+
users = {
|
329
|
+
'https://myinstitution.rl.talis.com/users/ABCD1234-FE98-DC76-BA54-54321FEBACD0' => {
|
330
|
+
email: 'anne.onymouse@myinstution.ac.uk',
|
331
|
+
firstName: 'Anne',
|
332
|
+
role: ['List publisher', 'List creator'],
|
333
|
+
surname: 'Onymous',
|
334
|
+
uri: 'https://myinstitution.rl.talis.com/users/ABCD1234-FE98-DC76-BA54-54321FEBACD0'
|
335
|
+
}
|
336
|
+
}
|
337
|
+
```
|
338
|
+
|
339
|
+
The data hash follows the JSON format documented by the
|
340
|
+
[Aspire JSON API](http://docs.talisrl.apiary.io/#reference/catalog/catalog-record-based-on-isbn/get-user-profile)
|
341
|
+
|
342
|
+
The `Aspire::UserLookup` class provides a simple means of loading an Aspire
|
343
|
+
"All User Profiles" report CSV.
|
344
|
+
|
345
|
+
```ruby
|
346
|
+
require 'aspire'
|
347
|
+
|
348
|
+
users = Aspire::UserLookup.new(filename: '/path/to/all_user_profiles.csv')
|
349
|
+
user = users['https://myinstitution.rl.talis.com/users/ABCD1234-FE98-DC76-BA54-54321FEBACD0']
|
350
|
+
```
|
351
|
+
|
352
|
+
#### <a name="model-factory"></a>Factory
|
353
|
+
|
354
|
+
The `Aspire::Object::Factory` class returns data model instances. Data is
|
355
|
+
read from an Aspire API data cache (see *Cache* above) except for user data,
|
356
|
+
which is supplied by a Hash mapping user URIs to a Hash of user data (see
|
357
|
+
*User Profiles* above).
|
358
|
+
|
359
|
+
```ruby
|
360
|
+
require 'aspire'
|
361
|
+
|
362
|
+
# Create a cache
|
363
|
+
cache = Aspire::Caching::Cache.new(...)
|
364
|
+
|
365
|
+
# Create a user hash from an Aspire "All User Profiles" report CSV
|
366
|
+
users = Aspire::UserLookup.new(filename: '/path/to/all_user_profiles.csv')
|
367
|
+
|
368
|
+
# Create a factory
|
369
|
+
factory = Aspire::Object::Factory.new(cache, users)
|
370
|
+
|
371
|
+
# Get a model instance by its URI
|
372
|
+
uri = 'https://myinstitution.rl.talis.com/lists/FFCB71DE-EE3A-1D42-BAAE-9CA9CFF0EE72'
|
373
|
+
list = factory.get(uri)
|
374
|
+
```
|
375
|
+
|
376
|
+
#### <a name="model-list"></a>List
|
377
|
+
|
378
|
+
`Aspire::Object::List` represents a resource list, composed of an
|
379
|
+
ordered sequence of `Aspire::Object::ListSection` and `Aspire::Object::ListItem`
|
380
|
+
instances. The ordering of list entries and the nested list structure are both
|
381
|
+
preserved from Aspire (see *Implementation Notes* for details).
|
382
|
+
|
383
|
+
`Aspire::Object::ListSection` represents a resource list section, composed of
|
384
|
+
an ordered sequence of `Aspire::Object::ListSection` and
|
385
|
+
`Aspire::Object::ListItem` instances (nested subsections and list items).
|
386
|
+
|
387
|
+
`Aspire::Object::ListItem` represents a single list item.
|
388
|
+
|
389
|
+
##### <a name="model-list-iter"></a>Iterating over lists and sections
|
390
|
+
|
391
|
+
`List` and `ListSection` act as ordered containers for child `ListSection` and
|
392
|
+
`ListItem` instances. Both classes support various iterators over their child
|
393
|
+
and parent objects:
|
394
|
+
|
395
|
+
```ruby
|
396
|
+
require 'aspire'
|
397
|
+
|
398
|
+
# Create a factory
|
399
|
+
factory = Aspire::Object::Factory.new(...)
|
400
|
+
|
401
|
+
# Get a list
|
402
|
+
list = factory.get(...)
|
403
|
+
|
404
|
+
# Iterate over the top-level list contents in list order
|
405
|
+
list.each { |item| # item is a ListSection or ListItem instance }
|
406
|
+
|
407
|
+
# Iterate over all ListItem instances in list order
|
408
|
+
# Nested list sections are iterated in depth-first order
|
409
|
+
list.each_item { |item| # item is a ListItem instance }
|
410
|
+
|
411
|
+
# Iterate over all ListSection instances in list order
|
412
|
+
# Nested list sections are iterated in depth-first order
|
413
|
+
list.each_section { |section| # section is a ListSection instance }
|
414
|
+
|
415
|
+
# Get a list of all ListItem instances in list order
|
416
|
+
# Nested list sections are iterated in depth-first order
|
417
|
+
items = list.items
|
418
|
+
|
419
|
+
# Get a list of all ListSection instances in list order
|
420
|
+
# Nested list sections are iterated in depth-first order
|
421
|
+
sections = list.sections
|
422
|
+
|
423
|
+
# Get the number of top-level list items (sections and items)
|
424
|
+
length = list.length(:entry)
|
425
|
+
|
426
|
+
# Get the number of ListItem instances
|
427
|
+
# Both forms are equivalent
|
428
|
+
length = list.length
|
429
|
+
length = list.length(:item)
|
430
|
+
|
431
|
+
# Get the number of top-level ListSection instances
|
432
|
+
length = list.length(:section)
|
433
|
+
|
434
|
+
# Get the parent list of a List, ListSection or ListItem
|
435
|
+
parent_list = list.parent_list
|
436
|
+
|
437
|
+
# Get the immediate parent section of a ListSection or ListItem
|
438
|
+
parent_section = list.parent_section
|
439
|
+
|
440
|
+
# Get a list of parent sections of a ListSection or ListItem in nearest ancestor
|
441
|
+
# first order
|
442
|
+
parent_sections = list.parent_sections
|
443
|
+
|
444
|
+
# Get a list of parent items (ListSection and List) of a ListSection or ListItem
|
445
|
+
# in nearest ancestor first order
|
446
|
+
parents = list.parents
|
447
|
+
|
448
|
+
# Get a list of parent items matching the supplied classes
|
449
|
+
parents = list.parents(List, ListSection)
|
450
|
+
|
451
|
+
# Get a list of parent items where the supplied block returns true
|
452
|
+
parents = list.parents { |item| item.is_a?(ListItem) }
|
453
|
+
```
|
454
|
+
|
455
|
+
##### <a name="model-list-properties"></a>List properties
|
456
|
+
|
457
|
+
```ruby
|
458
|
+
|
459
|
+
# Creation timestamp of the list as a DateTime
|
460
|
+
list.created
|
461
|
+
|
462
|
+
# Reading list creators as an array of Aspire::Object::User
|
463
|
+
list.creator
|
464
|
+
|
465
|
+
# Description of the list
|
466
|
+
list.description
|
467
|
+
|
468
|
+
# List items as a hash of Aspire::Object::ListItem indexed by item URI
|
469
|
+
list.items
|
470
|
+
|
471
|
+
# Timestamp of the most recent list publication as a DateTime
|
472
|
+
list.last_published
|
473
|
+
|
474
|
+
# Timestamp of the most recent list update as a DateTime
|
475
|
+
list.last_updated
|
476
|
+
|
477
|
+
# Modules referencing this list as an array of Aspire::Object::Module
|
478
|
+
list.modules
|
479
|
+
|
480
|
+
# Reading list name
|
481
|
+
list.name
|
482
|
+
list.to_s
|
483
|
+
|
484
|
+
# List owner as an Aspire::Object::User
|
485
|
+
list.owner
|
486
|
+
|
487
|
+
# List publisher as an Aspire::Object::User
|
488
|
+
list.publisher
|
489
|
+
|
490
|
+
# Period covered by the list as an Aspire::Object::TimePeriod
|
491
|
+
list.time_period
|
492
|
+
```
|
493
|
+
|
494
|
+
##### <a name="model-list-section-properties"></a>ListSection properties
|
495
|
+
|
496
|
+
```ruby
|
497
|
+
# Section description
|
498
|
+
section.description
|
499
|
+
|
500
|
+
# Section name
|
501
|
+
section.name
|
502
|
+
section.to_s
|
503
|
+
```
|
504
|
+
|
505
|
+
##### <a name="model-list-item-properties"></a>ListItem properties
|
506
|
+
|
507
|
+
```ruby
|
508
|
+
# Digitisation details for the item as an Aspire::Object::Digitisation
|
509
|
+
item.digitisation
|
510
|
+
|
511
|
+
# Importance of the item
|
512
|
+
item.importance
|
513
|
+
|
514
|
+
# Private library note for the item
|
515
|
+
item.library_note
|
516
|
+
|
517
|
+
# Identifier of the resource in the local library management system
|
518
|
+
item.local_control_number
|
519
|
+
|
520
|
+
# General public note for the item
|
521
|
+
item.note
|
522
|
+
|
523
|
+
# Student note if available, otherwise the general public note
|
524
|
+
item.public_note
|
525
|
+
|
526
|
+
# Resource for the item as an Aspire::Object::Resource
|
527
|
+
item.resource
|
528
|
+
|
529
|
+
# Public student note for the item
|
530
|
+
item.student_note
|
531
|
+
|
532
|
+
# Title of the item (i.e. the title of the associated resource)
|
533
|
+
item.title
|
534
|
+
# The resource title is always returned if it is available.
|
535
|
+
# If there is no associated resource, an alternative can be specified;
|
536
|
+
# the default is to return nil.
|
537
|
+
item.title(:library_note) # returns library_note
|
538
|
+
item.title(:note) # returns public_note || library_note
|
539
|
+
item.title(:public_note) # returns public_note
|
540
|
+
item.title(:uri) # returns the item URI
|
541
|
+
```
|
542
|
+
|
543
|
+
#### <a name="model-resource"></a>Resource
|
544
|
+
|
545
|
+
`Aspire::Object::Resource` represents the bibliographic item (book/chapter,
|
546
|
+
journal/article, online resource etc.) referenced by a list item.
|
547
|
+
|
548
|
+
Resources may be linked to other resources. For example, a book chapter resource
|
549
|
+
may be linked to its parent book, or a journal article to its parent journal.
|
550
|
+
|
551
|
+
##### <a name="model-resource-basic"></a>Basic Properties
|
552
|
+
|
553
|
+
```ruby
|
554
|
+
# Get the resource from the list item
|
555
|
+
resource = item.resource
|
556
|
+
|
557
|
+
# List of authors of the resource as an array of strings
|
558
|
+
resource.authors
|
559
|
+
|
560
|
+
# Book jacket image URL
|
561
|
+
resource.book_jacket_url
|
562
|
+
|
563
|
+
# Date of publication as a string
|
564
|
+
resource.date
|
565
|
+
|
566
|
+
# DOI for the resource
|
567
|
+
resource.doi
|
568
|
+
|
569
|
+
# Edition
|
570
|
+
resource.edition
|
571
|
+
|
572
|
+
# true if edition data is available, false if not
|
573
|
+
resource.edition_data
|
574
|
+
|
575
|
+
# Electronic ISSN for the resource
|
576
|
+
resource.eissn
|
577
|
+
|
578
|
+
# Child resources as an array of Aspire::Object::Resource
|
579
|
+
# - e.g. the chapters contained of a book or articles of a journal
|
580
|
+
resource.has_part
|
581
|
+
|
582
|
+
# Parent resources as an array of Aspire::Object::Resource
|
583
|
+
# - e.g. the book containing a chapter or journal containing an article
|
584
|
+
resource.is_part_of
|
585
|
+
|
586
|
+
# 10-digit ISBN for the resource
|
587
|
+
resource.isbn10
|
588
|
+
|
589
|
+
# 13-digit ISBN for the resource
|
590
|
+
resource.isbn13
|
591
|
+
|
592
|
+
# List of ISBNs for the resource
|
593
|
+
resource.isbns
|
594
|
+
|
595
|
+
# ISSN for the resource
|
596
|
+
resource.issn
|
597
|
+
|
598
|
+
# Issue
|
599
|
+
resource.issue
|
600
|
+
|
601
|
+
# Issue date as a string
|
602
|
+
resource.issued
|
603
|
+
|
604
|
+
# true if this is the latest edition, false otherwise
|
605
|
+
resource.latest_edition
|
606
|
+
|
607
|
+
# Local control number in the library catalogue
|
608
|
+
resource.local_control_number
|
609
|
+
|
610
|
+
# true if this is an online resource, false otherwise
|
611
|
+
resource.online_resource
|
612
|
+
|
613
|
+
# Page range
|
614
|
+
resource.page
|
615
|
+
|
616
|
+
# End page
|
617
|
+
resource.page_end
|
618
|
+
|
619
|
+
# Start page
|
620
|
+
resource.page_start
|
621
|
+
|
622
|
+
# Place of publication
|
623
|
+
resource.place_of_publication
|
624
|
+
|
625
|
+
# Publisher
|
626
|
+
resource.publisher
|
627
|
+
|
628
|
+
# Title of the resource
|
629
|
+
resource.title
|
630
|
+
|
631
|
+
# Type of the resource
|
632
|
+
resource.type
|
633
|
+
|
634
|
+
# URL of the resource
|
635
|
+
resource.url
|
636
|
+
|
637
|
+
# Volume
|
638
|
+
resource.volume
|
639
|
+
```
|
640
|
+
|
641
|
+
##### <a name="model-resource-linked"></a>Linked Resource Properties
|
642
|
+
|
643
|
+
Where resources are linked to other resources (e.g. chapters to books or
|
644
|
+
articles to journals) a number of shortcut properties are available. These
|
645
|
+
methods can be called on either the child or parent resource.
|
646
|
+
|
647
|
+
```ruby
|
648
|
+
# Article title for a journal or article resource
|
649
|
+
resource.article_title
|
650
|
+
|
651
|
+
# Book title for a book or chapter resource
|
652
|
+
resource.book_title
|
653
|
+
|
654
|
+
# Book chapter title for a book or chapter resource
|
655
|
+
resource.chapter_title
|
656
|
+
|
657
|
+
# Article or chapter title if available, otherwise the resource
|
658
|
+
# title
|
659
|
+
resource.citation_title
|
660
|
+
|
661
|
+
# Journal title for an article or journal resource
|
662
|
+
resource.journal_title
|
663
|
+
|
664
|
+
# Parent resource's title (book or journal title)
|
665
|
+
resource.part_of_title
|
666
|
+
|
667
|
+
# Child resource's title (article or chapter title)
|
668
|
+
resource.part_title
|
669
|
+
|
670
|
+
# Any resource property can be prefixed with "citation_"
|
671
|
+
# In this case, if the resource property is not set, the property of the
|
672
|
+
# parent resource is returned instead if applicable.
|
673
|
+
resource.citation_title # Returns resource.title or the parent title
|
674
|
+
resource.citation_isbn10 # Returns resource.isbn10 or the parent isbn10
|
675
|
+
```
|
676
|
+
|
677
|
+
#### <a name="model-digitisation"></a>Digitisation
|
678
|
+
|
679
|
+
`Aspire::Object::Digitisation` represents a Talis Digitised Content request
|
680
|
+
associated with a list item.
|
681
|
+
|
682
|
+
```ruby
|
683
|
+
# Get the digitisation request details from the list
|
684
|
+
digitisation = list.digitisation
|
685
|
+
|
686
|
+
# Digitisation bundle ID
|
687
|
+
digitisation.bundle_id
|
688
|
+
|
689
|
+
# Digitisation request ID
|
690
|
+
digitisation.request_id
|
691
|
+
|
692
|
+
# Digitisation request status
|
693
|
+
digitisation.request_status
|
694
|
+
```
|
695
|
+
|
696
|
+
#### <a name="model-module"></a>Module
|
697
|
+
|
698
|
+
`Aspire::Object::Module` represents a course module associated with a list.
|
699
|
+
|
700
|
+
```ruby
|
701
|
+
# Get the modules from the list
|
702
|
+
modules = list.modules
|
703
|
+
module = modules[0]
|
704
|
+
|
705
|
+
# Module code
|
706
|
+
module.code
|
707
|
+
|
708
|
+
# Module name
|
709
|
+
module.name
|
710
|
+
```
|
711
|
+
|
712
|
+
#### <a name="model-timeperiod"></a>TimePeriod
|
713
|
+
|
714
|
+
`Aspire::Object::TimePeriod` represents the time period covered by a list.
|
715
|
+
|
716
|
+
```ruby
|
717
|
+
# Get the time period from the list
|
718
|
+
period = list.time_period
|
719
|
+
|
720
|
+
# true if the list is currently within the time period, false otherwise
|
721
|
+
period.active
|
722
|
+
|
723
|
+
# End of the period as a Date
|
724
|
+
period.end_date
|
725
|
+
|
726
|
+
# Start of the period as a Date
|
727
|
+
period.start_date
|
728
|
+
|
729
|
+
# Title of the time period (e.g. "Winter Term 2016/17")
|
730
|
+
period.title
|
731
|
+
```
|
732
|
+
|
733
|
+
#### <a name="model-user"></a>User
|
734
|
+
|
735
|
+
`Aspire::Object::User` represents an Aspire user profile returned from the
|
736
|
+
[User Profile JSON API](http://docs.talisrl.apiary.io/reference/users/user-profile).
|
737
|
+
|
738
|
+
```ruby
|
739
|
+
# Get the list owner from the list
|
740
|
+
user = list.owner
|
741
|
+
|
742
|
+
# User first and last names
|
743
|
+
user.first_name
|
744
|
+
user.surname
|
745
|
+
|
746
|
+
# User email addresses as an array of strings
|
747
|
+
user.email
|
748
|
+
|
749
|
+
# User roles as an array of strings
|
750
|
+
user.role
|
751
|
+
```
|
752
|
+
|
753
|
+
#### Command-Line
|
754
|
+
|
755
|
+
### <a name="implementation"></a>Implementation Notes
|
756
|
+
|
757
|
+
#### <a name="implementation-structure"></a>Preserving List Structure
|
758
|
+
|
759
|
+
The Aspire Authenticated (JSON) API provides convenient access to resource list
|
760
|
+
items but does not preserve the ordering of list items, and supplies only the
|
761
|
+
immediately-enclosing section.
|
762
|
+
|
763
|
+
However, the Linked Data API includes sequencing data properties with keys of
|
764
|
+
the form `http://www.w3.org/1999/02/22-rdf-syntax-ns#_N` (where `N` is the
|
765
|
+
ordinal position of the item or section within its parent collection) whose
|
766
|
+
values are the URIs of the item or section. These properties allow the list
|
767
|
+
order to be recreated in the data model, and the nested section structure to be
|
768
|
+
recreated by recursively following the section URIs.
|
769
|
+
|
770
|
+
Consider the list:
|
771
|
+
* Item 1
|
772
|
+
* Section 2
|
773
|
+
* Item 2.1
|
774
|
+
* Item 2.2
|
775
|
+
* Item 2.3
|
776
|
+
* Section 3
|
777
|
+
* Item 3.1
|
778
|
+
* etc.
|
779
|
+
|
780
|
+
The Linked Data API response for the list will contain something like:
|
781
|
+
```json
|
782
|
+
{
|
783
|
+
“http://myinstitution.myreadinglists.org/lists/A56880F3-10B3-45EC-FD16-D29D0198AEE3": {
|
784
|
+
|
785
|
+
"http://www.w3.org/1999/02/22-rdf-syntax-ns#_1": [ {
|
786
|
+
"value": "http://myinstitution.myreadinglists.org/items/BEC9F28E-0663-751A-08D5-4729CBDD5991",
|
787
|
+
"type": "uri"
|
788
|
+
} ],
|
789
|
+
|
790
|
+
"http://www.w3.org/1999/02/22-rdf-syntax-ns#_2": [ {
|
791
|
+
"value": "http://myinstitution.myreadinglists.org/sections/5B357D24-3F3B-35FF-6EEC-3FD8964B523C",
|
792
|
+
"type": "uri"
|
793
|
+
} ],
|
794
|
+
|
795
|
+
"http://www.w3.org/1999/02/22-rdf-syntax-ns#_3": [ {
|
796
|
+
"value": "http://myinstitution.myreadinglists.org/sections/96DACBC0-EC2F-03CD-9A50-70082D2C1D83",
|
797
|
+
"type": "uri"
|
798
|
+
} ]
|
799
|
+
}
|
800
|
+
}
|
801
|
+
```
|
802
|
+
|
803
|
+
The Linked Data API response for "Section 2" will contain something like:
|
804
|
+
```json
|
805
|
+
{
|
806
|
+
"http://myinstitution.myreadinglists.org/sections/5B357D24-3F3B-35FF-6EEC-3FD8964B523C": {
|
807
|
+
|
808
|
+
"http://www.w3.org/1999/02/22-rdf-syntax-ns#_1": [ {
|
809
|
+
"value": "http://myinstitution.myreadinglists.org/items/B51508AB-5166-5CD5-30D5-9DF77BA461BB",
|
810
|
+
"type": "uri"
|
811
|
+
} ],
|
812
|
+
|
813
|
+
"http://www.w3.org/1999/02/22-rdf-syntax-ns#_2": [ {
|
814
|
+
"value": "http://myinstitution.myreadinglists.org/items/AD6F8D90-7EB6-9721-0FDE-3C26F7FA932C",
|
815
|
+
"type": "uri"
|
816
|
+
} ],
|
817
|
+
|
818
|
+
"http://www.w3.org/1999/02/22-rdf-syntax-ns#_3": [ {
|
819
|
+
"value": "http://myinstitution.myreadinglists.org/items/11234123-83EA-D529-8DA1-42BAAA64BF6F",
|
820
|
+
"type": "uri"
|
821
|
+
} ]
|
822
|
+
}
|
823
|
+
}
|
824
|
+
```
|
825
|
+
|
826
|
+
The data model for a list is built as follows:
|
827
|
+
1. get the Authenticated (JSON) API data for the list and build a Hash mapping
|
828
|
+
item URI to a ListItem instance
|
829
|
+
2. get the Linked Data API data for the list
|
830
|
+
3. build a List instance
|
831
|
+
4. for each sequencing data property in the list data
|
832
|
+
* if the value is an item URI, get the ListItem instance from the items Hash
|
833
|
+
* if the value is a section URI, build a ListSection instance (this
|
834
|
+
recursively creates subsections and list items)
|
835
|
+
* add the instance to the List's children at position `N`
|
836
|
+
|
837
|
+
## <a name="development"></a>Development
|
838
|
+
|
839
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
840
|
+
|
841
|
+
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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
842
|
+
|
843
|
+
## <a name="contributing"></a>Contributing
|
844
|
+
|
845
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/lulibrary/aspire. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
846
|
+
|
847
|
+
|
848
|
+
## <a name="license"></a>License
|
849
|
+
|
850
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
851
|
+
|