iiif-presentation 1.3.0 → 1.4.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55260d04809482b5ebd267655c07543d4c97e2d5166bdca7b26eff5b22725fe6
4
- data.tar.gz: 616cb247a11b55e56aac745508e5aa5000cae11c47b3e384d37ca2011b187c55
3
+ metadata.gz: 5f68d7e5cd133fa903625a262109591f8806016df292b205b9cda95f8cb4b5d0
4
+ data.tar.gz: 0fc72efe6625543c1c8462bbf0b810fe5882c5dbdee6156410654e9c29be6825
5
5
  SHA512:
6
- metadata.gz: e83acf715f8b6bf0513d1211ce5797237f28e9cead4c2cf3446b150ba496df81e16ba7b0d7ffae04aebe9cb40a4b84098c8d184e493f1259419cd0f9d99fae0b
7
- data.tar.gz: 2221b5bcfc51cabfefc039584a58b93bd16fa95f7729e7921017bdac46b6d71e8cea6cbcc687e38f96a903386d4b7daf56d66693868e719b7cb6944ccc1067a3
6
+ metadata.gz: bd825465b2f3d72e6003b7db8e5ac8020040daf36a924d35b0d8553e4e51910ca880e5017cc9e2b86f6424ef1ca1febe6010f28b52eb657d6d66713a6b31344d
7
+ data.tar.gz: dbb55a55e296777bfc1e422132604b83c53cdc16d63ba404575f1d4baee0afda7fc8cdb8f95480b4bdf83df6c23a4947d447306491ca65eb37273b717dce7e73
data/README.md CHANGED
@@ -44,13 +44,13 @@ canvas.label = 'My Canvas'
44
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
45
 
46
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
47
+ image['@id'] = "http://images.exampl.com/loris2/my-image/full/#{canvas.width},#{canvas.height}/0/default.jpg"
48
+ image.format = "image/jpeg"
49
+ image.width = canvas.width
50
+ image.height = canvas.height
51
+ image.service = service
52
52
 
53
- images = IIIF::Presentation::Resource.new('@type' => 'oa:Annotation', 'motivation' => 'sc:painting', '@id' => "#{canvas['@id']}/images", 'resource' => i)
53
+ images = IIIF::Presentation::Resource.new('@type' => 'oa:Annotation', 'motivation' => 'sc:painting', '@id' => "#{canvas['@id']}/images", 'resource' => image)
54
54
 
55
55
  canvas.images << images
56
56
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0
1
+ 1.4.1
@@ -7,7 +7,11 @@ module IIIF
7
7
  TYPE = 'oa:Annotation'
8
8
 
9
9
  def required_keys
10
- super + %w{ motivation }
10
+ super + %w{ motivation on }
11
+ end
12
+
13
+ def string_only_keys
14
+ super + %w{ on }
11
15
  end
12
16
 
13
17
  def abstract_resource_only_keys
@@ -347,7 +347,9 @@ module IIIF
347
347
  hsh.keys.each do |key|
348
348
  new_key = key.underscore == key ? key : key.underscore
349
349
  if new_key == 'service'
350
- new_object[new_key] = IIIF::V3::AbstractResource.from_ordered_hash(hsh[key], IIIF::V3::Presentation::Service)
350
+ new_object[new_key] = hsh[key].map do |entry|
351
+ IIIF::V3::AbstractResource.from_ordered_hash(entry, IIIF::V3::Presentation::Service)
352
+ end
351
353
  elsif new_key == 'body'
352
354
  new_object[new_key] = IIIF::V3::AbstractResource.from_ordered_hash(hsh[key], IIIF::V3::Presentation::Resource)
353
355
  elsif hsh[key].kind_of?(Hash)
@@ -63,8 +63,6 @@ module IIIF
63
63
  end
64
64
  end
65
65
 
66
- # NOTE: allowing 'items' or 'sequences' as Universal Viewer currently only accepts sequences
67
- # see https://github.com/sul-dlss/osullivan/issues/27, sul-dlss/purl/issues/167
68
66
  def validate_items_list(items_array)
69
67
  unless items_array.size >= 1
