iiif-presentation 1.0.0 → 1.2.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +35 -0
  3. data/.gitignore +1 -0
  4. data/Gemfile +2 -0
  5. data/README.md +22 -2
  6. data/VERSION +1 -1
  7. data/iiif-presentation.gemspec +1 -1
  8. data/lib/iiif/hash_behaviours.rb +1 -1
  9. data/lib/iiif/presentation/canvas.rb +4 -0
  10. data/lib/iiif/presentation/service.rb +12 -0
  11. data/lib/iiif/presentation.rb +5 -4
  12. data/lib/iiif/service.rb +40 -105
  13. data/lib/iiif/v3/abstract_resource.rb +491 -0
  14. data/lib/iiif/v3/presentation/annotation.rb +74 -0
  15. data/lib/iiif/v3/presentation/annotation_collection.rb +38 -0
  16. data/lib/iiif/v3/presentation/annotation_page.rb +53 -0
  17. data/lib/iiif/v3/presentation/canvas.rb +82 -0
  18. data/lib/iiif/v3/presentation/choice.rb +51 -0
  19. data/lib/iiif/v3/presentation/collection.rb +52 -0
  20. data/lib/iiif/v3/presentation/image_resource.rb +110 -0
  21. data/lib/iiif/v3/presentation/manifest.rb +82 -0
  22. data/lib/iiif/v3/presentation/range.rb +39 -0
  23. data/lib/iiif/v3/presentation/resource.rb +30 -0
  24. data/lib/iiif/v3/presentation/sequence.rb +66 -0
  25. data/lib/iiif/v3/presentation/service.rb +51 -0
  26. data/lib/iiif/v3/presentation.rb +36 -0
  27. data/spec/fixtures/v3/manifests/complete_from_spec.json +195 -0
  28. data/spec/fixtures/v3/manifests/minimal.json +49 -0
  29. data/spec/fixtures/v3/manifests/service_only.json +14 -0
  30. data/spec/fixtures/vcr_cassettes/pul_loris_cassette.json +1 -1
  31. data/spec/fixtures/vcr_cassettes/pul_loris_cassette_v3.json +1 -0
  32. data/spec/integration/iiif/presentation/image_resource_spec.rb +0 -1
  33. data/spec/integration/iiif/service_spec.rb +17 -32
  34. data/spec/integration/iiif/v3/abstract_resource_spec.rb +202 -0
  35. data/spec/integration/iiif/v3/presentation/image_resource_spec.rb +118 -0
  36. data/spec/spec_helper.rb +6 -0
  37. data/spec/unit/iiif/presentation/canvas_spec.rb +0 -1
  38. data/spec/unit/iiif/presentation/manifest_spec.rb +1 -1
  39. data/spec/unit/iiif/v3/abstract_resource_define_methods_for_spec.rb +78 -0
  40. data/spec/unit/iiif/v3/abstract_resource_spec.rb +293 -0
  41. data/spec/unit/iiif/v3/presentation/annotation_collection_spec.rb +36 -0
  42. data/spec/unit/iiif/v3/presentation/annotation_page_spec.rb +131 -0
  43. data/spec/unit/iiif/v3/presentation/annotation_spec.rb +389 -0
  44. data/spec/unit/iiif/v3/presentation/canvas_spec.rb +337 -0
  45. data/spec/unit/iiif/v3/presentation/choice_spec.rb +120 -0
  46. data/spec/unit/iiif/v3/presentation/collection_spec.rb +55 -0
  47. data/spec/unit/iiif/v3/presentation/image_resource_spec.rb +189 -0
  48. data/spec/unit/iiif/v3/presentation/manifest_spec.rb +370 -0
  49. data/spec/unit/iiif/v3/presentation/range_spec.rb +54 -0
  50. data/spec/unit/iiif/v3/presentation/resource_spec.rb +174 -0
  51. data/spec/unit/iiif/v3/presentation/sequence_spec.rb +222 -0
  52. data/spec/unit/iiif/v3/presentation/service_spec.rb +220 -0
  53. data/spec/unit/iiif/v3/presentation/shared_examples/abstract_resource_only_keys.rb +41 -0
  54. data/spec/unit/iiif/v3/presentation/shared_examples/any_type_keys.rb +31 -0
  55. data/spec/unit/iiif/v3/presentation/shared_examples/array_only_keys.rb +40 -0
  56. data/spec/unit/iiif/v3/presentation/shared_examples/hash_only_keys.rb +40 -0
  57. data/spec/unit/iiif/v3/presentation/shared_examples/int_only_keys.rb +45 -0
  58. data/spec/unit/iiif/v3/presentation/shared_examples/numeric_only_keys.rb +45 -0
  59. data/spec/unit/iiif/v3/presentation/shared_examples/string_only_keys.rb +26 -0
  60. data/spec/unit/iiif/v3/presentation/shared_examples/uri_only_keys.rb +31 -0
  61. metadata +82 -11
  62. data/.travis.yml +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 595091b364990a3c3c5119ad55629ad137b9f59ad2b59b5fd0ae763b12afc340
