osullivan 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +2 -0
  6. data/LICENSE +23 -0
  7. data/README.md +166 -0
  8. data/Rakefile +12 -0
  9. data/VERSION +1 -0
  10. data/lib/active_support/ordered_hash.rb +147 -0
  11. data/lib/iiif/hash_behaviours.rb +150 -0
  12. data/lib/iiif/presentation.rb +25 -0
  13. data/lib/iiif/presentation/abstract_resource.rb +75 -0
  14. data/lib/iiif/presentation/annotation.rb +25 -0
  15. data/lib/iiif/presentation/annotation_list.rb +28 -0
  16. data/lib/iiif/presentation/canvas.rb +45 -0
  17. data/lib/iiif/presentation/collection.rb +29 -0
  18. data/lib/iiif/presentation/image_resource.rb +115 -0
  19. data/lib/iiif/presentation/layer.rb +34 -0
  20. data/lib/iiif/presentation/manifest.rb +39 -0
  21. data/lib/iiif/presentation/range.rb +32 -0
  22. data/lib/iiif/presentation/resource.rb +21 -0
  23. data/lib/iiif/presentation/sequence.rb +35 -0
  24. data/lib/iiif/service.rb +418 -0
  25. data/osullivan.gemspec +27 -0
  26. data/spec/fixtures/manifests/complete_from_spec.json +171 -0
  27. data/spec/fixtures/manifests/minimal.json +40 -0
  28. data/spec/fixtures/manifests/service_only.json +11 -0
  29. data/spec/fixtures/vcr_cassettes/pul_loris_cassette.json +159 -0
  30. data/spec/integration/iiif/presentation/image_resource_spec.rb +123 -0
  31. data/spec/integration/iiif/service_spec.rb +211 -0
  32. data/spec/spec_helper.rb +104 -0
  33. data/spec/unit/active_support/ordered_hash_spec.rb +155 -0
  34. data/spec/unit/iiif/hash_behaviours_spec.rb +569 -0
  35. data/spec/unit/iiif/presentation/abstract_resource_spec.rb +133 -0
  36. data/spec/unit/iiif/presentation/annotation_list_spec.rb +7 -0
  37. data/spec/unit/iiif/presentation/annotation_spec.rb +7 -0
  38. data/spec/unit/iiif/presentation/canvas_spec.rb +40 -0
  39. data/spec/unit/iiif/presentation/collection_spec.rb +54 -0
  40. data/spec/unit/iiif/presentation/image_resource_spec.rb +13 -0
  41. data/spec/unit/iiif/presentation/layer_spec.rb +38 -0
  42. data/spec/unit/iiif/presentation/manifest_spec.rb +89 -0
  43. data/spec/unit/iiif/presentation/range_spec.rb +43 -0
  44. data/spec/unit/iiif/presentation/resource_spec.rb +16 -0
  45. data/spec/unit/iiif/presentation/sequence_spec.rb +110 -0
  46. data/spec/unit/iiif/presentation/shared_examples/abstract_resource_only_keys.rb +43 -0
  47. data/spec/unit/iiif/presentation/shared_examples/any_type_keys.rb +33 -0
  48. data/spec/unit/iiif/presentation/shared_examples/array_only_keys.rb +44 -0
  49. data/spec/unit/iiif/presentation/shared_examples/int_only_keys.rb +49 -0
  50. data/spec/unit/iiif/presentation/shared_examples/string_only_keys.rb +29 -0
  51. data/spec/unit/iiif/service_spec.rb +10 -0
  52. metadata +246 -0