70
68
  m = 'The items list must have at least one entry (and it must be a IIIF::V3::Presentation::Canvas)'
@@ -6,8 +6,7 @@ module IIIF
6
6
  class NavPlace < IIIF::V3::AbstractResource
7
7
  Rect = Struct.new(:coord1, :coord2)
8
8
 
9
- COORD_REGEX = /(?<hemisphere>[NSEW]) (?<degrees>\d+)[°⁰*] ?(?<minutes>\d+)?[ʹ']? ?(?<seconds>\d+)?[ʺ"]?/
10
-
9
+ COORD_REGEX = /(?:(?<hemisphere>[NSEW])\s*)?(?<degrees>\d+)[°⁰*](?:\s*(?<minutes>\d+)[\'ʹ′])?(?:\s*(?<seconds>\d+)["ʺ″])?(?:\s*(?<hemisphere>[NSEW]))?/
11
10
  def initialize(coordinate_texts:, base_uri:)
12
11
  @coordinate_texts = coordinate_texts
13
12
  @base_uri = base_uri
@@ -3,8 +3,8 @@ module IIIF
3
3
  module Presentation
4
4
  # Ranges are linked or embedded within the manifest in a structures field
5
5
  class Range < Sequence
6
-
7
6
  TYPE = 'Range'.freeze
7
+ VALID_ITEM_TYPES = [IIIF::V3::Presentation::Canvas, IIIF::V3::Presentation::Range]
8
8
 
9
9
  def required_keys
10
10
  super + %w{ id label }
@@ -28,11 +28,21 @@ module IIIF
28
28
 
29
29
  def validate
30
30
  super
31
+ validate_list(self['items']) if self['items']
32
+ validate_list(self['canvases']) if self['canvases']
31
33
  # TODO: Ranges must have URIs and they should be http(s) URIs.
32
- # TODO: Values of the members array must be canvas or range
33
34
  # TODO: contentAnnotations: links to AnnotationCollection
34
35
  # TODO: startCanvas: A link from a Sequence or Range to a Canvas that is contained within it
35
36
  end
37
+
38
+ private
39
+
40
+ def validate_list(canvas_array)
41
+ return if canvas_array.all? { |entry| VALID_ITEM_TYPES.include?(entry.class) }
42
+
43
+ m = "All entries in the (items or canvases) array must be one of #{VALID_ITEM_TYPES.join(', ')}"
44
+ raise IIIF::V3::Presentation::IllegalValueError, m
45
+ end
36
46
  end
37
47
  end
38
48
  end
@@ -37,29 +37,14 @@ module IIIF
37
37
  # NOTE: allowing 'items' or 'canvases' as Universal Viewer currently only accepts canvases
38
38
  # see https://github.com/sul-dlss/osullivan/issues/27, sul-dlss/purl/issues/167
39
39
  unless (self['items'] && self['items'].any?) ||
40
- (self['canvases'] && self['canvases'].any?)
41
- m = 'The (items or canvases) list must have at least one entry (and it must be a IIIF::V3::Presentation::Canvas)'
40
+ (self['canvases'] && self['canvases'].any?)
41
+ m = 'The (items or canvases) list must have at least one entry.'
42
42
  raise IIIF::V3::Presentation::MissingRequiredKeyError, m
43
43
  end
44
- validate_canvas_list(self['items']) if self['items']
45
- validate_canvas_list(self['canvases']) if self['canvases']
46
-
47
44
  # TODO: startCanvas: A link from a Sequence or Range to a Canvas that is contained within it
48
45
 
49
46
  # TODO: All external Sequences must have a dereference-able http(s) URI
50
47
  end
51
-
52
- def validate_canvas_list(canvas_array)
53
- unless canvas_array.size >= 1
54
- m = 'The (items or canvases) list must have at least one entry (and it must be a IIIF::V3::Presentation::Canvas)'
55
- raise IIIF::V3::Presentation::MissingRequiredKeyError, m
56
- end
57
-
58
- unless canvas_array.all? { |entry| entry.instance_of?(IIIF::V3::Presentation::Canvas) }
59
- m = 'All entries in the (items or canvases) list must be a IIIF::V3::Presentation::Canvas'
60
- raise IIIF::V3::Presentation::IllegalValueError, m
61
- end
62
- end
63
48
  end
64
49
  end
65
50
  end
@@ -28,11 +28,13 @@
28
28
  "description": "A longer description of this example book. It should give some real information.",
29
29
  "rights": "http://www.example.org/license.html",
30
30
  "attribution": "Provided by Example Organization",
31
- "service": {
32
- "@context": "http://example.org/ns/jsonld/context.json",
33
- "id": "http://example.org/service/example",
34
- "profile": "http://example.org/docs/example-service.html"
35
- },
31
+ "service": [
32
+ {
33
+ "@context": "http://example.org/ns/jsonld/context.json",
34
+ "id": "http://example.org/service/example",
35
+ "profile": "http://example.org/docs/example-service.html"
36
+ }
37
+ ],
36
38
  "seeAlso": {
37
39
  "id": "http://www.example.org/library/catalog/book1.marc",
38
40
  "format": "application/marc"
@@ -67,11 +69,13 @@
67
69
  "format": "image/jpeg",
68
70
  "height": 2000,
69
71
  "width": 1500,
70
- "service": {
71
- "@context": "http://iiif.io/api/image/2/context.json",
72
- "id": "http://www.example.org/images/book1-page1",
73
- "profile": "http://iiif.io/api/image/2/level1.json"
74
- }
72
+ "service": [
73
+ {
74
+ "@context": "http://iiif.io/api/image/2/context.json",
75
+ "id": "http://www.example.org/images/book1-page1",
76
+ "profile": "http://iiif.io/api/image/2/level1.json"
77
+ }
78
+ ]
75
79
  }
76
80
  }
77
81
  ]
@@ -102,25 +106,27 @@
102
106
  "format": "image/jpeg",
103
107
  "height": 2000,
104
108
  "width": 1500,
105
- "service": {
106
- "@context": "http://iiif.io/api/image/2/context.json",
107
- "id": "http://www.example.org/images/book1-page2",
108
- "profile": "http://iiif.io/api/image/2/level1.json",
109
- "height": 8000,
110
- "width": 6000,
111
- "tiles": [
112
- {
113
- "width": 512,
114
- "scaleFactors": [
115
- 1,
116
- 2,
117
- 4,
118
- 8,
119
- 16
120
- ]
121
- }
122
- ]
123
- }
109
+ "service": [
110
+ {
111
+ "@context": "http://iiif.io/api/image/2/context.json",
112
+ "id": "http://www.example.org/images/book1-page2",
113
+ "profile": "http://iiif.io/api/image/2/level1.json",
114
+ "height": 8000,
115
+ "width": 6000,
116
+ "tiles": [
117
+ {
118
+ "width": 512,
119
+ "scaleFactors": [
120
+ 1,
121
+ 2,
122
+ 4,
123
+ 8,
124
+ 16
125
+ ]
126
+ }
127
+ ]
128
+ }
129
+ ]
124
130
  },
