aspire 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +59 -0
  3. data/.rbenv-gemsets +1 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Dockerfile +20 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +851 -0
  10. data/Rakefile +10 -0
  11. data/aspire.gemspec +40 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/entrypoint.sh +11 -0
  15. data/exe/build-cache +13 -0
  16. data/lib/aspire.rb +11 -0
  17. data/lib/aspire/api.rb +2 -0
  18. data/lib/aspire/api/base.rb +198 -0
  19. data/lib/aspire/api/json.rb +195 -0
  20. data/lib/aspire/api/linked_data.rb +214 -0
  21. data/lib/aspire/caching.rb +4 -0
  22. data/lib/aspire/caching/builder.rb +356 -0
  23. data/lib/aspire/caching/cache.rb +365 -0
  24. data/lib/aspire/caching/cache_entry.rb +296 -0
  25. data/lib/aspire/caching/cache_logger.rb +63 -0
  26. data/lib/aspire/caching/util.rb +210 -0
  27. data/lib/aspire/cli/cache_builder.rb +123 -0
  28. data/lib/aspire/cli/command.rb +20 -0
  29. data/lib/aspire/enumerator/base.rb +29 -0
  30. data/lib/aspire/enumerator/json_enumerator.rb +130 -0
  31. data/lib/aspire/enumerator/linked_data_uri_enumerator.rb +32 -0
  32. data/lib/aspire/enumerator/report_enumerator.rb +64 -0
  33. data/lib/aspire/exceptions.rb +36 -0
  34. data/lib/aspire/object.rb +7 -0
  35. data/lib/aspire/object/base.rb +155 -0
  36. data/lib/aspire/object/digitisation.rb +43 -0
  37. data/lib/aspire/object/factory.rb +87 -0
  38. data/lib/aspire/object/list.rb +590 -0
  39. data/lib/aspire/object/module.rb +36 -0
  40. data/lib/aspire/object/resource.rb +371 -0
  41. data/lib/aspire/object/time_period.rb +47 -0
  42. data/lib/aspire/object/user.rb +46 -0
  43. data/lib/aspire/properties.rb +20 -0
  44. data/lib/aspire/user_lookup.rb +103 -0
  45. data/lib/aspire/util.rb +185 -0
  46. data/lib/aspire/version.rb +3 -0
  47. data/lib/retry.rb +197 -0
  48. metadata +274 -0