4
- data.tar.gz: ad8be940ec5dd37006b619456d23eb0d410f201a36587a6045917739ba69c94f
3
+ metadata.gz: a766530b4d9f912c49a41e9c6d5982282671c4188a26b27e5a4fb99176fae90c
4
+ data.tar.gz: bbb25e477c3563ab2a65ec0de5bfc634acffa93c00e7504992dfe539f0e89ad2
5
5
  SHA512:
6
- metadata.gz: 23600bbc87868cc9b7d0455228d0c7f8a1dca235a4e732b343e9dc0987f485f0dd4e59f64ae6434afec341ba8ae36add4ab48b91a0bc4d2be9e94596879b247e
7
- data.tar.gz: ddd645e0cb9fd3464547826504b4078798e02cb06d766c6b9e10fb28d7b4999e0a643414f7ddb737bf91d2469e6693e7cbdbb556bd73d191d50063442c3dae18
6
+ metadata.gz: ac981b561c8e99f41891f2e5246bf46e6de9766abde2269e5eb46e35baeabb4a74404366b8e83ff2eef4e8656ddcb3e8946cd07bfcf14a96c40ba10b29f2fab7
7
+ data.tar.gz: 126391967ebc6b1e3db349daed94e3aad1ba43f8083729c49b0c976131f5e3c85f8f507713d0f04721f98c494b0193b761a714f1129b9b044d0dcf181e21e112
@@ -0,0 +1,35 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ branches: [ "main" ]
13
+ pull_request:
14
+ branches: [ "main" ]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ test:
21
+
22
+ runs-on: ubuntu-latest
23
+ strategy:
24
+ matrix:
25
+ ruby-version: ['2.7', '3.0', '3.1', '3.2']
26
+
27
+ steps:
28
+ - uses: actions/checkout@v3
29
+ - name: Set up Ruby
30
+ uses: ruby/setup-ruby@v1
31
+ with:
32
+ ruby-version: ${{ matrix.ruby-version }}
33
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
34
+ - name: Run tests
35
+ run: bundle exec rake
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  coverage/
2
2
  pkg/
3
3
  Gemfile.lock
4
+ .pry_history
4
5
 
data/Gemfile CHANGED
@@ -8,3 +8,5 @@ end
8
8
  if ENV['FARADAY_VERSION']
9
9
  gem 'faraday', ENV['FARADAY_VERSION']
10
10
  end
11
+
12
+ gem 'debug'
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # O'Sullivan: A Ruby API for working with IIIF Presentation manifests
2
2
 