125
131
  "target": "http://www.example.org/iiif/book1/canvas/p2"
126
132
  }
@@ -153,11 +159,13 @@
153
159
  "format": "image/jpeg",
154
160
  "height": 2000,
155
161
  "width": 1500,
156
- "service": {
157
- "@context": "http://iiif.io/api/image/2/context.json",
158
- "id": "http://www.example.org/images/book1-page3",
159
- "profile": "http://iiif.io/api/image/2/level1.json"
160
- }
162
+ "service": [
163
+ {
164
+ "@context": "http://iiif.io/api/image/2/context.json",
165
+ "id": "http://www.example.org/images/book1-page3",
166
+ "profile": "http://iiif.io/api/image/2/level1.json"
167
+ }
168
+ ]
161
169
  }
162
170
  }
163
171
  ]
@@ -32,11 +32,13 @@
32
32
  "format": "image/jpeg",
33
33
  "height": 2000,
34
34
  "width": 1500,
35
- "service": {
36
- "@context": "http://iiif.io/api/image/2/context.json",
37
- "id": "http://www.example.org/images/book1-page1",
38
- "profile": "http://iiif.io/api/image/2/level1.json"
39
- }
35
+ "service": [
36
+ {
37
+ "@context": "http://iiif.io/api/image/2/context.json",
38
+ "id": "http://www.example.org/images/book1-page1",
39
+ "profile": "http://iiif.io/api/image/2/level1.json"
40
+ }
41
+ ]
40
42
  }