@@ -0,0 +1,27 @@
1
+ version = File.read(File.expand_path('../VERSION', __FILE__)).strip
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'osullivan'
5
+ spec.version = version
6
+ spec.authors = ['Jon Stroop']
7
+ spec.email = ['jpstroop@gmail.com']
8
+ spec.description = 'API for working with IIIF Presentation manifests.'
9
+ spec.summary = 'API for working with IIIF Presentation manifests.'
10
+ spec.license = 'Simplified BSD'
11
+ spec.homepage = 'https://github.com/jpstroop/osullivan'
12
+
13
+ spec.files = `git ls-files`.split($/)
14
+ spec.test_files = spec.files.grep(%r{^spec/})
15
+ spec.require_paths = ['lib']
16
+
17
+ spec.add_development_dependency 'bundler'
18
+ spec.add_development_dependency 'rake'
19
+ spec.add_development_dependency 'rspec'
20
+ spec.add_development_dependency 'coveralls'
21
+ spec.add_development_dependency 'webmock'
22
+ spec.add_development_dependency 'vcr', '~> 2.9.3'
23
+
24
+ spec.add_dependency 'json'
25
+ spec.add_dependency 'activesupport', '~> 3.2'
26
+ spec.add_dependency 'faraday', '~> 0.9.0'
27
+ end
@@ -0,0 +1,171 @@
1
+ {
2
+ "@context": "http://iiif.io/api/presentation/2/context.json",
3
+ "@id": "http://www.example.org/iiif/book1/manifest",
4
+ "@type": "sc:Manifest",
5
+ "label": "Book 1",
6
+ "metadata": [
7
+ {
8
+ "label": "Author",
9
+ "value": "Anne Author"
10
+ },
11
+ {
12
+ "label": "Published",
13
+ "value": [
14
+ {
15
+ "@value": "Paris, circa 1400",
16
+ "@language": "en"
17
+ },
18
+ {
19
+ "@value": "Paris, environ 14eme siecle",
20
+ "@language": "fr"
21
+ }
22
+ ]
23
+ }
24
+ ],
25
+ "description": "A longer description of this example book. It should give some real information.",
26
+ "license": "http://www.example.org/license.html",
27
+ "attribution": "Provided by Example Organization",
28
+ "service": {
29
+ "@context": "http://example.org/ns/jsonld/context.json",
30
+ "@id": "http://example.org/service/example",
31
+ "profile": "http://example.org/docs/example-service.html"
32
+ },
33
+ "seeAlso": {
34
+ "@id": "http://www.example.org/library/catalog/book1.marc",
35
+ "format": "application/marc"
36
+ },
37
+ "within": "http://www.example.org/collections/books/",
38
+ "sequences": [
39
+ {
40
+ "@id": "http://www.example.org/iiif/book1/sequence/normal",
41
+ "@type": "sc:Sequence",
42
+ "label": "Current Page Order",
43
+ "viewingDirection": "left-to-right",
44
+ "viewingHint": "paged",
45
+ "canvases": [
46
+ {
47
+ "@id": "http://www.example.org/iiif/book1/canvas/p1",
48
+ "@type": "sc:Canvas",
49
+ "label": "p. 1",
50
+ "height": 1000,
51
+ "width": 750,
52
+ "images": [
53
+ {
54
+ "@type": "oa:Annotation",
55
+ "motivation": "sc:painting",
56
+ "resource": {
57
+ "@id": "http://www.example.org/iiif/book1/res/page1.jpg",
58
+ "@type": "dctypes:Image",
59
+ "format": "image/jpeg",
60
+ "service": {
61
+ "@context": "http://iiif.io/api/image/2/context.json",
62
+ "@id": "http://www.example.org/images/book1-page1",
63
+ "profile": "http://iiif.io/api/image/2/level1.json"
64
+ },
65
+ "height": 2000,
66
+ "width": 1500
67
+ },
68
+ "on": "http://www.example.org/iiif/book1/canvas/p1"
69
+ }
70
+ ],
71
+ "otherContent": [
72
+ {
73
+ "@id": "http://www.example.org/iiif/book1/list/p1",
74
+ "@type": "sc:AnnotationList"
75
+ }
76
+ ]
77
+ },
78
+ {
79
+ "@id": "http://www.example.org/iiif/book1/canvas/p2",
80
+ "@type": "sc:Canvas",
81
+ "label": "p. 2",
82
+ "height": 1000,
83
+ "width": 750,
84
+ "images": [
85
+ {
86
+ "@type": "oa:Annotation",
87
+ "motivation": "sc:painting",
88
+ "resource": {
89
+ "@id": "http://www.example.org/images/book1-page2/full/1500,2000/0/default.jpg",
90
+ "@type": "dctypes:Image",
91
+ "format": "image/jpeg",
92
+ "height": 2000,
93
+ "width": 1500,
94
+ "service": {
95
+ "@context": "http://iiif.io/api/image/2/context.json",
96
+ "@id": "http://www.example.org/images/book1-page2",
97
+ "profile": "http://iiif.io/api/image/2/level1.json",
98
+ "height": 8000,
99
+ "width": 6000,
100
+ "tiles": [
101
+ {
102
+ "width": 512,
103
+ "scaleFactors": [
104
+ 1,
105
+ 2,
106
+ 4,
107
+ 8,
108
+ 16
109
+ ]
110
+ }
111
+ ]
112
+ }
113
+ },
114
+ "on": "http://www.example.org/iiif/book1/canvas/p2"
115
+ }
116
+ ],
117
+ "otherContent": [
118
+ {
119
+ "@id": "http://www.example.org/iiif/book1/list/p2",
120
+ "@type": "sc:AnnotationList"
121
+ }
122
+ ]
123
+ },
124
+ {
125
+ "@id": "http://www.example.org/iiif/book1/canvas/p3",
126
+ "@type": "sc:Canvas",
127
+ "label": "p. 3",
128
+ "height": 1000,
129
+ "width": 750,
130
+ "images": [
131
+ {
132
+ "@type": "oa:Annotation",
133
+ "motivation": "sc:painting",
134
+ "resource": {
135
+ "@id": "http://www.example.org/iiif/book1/res/page3.jpg",
136
+ "@type": "dctypes:Image",
137
+ "format": "image/jpeg",
138
+ "service": {
139
+ "@context": "http://iiif.io/api/image/2/context.json",
140
+ "@id": "http://www.example.org/images/book1-page3",
141
+ "profile": "http://iiif.io/api/image/2/level1.json"
142
+ },
143
+ "height": 2000,
144
+ "width": 1500
145
+ },
146
+ "on": "http://www.example.org/iiif/book1/canvas/p3"
147
+ }
148
+ ],
149
+ "otherContent": [
150
+ {
151
+ "@id": "http://www.example.org/iiif/book1/list/p3",
152
+ "@type": "sc:AnnotationList"
153
+ }
154
+ ]
155
+ }
156
+ ]
157
+ }
158
+ ],
159
+ "structures": [
160
+ {
161
+ "@id": "http://www.example.org/iiif/book1/range/r1",
162
+ "@type": "sc:Range",
163
+ "label": "Introduction",
164
+ "canvases": [
165
+ "http://www.example.org/iiif/book1/canvas/p1",
166
+ "http://www.example.org/iiif/book1/canvas/p2",
167
+ "http://www.example.org/iiif/book1/canvas/p3#xywh=0,0,750,300"
168
+ ]
169
+ }
170
+ ]
171
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "@context": "http://iiif.io/api/presentation/2/context.json",
3
+ "@type": "sc:Manifest",
4
+ "@id": "http://www.example.org/iiif/book1/manifest",
5
+ "label": "Book 1",
6
+ "sequences": [
7
+ {
8
+ "@id": "http://www.example.org/iiif/book1/sequence/normal",
9
+ "label": "Current Page Order",
10
+ "canvases": [
11
+ {
12
+ "@id": "http://www.example.org/iiif/book1/canvas/p1",
13
+ "@type": "sc:Canvas",
14
+ "label": "p. 1",
15
+ "height": 1000,
16
+ "width": 750,
17
+ "images": [
18
+ {
19
+ "@type": "oa:Annotation",
20
+ "motivation": "sc:painting",
21
+ "on": "http://www.example.org/iiif/book1/canvas/p1",
22
+ "resource": {
23
+ "@id": "http://www.example.org/iiif/book1/res/page1.jpg",
24
+ "@type": "dctypes:Image",
25
+ "format": "image/jpeg",
26
+ "height": 2000,
27
+ "width": 1500,
28
+ "service": {
29
+ "@context": "http://iiif.io/api/image/2/context.json",
30
+ "@id": "http://www.example.org/images/book1-page1",
31
+ "profile": "http://iiif.io/api/image/2/level1.json"
32
+ }
33
+ }
34
+ }
35
+ ]
36
+ }
37
+ ]
38
+ }
39
+ ]
40
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "@context": "http://iiif.io/api/presentation/2/context.json",
3
+ "@type": "sc:Manifest",
4
+ "@id": "http://www.example.org/iiif/book1/manifest",
5
+ "label": "Book 1",
6
+ "service": {
7
+ "@context": "http://example.org/ns/jsonld/context.json",
8
+ "@id": "http://example.org/service/example",
9
+ "profile": "http://example.org/docs/example-service.html"
10
+ }
11
+ }
@@ -0,0 +1,159 @@
1
+ {
2
+ "http_interactions": [
3
+ {
4
+ "request": {
5
+ "method": "get",
6
+ "uri": "http://libimages.princeton.edu/loris2/pudl0001%2F4612422%2F00000001.jp2/info.json",
7
+ "body": {
8
+ "encoding": "US-ASCII",
9
+ "string": ""
10
+ },
11
+ "headers": {
12
+ "User-Agent": [
13
+ "Faraday v0.9.0"
14
+ ],
15
+ "Accept-Encoding": [
16
+ "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
17
+ ],
18
+ "Accept": [
19
+ "*/*"
20
+ ]
21
+ }
22
+ },
23
+ "response": {
24
+ "status": {
25
+ "code": 200,
26
+ "message": "OK"
27
+ },
28
+ "headers": {
29
+ "Server": [
30
+ "Apache/2.2.22 (Ubuntu)"
31
+ ],
32
+ "Link": [
33
+ "<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\""
34
+ ],
35
+ "Access-Control-Allow-Origin": [
36
+ "*"
37
+ ],
38
+ "Last-Modified": [
39
+ "Wed, 26 Nov 2014 21:16:14 GMT"
40
+ ],
41
+ "Cache-Control": [
42
+ "max-age=5184000"
43
+ ],
44
+ "Expires": [
45
+ "Sun, 25 Jan 2015 21:16:14 GMT"
46
+ ],
47
+ "Content-Type": [
48
+ "application/json"
49
+ ],
50
+ "Content-Length": [
51
+ "737"
52
+ ],
53
+ "Accept-Ranges": [
54
+ "bytes"
55
+ ],
56
+ "Date": [
57
+ "Thu, 27 Nov 2014 00:55:14 GMT"
58
+ ],
59
+ "X-Varnish": [
60
+ "1471998866 1471996229"
61
+ ],
62
+ "Age": [
63
+ "13140"
64
+ ],
65
+ "Via": [
66
+ "1.1 varnish"
67
+ ],
68
+ "Connection": [
69
+ "keep-alive"
70
+ ]
71
+ },
72
+ "body": {
73
+ "encoding": "UTF-8",
74
+ "string": "{\"profile\": [\"http://iiif.io/api/image/2/level2.json\", {\"supports\": [\"canonicalLinkHeader\", \"profileLinkHeader\", \"mirroring\", \"rotationArbitrary\", \"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\": \"http://libimages.princeton.edu/loris2/pudl0001%2F4612422%2F00000001.jp2\"}"
75
+ },
76
+ "http_version": null
77
+ },
78
+ "recorded_at": "Thu, 27 Nov 2014 01:00:58 GMT"
79
+ },
80
+ {
81
+ "request": {
82
+ "method": "get",
83
+ "uri": "http://libimages.princeton.edu/loris2/xxxx%2F4612422%2F00000001.jp2/info.json",
84
+ "body": {
85
+ "encoding": "US-ASCII",
86
+ "string": ""
87
+ },
88
+ "headers": {
89
+ "User-Agent": [
90
+ "Faraday v0.9.0"
91
+ ],
92
+ "Accept-Encoding": [
93
+ "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
94
+ ],
95
+ "Accept": [
96
+ "*/*"
97
+ ]
98
+ }
99
+ },
100
+ "response": {
101
+ "status": {
102
+ "code": 404,
103
+ "message": "NOT FOUND"
104
+ },
105
+ "headers": {
106
+ "Server": [
107
+ "Apache/2.2.22 (Ubuntu)"
108
+ ],
109
+ "Link": [
110
+ "<http://iiif.io/api/image/2/level2.json>;rel=\"profile\""
111
+ ],
112
+ "Access-Control-Allow-Origin": [
113
+ "*"
114
+ ],
115
+ "Cache-Control": [
116
+ "max-age=5184000"
117
+ ],
118
+ "Expires": [
119
+ "Mon, 26 Jan 2015 10:20:46 GMT"
120
+ ],
121
+ "Vary": [
122
+ "Accept-Encoding"
123
+ ],
124
+ "Content-Type": [
125
+ "text/plain"
126
+ ],
127
+ "Content-Length": [
128
+ "88"
129
+ ],
130
+ "Accept-Ranges": [
131
+ "bytes"
132
+ ],
133
+ "Date": [
134
+ "Thu, 27 Nov 2014 10:20:46 GMT"
135
+ ],
136
+ "X-Varnish": [
137
+ "1472002765"
138
+ ],
139
+ "Age": [
140
+ "0"
141
+ ],
142
+ "Via": [
143
+ "1.1 varnish"
144
+ ],
145
+ "Connection": [
146
+ "keep-alive"
147
+ ]
148
+ },
149
+ "body": {
150
+ "encoding": "UTF-8",
151
+ "string": "Not Found: could not resolve identifier: xxxx%2F4612422%2F00000001.jp2 (404)"
152
+ },
153
+ "http_version": null
154
+ },
155
+ "recorded_at": "Thu, 27 Nov 2014 10:26:31 GMT"
156
+ }
157
+ ],
158
+ "recorded_with": "VCR 2.9.3"
159
+ }
@@ -0,0 +1,123 @@
1
+ describe IIIF::Presentation::ImageResource do
2
+ vcr_options = {
3
+ cassette_name: 'pul_loris_cassette',
4
+ record: :new_episodes,
5
+ serialize_with: :json
6
+ }
7
+
8
+ describe 'self#create_image_api_image_resource', vcr: vcr_options do
9
+
10
+ let(:image_server) { 'http://libimages.princeton.edu/loris2' }
11
+
12
+ let(:valid_service_id) {
13
+ id = 'pudl0001%2F4612422%2F00000001.jp2'
14
+ "#{image_server}/#{id}"
15
+ }
16
+
17
+ let(:invalid_service_id) {
18
+ id = 'xxxx%2F4612422%2F00000001.jp2'
19
+ "#{image_server}/#{id}"
20
+ }
21
+
22
+ it 'returns an ImageResource' do
23
+ instance = described_class.create_image_api_image_resource(service_id: valid_service_id)
24
+ expect(instance.class).to be described_class
25
+ end
26
+
27
+
28
+ describe 'has expected values from our fixture' do
29
+ it 'when copy_info is false' do
30
+ opts = { service_id: valid_service_id }
31
+ resource = described_class.create_image_api_image_resource(opts)
32
+ # expect(resource['@context']).to eq 'http://iiif.io/api/presentation/2/context.json'
33
+ # @context is only added when we call to_json...
34
+ expect(resource['@id']).to eq 'http://libimages.princeton.edu/loris2/pudl0001%2F4612422%2F00000001.jp2/full/!200,200/0/default.jpg'
35
+ expect(resource['@type']).to eq 'dcterms:Image'
36
+ expect(resource.format).to eq "image/jpeg"
37
+ expect(resource.width).to eq 3047
38
+ expect(resource.height).to eq 7200
39
+ expect(resource.service['@context']).to eq 'http://iiif.io/api/image/2/context.json'
40
+ expect(resource.service['@id']).to eq 'http://libimages.princeton.edu/loris2/pudl0001%2F4612422%2F00000001.jp2'
41
+ expect(resource.service['profile']).to eq 'http://iiif.io/api/image/2/level2.json'
42
+ end
43
+ it 'copies over all teh infos (when copy_info is true)' do
44
+ opts = { service_id: valid_service_id, copy_info: true }
45
+ resource = described_class.create_image_api_image_resource(opts)
46
+ expect(resource['@id']).to eq 'http://libimages.princeton.edu/loris2/pudl0001%2F4612422%2F00000001.jp2/full/!200,200/0/default.jpg'
47
+ expect(resource['@type']).to eq 'dcterms:Image'
48
+ expect(resource.format).to eq "image/jpeg"
49
+ expect(resource.width).to eq 3047
50
+ expect(resource.height).to eq 7200
51
+ expect(resource.service['@context']).to eq 'http://iiif.io/api/image/2/context.json'
52
+ expect(resource.service['@id']).to eq 'http://libimages.princeton.edu/loris2/pudl0001%2F4612422%2F00000001.jp2'
53
+ expect(resource.service['profile']).to eq [
54
+ 'http://iiif.io/api/image/2/level2.json',
55
+ {
56
+ 'supports' => [
57
+ 'canonicalLinkHeader', 'profileLinkHeader', 'mirroring',
58
+ 'rotationArbitrary', 'sizeAboveFull'
59
+ ],
60
+ 'qualities' => ['default', 'bitonal', 'gray', 'color'],
61
+ 'formats'=>['jpg', 'png', 'gif', 'webp']
62
+ }
63
+ ]
64
+ expect(resource.service['tiles']).to eq [ {
65
+ 'width' => 1024,
66
+ 'scaleFactors' => [ 1, 2, 4, 8, 16, 32 ]
67
+ } ]
68
+ expect(resource.service['sizes']).to eq [
69
+ {'width' => 96, 'height' => 225 },
70
+ {'width' => 191, 'height' => 450 },
71
+ {'width' => 381, 'height' => 900 },
72
+ {'width' => 762, 'height' => 1800 },
73
+ {'width' => 1524, 'height' => 3600 },
74
+ {'width' => 3047, 'height' => 7200 }
75
+ ]
76
+ end
77
+ end
78
+
79
+ describe 'respects the params we supply' do
80
+ it ':resource_id' do
81
+ r_id = 'http://example.edu/images/some.jpg'
82
+ opts = { service_id: valid_service_id, resource_id: r_id}
83
+ resource = described_class.create_image_api_image_resource(opts)
84
+ expect(resource['@id']).to eq r_id
85
+ end
86
+ it ':width' do
87
+ width = 42
88
+ opts = { service_id: valid_service_id, width: width}
89
+ resource = described_class.create_image_api_image_resource(opts)
90
+ expect(resource.width).to eq width
91
+ end
92
+ it ':height' do
93
+ height = 42
94
+ opts = { service_id: valid_service_id, height: height}
95
+ resource = described_class.create_image_api_image_resource(opts)
96
+ expect(resource.height).to eq height
97
+ end
98
+ it ':profile (service[\'profile\'])' do
99
+ profile = 'http://iiif.io/api/image/2/level1.json'
100
+ opts = { service_id: valid_service_id, profile: profile}
101
+ resource = described_class.create_image_api_image_resource(opts)
102
+ expect(resource.service['profile']).to eq profile
103
+ end
104
+ end
105
+
106
+ describe 'errors' do
107
+ it 'raises if :service_id is not included' do
108
+ expect {
109
+ described_class.create_image_api_image_resource
110
+ }.to raise_error
111
+ end
112
+ it 'raises if the info can\'t be pulled in' do
113
+ expect {
114
+ described_class.create_image_api_image_resource(service_id: invalid_service_id)
115
+ }.to raise_error
116
+ end
117
+ end
118
+
119
+
120
+ end
121
+ end
122
+
123
+