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
@@ -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