41
43
  }
42
44
  ]
@@ -6,9 +6,11 @@
6
6
  "type": "Manifest",
7
7
  "id": "http://www.example.org/iiif/book1/manifest",
8
8
  "label": "Book 1",
9
- "service": {
10
- "@context": "http://example.org/ns/jsonld/context.json",
11
- "id": "http://example.org/service/example",
12
- "profile": "http://example.org/docs/example-service.html"
13
- }
9
+ "service": [
10
+ {
11
+ "@context": "http://example.org/ns/jsonld/context.json",
12
+ "id": "http://example.org/service/example",
13
+ "profile": "http://example.org/docs/example-service.html"
14
+ }
15
+ ]
14
16
  }
@@ -47,12 +47,14 @@ describe IIIF::V3::AbstractResource do
47
47
  "id": "http://example.com/manifest",
48
48
  "type": "Manifest",
49
49
  "label": "My Manifest",
50
- "service": {
51
- "@context": "http://iiif.io/api/image/2/context.json",
52
- "@id":"http://www.example.org/images/book1-page1",
53
- "id":"http://www.example.org/images/book1-page1",
54
- "profile":"http://iiif.io/api/image/2/profiles/level2.json"
55
- },
50
+ "service": [
51
+ {
52
+ "@context": "http://iiif.io/api/image/2/context.json",
53
+ "@id":"http://www.example.org/images/book1-page1",
54
+ "id":"http://www.example.org/images/book1-page1",
55
+ "profile":"http://iiif.io/api/image/2/profiles/level2.json"
56
+ }
57
+ ],
56
58
  "some_other_thing": {
57
59
  "foo" : "bar"
58
60
  },
@@ -95,7 +97,7 @@ describe IIIF::V3::AbstractResource do
95
97
  it 'turns services into Services' do
96
98
  expected_klass = IIIF::V3::Presentation::Service
97
99
  parsed = described_class.from_ordered_hash(fixture)
98
- expect(parsed['service'].class).to be expected_klass
100
+ expect(parsed['service'].map(&:class).uniq).to contain_exactly expected_klass
99
101
  end
100
102
 
101
103
  it 'round-trips' do
@@ -12,7 +12,7 @@ describe IIIF::V3::Presentation::Annotation do
12
12
  'id' => content_id,
13
13
  'type' => content_type,
14
14
  'format' => mimetype,
15
- 'service' => image_2_api_service
15
+ 'service' => [image_2_api_service]
16
16
  )}
17
17
 
18
18
  describe '#required_keys' do
@@ -183,14 +183,16 @@ describe IIIF::V3::Presentation::Annotation do
183
183
  let(:img_mime) { 'image/jpeg' }
184
184
  let(:img_h) { 2000 }
185
185
  let(:img_w) { 1500 }