@@ -0,0 +1,87 @@
1
+ require 'aspire/object/list'
2
+ require 'aspire/object/module'
3
+ require 'aspire/object/resource'
4
+ require 'aspire/util'
5
+
6
+ module Aspire
7
+ module Object
8
+ # A factory returning reading list objects given the object's URI
9
+ class Factory
10
+ include Aspire::Util
11
+
12
+ # @!attribute [rw] cache
13
+ # @return [Aspire::Caching::Cache] the cache for retrieving data
14
+ attr_accessor :cache
15
+
16
+ # @!attribute [rw] users
17
+ # @return [Hash<String, Aspire::Object::User>] a hash of user profiles
18
+ # indexed by URI
19
+ attr_accessor :users
20
+
21
+ # Initialises a new Factory instance
22
+ # @param cache [Aspire::Caching::Cache] the cache for retrieving data
23
+ # @param users [Hash<String, Aspire::Object::User>] a hash of user
24
+ # profiles indexed by URI
25
+ # @return [void]
26
+ def initialize(cache, users = nil)
27
+ self.cache = cache
28
+ self.users = users || {}
29
+ end
30
+
31
+ # Returns a new API list object (Aspire::Object::ListBase subclass) given
32
+ # its URI
33
+ # @param uri [String] the URI of the object
34
+ # @param parent [Aspire::Object::ListBase] this object's parent object
35
+ # @param json [Hash] the parsed JSON API data for the object
36
+ # @param ld [Hash] the parsed linked data API data for the object
37
+ # @return [Aspire::Object::ListBase] the list object
38
+ def get(uri = nil, parent = nil, json: nil, ld: nil)
39
+ return nil if uri.nil? || uri.empty?
40
+ get_exceptions(uri, parent, json: json, ld: ld) ||
41
+ get_linked_data(uri, parent, json: json, ld: ld)
42
+ end
43
+
44
+ private
45
+
46
+ # Returns a new object from sources other than the linked data API
47
+ # @param uri [String] the URI of the object
48
+ # @param parent [Aspire::Object::ListBase] this object's parent object
49
+ # @param json [Hash] the parsed JSON API data for the object
50
+ # @param ld [Hash] the parsed linked data API data for the object
51
+ # @return [Aspire::Object::ListBase, nil] the list object or nil if not
52
+ # available
53
+ def get_exceptions(uri = nil, parent = nil, json: nil, ld: nil)
54
+ # Get item data from the parent list's JSON API data
55
+ return ListItem.new(uri, self, parent) if item?(uri)
56
+ # Get resource data from the JSON API
57
+ if resource?(uri) && !json.nil?
58
+ return Resource.new(uri, self, json: json, ld: ld)
59
+ end
60
+ # Get user data from the users lookup table
61
+ # - normalise the URI to the form used by the linked data API
62
+ return users[cache.linked_data_url(uri), self] if user?(uri)
63
+ # Otherwise no exceptions
64
+ nil
65
+ end
66
+
67
+ # Returns a new object from the linked data API
68
+ # @param uri [String] the URI of the object
69
+ # @param parent [Aspire::Object::ListBase] this object's parent object
70
+ # @param json [Hash] the parsed JSON API data for the object
71
+ # @param ld [Hash] the parsed linked data API data for the object
72
+ # @return [Aspire::Object::ListBase, nil] the list object or nil if not
73
+ # available
74
+ def get_linked_data(uri, parent = nil, json: nil, ld: nil)
75
+ # Call #linked_data to determine whether uri is present in ld
76
+ ld = linked_data(uri, ld) || cache.read(uri)
77
+ return List.new(uri, self, parent, json: json, ld: ld) if list?(uri)
78
+ return Module.new(uri, self, json: json, ld: ld) if module?(uri)
79
+ return Resource.new(uri, self, json: json, ld: ld) if resource?(uri)
80
+ if section?(uri)
81
+ return ListSection.new(uri, self, parent, json: json, ld: ld)
82
+ end
83
+ nil
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,590 @@
1
+ require 'aspire/object/base'
2
+ require 'aspire/properties'
3
+
4
+ module Aspire
5
+ module Object
6
+ # The abstract base class of reading list objects (items, lists, sections)
7
+ class ListBase < Aspire::Object::Base
8
+ # The Aspire linked data API returns properties of the form
9
+ # "#{KEY_PREFIX}_n" where n is a 1-based numeric index denoting the
10
+ # display order of the property.
11
+ KEY_PREFIX = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'.freeze
12
+
13
+ # @!attribute [rw] entries
14
+ # @return [Array<Aspire::Object::ListBase>] the ordered list of child
15
+ # objects
16
+ attr_accessor :entries
17
+
18
+ # @!attribute [rw] parent
19
+ # @return [Aspire::Object::ListBase] the parent reading list object of
20
+ # this object
21
+ attr_accessor :parent
22
+
23
+ # Initialises a new ListBase instance
24
+ # @param uri [String] the reading list object URI (item/list/section)
25
+ # @param factory [Aspire::Object::Factory] a factory returning ListBase
26
+ # subclass instances
27
+ # @param parent [Aspire::Object::ListBase] the parent reading list object
28
+ # of this object
29
+ # @param json [Hash] the parsed JSON data from the Aspire JSON API
30
+ # @param ld [Hash] the parsed JSON data from the Aspire linked data API
31
+ # @return [void]
32
+ def initialize(uri, factory, parent = nil, json: nil, ld: nil)
33
+ super(uri, factory)
34
+ self.parent = parent
35
+ self.entries = get_entries(ld: ld)
36
+ end
37
+
38
+ # Iterates over the child reading list objects in display order
39
+ # @yield [entry] passes the child reading list object to the block
40
+ # @yieldparam entry [Aspire::Object::ListBase] the reading list object
41
+ def each(&block)
42
+ entries.each(&block)
43
+ end
44
+
45
+ # Iterates over the child list items in display order (depth-first tree
46
+ # traversal)
47
+ # @yield [entry] passes the list item to the block
48
+ # @yieldparam entry [Aspire::Object::ListItem] the reading list item
49
+ # @return [void]
50
+ def each_item(&block)
51
+ each do |entry|
52
+ if entry.is_a?(ListItem)
53
+ # Pass the list item to the block
54
+ yield(entry) if block_given?
55
+ else
56
+ # Iterate the entry's list items
57
+ entry.each_item(&block)
58
+ end
59
+ end
60
+ nil
61
+ end
62
+
63
+ # Iterates over the child list sections in display order (depth-first tree
64
+ # traversal)
65
+ # @yield [entry] passes the list section to the block
66
+ # @yieldparam entry [Aspire::Object::ListSection] the reading list section
67
+ # @return [void]
68
+ def each_section(&block)
69
+ each do |entry|
70
+ if entry.is_a?(List)
71
+ # Iterate the list's sections
72
+ entry.each_section(&block)
73
+ elsif entry.is_a?(ListSection)
74
+ # Pass the list section to the block
75
+ yield(entry) if block_given?
76
+ end
77
+ end
78
+ nil
79
+ end
80
+
81
+ # Returns a list of child reading list objects in display order
82
+ # @param ld [Hash] the parsed JSON data from the Aspire linked data API
83
+ # @return [Array<Aspire::Object::ListBase>] the ordered list of child
84
+ # objects
85
+ def get_entries(ld: nil)
86
+ entries = []
87
+ data = linked_data(uri, ld)
88
+ return entries unless data
89
+ data.each { |key, value| get_ordered_entry(key, value, entries, ld) }
90
+ entries
91
+ end
92
+
93
+ # Returns the child items of this object in display order
94
+ # @return [Array<Aspire::Object::ListItem>] the child list items
95
+ def items
96
+ result = []
97
+ each_item { |item| result.push(item) }
98
+ result
99
+ end
100
+
101
+ # Returns the number of items in the list
102
+ # @param item_type [Symbol] selects the list entry type to count
103
+ # :entry = top-level item or section
104
+ # :item = list item (default)
105
+ # :section = top-level section
106
+ # @return [Integer] the number of list entry instances
107
+ def length(item_type = nil)
108
+ item_type ||= :item
109
+ # Return the number of top-level entries (items and sections)
110
+ return entries.length if item_type == :entry
111
+ # Return the sum of the number of list items in each entry
112
+ if item_type == :item
113
+ entries.reduce(0) { |count, entry| count + entry.length(:item) }
114
+ end
115
+ # Return the number of top-level sections
116
+ return sections.length if item_type == :section
117
+ # Otherwise return 0 for unknown item types
118
+ 0
119
+ end
120
+
121
+ # Returns the parent list of this object
122
+ # @return [Aspire::Object::List] the parent reading list
123
+ def parent_list
124
+ parent_lists[0]
125
+ end
126
+
127
+ # Returns the ancestor lists of this object (nearest ancestor first)
128
+ # @return [Array<Aspire::Object::List>] the ancestor reading lists
129
+ def parent_lists
130
+ parents(List)
131
+ end
132
+
133
+ # Returns the parent section of this object
134
+ # @return [Aspire::Object::ListSection] the parent reading list section
135
+ def parent_section
136
+ parent_sections[0]
137
+ end
138
+
139
+ # Returns the ancestor sections of this object (nearest ancestor first)
140
+ # @return [Array<Aspire::Object::ListSection>] the ancestor reading list
141
+ # sections
142
+ def parent_sections
143
+ parents(ListSection)
144
+ end
145
+
146
+ # Returns a list of ancestor reading list objects of this object (nearest
147
+ # ancestor first)
148
+ # Positional parameters are the reading list classes to include in the
149
+ # result. If no classes are specified, all classes are included.
150
+ # @yield [ancestor] passes the ancestor to the block
151
+ # @yieldparam ancestor [Aspire::Object::ListBase] the reading list object
152
+ # @yieldreturn [Boolean] if true, include in the ancestor list, otherwise
153
+ # ignore
154
+ def parents(*classes, &block)
155
+ result = []
156
+ ancestor = parent
157
+ until ancestor.nil?
158
+ result.push(ancestor) if parents_include?(ancestor, *classes, &block)
159
+ ancestor = ancestor.parent
160
+ end
161
+ result
162
+ end
163
+
164
+ # Returns true if ancestor should be included as a parent, false otherwise
165
+ # @param ancestor [Aspire::Object::ListBase] the reading list object
166
+ # Remaining positional parameters are the reading list classes to include
167
+ # in the result. If no classes are specified, all classes are included.
168
+ # @yield [ancestor] passes the ancestor to the block
169
+ # @yieldparam ancestor [Aspire::Object::ListBase]
170
+ # the reading list object
171
+ # @yieldreturn [Boolean] if true, include in the ancestor list, otherwise
172
+ # ignore
173
+ def parents_include?(ancestor, *classes)
174
+ # Filter ancestors by class
175
+ if classes.nil? || classes.empty? || classes.include?(ancestor.class)
176
+ # The ancestor is allowed by class, but may be disallowed by a code
177
+ # block which returns false. If the code block returns true or is not
178
+ # given, the ancestor is included.
179
+ return block_given? && !yield(ancestor) ? false : true
180
+ end
181
+ # Otherwise the ancestor is not allowed by class
182
+ false
183
+ end
184
+
185
+ # Returns the child sections of this object
186
+ # @return [Array<Aspire::Object::ListSection>] the child list sections
187
+ def sections
188
+ entries.select { |e| e.is_a?(ListSection) }
189
+ end
190
+
191
+ private
192
+
193
+ # Adds a child object to the entries array if key is an ordered property
194
+ # @param key [String] the property name URI
195
+ # @param value [Hash] the property value hash
196
+ # @param entries [Array<Aspire::Object::ListBase>] the ordered list of
197
+ # child objects
198
+ # @param ld [Hash] the parsed JSON data from the Aspire linked data API
199
+ # @return [Aspire::Object::ListBase] the list object
200
+ def get_ordered_entry(key, value, entries, ld)
201
+ prefix, index = key.split('_')
202
+ return nil unless prefix == KEY_PREFIX
203
+ uri = value[0]['value']
204
+ entries[index.to_i - 1] = factory.get(uri, self, ld: ld)
205
+ end
206
+ end
207
+
208
+ # Represents a reading list in the Aspire API
209
+ class List < ListBase
210
+ include Aspire::Properties
211
+
212
+ # @!attribute [rw] created
213
+ # @return [DateTime] the creation timestamp of the list
214
+ attr_accessor :created
215
+
216
+ # @!attribute [rw] creator
217
+ # @return [Array<Aspire::Object::User>] the reading list creators
218
+ attr_accessor :creator
219
+
220
+ # @!attribute [rw] description
221
+ # @return [String] the description of the list
222
+ attr_accessor :description
223
+
224
+ # @!attribute [rw] items
225
+ # @return [Hash<String, Aspire::Object::ListItem>] a hash of ListItems
226
+ # indexed by item URI
227
+ attr_accessor :items
228
+
229
+ # @!attribute [rw] last_published
230
+ # @return [DateTime] the timestamp of the most recent list publication
231
+ attr_accessor :last_published
232
+
233
+ # @!attribute [rw] last_updated
234
+ # @return [DateTime] the timestamp of the most recent list update
235
+ attr_accessor :last_updated
236
+
237
+ # @!attribute [rw] modules
238
+ # @return [Array<Aspire::Object::Module>] the modules referencing this
239
+ # list
240
+ attr_accessor :modules
241
+
242
+ # @!attribute [rw] name
243
+ # @return [String] the reading list name
244
+ attr_accessor :name
245
+
246
+ # @!attribute [rw] owner
247
+ # @return [Aspire::Object::User] the list owner
248
+ attr_accessor :owner
249
+
250
+ # @!attribute [rw] publisher
251
+ # @return [Aspire::Object::User] the list publisher
252
+ attr_accessor :publisher
253
+
254
+ # @!attribute [rw] time_period
255
+ # @return [Aspire::Object::TimePeriod] the period covered by the list
256
+ attr_accessor :time_period
257
+
258
+ # Initialises a new List instance
259
+ # @param uri [String] the URI of the object
260
+ # @param factory [Aspire::Object::Factory] a factory returning ListBase
261
+ # subclass instances
262
+ # @param parent [Aspire::Object::ListBase] this object's parent object
263
+ # @param json [Hash] the parsed JSON data from the Aspire JSON API
264
+ # @param ld [Hash] the parsed JSON data from the Aspire linked data API
265
+ # @return [void]
266
+ def initialize(uri, factory, parent = nil, json: nil, ld: nil)
267
+ # Set properties from the Reading Lists JSON API
268
+ # - this must be called before the superclass constructor so that item
269
+ # details are available
270
+ init_json_data(uri, factory, json)
271
+ # Initialise the superclass
272
+ super(uri, factory, parent, json: json, ld: ld)
273
+ # Set properties from the linked data API data
274
+ init_linked_data(ld)
275
+ end
276
+
277
+ # Returns the number of items in the list
278
+ # @see (Aspire::Object::ListBase#length)
279
+ def length(item_type = nil)
280
+ item_type ||= :item
281
+ # The item length of a list is the length of the items property,
282
+ # avoiding the need to sum list entry lengths
283
+ item_type == :item ? items.length : super(item_type)
284
+ end
285
+
286
+ # Returns a string representation of the List instance (the name)
287
+ # @return [String] the string representation of the List instance
288
+ def to_s
289
+ name || super
290
+ end
291
+
292
+ private
293
+
294
+ # Retrieves the list details and history from the Aspire JSON API
295
+ # @param uri [String] the URI of the object
296
+ # @param factory [Aspire::Object::Factory] a factory returning ListBase
297
+ # subclass instances
298
+ # @param json [Hash] the parsed JSON data from the Aspire JSON API
299
+ # @return [void]
300
+ def init_json_data(uri, factory, json = nil)
301
+ init_json_defaults
302
+ # Get the list details
303
+ json ||= factory.cache.read(uri, json: true)
304
+ if json
305
+ self.name = json['name']
306
+ init_json_items(json['items'])
307
+ init_json_modules(json['modules'], factory)
308
+ init_json_time_period(json['timePeriod'], factory)
309
+ end
310
+ # Return the parsed JSON data from the Aspire list details JSON API
311
+ json
312
+ end
313
+
314
+ # Sets the property defaults for JSON API fields
315
+ # @return [void]
316
+ def init_json_defaults
317
+ # Default values
318
+ self.modules = nil
319
+ self.name = nil
320
+ self.time_period = nil
321
+ end
322
+
323
+ # Builds a mapping from item URI to JSON data for items from the JSON API
324
+ # @param items [Array<Hash>] the parsed JSON data for the items array
325
+ # @return [void]
326
+ def init_json_items(items)
327
+ # A hash mapping item URI to item
328
+ self.items = {}
329
+ return unless items
330
+ items.each { |item| self.items[item['uri']] = item }
331
+ end
332
+
333
+ # Builds a list of Module instances for modules from the JSON API
334
+ # @param mods [Array<Hash>] the parsed JSON data for the modules array
335
+ # @param factory [Aspire::Object::Factory] a factory returning ListBase
336
+ # subclass instances
337
+ # @return [void]
338
+ def init_json_modules(mods, factory)
339
+ return unless mods
340
+ self.modules = mods.map { |m| Module.new(m['uri'], factory, json: m) }
341
+ end
342
+
343
+ # Sets the time period for the list from the JSON API
344
+ # @param period [Array<Hash>] the parsed JSON data for the time period
345
+ # @param factory [Aspire::Object::Factory] a factory returning ListBase
346
+ # subclass instances
347
+ # @return [void]
348
+ def init_json_time_period(period, factory)
349
+ self.time_period = if period
350
+ TimePeriod.new(period['uri'], factory,
351
+ json: period)
352
+ end
353
+ end
354
+
355
+ # Sets reading list properties from the Aspire linked data API
356
+ # @return [void]
357
+ def init_linked_data(ld = nil)
358
+ list_data = linked_data(uri, ld)
359
+ init_linked_data_creator(list_data, ld)
360
+ init_linked_data_modules(list_data, ld)
361
+ init_linked_data_owner(list_data, ld)
362
+ init_linked_data_publisher(list_data, ld)
363
+ self.created = get_date(CREATED, list_data)
364
+ self.description = get_property(DESCRIPTION, list_data)
365
+ self.last_published = get_date(LAST_PUBLISHED, list_data)
366
+ self.last_updated = get_date(LAST_UPDATED, list_data)
367
+ self.name = get_property(NAME, list_data) unless name
368
+ end
369
+
370
+ # Sets the reading list creator
371
+ # @param list_data [Hash] the parsed JSON data for the list from the
372
+ # Aspire linked data API
373
+ # @param ld [Hash] the parsed JSON data from the Aspire linked data API
374
+ # @return [void]
375
+ def init_linked_data_creator(list_data, ld)
376
+ has_creator = get_property(HAS_CREATOR, list_data, single: false) || []
377
+ self.creator = has_creator.map { |u| factory.get(u, ld: ld) }
378
+ end
379
+
380
+ # Sets the list modules
381
+ # @param list_data [Hash] the parsed JSON data for the list from the
382
+ # Aspire linked data API
383
+ # @param ld [Hash] the parsed JSON data from the Aspire linked data API
384
+ # @return [void]
385
+ def init_linked_data_modules(list_data, ld)
386
+ return unless modules.nil?
387
+ mods = get_property(USED_BY, list_data, single: false) || []
388
+ self.modules = mods.map { |u| factory.get(u, ld: ld) } if mods
389
+ end
390
+
391
+ # Sets the list owner
392
+ # @param list_data [Hash] the parsed JSON data for the list from the
393
+ # Aspire linked data API
394
+ # @param ld [Hash] the parsed JSON data from the Aspire linked data API
395
+ # @return [void]
396
+ def init_linked_data_owner(list_data, ld)
397
+ has_owner = get_property(HAS_OWNER, list_data, single: false) || []
398
+ self.owner = has_owner.map { |u| factory.get(u, ld: ld) }
399
+ end
400
+
401
+ # Sets the list publisher
402
+ # @param list_data [Hash] the parsed JSON data for the list from the
403
+ # Aspire linked data API
404
+ # @param ld [Hash] the parsed JSON data from the Aspire linked data API
405
+ # @return [void]
406
+ def init_linked_data_publisher(list_data, ld)
407
+ published_by = get_property(PUBLISHED_BY, list_data)
408
+ self.publisher = factory.get(published_by, ld: ld)
409
+ end
410
+ end
411
+
412
+ # Represents a reading list item (citation) in the Aspire API
413
+ class ListItem < ListBase
414
+ # @!attribute [rw] digitisation
415
+ # @return [Aspire::Object::Digitisation]
416
+ # the digitisation details for the item
417
+ attr_accessor :digitisation
418
+
419
+ # @!attribute [rw] importance
420
+ # @return [String] the importance of the item
421
+ attr_accessor :importance
422
+
423
+ # @!attribute [rw] library_note
424
+ # @return [String] the internal library note for the item
425
+ attr_accessor :library_note
426
+
427
+ # @!attribute [rw] local_control_number
428
+ # @return [String] the identifier of the resource in the local library
429
+ # management system
430
+ attr_accessor :local_control_number
431
+
432
+ # @!attribute [rw] note
433
+ # @return [String] the public note for the item
434
+ attr_accessor :note
435
+
436
+ # @!attribute [rw] resource
437
+ # @return [Aspire::Object::Resource] the resource for
438
+ # the item
439
+ attr_accessor :resource
440
+
441
+ # @!attribute [rw] student_note
442
+ # @return [String] the public note for the item
443
+ attr_accessor :student_note
444
+
445
+ # @!attribute [rw] title
446
+ # @return [String] the title of the item
447
+ attr_accessor :title
448
+
449
+ # Initialises a new ListItem instance
450
+ # @param uri [String] the reading list object URI (item/list/section)
451
+ # @param factory [Aspire::Object::Factory]
452
+ # a factory returning ReadingListBase subclass instances
453
+ # @param parent [Aspire::Object::ListBase]
454
+ # the parent reading list object of this object
455
+ # @param json [Hash] the parsed JSON data from the Aspire JSON API
456
+ # @param ld [Hash] the parsed JSON data from the Aspire linked data API
457
+ # @return [void]
458
+ def initialize(uri, factory, parent = nil, json: nil, ld: nil)
459
+ super(uri, factory, parent, json: json, ld: ld)
460
+ json ||= init_list_items
461
+ init_digitisation(json)
462
+ init_resource(json)
463
+ self.importance = get_property('importance', json)
464
+ self.library_note = get_property('libraryNote', json)
465
+ self.local_control_number = get_property('lcn', json)
466
+ self.note = get_property('note', json)
467
+ self.student_note = get_property('studentNote', json)
468
+ self.title = get_property('title', json)
469
+ end
470
+
471
+ # Returns the length of the list item
472
+ # @see (Aspire::Object::ListBase#length)
473
+ def length(item_type = nil)
474
+ item_type ||= :item
475
+ # List items return an item length of 1 to enable summation of
476
+ # list/section lengths
477
+ item_type == :item ? 1 : super(item_type)
478
+ end
479
+
480
+ # Returns the public (student or general) note
481
+ # @return [String] the student note or general note
482
+ def public_note
483
+ student_note || note
484
+ end
485
+
486
+ # Returns the resource title or public note if no resource is available
487
+ # @param alt [Symbol] the alternative if no resource is available
488
+ # :library_note or :private_note = the library note
489
+ # :note = the student note, or the library note if no student note is
490
+ # available
491
+ # :public_note, :student_note = the student note
492
+ # :uri = the list item URI
493
+ # @return [String] the resource title or alternative
494
+ def title(alt = nil)
495
+ # Return the resource title if available
496
+ return resource.title || @title if resource
497
+ # Otherwise return the specified alternative
498
+ title_alt(alt)
499
+ end
500
+
501
+ # Returns a string representation of the ListItem instance (the citation
502
+ # title or note)
503
+ # @return [String] the string representation of the ListItem instance
504
+ def to_s
505
+ title(:public_note).to_s
506
+ end
507
+
508
+ private
509
+
510
+ # Returns the digitisation request JSON data if available, nil if not
511
+ # @param json [Hash] the JSON API data
512
+ # @return [void]
513
+ def init_digitisation(json)
514
+ dig_json = json ? json['digitisation'] : nil
515
+ dig_ld = nil
516
+ self.digitisation = if dig_json || dig_ld
517
+ Digitisation.new(json: dig_json, ld: dig_ld)
518
+ end
519
+ end
520
+
521
+ # Returns the list of JSON API list items from the parent list
522
+ # @return [Array<Hash>] the list items, or nil if none are available
523
+ def init_list_items
524
+ owner = parent_list
525
+ owner && owner.items ? owner.items[uri] : nil
526
+ end
527
+
528
+ # Sets the resource
529
+ # @param json [Hash] the parsed JSON resource data
530
+ # @return nil
531
+ def init_resource(json)
532
+ res_json = json ? json['resource'] : nil
533
+ if res_json.is_a?(Array)
534
+ res_json = res_json.empty? ? nil : res_json[0]
535
+ puts("WARNING: used first resource of #{res_json}") if res_json
536
+ end
537
+ self.resource = factory.get(res_json['uri'], json: res_json) if res_json
538
+ # resource_json = json ? json['resource'] : nil
539
+ # resource_uri = get_property('http://purl.org/vocab/resourcelist/schema#resource', item_ld)
540
+ # resource = resource_json # || resource_uri ? factory.get(resource_uri, json: resource_json, ld: ld) : nil
541
+ end
542
+
543
+ # Returns the alternative resource title
544
+ # @param (see #title)
545
+ # @return [String] the alternative to the resource title
546
+ def title_alt(alt)
547
+ return library_note if %w[library_note private_note].include?(alt)
548
+ return public_note || library_note if alt == :note
549
+ return public_note if %w[public_note student_note].include?(alt)
550
+ return uri if alt == :uri
551
+ nil
552
+ end
553
+ end
554
+
555
+ # Represents a reading list section in the Aspire API
556
+ class ListSection < ListBase
557
+ include Aspire::Properties
558
+
559
+ # @!attribute [rw] description
560
+ # @return [String] the reading list section description
561
+ attr_accessor :description
562
+
563
+ # @!attribute [rw] name
564
+ # @return [String] the reading list section name
565
+ attr_accessor :name
566
+
567
+ # Initialises a new ListSection instance
568
+ # @param uri [String] the URI of the object
569
+ # @param factory [Aspire::Object::Factory] a factory returning ListBase
570
+ # subclass instances
571
+ # @param parent [Aspire::Object::ListBase] this object's parent object
572
+ # @param json [Hash] the parsed JSON data from the Aspire JSON API
573
+ # @param ld [Hash] the parsed JSON data from the Aspire linked data API
574
+ # @return [void]
575
+ def initialize(uri, factory, parent = nil, json: nil, ld: nil)
576
+ super(uri, factory, parent, json: json, ld: ld)
577
+ section_ld = linked_data(uri, ld)
578
+ self.description = get_property(DESCRIPTION, section_ld)
579
+ self.name = get_property(NAME, section_ld)
580
+ end
581
+
582
+ # Returns a string representation of the ListSection instance (the section
583
+ # name)
584
+ # @return [String] the string representation of the ListSection instance
585
+ def to_s
586
+ name || super
587
+ end
588
+ end
589
+ end
590
+ end