iiif-presentation 1.4.2 → 1.5.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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +8 -1
  3. data/.standard.yml +1 -0
  4. data/Gemfile +5 -5
  5. data/README.md +1 -1
  6. data/Rakefile +4 -2
  7. data/VERSION +1 -1
  8. data/bin/standardrb +16 -0
  9. data/iiif-presentation.gemspec +23 -23
  10. data/lib/iiif/hash_behaviours.rb +29 -33
  11. data/lib/iiif/ordered_hash.rb +33 -33
  12. data/lib/iiif/presentation/abstract_resource.rb +17 -22
  13. data/lib/iiif/presentation/annotation.rb +9 -11
  14. data/lib/iiif/presentation/annotation_list.rb +8 -10
  15. data/lib/iiif/presentation/canvas.rb +11 -13
  16. data/lib/iiif/presentation/collection.rb +7 -9
  17. data/lib/iiif/presentation/image_resource.rb +33 -35
  18. data/lib/iiif/presentation/layer.rb +8 -10
  19. data/lib/iiif/presentation/manifest.rb +9 -12
  20. data/lib/iiif/presentation/range.rb +8 -10
  21. data/lib/iiif/presentation/resource.rb +5 -7
  22. data/lib/iiif/presentation/sequence.rb +8 -11
  23. data/lib/iiif/presentation/service.rb +1 -2
  24. data/lib/iiif/presentation.rb +7 -7
  25. data/lib/iiif/service.rb +121 -102
  26. data/lib/iiif/v3/abstract_resource.rb +127 -130
  27. data/lib/iiif/v3/presentation/annotation.rb +20 -21
  28. data/lib/iiif/v3/presentation/annotation_collection.rb +7 -9
  29. data/lib/iiif/v3/presentation/annotation_page.rb +14 -15
  30. data/lib/iiif/v3/presentation/canvas.rb +21 -22
  31. data/lib/iiif/v3/presentation/choice.rb +14 -15
  32. data/lib/iiif/v3/presentation/collection.rb +7 -8
  33. data/lib/iiif/v3/presentation/image_resource.rb +21 -22
  34. data/lib/iiif/v3/presentation/manifest.rb +18 -19
  35. data/lib/iiif/v3/presentation/nav_place.rb +125 -29
  36. data/lib/iiif/v3/presentation/range.rb +10 -10
  37. data/lib/iiif/v3/presentation/resource.rb +4 -5
  38. data/lib/iiif/v3/presentation/sequence.rb +10 -11
  39. data/lib/iiif/v3/presentation/service.rb +12 -13
  40. data/lib/iiif/v3/presentation.rb +8 -8
  41. data/spec/integration/iiif/presentation/image_resource_spec.rb +54 -58
  42. data/spec/integration/iiif/service_spec.rb +76 -77
  43. data/spec/integration/iiif/v3/abstract_resource_spec.rb +72 -73
  44. data/spec/integration/iiif/v3/presentation/image_resource_spec.rb +53 -56
  45. data/spec/spec_helper.rb +56 -64
  46. data/spec/unit/iiif/hash_behaviours_spec.rb +384 -387
  47. data/spec/unit/iiif/ordered_hash_spec.rb +86 -91
  48. data/spec/unit/iiif/presentation/abstract_resource_spec.rb +64 -70
  49. data/spec/unit/iiif/presentation/annotation_list_spec.rb +1 -3
  50. data/spec/unit/iiif/presentation/annotation_spec.rb +1 -3
  51. data/spec/unit/iiif/presentation/canvas_spec.rb +11 -14
  52. data/spec/unit/iiif/presentation/collection_spec.rb +26 -33
  53. data/spec/unit/iiif/presentation/image_resource_spec.rb +4 -6
  54. data/spec/unit/iiif/presentation/layer_spec.rb +16 -20
  55. data/spec/unit/iiif/presentation/manifest_spec.rb +45 -48
  56. data/spec/unit/iiif/presentation/range_spec.rb +18 -22
  57. data/spec/unit/iiif/presentation/resource_spec.rb +3 -6
  58. data/spec/unit/iiif/presentation/sequence_spec.rb +58 -63
  59. data/spec/unit/iiif/presentation/shared_examples/abstract_resource_only_keys.rb +4 -10
  60. data/spec/unit/iiif/presentation/shared_examples/any_type_keys.rb +5 -9
  61. data/spec/unit/iiif/presentation/shared_examples/array_only_keys.rb +9 -17
  62. data/spec/unit/iiif/presentation/shared_examples/int_only_keys.rb +6 -14
  63. data/spec/unit/iiif/presentation/shared_examples/string_only_keys.rb +6 -13
  64. data/spec/unit/iiif/service_spec.rb +8 -13
  65. data/spec/unit/iiif/v3/abstract_resource_define_methods_for_spec.rb +39 -37
  66. data/spec/unit/iiif/v3/abstract_resource_spec.rb +124 -131
  67. data/spec/unit/iiif/v3/presentation/annotation_collection_spec.rb +14 -17
  68. data/spec/unit/iiif/v3/presentation/annotation_page_spec.rb +71 -69
  69. data/spec/unit/iiif/v3/presentation/annotation_spec.rb +249 -233
  70. data/spec/unit/iiif/v3/presentation/canvas_spec.rb +211 -193
  71. data/spec/unit/iiif/v3/presentation/choice_spec.rb +81 -77
  72. data/spec/unit/iiif/v3/presentation/collection_spec.rb +24 -29
  73. data/spec/unit/iiif/v3/presentation/image_resource_spec.rb +70 -71
  74. data/spec/unit/iiif/v3/presentation/manifest_spec.rb +248 -235
  75. data/spec/unit/iiif/v3/presentation/nav_place_spec.rb +249 -49
  76. data/spec/unit/iiif/v3/presentation/range_spec.rb +28 -29
  77. data/spec/unit/iiif/v3/presentation/resource_spec.rb +83 -82
  78. data/spec/unit/iiif/v3/presentation/sequence_spec.rb +125 -119
  79. data/spec/unit/iiif/v3/presentation/service_spec.rb +123 -124
  80. data/spec/unit/iiif/v3/presentation/shared_examples/abstract_resource_only_keys.rb +4 -8
  81. data/spec/unit/iiif/v3/presentation/shared_examples/any_type_keys.rb +5 -7
  82. data/spec/unit/iiif/v3/presentation/shared_examples/array_only_keys.rb +9 -13
  83. data/spec/unit/iiif/v3/presentation/shared_examples/hash_only_keys.rb +9 -13
  84. data/spec/unit/iiif/v3/presentation/shared_examples/int_only_keys.rb +6 -10
  85. data/spec/unit/iiif/v3/presentation/shared_examples/numeric_only_keys.rb +6 -10
  86. data/spec/unit/iiif/v3/presentation/shared_examples/string_only_keys.rb +6 -10
  87. data/spec/unit/iiif/v3/presentation/shared_examples/uri_only_keys.rb +9 -13
  88. metadata +26 -65