186
- let(:img_hw_resource) { IIIF::V3::Presentation::ImageResource.new(
187
- 'id' => content_id,
188
- 'type' => img_type,
189
- 'format' => img_mime,
190
- 'height' => img_h,
191
- 'width' => img_w,
192
- 'service' => image_2_api_service
193
- )}
186
+ let(:img_hw_resource) do
187
+ IIIF::V3::Presentation::ImageResource.new(
188
+ 'id' => content_id,
189
+ 'type' => img_type,
190
+ 'format' => img_mime,
191
+ 'height' => img_h,
192
+ 'width' => img_w,
193
+ 'service' => [image_2_api_service]
194
+ )
195
+ end
194
196
  let(:my_anno) {
195
197
  anno = described_class.new
196
198
  anno['id'] = anno_id
@@ -209,22 +211,23 @@ describe IIIF::V3::Presentation::Annotation do
209
211
 
210
212
  describe 'and service with height and width and tiles' do
211
213
  let(:tiles_val) { [{"width" => 512, "scaleFactors" => [1,2,4,8,16]}] }
212
- let(:service) {
214
+ let(:service) do
213
215
  s = image_2_api_service
214
216
  s['height'] = 8000
215
217
  s['width'] = 6000
216
218
  s['tiles'] = tiles_val
217
- s
218
- }
219
+ [s]
220
+ end
219
221
  it 'validates' do
220
222
  img_hw_resource['service'] = service
221
223
  expect{my_anno.validate}.not_to raise_error
222
224
  end
223
225
  it "body['service'] has expected additional values'" do
224
- expect(my_anno['body']['service']).to eq service
225
- expect(my_anno['body']['service']['height']).to eq 8000
226
- expect(my_anno['body']['service']['width']).to eq 6000
227
- expect(my_anno['body']['service']['tiles']).to eq tiles_val
226
+ annotation_service = my_anno['body']['service'].first
227
+ expect(annotation_service).to eq service.first
228
+ expect(annotation_service['height']).to eq 8000
229
+ expect(annotation_service['width']).to eq 6000
230
+ expect(annotation_service['tiles']).to eq tiles_val
228
231
  end
229
232
  end
230
233
  end
@@ -131,7 +131,7 @@ describe IIIF::V3::Presentation::ImageResource do
131
131
  'format' => img_mimetype,
132
132
  'height' => height,
133
133
  'width' => width,
134
- 'service' => image_v2_service
134
+ 'service' => [image_v2_service]
135
135
  })
136
136
  }
137
137
  describe 'simpler' do
@@ -146,7 +146,7 @@ describe IIIF::V3::Presentation::ImageResource do
146
146
  expect(image_object.format).to eq img_mimetype
147
147
  expect(image_object.height).to eq height
148
148
  expect(image_object.width).to eq width
149
- expect(image_object.service).to eq image_v2_service
149
+ expect(image_object.service.first).to eq image_v2_service
150
150
  end
151
151
  end
152
152
  describe 'height and width in service' do
@@ -156,27 +156,27 @@ describe IIIF::V3::Presentation::ImageResource do
156
156
  # "format": "image/jpeg",
157
157
  # "height":2000,
158
158
  # "width":1500,
159
- # "service": {
159
+ # "service": [{
160
160
  # "@context": "http://iiif.io/api/image/2/context.json",
161
161
  # "id": "http://example.org/images/book1-page2",
162
162
  # "profile": "http://iiif.io/api/image/2/level1.json",
163
163
  # "height":8000,
164
164
  # "width":6000,
165
165
  # "tiles": [{"width": 512, "scaleFactors": [1,2,4,8,16]}]
166
- # }
166
+ # }]
167
167
  # }
168
168
  let(:img_obj) {
169
169
  img = image_object
170
- img.service['height'] = 6666
171
- img.service['width'] = 9999
172
- img.service['tiles'] = [{"width" => 512, "scaleFactors" => [1,2,4,8,16]}]
170
+ img.service.first['height'] = 6666
171
+ img.service.first['width'] = 9999
172
+ img.service.first['tiles'] = [{"width" => 512, "scaleFactors" => [1,2,4,8,16]}]
173
173
  img
174
174
  }
175
175
  it 'validates' do
176
176
  expect{img_obj.validate}.not_to raise_error
177
177
  end
178
178
  it 'has expected service value' do
179
- service_obj = img_obj.service
179
+ service_obj = img_obj.service.first
180
180
  expect(service_obj.class).to eq IIIF::V3::Presentation::Service
181
181
  expect(service_obj.keys.size).to eq 7
182
182
  expect(service_obj['height']).to eq 6666
@@ -176,7 +176,7 @@ describe IIIF::V3::Presentation::Manifest do
176
176
  let!(:thumbnail_image) { IIIF::V3::Presentation::ImageResource.new({
177
177
  "id" => "https://example.org/image/iiif/abc666_05_0001/full/!400,400/0/default.jpg",
178
178
  "format" => "image/jpeg",
179
- "service" => thumbnail_image_service
179
+ "service" => [thumbnail_image_service]
180
180
  })}