3
- [![Build Status](https://travis-ci.org/iiif-prezi/osullivan.svg?branch=development)](https://travis-ci.org/iiif-prezi/osullivan)
4
3
  [![Coverage Status](https://coveralls.io/repos/github/iiif-prezi/osullivan/badge.svg?branch=development)](https://coveralls.io/github/iiif-prezi/osullivan?branch=development)
4
+ [![Gem Version](https://badge.fury.io/rb/iiif-presentation.svg)](https://badge.fury.io/rb/iiif-presentation)
5
5
 
6
6
 
7
7
  ## Installation
@@ -25,6 +25,11 @@ seed = {
25
25
  # Any options you add are added to the object
26
26
  manifest = IIIF::Presentation::Manifest.new(seed)
27
27
 
28
+ # sequences array is generated for you, but let's add a sequence object
29
+ sequence = IIIF::Presentation::Sequence.new()
30
+ sequence['@id'] = "http://example.com/manifest/seq/"
31
+ manifest.sequences << sequence
32
+
28
33
  canvas = IIIF::Presentation::Canvas.new()
29
34
  # All classes act like `ActiveSupport::OrderedHash`es, for the most part.
30
35
  # Use `[]=` to set JSON-LD properties...
@@ -35,10 +40,25 @@ canvas.width = 10
35
40
  canvas.height = 20
36
41
  canvas.label = 'My Canvas'
37
42
 
43
+ # Add images
44
+ service = IIIF::Presentation::Resource.new('@context' => 'http://iiif.io/api/image/2/context.json', 'profile' => 'http://iiif.io/api/image/2/level2.json', '@id' => "http://images.exampl.com/loris2/my-image")
45
+
46
+ image = IIIF::Presentation::ImageResource.new()
47
+ i['@id'] = "http://images.exampl.com/loris2/my-image/full/#{canvas.width},#{canvas.height}/0/default.jpg"
48
+ i.format = "image/jpeg"
49
+ i.width = canvas.width
50
+ i.height = canvas.height
51
+ i.service = service
52
+
53
+ images = IIIF::Presentation::Resource.new('@type' => 'oa:Annotation', 'motivation' => 'sc:painting', '@id' => "#{canvas['@id']}/images", 'resource' => i)
54
+
55
+ canvas.images << images
56
+
57
+ # Add other content resources
38
58
  oc = IIIF::Presentation::Resource.new('@id' => 'http://example.com/content')
39
59
  canvas.other_content << oc
40
60
 
41
- manifest.sequences << canvas
61
+ manifest.sequences.first.canvases << canvas
42
62
 
43
63
  puts manifest.to_json(pretty: true)
44
64
  ```
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 1.2.0
@@ -24,5 +24,5 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_dependency 'json'
26
26
  spec.add_dependency 'activesupport', '>= 3.2.18'
27
- spec.add_dependency 'faraday', '>= 0.9'
27
+ spec.add_dependency 'faraday', '~> 2.7'
28
28
  end
@@ -8,7 +8,7 @@ module IIIF
8
8
  # * reject
9
9
  # * replace
10
10
 
11
- def_delegators :@data, :[], :[]=, :camelize_keys, :delete, :empty?,
11
+ def_delegators :@data, :[], :[]=, :camelize_keys, :delete, :dig, :empty?,
12
12
  :fetch, :has_key?, :has_value?, :include?, :insert, :insert_after,
13
13
  :insert_before, :key, :key?, :keys, :length, :member?, :shift, :size,
14
14
  :snakeize_keys, :store, :unshift, :value?, :values
@@ -25,6 +25,10 @@ module IIIF
25
25
  super + %w{ width height }
26
26
  end
27
27
 
28
+ def numeric_only_keys
29
+ super + %w{ duration }
30
+ end
31
+
28
32
  def legal_viewing_hint_values
29
33
  super + %w{ non-paged }
30
34
  end
@@ -0,0 +1,12 @@
1
+ require File.join(File.dirname(__FILE__), 'abstract_resource')
2
+
3
+ module IIIF
4
+ module Presentation
5
+ class Service < AbstractResource
6
+ def required_keys
7
+ []
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -1,6 +1,6 @@
1
1
  require File.join(File.dirname(__FILE__), 'service')
2
2
  %w{
3
- abstract_resource
3
+ abstract_resource
4
4
  annotation
5
5
  annotation_list
6
6
  canvas
@@ -8,9 +8,10 @@ abstract_resource
8
8
  layer
9
9
  manifest
10
10
  resource
11
- image_resource
12
- sequence
13
- range
11
+ image_resource
12
+ sequence
13
+ service
14
+ range
14
15
  }.each do |f|
15
16
  require File.join(File.dirname(__FILE__), 'presentation', f)
16
17
  end
data/lib/iiif/service.rb CHANGED
@@ -17,6 +17,7 @@ module IIIF
17
17
  def abstract_resource_only_keys; %w{ }; end
18
18
  def hash_only_keys; %w{ }; end
19
19
  def int_only_keys; %w{ }; end
20
+ def numeric_only_keys; %w{ }; end
20
21
 
21
22
  def initialize(hsh={})
22
23
  @data = IIIF::OrderedHash[hsh]
@@ -24,6 +25,7 @@ module IIIF
24
25
  self.define_methods_for_array_only_keys
25
26
  self.define_methods_for_string_only_keys
26
27
  self.define_methods_for_int_only_keys
28
+ self.define_methods_for_numeric_only_keys
27
29
  self.define_methods_for_abstract_resource_only_keys
28
30
  self.snakeize_keys
29
31
  end
@@ -33,9 +35,9 @@ module IIIF
33
35
  # Parse from a file path, string, or existing hash
34
36
  def parse(s)
35
37
  ordered_hash = nil
36
- if s.kind_of?(String) && File.exists?(s)
38
+ if s.kind_of?(String) && File.exist?(s)
37
39
  ordered_hash = IIIF::OrderedHash[JSON.parse(IO.read(s))]
38
- elsif s.kind_of?(String) && !File.exists?(s)
40
+ elsif s.kind_of?(String) && !File.exist?(s)
39
41
  ordered_hash = IIIF::OrderedHash[JSON.parse(s)]
40
42
  elsif s.kind_of?(Hash)
41
43
  ordered_hash = IIIF::OrderedHash[s]
@@ -210,7 +212,7 @@ module IIIF
210
212
  new_object[new_key] = []
211
213
  hsh[key].each do |member|
212
214
  if new_key == 'service'
213
- new_object[new_key] << IIIF::Service.from_ordered_hash(member, IIIF::Service)
215
+ new_object[new_key] << IIIF::Service.from_ordered_hash(member, IIIF::Presentation::Service)
214
216
  elsif new_key == 'resource'
215
217
  new_object[new_key] << IIIF::Service.from_ordered_hash(hsh[key], IIIF::Presentation::Resource)
216
218
  elsif member.kind_of?(Hash)
@@ -221,7 +223,7 @@ module IIIF
221
223
  end
222
224
  end
223
225
  elsif new_key == 'service'
224
- new_object[new_key] = IIIF::Service.from_ordered_hash(hsh[key], IIIF::Service)
226
+ new_object[new_key] = IIIF::Service.from_ordered_hash(hsh[key], IIIF::Presentation::Service)
225
227
  elsif new_key == 'resource'
226
228
  new_object[new_key] = IIIF::Service.from_ordered_hash(hsh[key], IIIF::Presentation::Resource)
227
229
  elsif hsh[key].kind_of?(Hash)
@@ -255,56 +257,22 @@ module IIIF
255
257
  end
256
258
 
257
259
  def define_methods_for_any_type_keys
260
+ define_accessor_methods(*any_type_keys)
261
+
262
+ # override the getter defined by define_accessor_methods to avoid returning
263
+ # an array for empty values.
258
264
  any_type_keys.each do |key|
259
- # Setters
260
- define_singleton_method("#{key}=") do |arg|
261
- self.send('[]=', key, arg)
262
- end
263
- if key.camelize(:lower) != key
264
- define_singleton_method("#{key.camelize(:lower)}=") do |arg|
265
- self.send('[]=', key, arg)
266
- end
267
- end
268
- # Getters
269
265
  define_singleton_method(key) do
270
266
  self.send('[]', key)
271
267
  end
272
- if key.camelize(:lower) != key
273
- define_singleton_method(key.camelize(:lower)) do
274
- self.send('[]', key)
275
- end
276
- end
277
268
  end
278
269
  end
279
270
 
280
271
  def define_methods_for_array_only_keys
281
- array_only_keys.each do |key|
282
- # Setters
283
- define_singleton_method("#{key}=") do |arg|
284
- unless arg.kind_of?(Array)
285
- m = "#{key} must be an Array."
286
- raise IIIF::Presentation::IllegalValueError, m
287
- end
288
- self.send('[]=', key, arg)
289
- end
290
- if key.camelize(:lower) != key
291
- define_singleton_method("#{key.camelize(:lower)}=") do |arg|
292
- unless arg.kind_of?(Array)
293
- m = "#{key} must be an Array."
294
- raise IIIF::Presentation::IllegalValueError, m
295
- end
296
- self.send('[]=', key, arg)
297
- end
298
- end
299
- # Getters
300
- define_singleton_method(key) do
301
- self[key] ||= []
302
- self[key]
303
- end
304
- if key.camelize(:lower) != key
305
- define_singleton_method(key.camelize(:lower)) do
306
- self.send('[]', key)
307
- end
272
+ define_accessor_methods(*array_only_keys) do |key, arg|
273
+ unless arg.kind_of?(Array)
274
+ m = "#{key} must be an Array."
275
+ raise IIIF::Presentation::IllegalValueError, m
308
276
  end
309
277
  end
310
278
  end
@@ -314,85 +282,53 @@ module IIIF
314
282
  abstract_resource_only_keys.each do |hsh|
315
283
  key = hsh[:key]
316
284
  type = hsh[:type]
317
- # Setters
318
- define_singleton_method("#{key}=") do |arg|
285
+
286
+ define_accessor_methods(key) do |key, arg|
319
287
  unless arg.kind_of?(type)
320
288
  m = "#{key} must be an #{type}."
321
289
  raise IIIF::Presentation::IllegalValueError, m
322
290
  end
323
- self.send('[]=', key, arg)
324
- end
325
- if key.camelize(:lower) != key
326
- define_singleton_method("#{key.camelize(:lower)}=") do |arg|
327
- unless arg.kind_of?(type)
328
- m = "#{key} must be an #{type}."
329
- raise IIIF::Presentation::IllegalValueError, m
330
- end
331
- self.send('[]=', key, arg)
332
- end
333
- end
334
- # Getters
335
- define_singleton_method(key) do
336
- self[key] ||= []
337
- self[key]
338
- end
339
- if key.camelize(:lower) != key
340
- define_singleton_method(key.camelize(:lower)) do
341
- self.send('[]', key)
342
- end
343
291
  end
344
292
  end
345
293
  end
346
294
 
347
-
348
295
  def define_methods_for_string_only_keys
349
- string_only_keys.each do |key|
350
- # Setter
351
- define_singleton_method("#{key}=") do |arg|
352
- unless arg.kind_of?(String)
353
- m = "#{key} must be an String."
354
- raise IIIF::Presentation::IllegalValueError, m
355
- end
356
- self.send('[]=', key, arg)
357
- end
358
- if key.camelize(:lower) != key
359
- define_singleton_method("#{key.camelize(:lower)}=") do |arg|
360
- unless arg.kind_of?(String)
361
- m = "#{key} must be an String."
362
- raise IIIF::Presentation::IllegalValueError, m
363
- end
364
- self.send('[]=', key, arg)
365
- end
296
+ define_accessor_methods(*string_only_keys) do |key, arg|
297
+ unless arg.kind_of?(String)
298
+ m = "#{key} must be an String."
299
+ raise IIIF::Presentation::IllegalValueError, m
366
300
  end
367
- # Getter
368
- define_singleton_method(key) do
369
- self[key] ||= []
370
- self[key]
301
+ end
302
+ end
303
+
304
+ def define_methods_for_int_only_keys
305
+ define_accessor_methods(*int_only_keys) do |key, arg|
306
+ unless arg.kind_of?(Integer) && arg > 0
307
+ m = "#{key} must be a positive Integer."
308
+ raise IIIF::Presentation::IllegalValueError, m
371
309
  end
372
- if key.camelize(:lower) != key
373
- define_singleton_method(key.camelize(:lower)) do
374
- self.send('[]', key)
375
- end
310
+ end
311
+ end
312
+
313
+ def define_methods_for_numeric_only_keys
314
+ define_accessor_methods(*numeric_only_keys) do |key, arg|
315
+ unless arg.kind_of?(Numeric) && arg > 0
316
+ m = "#{key} must be a positive Integer or Float."
317
+ raise IIIF::Presentation::IllegalValueError, m
376
318
  end
377
319
  end
378
320
  end
379
321
 
380
- def define_methods_for_int_only_keys
381
- int_only_keys.each do |key|
322
+ def define_accessor_methods(*keys, &validation)
323
+ keys.each do |key|
382
324
  # Setter
383
325
  define_singleton_method("#{key}=") do |arg|
384
- unless arg.kind_of?(Integer) && arg > 0
385
- m = "#{key} must be a positive Integer."
386
- raise IIIF::Presentation::IllegalValueError, m
387
- end
326
+ validation.call(key, arg) if block_given?
388
327
  self.send('[]=', key, arg)
389
328
  end
390
329
  if key.camelize(:lower) != key
391
330
  define_singleton_method("#{key.camelize(:lower)}=") do |arg|
392
- unless arg.kind_of?(Integer) && arg > 0
393
- m = "#{key} must be a positive Integer."
394
- raise IIIF::Presentation::IllegalValueError, m
395
- end
331
+ validation.call(key, arg) if block_given?
396
332
  self.send('[]=', key, arg)
397
333
  end
398
334
  end
@@ -408,6 +344,5 @@ module IIIF
408
344
  end
409
345
  end
410
346
  end
411
-
412
347
  end
413
348
  end