@@ -2,93 +2,293 @@ describe IIIF::V3::Presentation::NavPlace do
2
2
  let(:subject) { described_class.new(coordinate_texts: coordinate_texts, base_uri: base_uri) }
3
3
  let(:base_uri) { "https://purl.stanford.edu" }
4
4
  let(:invalid_coordinates) { ["bogus", "stuff", "is", "here"] }
5
+ let(:out_of_range_coordinates) { ["W 008°00′00″--W 004°00′00″/N 059°00′00″--N 543°00′00″"] }
6
+ let(:wrong_axis_hemisphere_coordinates) { ["(W 117°57'--W 117°39'/E 34°50'--E 32°48')."] }
5
7
  let(:valid_coordinates) do
6
8
  ["W 23°54'00\"--E 53°36'00\"/N 71°19'00\"--N 33°30'00\"",
7
- 'E 103°48ʹ/S 3°46ʹ).',
8
- 'X 103°48ʹ/Y 3°46ʹ).',
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
- ]
9
+ "E 103°48ʹ/S 3°46ʹ).",
10
+ "X 103°48ʹ/Y 3°46ʹ).",
11
+ "In decimal degrees: (E 138.0--W 074.0/N 073.0--N 041.2).",
12
+ "23°54′00″W -- 53°36′00″E / 71°19′00″N -- 33°30′00″N"]
12
13
  end
13
14
  let(:nav_place) do