181
181
  let!(:manifest_object) { described_class.new({
182
182
  "id" => "https://example.org/abc666/iiif3/manifest",
@@ -189,7 +189,7 @@ describe IIIF::V3::Presentation::Manifest do
189
189
  "items" => [canvas_object],
190
190
  "logo" => {
191
191
  "id" => "https://example.org/logo/full/400,/0/default.jpg",
192
- "service" => logo_service
192
+ "service" => [logo_service]
193
193
  },
194
194
  "seeAlso" => {
195
195
  "id" => "https://example.org/abc666.mods",
@@ -242,7 +242,7 @@ describe IIIF::V3::Presentation::Manifest do
242
242
  },
243
243
  "logo" => {
244
244
  "id" => "https://example.org/logo/full/400,/0/default.jpg",
245
- "service" => logo_service
245
+ "service" => [logo_service]
246
246
  },
247
247
  "seeAlso" => {
248
248
  "id" => "https://example.org/abc666.mods",
@@ -6,7 +6,9 @@ describe IIIF::V3::Presentation::NavPlace do
6
6
  ["W 23°54'00\"--E 53°36'00\"/N 71°19'00\"--N 33°30'00\"",
7
7
  'E 103°48ʹ/S 3°46ʹ).',
8
8
  'X 103°48ʹ/Y 3°46ʹ).',
9
- 'In decimal degrees: (E 138.0--W 074.0/N 073.0--N 041.2).']
9
+ 'In decimal degrees: (E 138.0--W 074.0/N 073.0--N 041.2).', # currently invalid therefore doesn't show up in nav_place
10
+ '23°54′00″W -- 53°36′00″E / 71°19′00″N -- 33°30′00″N'
11
+ ]
10
12
  end
11
13
  let(:nav_place) do
12
14
  { id: 'https://purl.stanford.edu/feature-collection/1',
@@ -23,7 +25,21 @@ describe IIIF::V3::Presentation::NavPlace do
23
25
  { id: 'https://purl.stanford.edu/iiif/feature/2',
24
26
  type: 'Feature',
25
27
  properties: {},
26
- geometry: { type: 'Point', coordinates: ['103.8', '-3.766666'] } }] }
28
+ geometry: { type: 'Point', coordinates: ['103.8', '-3.766666'] } },
29
+ { id: 'https://purl.stanford.edu/iiif/feature/3',
30
+ type: 'Feature',
31
+ properties: {},
32
+ geometry: { coordinates: ['103.8', '3.766666'], type: "Point" } },
33
+ { id: 'https://purl.stanford.edu/iiif/feature/4',
34
+ type: 'Feature',
35
+ properties: {},
36
+ geometry: { type: 'Polygon',
37
+ coordinates: [[['-23.9', '71.316666'],
38
+ ['53.6', '71.316666'],
39
+ ['53.6', '33.5'],
40
+ ['-23.9', '33.5'],
41
+ ['-23.9', '71.316666']]] } }
42
+ ] }
27
43
  end
28
44
 
29
45
  describe '#build' do
@@ -49,6 +49,25 @@ describe IIIF::V3::Presentation::Range do
49
49
  end
50
50
 
51
51
  describe '#validate' do
52
+ let(:bad_val_msg) do
53
+ "All entries in the (items or canvases) array must be one of #{IIIF::V3::Presentation::Range::VALID_ITEM_TYPES.join(', ')}"
54
+ end
55
+ describe 'items' do
56
+ it 'raises IllegalValueError for items entry that is not valid type' do
57
+ subject['id'] = 'test_range'
58
+ subject['label'] = { 'en' => ['Test Range'] }
59
+ subject['items'] = [IIIF::V3::Presentation::Range.new('id' => 'child_range', 'label' => ['Child Label']),
60
+ IIIF::V3::Presentation::AnnotationPage.new]
61
+ expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, bad_val_msg)
62
+ end
63
+ end
64
+ describe 'canvases' do
65
+ it 'raises IllegalValueError for canvases entry that is not valid type' do
66
+ subject['id'] = 'test_range'
67
+ subject['label'] = { 'en' => ['Test Range'] }
68
+ subject['canvases'] = [IIIF::V3::Presentation::Canvas.new, IIIF::V3::Presentation::AnnotationPage.new]
69
+ expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, bad_val_msg)
70
+ end
71
+ end
52
72
  end
