iiif-presentation 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +2 -2
  3. data/.gitignore +1 -0
  4. data/Gemfile +2 -0
  5. data/README.md +22 -1
  6. data/VERSION +1 -1
  7. data/iiif-presentation.gemspec +2 -1
  8. data/lib/iiif/presentation/canvas.rb +4 -0
  9. data/lib/iiif/presentation/service.rb +12 -0
  10. data/lib/iiif/presentation.rb +5 -4
  11. data/lib/iiif/service.rb +38 -103
  12. data/lib/iiif/v3/abstract_resource.rb +491 -0
  13. data/lib/iiif/v3/presentation/annotation.rb +74 -0
  14. data/lib/iiif/v3/presentation/annotation_collection.rb +38 -0
  15. data/lib/iiif/v3/presentation/annotation_page.rb +53 -0
  16. data/lib/iiif/v3/presentation/canvas.rb +82 -0
  17. data/lib/iiif/v3/presentation/choice.rb +51 -0
  18. data/lib/iiif/v3/presentation/collection.rb +52 -0
  19. data/lib/iiif/v3/presentation/image_resource.rb +110 -0
  20. data/lib/iiif/v3/presentation/manifest.rb +82 -0
  21. data/lib/iiif/v3/presentation/nav_place.rb +109 -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 +37 -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/nav_place_spec.rb +80 -0
  50. data/spec/unit/iiif/v3/presentation/range_spec.rb +54 -0
  51. data/spec/unit/iiif/v3/presentation/resource_spec.rb +174 -0
  52. data/spec/unit/iiif/v3/presentation/sequence_spec.rb +222 -0
  53. data/spec/unit/iiif/v3/presentation/service_spec.rb +220 -0
  54. data/spec/unit/iiif/v3/presentation/shared_examples/abstract_resource_only_keys.rb +41 -0
  55. data/spec/unit/iiif/v3/presentation/shared_examples/any_type_keys.rb +31 -0
  56. data/spec/unit/iiif/v3/presentation/shared_examples/array_only_keys.rb +40 -0
  57. data/spec/unit/iiif/v3/presentation/shared_examples/hash_only_keys.rb +40 -0
  58. data/spec/unit/iiif/v3/presentation/shared_examples/int_only_keys.rb +45 -0
  59. data/spec/unit/iiif/v3/presentation/shared_examples/numeric_only_keys.rb +45 -0
  60. data/spec/unit/iiif/v3/presentation/shared_examples/string_only_keys.rb +26 -0
  61. data/spec/unit/iiif/v3/presentation/shared_examples/uri_only_keys.rb +31 -0
  62. metadata +93 -5