14
- { id: 'https://purl.stanford.edu/feature-collection/1',
15
- type: 'FeatureCollection',
16
- features: [{ id: 'https://purl.stanford.edu/iiif/feature/1',
17
- type: 'Feature',
18
- properties: {},
19
- geometry: { type: 'Polygon',
20
- coordinates: [[['-23.9', '71.316666'],
21
- ['53.6', '71.316666'],
22
- ['53.6', '33.5'],
23
- ['-23.9', '33.5'],
24
- ['-23.9', '71.316666']]] } },
25
- { id: 'https://purl.stanford.edu/iiif/feature/2',
26
- type: 'Feature',
27
- properties: {},
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
- ] }
15
+ {id: "https://purl.stanford.edu/feature-collection/1",
16
+ type: "FeatureCollection",
17
+ features: [{id: "https://purl.stanford.edu/iiif/feature/1",
18
+ type: "Feature",
19
+ properties: {},
20
+ geometry: {type: "Polygon",
21
+ coordinates: [[[-23.9, 33.5],
22
+ [53.6, 33.5],
23
+ [53.6, 71.316667],
24
+ [-23.9, 71.316667],
25
+ [-23.9, 33.5]]]}},
26
+ {id: "https://purl.stanford.edu/iiif/feature/2",
27
+ type: "Feature",
28
+ properties: {},
29
+ geometry: {type: "Point", coordinates: [103.8, -3.766667]}},
30
+ {id: "https://purl.stanford.edu/iiif/feature/3",
31
+ type: "Feature",
32
+ properties: {},
33
+ geometry: {coordinates: [103.8, 3.766667], type: "Point"}},
34
+ {id: "https://purl.stanford.edu/iiif/feature/4",
35
+ type: "Feature",
36
+ properties: {},
37
+ geometry: {type: "MultiPolygon",
38
+ coordinates: [[[[138.0, 41.2],
39
+ [180.0, 41.2],
40
+ [180.0, 73.0],
41
+ [138.0, 73.0],
42
+ [138.0, 41.2]]],
43
+ [[[-180.0, 41.2],
44
+ [-74.0, 41.2],
45
+ [-74.0, 73.0],
46
+ [-180.0, 73.0],
47
+ [-180.0, 41.2]]]]}},
48
+ {id: "https://purl.stanford.edu/iiif/feature/5",
49
+ type: "Feature",
50
+ properties: {},
51
+ geometry: {type: "Polygon",
52
+ coordinates: [[[-23.9, 33.5],
53
+ [53.6, 33.5],
54
+ [53.6, 71.316667],
55
+ [-23.9, 71.316667],
56
+ [-23.9, 33.5]]]}}]}
57
+ end
58
+ let(:nav_place_without_ids) do
59
+ without_id = ->(resource) { resource.except(:id) }
60
+ without_id.call(nav_place).merge(features: nav_place[:features].map(&without_id))
61
+ end
62
+
63
+ describe "#initialize" do
64
+ let(:coordinate_texts) { valid_coordinates }
65
+
66
+ ["", " ", "/path", "example.com", "ftp://my.server"].each do |value|
67
+ context "when base_uri is #{value.inspect}" do
68
+ let(:base_uri) { value }
69
+
70
+ it "raises ArgumentError" do
71
+ expect { subject }.to raise_error(ArgumentError, /base_uri must be an absolute HTTP\(S\) URI/)
72
+ end
73
+ end
74
+ end
43
75
  end
44
76
 
45
- describe '#build' do
46
- context 'when coordinates are valid' do
77
+ describe "#build" do
78
+ context "when coordinates are valid" do
47
79
  let(:coordinate_texts) { valid_coordinates }
48
80
 
49
- it 'returns navPlace' do
50
- expect(subject.build).to eq nav_place
81
+ # Compare as JSON: Ruby's == treats a BigDecimal as equal to a Float, but
82
+ # a BigDecimal serializes as a string, which GeoJSON does not allow.
83
+ it "returns navPlace" do
84
+ expect(JSON.parse(subject.build.to_json)).to eq JSON.parse(nav_place.to_json)
85
+ end
86
+
87
+ context "without a base_uri" do
88
+ let(:subject) { described_class.new(coordinate_texts: coordinate_texts) }
89
+
90
+ it "leaves out the ids" do
91
+ expect(JSON.parse(subject.build.to_json)).to eq JSON.parse(nav_place_without_ids.to_json)
92
+ end
93
+ end
94
+ end
95
+
96
+ context "when the north and south edges are given in the reverse order" do
97
+ let(:coordinate_texts) { ["W 10°00ʹ--E 20°00ʹ/S 5°00ʹ--N 5°00ʹ"] }
98
+
99
+ it "still runs the ring counterclockwise" do
100
+ expect(JSON.parse(subject.build.to_json)["features"][0]["geometry"]).to eq(
101
+ "type" => "Polygon",
102
+ "coordinates" => [[[-10.0, -5.0], [20.0, -5.0], [20.0, 5.0], [-10.0, 5.0], [-10.0, -5.0]]]
103
+ )
104
+ end
105
+ end
106
+
107
+ context "when a box crosses the antimeridian" do
108
+ let(:coordinate_texts) { ["E 170°00ʹ--W 170°00ʹ/N 10°00ʹ--S 10°00ʹ"] }
109
+
110
+ it "splits it into a MultiPolygon, per the spec" do
111
+ expect(JSON.parse(subject.build.to_json)["features"][0]["geometry"]).to eq(
112
+ "type" => "MultiPolygon",
113
+ "coordinates" => [
114
+ [[[170.0, -10.0], [180.0, -10.0], [180.0, 10.0], [170.0, 10.0], [170.0, -10.0]]],
115
+ [[[-180.0, -10.0], [-170.0, -10.0], [-170.0, 10.0], [-180.0, 10.0], [-180.0, -10.0]]]
116
+ ]
117
+ )
51
118
  end
52
119
  end
53
120
 
54
- context 'when coordinates are not present' do
121
+ context "when a box has an edge on the antimeridian" do
122
+ let(:coordinate_texts) { ["E 170°00ʹ--W 180°00ʹ/N 10°00ʹ--S 10°00ʹ", "E 180°00ʹ--W 170°00ʹ/N 10°00ʹ--S 10°00ʹ"] }
123
+
124
+ it "makes bounds that do not require splitting" do
125
+ geometries = JSON.parse(subject.build.to_json)["features"].map { |feature| feature["geometry"] }
126
+ expect(geometries).to eq [
127
+ {"type" => "Polygon", "coordinates" => [[[170.0, -10.0], [180.0, -10.0], [180.0, 10.0], [170.0, 10.0], [170.0, -10.0]]]},
128
+ {"type" => "Polygon", "coordinates" => [[[-180.0, -10.0], [-170.0, -10.0], [-170.0, 10.0], [-180.0, 10.0], [-180.0, -10.0]]]}
129
+ ]
130
+ end
131
+ end
132
+
133
+ context "with decimal degrees" do
134
+ let(:coordinate_texts) { ["E 138.5°/N 35.25°"] }
135
+
136
+ it "reads the whole number" do
137
+ expect(JSON.parse(subject.build.to_json)["features"][0]["geometry"]).to eq(
138
+ "type" => "Point", "coordinates" => [138.5, 35.25]
139
+ )
140
+ end
141
+ end
142
+
143
+ context "with decimal degrees and minutes and seconds" do
144
+ let(:coordinate_texts) { ["W 122°15.5ʹ/N 37°26ʹ30.5″"] }
145
+
146
+ it "keeps them" do
147
+ expect(JSON.parse(subject.build.to_json)["features"][0]["geometry"]).to eq(
148
+ "type" => "Point", "coordinates" => [-122.258333, 37.441806]
149
+ )
150
+ end
151
+ end
152
+
153
+ context "when coordinates are not present" do
55
154
  let(:coordinate_texts) { [] }
56
155
 
57
- it 'raises ArgumentError' do
156
+ it "raises ArgumentError" do
58
157
  expect { subject.build }.to raise_error(ArgumentError)
59
158
  end
60
159
  end
61
160
 
62
- context 'when coordinates are invalid' do
161
+ context "when coordinates are invalid" do
63
162
  let(:coordinate_texts) { invalid_coordinates }
64
163
 
65
- it 'raises ArgumentError' do
164
+ it "raises ArgumentError" do
66
165
  expect { subject.build }.to raise_error(ArgumentError)
67
166
  end
68
167
  end
168
+
169
+ context "when coordinates are out of range" do
170
+ let(:coordinate_texts) { out_of_range_coordinates }
171
+
172
+ it "raises ArgumentError" do
173
+ expect { subject.build }.to raise_error(ArgumentError, "invalid coordinates")
174
+ end
175
+ end
176
+
177
+ context "when a hemisphere does not belong to its axis" do
178
+ let(:coordinate_texts) { wrong_axis_hemisphere_coordinates }
179
+
180
+ it "raises ArgumentError" do
181
+ expect { subject.build }.to raise_error(ArgumentError, "invalid coordinates")
182
+ end
183
+ end
184
+
185
+ context "when only some coordinates are unusable" do
186
+ let(:coordinate_texts) { out_of_range_coordinates + valid_coordinates }
187
+
188
+ it "skips them and builds the rest" do
189
+ expect(subject.build).to eq nav_place
190
+ end
191
+ end
69
192
  end
70
193
 
71
- describe '#valid' do
72
- context 'when coordinates are valid' do
194
+ describe "#valid" do
195
+ context "when coordinates are valid" do
73
196
  let(:coordinate_texts) { valid_coordinates }
74
197
 
75
- it 'returns true' do
198
+ it "returns true" do
76
199
  expect(subject.valid?).to be true
77
200
  end
78
201
  end
79
202
 
80
- context 'when coordinates are not present' do
203
+ context "when coordinates are not present" do
81
204
  let(:coordinate_texts) { [] }
82
205
 
83
- it 'returns false' do
206
+ it "returns false" do
84
207
  expect(subject.valid?).to be false
85
208
  end
86
209
  end
87
210
 
88
- context 'when coordinates are invalid' do
211
+ context "when coordinates are invalid" do
89
212
  let(:coordinate_texts) { invalid_coordinates }
90
213
 
91
- it 'returns false' do
214
+ it "returns false" do
215
+ expect(subject.valid?).to be false
216
+ end
217
+ end
218
+
219
+ context "when coordinates are out of range" do
220
+ let(:coordinate_texts) { out_of_range_coordinates }
221
+
222
+ it "returns false instead of raising" do
223
+ expect(subject.valid?).to be false
224
+ end
225
+ end
226
+
227
+ context "when a hemisphere does not belong to its axis" do
228
+ let(:coordinate_texts) { wrong_axis_hemisphere_coordinates }
229
+
230
+ it "returns false instead of raising" do
231
+ expect(subject.valid?).to be false
232
+ end
233
+ end
234
+ end
235
+
236
+ describe "WKT coordinate texts" do
237
+ let(:geometries) { JSON.parse(subject.build.to_json)["features"].map { |feature| feature["geometry"] } }
238
+
239
+ context "with a point, a box, and a box split at the antimeridian" do
240
+ let(:coordinate_texts) do
241
+ ["POINT(-118.243700 34.052200)",
242
+ "POLYGON((-23.900000 33.500000, 53.600000 33.500000, 53.600000 71.316667, -23.900000 71.316667, -23.900000 33.500000))",
243
+ "MULTIPOLYGON(((170.000000 -10.000000, 180.000000 -10.000000, 180.000000 10.000000, 170.000000 10.000000, 170.000000 -10.000000)), " \
244
+ "((-180.000000 -10.000000, -170.000000 -10.000000, -170.000000 10.000000, -180.000000 10.000000, -180.000000 -10.000000)))"]
245
+ end
246
+
247
+ it "reads each as its GeoJSON geometry" do
248
+ expect(geometries).to eq [
249
+ {"type" => "Point", "coordinates" => [-118.2437, 34.0522]},
250
+ JSON.parse(nav_place[:features][0][:geometry].to_json),
251
+ {"type" => "MultiPolygon",
252
+ "coordinates" => [
253
+ [[[170.0, -10.0], [180.0, -10.0], [180.0, 10.0], [170.0, 10.0], [170.0, -10.0]]],
254
+ [[[-180.0, -10.0], [-170.0, -10.0], [-170.0, 10.0], [-180.0, 10.0], [-180.0, -10.0]]]
255
+ ]}
256
+ ]
257
+ end
258
+ end
259
+
260
+ context "when rings run the other way" do
261
+ let(:coordinate_texts) { ["polygon ((0 0, 0 10, 10 10, 10 0, 0 0), (2 2, 4 2, 4 4, 2 4, 2 2))"] }
262
+
263
+ it "turns the exterior counterclockwise and holes clockwise" do
264
+ expect(geometries).to eq [
265
+ {"type" => "Polygon",
266
+ "coordinates" => [
267
+ [[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]],
268
+ [[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [4.0, 2.0], [2.0, 2.0]]
269
+ ]}
270
+ ]
271
+ end
272
+ end
273
+
274
+ context "alongside other coordinate texts" do
275
+ let(:coordinate_texts) { ["E 103°48ʹ/S 3°46ʹ).", "POINT(1.5 2.5)"] }
276
+
277
+ it "keeps their order" do
278
+ expect(geometries).to eq [
279
+ {"type" => "Point", "coordinates" => [103.8, -3.766667]},
280
+ {"type" => "Point", "coordinates" => [1.5, 2.5]}
281
+ ]
282
+ end
283
+ end
284
+
285
+ context "when the WKT cannot be used" do
286
+ let(:coordinate_texts) do
287
+ ["POINT(200 0)", "POINT(1 2 3)", "POINT EMPTY", "POLYGON((0 0, 1 1))", "POLYGON((0 0, 1 0, 1 1, 0 1, 0 2))",
288
+ "POLYGON((0 0, 1 0, 1 1, 0 0", "MULTIPOLYGON()", "LINESTRING(0 0, 1 1)"]
289
+ end
290
+
291
+ it "skips it" do
92
292
  expect(subject.valid?).to be false
93
293
  end
94
294
  end
@@ -1,71 +1,70 @@
1
1
  describe IIIF::V3::Presentation::Range do
2
-
3
2
  let(:fixed_values) do
4
3
  {
5
- 'id' => 'http://www.example.org/iiif/book1/range/r1',
6
- 'type' => 'Range',
7
- 'label' => 'Introduction',
8
- 'members' => [
4
+ "id" => "http://www.example.org/iiif/book1/range/r1",
5
+ "type" => "Range",
6
+ "label" => "Introduction",
7
+ "members" => [
9
8
  {
10
- "id" => 'http://www.example.org/iiif/book1/range/r1-1',
9
+ "id" => "http://www.example.org/iiif/book1/range/r1-1",
11
10
  "type" => "Range"
12
11
  },
13
12
  {
14
- "id" => 'http://www.example.org/iiif/book1/range/r1-2',
13
+ "id" => "http://www.example.org/iiif/book1/range/r1-2",
15
14
  "type" => "Range"
16
15
  },
17
16
  {
18
- "id" => 'http://www.example.org/iiif/book1/canvas/p1',
17
+ "id" => "http://www.example.org/iiif/book1/canvas/p1",
19
18
  "type" => "Canvas"
20
19
  },
21
20
  {
22
- "id" => 'http://www.example.org/iiif/book1/canvas/p2',
21
+ "id" => "http://www.example.org/iiif/book1/canvas/p2",
23
22
  "type" => "Canvas"
24
23
  },
25
24
  {
26
- "id" => 'http://www.example.org/iiif/book1/canvas/p3#xywh=0,0,750,300',
25
+ "id" => "http://www.example.org/iiif/book1/canvas/p3#xywh=0,0,750,300",
27
26
  "type" => "Canvas"
28
27
  }
29
28
  ]
30
29
  }
31
30
  end
32
31
 
33
- describe '#initialize' do
34
- it 'sets type to Range by default' do
35
- expect(subject['type']).to eq 'Range'
32
+ describe "#initialize" do
33
+ it "sets type to Range by default" do
34
+ expect(subject["type"]).to eq "Range"
36
35
  end
37
36
  end
38
37
 
39
38
  describe "#{described_class}.define_methods_for_array_only_keys" do
40
- it_behaves_like 'it has the appropriate methods for array-only keys v3'
39
+ it_behaves_like "it has the appropriate methods for array-only keys v3"
41
40
  end
42
41
 
43
42
  describe "#{described_class}.define_methods_for_string_only_keys" do
44
- it_behaves_like 'it has the appropriate methods for string-only keys v3'
43
+ it_behaves_like "it has the appropriate methods for string-only keys v3"
45
44
  end
46
45
 
47
46
  describe "#{described_class}.define_methods_for_any_type_keys" do
48
- it_behaves_like 'it has the appropriate methods for any-type keys v3'
47
+ it_behaves_like "it has the appropriate methods for any-type keys v3"
49
48
  end
50
49
 
51
- describe '#validate' do
50
+ describe "#validate" do
52
51
  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(', ')}"
52
+ "All entries in the (items or canvases) array must be one of #{IIIF::V3::Presentation::Range::VALID_ITEM_TYPES.join(", ")}"
54
53
  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]
54
+ describe "items" do
55
+ it "raises IllegalValueError for items entry that is not valid type" do
56
+ subject["id"] = "test_range"
57
+ subject["label"] = {"en" => ["Test Range"]}
58
+ subject["items"] = [IIIF::V3::Presentation::Range.new("id" => "child_range", "label" => ["Child Label"]),
59
+ IIIF::V3::Presentation::AnnotationPage.new]
61
60
  expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, bad_val_msg)
62
61
  end
63
62
  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]
63
+ describe "canvases" do
64
+ it "raises IllegalValueError for canvases entry that is not valid type" do
65
+ subject["id"] = "test_range"
66
+ subject["label"] = {"en" => ["Test Range"]}
67
+ subject["canvases"] = [IIIF::V3::Presentation::Canvas.new, IIIF::V3::Presentation::AnnotationPage.new]
69
68
  expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, bad_val_msg)
70
69
  end
71
70
  end