53
-
54
73
  end
@@ -57,8 +57,7 @@ describe IIIF::V3::Presentation::Sequence do
57
57
  end
58
58
 
59
59
  describe '#validate' do
60
- let(:req_key_msg) { "The (items or canvases) list must have at least one entry (and it must be a IIIF::V3::Presentation::Canvas)" }
61
- let(:bad_val_msg) { "All entries in the (items or canvases) list must be a IIIF::V3::Presentation::Canvas" }
60
+ let(:req_key_msg) { "The (items or canvases) list must have at least one entry." }
62
61
  it 'raises MissingRequiredKeyError if no items or canvases key' do
63
62
  expect { subject.validate }.to raise_error(IIIF::V3::Presentation::MissingRequiredKeyError, req_key_msg)
64
63
  end
@@ -67,20 +66,12 @@ describe IIIF::V3::Presentation::Sequence do
67
66
  subject['items'] = []
68
67
  expect { subject.validate }.to raise_error(IIIF::V3::Presentation::MissingRequiredKeyError, req_key_msg)
69
68
  end
70
- it 'raises IllegalValueError for items entry that is not a Canvas' do
71
- subject['items'] = [IIIF::V3::Presentation::Canvas.new, IIIF::V3::Presentation::AnnotationPage.new]
72
- expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, bad_val_msg)
73
- end
74
69
  end
75
70
  describe 'canvases' do
76
71
  it 'raises MissingRequiredKeyError for canvases as empty Array' do
77
72
  subject['items'] = []
78
73
  expect { subject.validate }.to raise_error(IIIF::V3::Presentation::MissingRequiredKeyError, req_key_msg)
79
74
  end
80
- it 'raises IllegalValueError for canvases entry that is not a Canvas' do
81
- subject['canvases'] = [IIIF::V3::Presentation::Canvas.new, IIIF::V3::Presentation::AnnotationPage.new]
82
- expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, bad_val_msg)
83
- end
84
75
  end
85
76
  end
86
77
 
@@ -186,21 +177,21 @@ describe IIIF::V3::Presentation::Sequence do
186
177
  'thumbnail' => [{
187
178
  'id' => 'http://www.example.org/images/book1-page1/full/80,100/0/default.jpg',
188
179
  'type' => 'Image',
189
- 'service'=> {
180
+ 'service'=> [{
190
181
  '@context' => 'http://iiif.io/api/image/2/context.json',
191
182
  'id' => 'http://www.example.org/images/book1-page1',
192
183
  'profile' => 'http://iiif.io/api/image/2/level1.json'
193
- }
184
+ }]
194
185
  }],
195
186
  'attribution' => 'Provided by Example Organization',
196
187
  'rights' => [{'id' => 'http://www.example.org/license.html'}],
197
188
  'logo' => 'http://www.example.org/logos/institution1.jpg',
198
189
  'see_also' => 'http://www.example.org/library/catalog/book1.xml',
199
- 'service' => {
190
+ 'service' => [{
200
191
  '@context' => 'http://example.org/ns/jsonld/context.json',
201
192
  'id' => 'http://example.org/service/example',
202
193
  'profile' => 'http://example.org/docs/example-service.html'
203
- },
194
+ }],
204
195
  'related' => {
205
196
  'id' => 'http://www.example.org/videos/video-book1.mpg',
206
197
  'format' => 'video/mpeg'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iiif-presentation
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Stroop
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-01 00:00:00.000000000 Z
11
+ date: 2025-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -285,7 +285,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
285
285
  - !ruby/object:Gem::Version
286
286
  version: '0'
287
287
  requirements: []
288
- rubygems_version: 3.4.21
288
+ rubygems_version: 3.5.17
289
289
  signing_key:
290
290
  specification_version: 4
291
291
  summary: API for working with IIIF Presentation manifests.