iiif-presentation 1.4.1 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +8 -1
- data/.standard.yml +1 -0
- data/Gemfile +5 -5
- data/README.md +1 -1
- data/Rakefile +4 -2
- data/VERSION +1 -1
- data/bin/standardrb +16 -0
- data/iiif-presentation.gemspec +23 -23
- data/lib/iiif/hash_behaviours.rb +29 -33
- data/lib/iiif/ordered_hash.rb +33 -33
- data/lib/iiif/presentation/abstract_resource.rb +17 -22
- data/lib/iiif/presentation/annotation.rb +9 -11
- data/lib/iiif/presentation/annotation_list.rb +8 -10
- data/lib/iiif/presentation/canvas.rb +11 -13
- data/lib/iiif/presentation/collection.rb +7 -9
- data/lib/iiif/presentation/image_resource.rb +33 -35
- data/lib/iiif/presentation/layer.rb +8 -10
- data/lib/iiif/presentation/manifest.rb +9 -12
- data/lib/iiif/presentation/range.rb +8 -10
- data/lib/iiif/presentation/resource.rb +5 -7
- data/lib/iiif/presentation/sequence.rb +8 -11
- data/lib/iiif/presentation/service.rb +1 -2
- data/lib/iiif/presentation.rb +7 -7
- data/lib/iiif/service.rb +121 -102
- data/lib/iiif/v3/abstract_resource.rb +132 -144
- data/lib/iiif/v3/presentation/annotation.rb +20 -21
- data/lib/iiif/v3/presentation/annotation_collection.rb +7 -9
- data/lib/iiif/v3/presentation/annotation_page.rb +14 -15
- data/lib/iiif/v3/presentation/canvas.rb +21 -22
- data/lib/iiif/v3/presentation/choice.rb +14 -15
- data/lib/iiif/v3/presentation/collection.rb +7 -8
- data/lib/iiif/v3/presentation/image_resource.rb +21 -22
- data/lib/iiif/v3/presentation/manifest.rb +18 -19
- data/lib/iiif/v3/presentation/nav_place.rb +125 -29
- data/lib/iiif/v3/presentation/range.rb +10 -10
- data/lib/iiif/v3/presentation/resource.rb +4 -5
- data/lib/iiif/v3/presentation/sequence.rb +10 -11
- data/lib/iiif/v3/presentation/service.rb +12 -13
- data/lib/iiif/v3/presentation.rb +8 -8
- data/spec/integration/iiif/presentation/image_resource_spec.rb +54 -58
- data/spec/integration/iiif/service_spec.rb +76 -77
- data/spec/integration/iiif/v3/abstract_resource_spec.rb +73 -73
- data/spec/integration/iiif/v3/presentation/image_resource_spec.rb +53 -56
- data/spec/spec_helper.rb +56 -64
- data/spec/unit/iiif/hash_behaviours_spec.rb +384 -387
- data/spec/unit/iiif/ordered_hash_spec.rb +86 -91
- data/spec/unit/iiif/presentation/abstract_resource_spec.rb +64 -70
- data/spec/unit/iiif/presentation/annotation_list_spec.rb +1 -3
- data/spec/unit/iiif/presentation/annotation_spec.rb +1 -3
- data/spec/unit/iiif/presentation/canvas_spec.rb +11 -14
- data/spec/unit/iiif/presentation/collection_spec.rb +26 -33
- data/spec/unit/iiif/presentation/image_resource_spec.rb +4 -6
- data/spec/unit/iiif/presentation/layer_spec.rb +16 -20
- data/spec/unit/iiif/presentation/manifest_spec.rb +45 -48
- data/spec/unit/iiif/presentation/range_spec.rb +18 -22
- data/spec/unit/iiif/presentation/resource_spec.rb +3 -6
- data/spec/unit/iiif/presentation/sequence_spec.rb +58 -63
- data/spec/unit/iiif/presentation/shared_examples/abstract_resource_only_keys.rb +4 -10
- data/spec/unit/iiif/presentation/shared_examples/any_type_keys.rb +5 -9
- data/spec/unit/iiif/presentation/shared_examples/array_only_keys.rb +9 -17
- data/spec/unit/iiif/presentation/shared_examples/int_only_keys.rb +6 -14
- data/spec/unit/iiif/presentation/shared_examples/string_only_keys.rb +6 -13
- data/spec/unit/iiif/service_spec.rb +8 -13
- data/spec/unit/iiif/v3/abstract_resource_define_methods_for_spec.rb +39 -37
- data/spec/unit/iiif/v3/abstract_resource_spec.rb +128 -143
- data/spec/unit/iiif/v3/presentation/annotation_collection_spec.rb +14 -17
- data/spec/unit/iiif/v3/presentation/annotation_page_spec.rb +71 -69
- data/spec/unit/iiif/v3/presentation/annotation_spec.rb +249 -233
- data/spec/unit/iiif/v3/presentation/canvas_spec.rb +211 -193
- data/spec/unit/iiif/v3/presentation/choice_spec.rb +81 -77
- data/spec/unit/iiif/v3/presentation/collection_spec.rb +24 -29
- data/spec/unit/iiif/v3/presentation/image_resource_spec.rb +70 -71
- data/spec/unit/iiif/v3/presentation/manifest_spec.rb +248 -237
- data/spec/unit/iiif/v3/presentation/nav_place_spec.rb +249 -49
- data/spec/unit/iiif/v3/presentation/range_spec.rb +28 -29
- data/spec/unit/iiif/v3/presentation/resource_spec.rb +83 -82
- data/spec/unit/iiif/v3/presentation/sequence_spec.rb +126 -120
- data/spec/unit/iiif/v3/presentation/service_spec.rb +123 -124
- data/spec/unit/iiif/v3/presentation/shared_examples/abstract_resource_only_keys.rb +4 -8
- data/spec/unit/iiif/v3/presentation/shared_examples/any_type_keys.rb +5 -7
- data/spec/unit/iiif/v3/presentation/shared_examples/array_only_keys.rb +9 -13
- data/spec/unit/iiif/v3/presentation/shared_examples/hash_only_keys.rb +9 -13
- data/spec/unit/iiif/v3/presentation/shared_examples/int_only_keys.rb +6 -10
- data/spec/unit/iiif/v3/presentation/shared_examples/numeric_only_keys.rb +6 -10
- data/spec/unit/iiif/v3/presentation/shared_examples/string_only_keys.rb +6 -10
- data/spec/unit/iiif/v3/presentation/shared_examples/uri_only_keys.rb +9 -13
- metadata +27 -69
|
@@ -1,29 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
shared_examples 'it has the appropriate methods for string-only keys' do
|
|
4
|
-
|
|
1
|
+
shared_examples "it has the appropriate methods for string-only keys" do
|
|
5
2
|
described_class.new.string_only_keys.each do |prop|
|
|
6
|
-
|
|
7
3
|
describe "#{prop}=" do
|
|
8
4
|
it "sets #{prop}" do
|
|
9
|
-
ex =
|
|
5
|
+
ex = "foo"
|
|
10
6
|
subject.send("#{prop}=", ex)
|
|
11
7
|
expect(subject[prop]).to eq ex
|
|
12
8
|
end
|
|
13
|
-
it
|
|
14
|
-
expect { subject.send("#{prop}=", [
|
|
9
|
+
it "raises an exception when attempting to set it to something other than a String" do
|
|
10
|
+
expect { subject.send("#{prop}=", ["Foo"]) }.to raise_error IIIF::Presentation::IllegalValueError
|
|
15
11
|
end
|
|
16
12
|
end
|
|
17
13
|
|
|
18
|
-
describe
|
|
14
|
+
describe prop.to_s do
|
|
19
15
|
it "gets #{prop}" do
|
|
20
|
-
ex =
|
|
16
|
+
ex = "bar"
|
|
21
17
|
subject[prop] = ex
|
|
22
18
|
expect(subject.send(prop)).to eq ex
|
|
23
19
|
end
|
|
24
20
|
end
|
|
25
|
-
|
|
26
21
|
end
|
|
27
|
-
|
|
28
22
|
end
|
|
29
|
-
|
|
@@ -1,28 +1,23 @@
|
|
|
1
1
|
describe IIIF::Service do
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
TYPE = "sc:Collection"
|
|
2
|
+
describe "self#get_descendant_class_by_jld_type" do
|
|
3
|
+
let(:dummy_class) do
|
|
4
|
+
Class.new(IIIF::Service) do
|
|
5
|
+
const_set(:TYPE, "sc:Collection")
|
|
7
6
|
def self.singleton_class?
|
|
8
7
|
true
|
|
9
8
|
end
|
|
10
9
|
end
|
|
11
10
|
end
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
end
|
|
15
|
-
it 'gets the right class' do
|
|
16
|
-
klass = described_class.get_descendant_class_by_jld_type('sc:Canvas')
|
|
11
|
+
it "gets the right class" do
|
|
12
|
+
klass = described_class.get_descendant_class_by_jld_type("sc:Canvas")
|
|
17
13
|
expect(klass).to be IIIF::Presentation::Canvas
|
|
18
14
|
end
|
|
19
15
|
context "when there are singleton classes which are returned" do
|
|
20
16
|
it "gets the right class" do
|
|
21
|
-
allow(IIIF::Service).to receive(:descendants).and_return([
|
|
22
|
-
klass = described_class.get_descendant_class_by_jld_type(
|
|
17
|
+
allow(IIIF::Service).to receive(:descendants).and_return([dummy_class, IIIF::Presentation::Collection])
|
|
18
|
+
klass = described_class.get_descendant_class_by_jld_type("sc:Collection")
|
|
23
19
|
expect(klass).to be IIIF::Presentation::Collection
|
|
24
20
|
end
|
|
25
21
|
end
|
|
26
22
|
end
|
|
27
|
-
|
|
28
23
|
end
|
|
@@ -1,78 +1,80 @@
|
|
|
1
1
|
class AbstractResourceSubClass < IIIF::V3::AbstractResource
|
|
2
|
-
TYPE =
|
|
2
|
+
TYPE = "Ignore"
|
|
3
3
|
# need a property here for type of key not already initialized in AbstractResource
|
|
4
4
|
def array_only_keys
|
|
5
|
-
super + %w
|
|
5
|
+
super + %w[array my_array]
|
|
6
6
|
end
|
|
7
|
+
|
|
7
8
|
def hash_only_keys
|
|
8
|
-
super + %w
|
|
9
|
+
super + %w[hash my_hash]
|
|
9
10
|
end
|
|
11
|
+
|
|
10
12
|
def int_only_keys
|
|
11
|
-
super + %w
|
|
13
|
+
super + %w[int my_int]
|
|
12
14
|
end
|
|
15
|
+
|
|
13
16
|
def numeric_only_keys
|
|
14
|
-
super + %w
|
|
17
|
+
super + %w[num my_num]
|
|
15
18
|
end
|
|
19
|
+
|
|
16
20
|
def uri_only_keys
|
|
17
|
-
super + %w
|
|
21
|
+
super + %w[uri my_uri]
|
|
18
22
|
end
|
|
19
23
|
|
|
20
|
-
def initialize(
|
|
21
|
-
|
|
22
|
-
super(hsh)
|
|
24
|
+
def initialize(_hsh = {})
|
|
25
|
+
super({"type" => "a:SubClass"})
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
describe AbstractResourceSubClass do
|
|
27
|
-
|
|
28
30
|
describe "*define_methods_for_any_type_keys" do
|
|
29
31
|
# shared_example expects fixed_values; these are roughly based on Stanford purl code
|
|
30
32
|
# (see https://github.com/sul-dlss/purl/blob/master/app/models/iiif3_presentation_manifest.rb)
|
|
31
33
|
let(:fixed_values) do
|
|
32
34
|
{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
"label" => "foo",
|
|
36
|
+
"description" => "bar",
|
|
37
|
+
"thumbnail" => IIIF::V3::Presentation::ImageResource.new(
|
|
38
|
+
"type" => "Image",
|
|
39
|
+
"id" => "http://example.org/full/!400,400/0/default.jpg",
|
|
40
|
+
"format" => "image/jpeg"
|
|
41
|
+
),
|
|
42
|
+
"attribution" => ["foo"],
|
|
43
|
+
"logo" => {
|
|
44
|
+
"id" => "https://example.org/default.jpg",
|
|
45
|
+
"service" => IIIF::V3::Presentation::Service.new(
|
|
46
|
+
"@context" => "http://iiif.io/api/image/2/context.json",
|
|
47
|
+
"@id" => "http://example.org/1",
|
|
48
|
+
"id" => "http://example.org/1",
|
|
49
|
+
"profile" => "http://example.org/whatever"
|
|
48
50
|
)
|
|
49
51
|
},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
"see_also" => {
|
|
53
|
+
"id" => "http://example.org/whatever",
|
|
54
|
+
"format" => "application/mods+xml"
|
|
53
55
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
"related" => ["no", "idea"],
|
|
57
|
+
"within" => {"foo" => "bar"}
|
|
56
58
|
}
|
|
57
59
|
end
|
|
58
|
-
it_behaves_like
|
|
60
|
+
it_behaves_like "it has the appropriate methods for any-type keys v3"
|
|
59
61
|
end
|
|
60
62
|
describe "*define_methods_for_array_only_keys" do
|
|
61
|
-
it_behaves_like
|
|
63
|
+
it_behaves_like "it has the appropriate methods for array-only keys v3"
|
|
62
64
|
end
|
|
63
65
|
describe "*define_methods_for_hash_only_keys" do
|
|
64
|
-
it_behaves_like
|
|
66
|
+
it_behaves_like "it has the appropriate methods for hash-only keys v3"
|
|
65
67
|
end
|
|
66
68
|
describe "*define_methods_for_int_only_keys" do
|
|
67
|
-
it_behaves_like
|
|
69
|
+
it_behaves_like "it has the appropriate methods for integer-only keys v3"
|
|
68
70
|
end
|
|
69
71
|
describe "*define_methods_for_numeric_only_keys" do
|
|
70
|
-
it_behaves_like
|
|
72
|
+
it_behaves_like "it has the appropriate methods for numeric-only keys v3"
|
|
71
73
|
end
|
|
72
74
|
describe "*define_methods_for_string_only_keys" do
|
|
73
|
-
it_behaves_like
|
|
75
|
+
it_behaves_like "it has the appropriate methods for string-only keys v3"
|
|
74
76
|
end
|
|
75
77
|
describe "*define_methods_for_uri_only_keys" do
|
|
76
|
-
it_behaves_like
|
|
78
|
+
it_behaves_like "it has the appropriate methods for uri-only keys v3"
|
|
77
79
|
end
|
|
78
80
|
end
|
|
@@ -1,293 +1,278 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require_relative
|
|
1
|
+
require "active_support/inflector"
|
|
2
|
+
require "json"
|
|
3
|
+
require_relative "../../../../lib/iiif/hash_behaviours"
|
|
4
4
|
|
|
5
5
|
describe IIIF::V3::AbstractResource do
|
|
6
|
-
|
|
7
|
-
let(:
|
|
8
|
-
let(:manifest_from_spec_path) { File.join(fixtures_dir, 'v3/manifests/complete_from_spec.json') }
|
|
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") }
|
|
9
8
|
let(:abstract_resource_subclass) do
|
|
10
9
|
Class.new(IIIF::V3::AbstractResource) do
|
|
11
10
|
include IIIF::HashBehaviours
|
|
12
11
|
|
|
13
|
-
def initialize(hsh={})
|
|
14
|
-
hsh[
|
|
15
|
-
super
|
|
12
|
+
def initialize(hsh = {})
|
|
13
|
+
hsh["type"] = "a:SubClass" unless hsh.has_key?("type")
|
|
14
|
+
super
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
def required_keys
|
|
19
|
-
super + %w
|
|
18
|
+
super + %w[id]
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
def prohibited_keys
|
|
23
|
-
super + %w
|
|
22
|
+
super + %w[verboten]
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
def legal_viewing_hint_values
|
|
27
|
-
%w
|
|
26
|
+
%w[viewing_hint1 viewing_hint2]
|
|
28
27
|
end
|
|
29
28
|
end
|
|
30
29
|
end
|
|
31
30
|
subject do
|
|
32
31
|
instance = abstract_resource_subclass.new
|
|
33
|
-
instance[
|
|
32
|
+
instance["id"] = "http://example.com/prefix/manifest/123"
|
|
34
33
|
instance
|
|
35
34
|
end
|
|
36
35
|
|
|
37
|
-
describe
|
|
38
|
-
it
|
|
39
|
-
expect(subject.required_keys).to eq %w
|
|
36
|
+
describe "#required_keys" do
|
|
37
|
+
it "accumulate" do
|
|
38
|
+
expect(subject.required_keys).to eq %w[type id]
|
|
40
39
|
end
|
|
41
40
|
end
|
|
42
41
|
|
|
43
|
-
describe
|
|
44
|
-
it
|
|
42
|
+
describe "#initialize" do
|
|
43
|
+
it "raises an error if you try to instantiate AbstractResource" do
|
|
45
44
|
expect { IIIF::V3::AbstractResource.new }.to raise_error(RuntimeError)
|
|
46
45
|
end
|
|
47
|
-
it
|
|
48
|
-
expect(subject[
|
|
46
|
+
it "sets type" do
|
|
47
|
+
expect(subject["type"]).to eq "a:SubClass"
|
|
49
48
|
end
|
|
50
|
-
it
|
|
49
|
+
it "can take any old hash" do
|
|
51
50
|
hsh = JSON.parse(IO.read(manifest_from_spec_path))
|
|
52
51
|
new_instance = abstract_resource_subclass.new(hsh)
|
|
53
|
-
expect(new_instance[
|
|
52
|
+
expect(new_instance["label"]["en"]).to include "Book 1"
|
|
54
53
|
end
|
|
55
54
|
end
|
|
56
55
|
|
|
57
|
-
describe
|
|
58
|
-
it
|
|
56
|
+
describe "#validate" do
|
|
57
|
+
it "raises MissingRequiredKeyError if required key is missing" do
|
|
59
58
|
subject.required_keys.each { |k| subject.delete(k) }
|
|
60
59
|
expect { subject.validate }.to raise_error IIIF::V3::Presentation::MissingRequiredKeyError
|
|
61
60
|
end
|
|
62
|
-
it
|
|
63
|
-
subject[
|
|
61
|
+
it "raises ProhibitedKeyError if prohibited key is present" do
|
|
62
|
+
subject["verboten"] = 666
|
|
64
63
|
exp_err_msg = "verboten is a prohibited key in #{subject.class}"
|
|
65
64
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::ProhibitedKeyError, exp_err_msg)
|
|
66
65
|
end
|
|
67
|
-
it
|
|
68
|
-
subject[
|
|
66
|
+
it "raises IllegalValueError for bad viewing_direction" do
|
|
67
|
+
subject["viewing_direction"] = "foo"
|
|
69
68
|
exp_err_msg = "viewingDirection must be one of #{subject.legal_viewing_direction_values}"
|
|
70
69
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_err_msg)
|
|
71
70
|
end
|
|
72
|
-
describe
|
|
73
|
-
it
|
|
74
|
-
subject[
|
|
71
|
+
describe "viewing_hint" do
|
|
72
|
+
it "can be a uri" do
|
|
73
|
+
subject["viewing_hint"] = "https://example.org/viewiing_hint"
|
|
75
74
|
expect { subject.validate }.not_to raise_error
|
|
76
75
|
end
|
|
77
|
-
it
|
|
78
|
-
subject[
|
|
76
|
+
it "can be a member of legal_viewing_hint_values" do
|
|
77
|
+
subject["viewing_hint"] = subject.legal_viewing_hint_values.first
|
|
79
78
|
expect { subject.validate }.not_to raise_error
|
|
80
79
|
end
|
|
81
|
-
it
|
|
82
|
-
subject[
|
|
80
|
+
it "raises IllegalValueError for bad viewing_hint" do
|
|
81
|
+
subject["viewing_hint"] = "foo"
|
|
83
82
|
exp_err_msg = "viewingHint for #{subject.class} must be one or more of #{subject.legal_viewing_hint_values} or a URI"
|
|
84
83
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_err_msg)
|
|
85
84
|
end
|
|
86
|
-
it
|
|
87
|
-
subject[
|
|
85
|
+
it "can have multiple values" do
|
|
86
|
+
subject["viewing_hint"] = [subject.legal_viewing_hint_values.first, subject.legal_viewing_hint_values.last]
|
|
88
87
|
expect { subject.validate }.not_to raise_error
|
|
89
88
|
end
|
|
90
89
|
end
|
|
91
|
-
describe
|
|
92
|
-
it
|
|
93
|
-
subject[
|
|
90
|
+
describe "metadata" do
|
|
91
|
+
it "raises IllegalValueError for entry that is not a Hash" do
|
|
92
|
+
subject["metadata"] = ["error"]
|
|
94
93
|
exp_err_msg = "metadata must be an Array with Hash members"
|
|
95
94
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_err_msg)
|
|
96
95
|
end
|
|
97
96
|
it 'does not raise error for entry that contains exactly "label" and "value"' do
|
|
98
|
-
subject[
|
|
97
|
+
subject["metadata"] = [{"label" => "bar", "value" => "foo"}]
|
|
99
98
|
expect { subject.validate }.not_to raise_error
|
|
100
99
|
end
|
|
101
100
|
it 'raises IllegalValueError for entry that does not contain exactly "label" and "value"' do
|
|
102
|
-
subject[
|
|
101
|
+
subject["metadata"] = [{"label" => "bar", "bar" => "foo"}]
|
|
103
102
|
exp_err_msg = "metadata members must be a Hash of keys 'label' and 'value'"
|
|
104
103
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_err_msg)
|
|
105
104
|
end
|
|
106
105
|
end
|
|
107
|
-
describe
|
|
108
|
-
let
|
|
109
|
-
let
|
|
110
|
-
it
|
|
111
|
-
subject[
|
|
106
|
+
describe "thumbnail" do
|
|
107
|
+
let(:exp_basic_err_msg) { "thumbnail must be an Array with Hash or ImageResource members" }
|
|
108
|
+
let(:exp_keys_msg) { 'thumbnail members must include keys "id" and "type"' }
|
|
109
|
+
it "raises IllegalValueError for entry that is not a Hash or ImageResource object" do
|
|
110
|
+
subject["thumbnail"] = ["error"]
|
|
112
111
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_basic_err_msg)
|
|
113
112
|
end
|
|
114
113
|
it 'does not raise error for entry Hash with "id" and "type"' do
|
|
115
|
-
subject[
|
|
114
|
+
subject["thumbnail"] = [{"id" => "bar", "type" => "foo", "random" => "xxx"}]
|
|
116
115
|
expect { subject.validate }.not_to raise_error
|
|
117
116
|
end
|
|
118
117
|
it 'raises IllegalValueError for entry Hash that does not contain "id" and "type"' do
|
|
119
|
-
subject[
|
|
118
|
+
subject["thumbnail"] = [{"id" => "bar"}]
|
|
120
119
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_keys_msg)
|
|
121
120
|
end
|
|
122
121
|
it 'does not raise error for entry ImageResource with "id"' do
|
|
123
|
-
subject[
|
|
122
|
+
subject["thumbnail"] = [IIIF::V3::Presentation::ImageResource.new({"id" => "bar", "label" => "xxx"})]
|
|
124
123
|
expect { subject.validate }.not_to raise_error
|
|
125
124
|
end
|
|
126
|
-
it
|
|
127
|
-
subject[
|
|
125
|
+
it "raises IllegalValueError for entry ImageResource without id" do
|
|
126
|
+
subject["thumbnail"] = [IIIF::V3::Presentation::ImageResource.new]
|
|
128
127
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_keys_msg)
|
|
129
128
|
end
|
|
130
129
|
end
|
|
131
|
-
describe
|
|
132
|
-
it
|
|
133
|
-
subject[
|
|
130
|
+
describe "nav_date" do
|
|
131
|
+
it "does not raise error for value of form YYYY-MM-DDThh:mm:ssZ" do
|
|
132
|
+
subject["nav_date"] = "1991-01-02T13:04:27Z"
|
|
134
133
|
expect { subject.validate }.not_to raise_error
|
|
135
134
|
end
|
|
136
|
-
it
|
|
137
|
-
subject[
|
|
138
|
-
exp_err_msg =
|
|
135
|
+
it "raises IllegalValueError for value not of form YYYY-MM-DDThh:mm:ssZ" do
|
|
136
|
+
subject["nav_date"] = "1991-01-02T13:04:27+0500"
|
|
137
|
+
exp_err_msg = "nav_date must be of form YYYY-MM-DDThh:mm:ssZ"
|
|
139
138
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_err_msg)
|
|
140
139
|
end
|
|
141
140
|
end
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError
|
|
141
|
+
|
|
142
|
+
describe "rights" do
|
|
143
|
+
it "raises IllegalValueError for entry that is not a string" do
|
|
144
|
+
subject["rights"] = ["error"]
|
|
145
|
+
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError)
|
|
147
146
|
end
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
|
|
148
|
+
it "does not raise error for entry that is string" do
|
|
149
|
+
subject["rights"] = "http://example.org/rights"
|
|
150
150
|
expect { subject.validate }.not_to raise_error
|
|
151
151
|
end
|
|
152
|
-
it 'raises IllegalValueError for entry with "id" that is not URI' do
|
|
153
|
-
subject['rights'] = [{ 'id' => 'bar', 'format' => 'text/html' }]
|
|
154
|
-
exp_err_msg = "id value must be a String containing a URI for #{subject.class}"
|
|
155
|
-
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_err_msg)
|
|
156
|
-
end
|
|
157
|
-
it 'raises IllegalValueError for entry that does not contain "id"' do
|
|
158
|
-
subject['rights'] = [{ 'whoops' => 'http://example.org/rights' }]
|
|
159
|
-
exp_err_msg = 'rights members must be a Hash including "id"'
|
|
160
|
-
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_err_msg)
|
|
161
|
-
end
|
|
162
152
|
end
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
153
|
+
|
|
154
|
+
describe "rendering" do
|
|
155
|
+
it "raises IllegalValueError for entry that is not a Hash" do
|
|
156
|
+
subject["rendering"] = ["error"]
|
|
166
157
|
exp_err_msg = "rendering must be an Array with Hash members"
|
|
167
158
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_err_msg)
|
|
168
159
|
end
|
|
169
160
|
it 'does not raise error for entry with "label" and "format"' do
|
|
170
|
-
subject[
|
|
161
|
+
subject["rendering"] = [{"label" => "bar", "format" => "foo", "random" => "xxx"}]
|
|
171
162
|
expect { subject.validate }.not_to raise_error
|
|
172
163
|
end
|
|
173
164
|
it 'raises IllegalValueError for entry that does not contain "label" and "format"' do
|
|
174
|
-
subject[
|
|
165
|
+
subject["rendering"] = [{"label" => "bar"}]
|
|
175
166
|
exp_err_msg = 'rendering members must be a Hash including keys "label" and "format"'
|
|
176
167
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_err_msg)
|
|
177
168
|
end
|
|
178
169
|
end
|
|
179
|
-
describe
|
|
180
|
-
it
|
|
181
|
-
subject.startCanvas =
|
|
170
|
+
describe "startCanvas" do
|
|
171
|
+
it "raises IllegalValueError for entry that is not URI" do
|
|
172
|
+
subject.startCanvas = "foo"
|
|
182
173
|
exp_err_msg = "startCanvas value must be a String containing a URI for #{subject.class}"
|
|
183
174
|
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_err_msg)
|
|
184
175
|
end
|
|
185
176
|
end
|
|
186
177
|
end
|
|
187
178
|
|
|
188
|
-
describe
|
|
189
|
-
it
|
|
179
|
+
describe "A nested object (e.g. self['metadata'])" do
|
|
180
|
+
it "returns [] if not set" do
|
|
190
181
|
expect(subject.metadata).to eq([])
|
|
191
182
|
end
|
|
192
|
-
it
|
|
183
|
+
it "is not in #to_ordered_hash at all if we access it but do not append to it" do
|
|
193
184
|
subject.metadata # touch it
|
|
194
185
|
expect(subject.metadata).to eq([])
|
|
195
|
-
expect(subject.to_ordered_hash.has_key?(
|
|
186
|
+
expect(subject.to_ordered_hash.has_key?("metadata")).to be_falsey
|
|
196
187
|
end
|
|
197
|
-
it
|
|
188
|
+
it "gets structured as we'd expect" do
|
|
198
189
|
subject.metadata << {
|
|
199
|
-
|
|
200
|
-
|
|
190
|
+
"label" => "Author",
|
|
191
|
+
"value" => "Anne Author"
|
|
201
192
|
}
|
|
202
193
|
subject.metadata << {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
{
|
|
206
|
-
{
|
|
194
|
+
"label" => "Published",
|
|
195
|
+
"value" => [
|
|
196
|
+
{"@value" => "Paris, circa 1400", "@language" => "en"},
|
|
197
|
+
{"@value" => "Paris, environ 14eme siecle", "@language" => "fr"}
|
|
207
198
|
]
|
|
208
199
|
}
|
|
209
|
-
expect(subject.metadata[0][
|
|
210
|
-
expect(subject.metadata[1][
|
|
211
|
-
expect(subject.metadata[1][
|
|
200
|
+
expect(subject.metadata[0]["label"]).to eq("Author")
|
|
201
|
+
expect(subject.metadata[1]["value"].length).to eq(2)
|
|
202
|
+
expect(subject.metadata[1]["value"].reverse.find { |e| e["@language"] == "fr" }["@value"]).to eq("Paris, environ 14eme siecle")
|
|
212
203
|
end
|
|
213
204
|
|
|
214
|
-
it
|
|
205
|
+
it "roundtrips" do
|
|
215
206
|
subject.metadata << {
|
|
216
|
-
|
|
217
|
-
|
|
207
|
+
"label" => "Author",
|
|
208
|
+
"value" => "Anne Author"
|
|
218
209
|
}
|
|
219
210
|
subject.metadata << {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
{
|
|
223
|
-
{
|
|
211
|
+
"label" => "Published",
|
|
212
|
+
"value" => [
|
|
213
|
+
{"@value" => "Paris, circa 1400", "@language" => "en"},
|
|
214
|
+
{"@value" => "Paris, environ 14eme siecle", "@language" => "fr"}
|
|
224
215
|
]
|
|
225
216
|
}
|
|
226
|
-
File.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
parsed
|
|
230
|
-
expect(parsed[
|
|
231
|
-
|
|
232
|
-
expect(parsed['metadata'][1]['value'].select { |e| e['@language'] == 'fr'}.last['@value']).to eq('Paris, environ 14eme siecle')
|
|
233
|
-
File.delete('/tmp/osullivan-spec.json')
|
|
217
|
+
File.write("/tmp/osullivan-spec.json", subject.to_json)
|
|
218
|
+
parsed = subject.class.parse("/tmp/osullivan-spec.json")
|
|
219
|
+
expect(parsed["metadata"][0]["label"]).to eq("Author")
|
|
220
|
+
expect(parsed["metadata"][1]["value"].length).to eq(2)
|
|
221
|
+
expect(parsed["metadata"][1]["value"].reverse.find { |e| e["@language"] == "fr" }["@value"]).to eq("Paris, environ 14eme siecle")
|
|
222
|
+
File.delete("/tmp/osullivan-spec.json")
|
|
234
223
|
end
|
|
235
224
|
end
|
|
236
225
|
|
|
237
|
-
describe
|
|
238
|
-
describe
|
|
239
|
-
before(:each) { subject.delete(
|
|
240
|
-
it
|
|
241
|
-
expect(subject.has_key?(
|
|
242
|
-
expect(subject.to_ordered_hash.has_key?(
|
|
243
|
-
end
|
|
244
|
-
it
|
|
245
|
-
expect(subject.has_key?(
|
|
246
|
-
expect(subject.to_ordered_hash(include_context: false).has_key?(
|
|
247
|
-
end
|
|
248
|
-
it
|
|
249
|
-
different_ctxt =
|
|
250
|
-
subject[
|
|
226
|
+
describe "#to_ordered_hash" do
|
|
227
|
+
describe "adds the @context" do
|
|
228
|
+
before(:each) { subject.delete("@context") }
|
|
229
|
+
it "by default" do
|
|
230
|
+
expect(subject.has_key?("@context")).to be_falsey
|
|
231
|
+
expect(subject.to_ordered_hash.has_key?("@context")).to be_truthy
|
|
232
|
+
end
|
|
233
|
+
it "unless you say not to" do
|
|
234
|
+
expect(subject.has_key?("@context")).to be_falsey
|
|
235
|
+
expect(subject.to_ordered_hash(include_context: false).has_key?("@context")).to be_falsey
|
|
236
|
+
end
|
|
237
|
+
it "or it's already there" do
|
|
238
|
+
different_ctxt = "http://example.org/context"
|
|
239
|
+
subject["@context"] = different_ctxt
|
|
251
240
|
oh = subject.to_ordered_hash
|
|
252
|
-
expect(oh[
|
|
241
|
+
expect(oh["@context"]).to eq different_ctxt
|
|
253
242
|
end
|
|
254
243
|
end
|
|
255
244
|
|
|
256
|
-
describe
|
|
245
|
+
describe "runs the validations" do
|
|
257
246
|
let(:error) { IIIF::V3::Presentation::MissingRequiredKeyError }
|
|
258
|
-
before(:each) { subject.delete(
|
|
259
|
-
it
|
|
247
|
+
before(:each) { subject.delete("id") }
|
|
248
|
+
it "raises exceptions" do
|
|
260
249
|
expect { subject.to_ordered_hash }.to raise_error error
|
|
261
250
|
end
|
|
262
|
-
it
|
|
251
|
+
it "unless you tell it not to" do
|
|
263
252
|
expect { subject.to_ordered_hash(force: true) }.to_not raise_error
|
|
264
253
|
end
|
|
265
254
|
end
|
|
266
255
|
end
|
|
267
256
|
|
|
268
|
-
describe
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
TYPE
|
|
257
|
+
describe "*get_descendant_class_by_jld_type" do
|
|
258
|
+
let(:dummy_class) do
|
|
259
|
+
Class.new(IIIF::V3::AbstractResource) do
|
|
260
|
+
const_set(:TYPE, "Collection")
|
|
272
261
|
def self.singleton_class?
|
|
273
262
|
true
|
|
274
263
|
end
|
|
275
264
|
end
|
|
276
265
|
end
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
end
|
|
280
|
-
it 'gets the right class' do
|
|
281
|
-
klass = described_class.get_descendant_class_by_jld_type('Canvas')
|
|
266
|
+
it "gets the right class" do
|
|
267
|
+
klass = described_class.get_descendant_class_by_jld_type("Canvas")
|
|
282
268
|
expect(klass).to eq IIIF::V3::Presentation::Canvas
|
|
283
269
|
end
|
|
284
270
|
context "when there are singleton classes which are returned" do
|
|
285
271
|
it "gets the right class" do
|
|
286
|
-
allow(IIIF::V3::AbstractResource).to receive(:descendants).and_return([
|
|
287
|
-
klass = described_class.get_descendant_class_by_jld_type(
|
|
272
|
+
allow(IIIF::V3::AbstractResource).to receive(:descendants).and_return([dummy_class, IIIF::V3::Presentation::Collection])
|
|
273
|
+
klass = described_class.get_descendant_class_by_jld_type("Collection")
|
|
288
274
|
expect(klass).to eq IIIF::V3::Presentation::Collection
|
|
289
275
|
end
|
|
290
276
|
end
|
|
291
277
|
end
|
|
292
|
-
|
|
293
278
|
end
|