@@ -0,0 +1,37 @@
1
+ require_relative 'abstract_resource'
2
+ require_relative '../ordered_hash'
3
+
4
+ # NOTE: image_resource must follow resource due to inheritance
5
+ # NOTE: range must follow sequence due to inheritance
6
+ %w{
7
+ annotation
8
+ annotation_collection
9
+ annotation_page
10
+ canvas
11
+ choice
12
+ collection
13
+ manifest
14
+ nav_place
15
+ resource
16
+ image_resource
17
+ sequence
18
+ range
19
+ service
20
+ }.each do |f|
21
+ require File.join(File.dirname(__FILE__), 'presentation', f)
22
+ end
23
+
24
+ module IIIF
25
+ module V3
26
+ module Presentation
27
+ CONTEXT ||= [
28
+ 'http://www.w3.org/ns/anno.jsonld',
29
+ 'http://iiif.io/api/presentation/3/context.json'
30
+ ]
31
+
32
+ class MissingRequiredKeyError < StandardError; end
33
+ class ProhibitedKeyError < StandardError; end
34
+ class IllegalValueError < StandardError; end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,195 @@
1
+ {
2
+ "@context": [
3
+ "http://iiif.io/api/presentation/3/context.json",
4
+ "http://www.w3.org/ns/anno.jsonld"
5
+ ],
6
+ "id": "http://www.example.org/iiif/book1/manifest",
7
+ "type": "Manifest",
8
+ "label": { "en": [ "Book 1" ] },
9
+ "metadata": [
10
+ {
11
+ "label": "Author",
12
+ "value": "Anne Author"
13
+ },
14
+ {
15
+ "label": "Published",
16
+ "value": [
17
+ {
18
+ "@value": "Paris, circa 1400",
19
+ "@language": "en"
20
+ },
21
+ {
22
+ "@value": "Paris, environ 14eme siecle",
23
+ "@language": "fr"
24
+ }
25
+ ]
26
+ }
27
+ ],
28
+ "description": "A longer description of this example book. It should give some real information.",
29
+ "rights": "http://www.example.org/license.html",
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
+ },
36
+ "seeAlso": {
37
+ "id": "http://www.example.org/library/catalog/book1.marc",
38
+ "format": "application/marc"
39
+ },
40
+ "within": "http://www.example.org/collections/books/",
41
+ "sequences": [
42
+ {
43
+ "id": "http://www.example.org/iiif/book1/sequence/normal",
44
+ "type": "Sequence",
45
+ "label": "Current Page Order",
46
+ "viewingDirection": "left-to-right",
47
+ "viewingHint": "paged",
48
+ "canvases": [
49
+ {
50
+ "id": "http://www.example.org/iiif/book1/canvas/p1",
51
+ "type": "Canvas",
52
+ "label": "p. 1",
53
+ "height": 1000,
54
+ "width": 750,
55
+ "content": [
56
+ {
57
+ "id": "http://www.example.org/iiif/book1/page/p1",
58
+ "type": "AnnotationPage",
59
+ "items": [
60
+ {
61
+ "type": "Annotation",
62
+ "motivation": "painting",
63
+ "target": "http://www.example.org/iiif/book1/canvas/p1",
64
+ "body": {
65
+ "id": "http://www.example.org/iiif/book1/res/page1.jpg",
66
+ "type": "Image",
67
+ "format": "image/jpeg",
68
+ "height": 2000,
69
+ "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
+ }
75
+ }
76
+ }
77
+ ]
78
+ },
79
+ {
80
+ "id": "http://www.example.org/iiif/book1/page/p666",
81
+ "type": "AnnotationPage"
82
+ }
83
+ ]
84
+ },
85
+ {
86
+ "id": "http://www.example.org/iiif/book1/canvas/p2",
87
+ "type": "Canvas",
88
+ "label": "p. 2",
89
+ "height": 1000,
90
+ "width": 750,
91
+ "content": [
92
+ {
93
+ "id": "http://www.example.org/iiif/book1/page/p1",
94
+ "type": "AnnotationPage",
95
+ "items": [
96
+ {
97
+ "type": "Annotation",
98
+ "motivation": "painting",
99
+ "body": {
100
+ "id": "http://www.example.org/images/book1-page2/full/1500,2000/0/default.jpg",
101
+ "type": "Image",
102
+ "format": "image/jpeg",
103
+ "height": 2000,
104
+ "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
+ }
124
+ },
125
+ "target": "http://www.example.org/iiif/book1/canvas/p2"
126
+ }
127
+ ]
128
+ },
129
+ {
130
+ "id": "http://www.example.org/iiif/book1/list/p2",
131
+ "type": "AnnotationPage"
132
+ }
133
+ ]
134
+ },
135
+ {
136
+ "id": "http://www.example.org/iiif/book1/canvas/p3",
137
+ "type": "Canvas",
138
+ "label": "p. 3",
139
+ "height": 1000,
140
+ "width": 750,
141
+ "content": [
142
+ {
143
+ "id": "http://www.example.org/iiif/book1/page/p1",
144
+ "type": "AnnotationPage",
145
+ "items": [
146
+ {
147
+ "type": "Annotation",
148
+ "motivation": "painting",
149
+ "target": "http://www.example.org/iiif/book1/canvas/p3",
150
+ "body": {
151
+ "id": "http://www.example.org/iiif/book1/res/page3.jpg",
152
+ "type": "Image",
153
+ "format": "image/jpeg",
154
+ "height": 2000,
155
+ "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
+ }
161
+ }
162
+ }
163
+ ]
164
+ },
165
+ {
166
+ "id": "http://www.example.org/iiif/book1/page/p333",
167
+ "type": "AnnotationPage"
168
+ }
169
+ ]
170
+ }
171
+ ]
172
+ }
173
+ ],
174
+ "structures": [
175
+ {
176
+ "id": "http://www.example.org/iiif/book1/range/r1",
177
+ "type": "Range",
178
+ "label": "Introduction",
179
+ "members": [
180
+ {
181
+ "id": "http://www.example.org/iiif/book1/canvas/p1",
182
+ "type": "Canvas"
183
+ },
184
+ {
185
+ "id": "http://www.example.org/iiif/book1/canvas/p2",
186
+ "type": "Canvas"
187
+ },
188
+ {
189
+ "id": "http://www.example.org/iiif/book1/canvas/p3#xywh=0,0,750,300",
190
+ "type": "Canvas"
191
+ }
192
+ ]
193
+ }
194
+ ]
195
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "@context": [
3
+ "http://iiif.io/api/presentation/3/context.json",
4
+ "http://www.w3.org/ns/anno.jsonld"
5
+ ],
6
+ "type": "Manifest",
7
+ "id": "http://www.example.org/iiif/book1/manifest",
8
+ "label": "Book 1",
9
+ "sequences": [
10
+ {
11
+ "id": "http://www.example.org/iiif/book1/sequence/normal",
12
+ "label": "Current Page Order",
13
+ "canvases": [
14
+ {
15
+ "id": "http://www.example.org/iiif/book1/canvas/p1",
16
+ "type": "Canvas",
17
+ "label": "p. 1",
18
+ "height": 1000,
19
+ "width": 750,
20
+ "content": [
21
+ {
22
+ "id": "http://www.example.org/iiif/book1/page/p1",
23
+ "type": "AnnotationPage",
24
+ "items": [
25
+ {
26
+ "type": "Annotation",
27
+ "motivation": "painting",
28
+ "target": "http://www.example.org/iiif/book1/canvas/p1",
29
+ "body": {
30
+ "id": "http://www.example.org/iiif/book1/res/page1.jpg",
31
+ "type": "Image",
32
+ "format": "image/jpeg",
33
+ "height": 2000,
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
+ }
40
+ }
41
+ }
42
+ ]
43
+ }
44
+ ]
45
+ }
46
+ ]
47
+ }
48
+ ]
49
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "@context": [
3
+ "http://iiif.io/api/presentation/3/context.json",
4
+ "http://www.w3.org/ns/anno.jsonld"
5
+ ],
6
+ "type": "Manifest",
7
+ "id": "http://www.example.org/iiif/book1/manifest",
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
+ }
14
+ }
@@ -1 +1 @@
1
- {"http_interactions":[{"request":{"method":"get","uri":"https://libimages.princeton.edu/loris/pudl0001%2F4612422%2F00000001.jp2/info.json","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Faraday v1.0.1"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"date":["Wed, 17 Jun 2020 19:38:19 GMT"],"server":["Apache/2.4.18 (Ubuntu)"],"link":["<http://iiif.io/api/image/2/level2.json>;rel=\"profile\",<http://iiif.io/api/image/2/context.json>;rel=\"http://www.w3.org/ns/json-ld#context\";type=\"application/ld+json\""],"access-control-allow-origin":["*"],"access-control-allow-methods":["GET"],"access-control-allow-headers":["X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"],"last-modified":["Sat, 25 Jan 2020 18:37:32 GMT"],"content-length":["753"],"content-type":["application/json"]},"body":{"encoding":"UTF-8","string":"{\"profile\": [\"http://iiif.io/api/image/2/level2.json\", {\"supports\": [\"canonicalLinkHeader\", \"profileLinkHeader\", \"mirroring\", \"rotationArbitrary\", \"regionSquare\", \"sizeAboveFull\"], \"qualities\": [\"default\", \"bitonal\", \"gray\", \"color\"], \"formats\": [\"jpg\", \"png\", \"gif\", \"webp\"]}], \"tiles\": [{\"width\": 1024, \"scaleFactors\": [1, 2, 4, 8, 16, 32]}], \"protocol\": \"http://iiif.io/api/image\", \"sizes\": [{\"width\": 96, \"height\": 225}, {\"width\": 191, \"height\": 450}, {\"width\": 381, \"height\": 900}, {\"width\": 762, \"height\": 1800}, {\"width\": 1524, \"height\": 3600}, {\"width\": 3047, \"height\": 7200}], \"height\": 7200, \"width\": 3047, \"@context\": \"http://iiif.io/api/image/2/context.json\", \"@id\": \"https://libimages.princeton.edu/loris/pudl0001%2F4612422%2F00000001.jp2\"}"},"http_version":null},"recorded_at":"Wed, 17 Jun 2020 19:38:19 GMT"},{"request":{"method":"get","uri":"https://libimages.princeton.edu/loris/xxxx%2F4612422%2F00000001.jp2/info.json","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Faraday v1.0.1"]}},"response":{"status":{"code":404,"message":"NOT FOUND"},"headers":{"date":["Wed, 17 Jun 2020 19:38:20 GMT"],"server":["Apache/2.4.18 (Ubuntu)"],"link":["<http://iiif.io/api/image/2/level2.json>;rel=\"profile\""],"access-control-allow-origin":["*"],"access-control-allow-methods":["GET"],"access-control-allow-headers":["X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"],"content-length":["86"],"content-type":["text/plain"]},"body":{"encoding":"UTF-8","string":"Not Found: Source image not found for identifier: xxxx%2F4612422%2F00000001.jp2. (404)"},"http_version":null},"recorded_at":"Wed, 17 Jun 2020 19:38:20 GMT"}],"recorded_with":"VCR 5.1.0"}
1
+ {"http_interactions":[{"request":{"method":"get","uri":"https://libimages.princeton.edu/loris/pudl0001%2F4612422%2F00000001.jp2/info.json","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Faraday v1.0.1"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"date":["Wed, 17 Jun 2020 19:38:19 GMT"],"server":["Apache/2.4.18 (Ubuntu)"],"link":["<http://iiif.io/api/image/2/level2.json>;rel=\"profile\",<http://iiif.io/api/image/2/context.json>;rel=\"http://www.w3.org/ns/json-ld#context\";type=\"application/ld+json\""],"access-control-allow-origin":["*"],"access-control-allow-methods":["GET"],"access-control-allow-headers":["X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"],"last-modified":["Sat, 25 Jan 2020 18:37:32 GMT"],"content-length":["753"],"content-type":["application/json"]},"body":{"encoding":"UTF-8","string":"{\"profile\": [\"http://iiif.io/api/image/2/level2.json\", {\"supports\": [\"canonicalLinkHeader\", \"profileLinkHeader\", \"mirroring\", \"rotationArbitrary\", \"regionSquare\", \"sizeAboveFull\"], \"qualities\": [\"default\", \"bitonal\", \"gray\", \"color\"], \"formats\": [\"jpg\", \"png\", \"gif\", \"webp\"]}], \"tiles\": [{\"width\": 1024, \"scaleFactors\": [1, 2, 4, 8, 16, 32]}], \"protocol\": \"http://iiif.io/api/image\", \"sizes\": [{\"width\": 96, \"height\": 225}, {\"width\": 191, \"height\": 450}, {\"width\": 381, \"height\": 900}, {\"width\": 762, \"height\": 1800}, {\"width\": 1524, \"height\": 3600}, {\"width\": 3047, \"height\": 7200}], \"height\": 7200, \"width\": 3047, \"@context\": \"http://iiif.io/api/image/2/context.json\", \"@id\": \"https://libimages.princeton.edu/loris/pudl0001%2F4612422%2F00000001.jp2\"}"},"http_version":null},"recorded_at":"Wed, 17 Jun 2020 19:38:19 GMT"},{"request":{"method":"get","uri":"https://libimages.princeton.edu/loris/xxxx%2F4612422%2F00000001.jp2/info.json","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Faraday v1.0.1"]}},"response":{"status":{"code":404,"message":"NOT FOUND"},"headers":{"date":["Wed, 17 Jun 2020 19:38:20 GMT"],"server":["Apache/2.4.18 (Ubuntu)"],"link":["<http://iiif.io/api/image/2/level2.json>;rel=\"profile\""],"access-control-allow-origin":["*"],"access-control-allow-methods":["GET"],"access-control-allow-headers":["X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"],"content-length":["86"],"content-type":["text/plain"]},"body":{"encoding":"UTF-8","string":"Not Found: Source image not found for identifier: xxxx%2F4612422%2F00000001.jp2. (404)"},"http_version":null},"recorded_at":"Wed, 17 Jun 2020 19:38:20 GMT"}],"recorded_with":"VCR 5.1.0"}
@@ -0,0 +1 @@
1
+ {"http_interactions":[{"request":{"method":"get","uri":"https://libimages.princeton.edu/loris/pudl0001%2F4612422%2F00000001.jp2/info.json","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Faraday v0.12.1"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],"Accept":["*/*"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Date":["Tue, 27 Jun 2017 22:33:33 GMT"],"Server":["Apache/2.4.18 (Ubuntu)"],"Link":["<http://iiif.io/api/image/2/level2.json>;rel=\"profile\",<http://iiif.io/api/image/2/context.json>;rel=\"http://www.w3.org/ns/json-ld#context\";type=\"application/ld+json\""],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET"],"Access-Control-Allow-Headers":["X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"],"Last-Modified":["Tue, 11 Apr 2017 07:03:53 GMT"],"Content-Length":["753"],"Content-Type":["application/json"]},"body":{"encoding":"UTF-8","string":"{\"profile\": [\"http://iiif.io/api/image/2/level2.json\", {\"supports\": [\"canonicalLinkHeader\", \"profileLinkHeader\", \"mirroring\", \"rotationArbitrary\", \"regionSquare\", \"sizeAboveFull\"], \"qualities\": [\"default\", \"bitonal\", \"gray\", \"color\"], \"formats\": [\"jpg\", \"png\", \"gif\", \"webp\"]}], \"tiles\": [{\"width\": 1024, \"scaleFactors\": [1, 2, 4, 8, 16, 32]}], \"protocol\": \"http://iiif.io/api/image\", \"sizes\": [{\"width\": 96, \"height\": 225}, {\"width\": 191, \"height\": 450}, {\"width\": 381, \"height\": 900}, {\"width\": 762, \"height\": 1800}, {\"width\": 1524, \"height\": 3600}, {\"width\": 3047, \"height\": 7200}], \"height\": 7200, \"width\": 3047, \"@context\": \"http://iiif.io/api/image/2/context.json\", \"@id\": \"https://libimages.princeton.edu/loris/pudl0001%2F4612422%2F00000001.jp2\"}"},"http_version":null},"recorded_at":"Tue, 27 Jun 2017 22:33:33 GMT"},{"request":{"method":"get","uri":"https://libimages.princeton.edu/loris/xxxx%2F4612422%2F00000001.jp2/info.json","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Faraday v0.12.1"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],"Accept":["*/*"]}},"response":{"status":{"code":404,"message":"NOT FOUND"},"headers":{"Date":["Tue, 27 Jun 2017 22:33:33 GMT"],"Server":["Apache/2.4.18 (Ubuntu)"],"Link":["<http://iiif.io/api/image/2/level2.json>;rel=\"profile\""],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET"],"Access-Control-Allow-Headers":["X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"],"Content-Length":["86"],"Content-Type":["text/plain"]},"body":{"encoding":"UTF-8","string":"Not Found: Source image not found for identifier: xxxx%2F4612422%2F00000001.jp2. (404)"},"http_version":null},"recorded_at":"Tue, 27 Jun 2017 22:33:33 GMT"}],"recorded_with":"VCR 2.9.3"}
@@ -7,7 +7,6 @@ describe IIIF::Presentation::ImageResource do
7
7
 
8
8
  describe 'self#create_image_api_image_resource', vcr: vcr_options do
9
9
 
10
- # 301 moved to https.../loris
11
10
  let(:image_server) { 'https://libimages.princeton.edu/loris' }
12
11
 
13
12
  let(:valid_service_id) {
@@ -1,7 +1,4 @@
1
- require 'active_support/inflector'
2
- require 'json'
3
-
4
- describe IIIF::Service do
1
+ RSpec.describe IIIF::Service do
5
2
 
6
3
  let(:fixtures_dir) { File.join(File.dirname(__FILE__), '../../fixtures') }
7
4
  let(:manifest_from_spec_path) { File.join(fixtures_dir, 'manifests/complete_from_spec.json') }
@@ -39,6 +36,7 @@ describe IIIF::Service do
39
36
  end
40
37
 
41
38
  describe 'self#from_ordered_hash' do
39
+ subject(:parsed) { described_class.from_ordered_hash(fixture) }
42
40
  let(:fixture) { JSON.parse('{
43
41
  "@context": "http://iiif.io/api/presentation/2/context.json",
44
42
  "@id": "http://example.com/manifest",
@@ -87,35 +85,28 @@ describe IIIF::Service do
87
85
  }')
88
86
  }
89
87
  it 'doesn\'t raise a NoMethodError when we check the keys' do
90
- expect { described_class.from_ordered_hash(fixture) }.to_not raise_error
88
+ expect { parsed }.to_not raise_error
91
89
  end
90
+
92
91
  it 'turns the fixture into a Manifest instance' do
93
- expected_klass = IIIF::Presentation::Manifest
94
- parsed = described_class.from_ordered_hash(fixture)
95
- expect(parsed.class).to be expected_klass
92
+ expect(parsed).to be_a IIIF::Presentation::Manifest
96
93
  end
94
+
97
95
  it 'turns keys without "@type" into an OrderedHash' do
98
- expected_klass = IIIF::OrderedHash
99
- parsed = described_class.from_ordered_hash(fixture)
100
- expect(parsed['some_other_thing'].class).to be expected_klass
96
+ expect(parsed['some_other_thing']).to be_a IIIF::OrderedHash
101
97
  end
102
98
 
103
99
  it 'turns services into Services' do
104
- expected_klass = IIIF::Service
105
- parsed = described_class.from_ordered_hash(fixture)
106
- expect(parsed['service'].class).to be expected_klass
100
+ expect(parsed['service']).to be_a IIIF::Presentation::Service
107
101
  end
108
102
 
109
103
  it 'works with arrays of services' do
110
- expected_klass = IIIF::Service
111
104
  fixture['service'] = [fixture['service']]
112
- parsed = described_class.from_ordered_hash(fixture)
113
- expect(parsed['service'].first.class).to be expected_klass
105
+ expect(parsed['service'].first).to be_a IIIF::Presentation::Service
114
106
  end
115
107
 
116
108
  it 'round-trips' do
117
109
  fp = '/tmp/osullivan-spec.json'
118
- parsed = described_class.from_ordered_hash(fixture)
119
110
  File.open(fp,'w') do |f|
120
111
  f.write(parsed.to_json)
121
112
  end
@@ -125,31 +116,25 @@ describe IIIF::Service do
125
116
  expect(parsed.to_ordered_hash.to_a - from_file.to_ordered_hash.to_a).to eq []
126
117
  expect(from_file.to_ordered_hash.to_a - parsed.to_ordered_hash.to_a).to eq []
127
118
  end
119
+
128
120
  it 'turns each memeber of "sequences" into an instance of Sequence' do
129
- expected_klass = IIIF::Presentation::Sequence
130
- parsed = described_class.from_ordered_hash(fixture)
131
- parsed['sequences'].each do |s|
132
- expect(s.class).to be expected_klass
133
- end
121
+ expect(parsed['sequences']).to all be_a IIIF::Presentation::Sequence
134
122
  end
123
+
135
124
  it 'turns each member of sequences/canvaes in an instance of Canvas' do
136
- expected_klass = IIIF::Presentation::Canvas
137
- parsed = described_class.from_ordered_hash(fixture)
138
125
  parsed['sequences'].each do |s|
139
- s.canvases.each do |c|
140
- expect(c.class).to be expected_klass
141
- end
126
+ expect(s.canvases).to all be_a IIIF::Presentation::Canvas
142
127
  end
143
128
  end
129
+
144
130
  it 'turns the keys into snakes' do
145
- expect(described_class.from_ordered_hash(fixture).has_key?('seeAlso')).to be_falsey
146
- expect(described_class.from_ordered_hash(fixture).has_key?('see_also')).to be_truthy
131
+ expect(parsed.has_key?('seeAlso')).to be false
132
+ expect(parsed.has_key?('see_also')).to be true
147
133
  end
134
+
148
135
  it 'copies over plain-old key-values' do
149
- parsed = described_class.from_ordered_hash(fixture)
150
136
  expect(parsed['label']).to eq 'My Manifest'
151
137
  end
152
-
153
138
  end
154
139
 
155
140
  describe '#to_ordered_hash' do
@@ -0,0 +1,202 @@
1
+ require 'active_support/inflector'
2
+ require 'json'
3
+
4
+ describe IIIF::V3::AbstractResource do
5
+
6
+ let(:fixtures_dir) { File.join(File.dirname(__FILE__), '../../../fixtures') }
7
+ let(:manifest_from_spec_path) { File.join(fixtures_dir, 'v3/manifests/complete_from_spec.json') }
8
+
9
+ describe 'self.parse' do
10
+ it 'works from a file' do
11
+ s = described_class.parse(manifest_from_spec_path)
12
+ expect(s['label']['en']).to include 'Book 1'
13
+ end
14
+ it 'works from a string of JSON' do
15
+ file = File.open(manifest_from_spec_path, 'rb')
16
+ json_string = file.read
17
+ file.close
18
+ s = described_class.parse(json_string)
19
+ expect(s['label']['en']).to include 'Book 1'
20
+ end
21
+ describe 'works from a hash' do
22
+ it 'plain old' do
23
+ h = JSON.parse(IO.read(manifest_from_spec_path))
24
+ s = described_class.parse(h)
25
+ expect(s['label']['en']).to include 'Book 1'
26
+ end
27
+ it 'IIIF::OrderedHash' do
28
+ h = JSON.parse(IO.read(manifest_from_spec_path))
29
+ oh = IIIF::OrderedHash[h]
30
+ s = described_class.parse(oh)
31
+ expect(s['label']['en']).to include 'Book 1'
32
+ end
33
+ end
34
+ it 'turns camels to snakes' do
35
+ s = described_class.parse(manifest_from_spec_path)
36
+ expect(s.keys.include?('see_also')).to be_truthy
37
+ expect(s.keys.include?('seeAlso')).to be_falsey
38
+ end
39
+ end
40
+
41
+ describe 'self#from_ordered_hash' do
42
+ let(:fixture) { JSON.parse('{
43
+ "@context": [
44
+ "http://iiif.io/api/presentation/3/context.json",
45
+ "http://www.w3.org/ns/anno.jsonld"
46
+ ],
47
+ "id": "http://example.com/manifest",
48
+ "type": "Manifest",
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
+ },
56
+ "some_other_thing": {
57
+ "foo" : "bar"
58
+ },
59
+ "seeAlso": {
60
+ "id": "http://www.example.org/library/catalog/book1.marc",
61
+ "format": "application/marc"
62
+ },
63
+ "items": [
64
+ {
65
+ "id": "http://example.com/canvas",
66
+ "type": "Canvas",
67
+ "width": 10,
68
+ "height": 20,
69
+ "label": "My Canvas",
70
+ "content": [
71
+ {
72
+ "id": "http://example.com/content",
73
+ "type": "AnnotationPage",
74
+ "motivation": "painting"
75
+ }
76
+ ]
77
+ }
78
+ ]
79
+ }')
80
+ }
81
+ it 'doesn\'t raise a NoMethodError when we check the keys' do
82
+ expect { described_class.from_ordered_hash(fixture) }.to_not raise_error
83
+ end
84
+ it 'turns the fixture into a Manifest instance' do
85
+ expected_klass = IIIF::V3::Presentation::Manifest
86
+ parsed = described_class.from_ordered_hash(fixture)
87
+ expect(parsed.class).to be expected_klass
88
+ end
89
+ it 'turns keys without "type" into an OrderedHash' do
90
+ expected_klass = IIIF::OrderedHash
91
+ parsed = described_class.from_ordered_hash(fixture)
92
+ expect(parsed['some_other_thing'].class).to be expected_klass
93
+ end
94
+
95
+ it 'turns services into Services' do
96
+ expected_klass = IIIF::V3::Presentation::Service
97
+ parsed = described_class.from_ordered_hash(fixture)
98
+ expect(parsed['service'].class).to be expected_klass
99
+ end
100
+
101
+ it 'round-trips' do
102
+ fp = '/tmp/osullivan-spec.json'
103
+ parsed = described_class.from_ordered_hash(fixture)
104
+ File.open(fp,'w') do |f|
105
+ f.write(parsed.to_json)
106
+ end
107
+ from_file = IIIF::V3::Presentation::Service.parse('/tmp/osullivan-spec.json')
108
+ File.delete(fp)
109
+ # is this sufficient?
110
+ expect(parsed.to_ordered_hash.to_a - from_file.to_ordered_hash.to_a).to eq []
111
+ expect(from_file.to_ordered_hash.to_a - parsed.to_ordered_hash.to_a).to eq []
112
+ end
113
+ it 'turns each member of "items" into an instance of Sequence' do
114
+ parsed = described_class.from_ordered_hash(fixture)
115
+ parsed['items'].each do |s|
116
+ expect(s.class).to be IIIF::V3::Presentation::Canvas
117
+ end
118
+ end
119
+ it 'turns each member of sequences/items into an instance of Canvas' do
120
+ parsed = described_class.from_ordered_hash(fixture)
121
+ parsed['items'].each do |s|
122
+ s.items.each do |c|
123
+ expect(c.class).to be IIIF::V3::Presentation::Canvas
124
+ end
125
+ end
126
+ end
127
+ it 'turns the keys into snakes' do
128
+ expect(described_class.from_ordered_hash(fixture).has_key?('seeAlso')).to be_falsey
129
+ expect(described_class.from_ordered_hash(fixture).has_key?('see_also')).to be_truthy
130
+ end
131
+ it 'copies over plain-old key-values' do
132
+ parsed = described_class.from_ordered_hash(fixture)
133
+ expect(parsed['label']).to eq 'My Manifest'
134
+ end
135
+ end
136
+
137
+ describe '#to_ordered_hash' do
138
+ let(:logo_uri) { 'http://www.example.org/logos/institution1.jpg' }
139
+ let(:within_uri) { 'http://www.example.org/collections/books/' }
140
+ let(:see_also) { 'http://www.example.org/library/catalog/book1.xml' }
141
+ # NOTE: Using Service to test, as we can't initialize the abstract class
142
+ let(:instantiated_class) { IIIF::V3::Presentation::Service.new }
143
+
144
+ describe 'it puts the json-ld keys at the top' do
145
+ let(:extra_props) { [
146
+ ['label','foo'],
147
+ ['logo','http://example.com/logo.jpg'],
148
+ ['within','http://example.com/something']
149
+ ] }
150
+ let(:sorted_ld_keys) {
151
+ instantiated_class.keys.select { |k| k.start_with?('@') }.sort!
152
+ }
153
+ before(:each) {
154
+ extra_props.reverse.each do |k,v|
155
+ instantiated_class.unshift(k,v)
156
+ end
157
+ }
158
+
159
+ it 'by default' do
160
+ (0..extra_props.length-1).each do |i|
161
+ expect(instantiated_class.keys[i]).to eq(extra_props[i][0])
162
+ end
163
+ oh = instantiated_class.to_ordered_hash
164
+ (0..sorted_ld_keys.length-1).each do |i|
165
+ expect(oh.keys[i]).to eq(sorted_ld_keys[i])
166
+ end
167
+ end
168
+ it 'unless you say not to' do
169
+ (0..extra_props.length-1).each do |i|
170
+ expect(instantiated_class.keys[i]).to eq(extra_props[i][0])
171
+ end
172
+ oh = instantiated_class.to_ordered_hash(sort_json_ld_keys: false)
173
+ (0..extra_props.length-1).each do |i|
174
+ expect(oh.keys[i]).to eq(extra_props[i][0])
175
+ end
176
+ end
177
+ end
178
+
179
+ describe 'removes empty keys' do
180
+ it 'if they\'re arrays' do
181
+ instantiated_class['logo'] = logo_uri
182
+ instantiated_class['within'] = []
183
+ ordered_hash = instantiated_class.to_ordered_hash
184
+ expect(ordered_hash.has_key?('within')).to be_falsey
185
+ end
186
+ it 'if they\'re nil' do
187
+ instantiated_class['logo'] = logo_uri
188
+ instantiated_class['within'] = nil
189
+ ordered_hash = instantiated_class.to_ordered_hash
190
+ expect(ordered_hash.has_key?('within')).to be_falsey
191
+ end
192
+ end
193
+
194
+ it 'converts snake_case keys to camelCase' do
195
+ instantiated_class['see_also'] = logo_uri
196
+ instantiated_class['within'] = within_uri
197
+ ordered_hash = instantiated_class.to_ordered_hash
198
+ expect(ordered_hash.keys.include?('seeAlso')).to be_truthy
199
+ end
200
+ end
201
+
202
+ end