ladder 0.3.1 → 0.3.2
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/.gitignore +4 -1
- data/.semver +5 -0
- data/Gemfile +1 -1
- data/README.md +11 -11
- data/Rakefile +1 -2
- data/ladder.gemspec +26 -23
- data/lib/ladder.rb +1 -1
- data/lib/ladder/file.rb +49 -40
- data/lib/ladder/resource.rb +204 -60
- data/lib/ladder/resource/dynamic.rb +134 -83
- data/lib/ladder/resource/serializable.rb +56 -43
- data/lib/ladder/searchable.rb +14 -12
- data/lib/ladder/searchable/background.rb +40 -31
- data/lib/ladder/searchable/file.rb +33 -26
- data/lib/ladder/searchable/resource.rb +26 -15
- data/lib/ladder/version.rb +2 -2
- data/spec/ladder/file_spec.rb +9 -7
- data/spec/ladder/resource/dynamic_spec.rb +13 -143
- data/spec/ladder/resource_spec.rb +47 -226
- data/spec/ladder/searchable/background_spec.rb +37 -42
- data/spec/ladder/searchable/file_spec.rb +8 -5
- data/spec/ladder/searchable/resource_spec.rb +30 -38
- data/spec/shared/file.rb +9 -9
- data/spec/shared/graph.jsonld +31 -0
- data/spec/shared/resource.rb +397 -14
- data/spec/shared/searchable/file.rb +2 -4
- data/spec/shared/searchable/resource.rb +137 -145
- data/spec/spec_helper.rb +3 -2
- metadata +49 -4
@@ -14,8 +14,13 @@ describe Ladder::Searchable::Background do
|
|
14
14
|
class Thing
|
15
15
|
include Ladder::Resource
|
16
16
|
include Ladder::Searchable::Background
|
17
|
+
configure type: RDF::DC.BibliographicResource
|
18
|
+
|
19
|
+
field :alt
|
20
|
+
property :alt, predicate: RDF::DC.alternative # non-localized literal
|
21
|
+
property :title, predicate: RDF::DC.title # localized literal
|
17
22
|
end
|
18
|
-
|
23
|
+
|
19
24
|
class Datastream
|
20
25
|
include Ladder::File
|
21
26
|
include Ladder::Searchable::Background
|
@@ -24,37 +29,27 @@ describe Ladder::Searchable::Background do
|
|
24
29
|
|
25
30
|
after do
|
26
31
|
Object.send(:remove_const, :LADDER_BASE_URI) if Object
|
27
|
-
Object.send(:remove_const,
|
28
|
-
Object.send(:remove_const,
|
32
|
+
Object.send(:remove_const, 'Thing') if Object
|
33
|
+
Object.send(:remove_const, 'Datastream') if Object
|
29
34
|
end
|
30
35
|
|
31
|
-
shared_context 'with
|
32
|
-
|
33
|
-
subject.class.configure type: RDF::DC.BibliographicResource
|
36
|
+
shared_context 'with relations' do
|
37
|
+
let(:person) { Person.new }
|
34
38
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
+
before do
|
40
|
+
class Person
|
41
|
+
include Ladder::Resource
|
42
|
+
include Ladder::Searchable::Background
|
43
|
+
configure type: RDF::FOAF.Person
|
39
44
|
|
40
|
-
|
41
|
-
|
42
|
-
|
45
|
+
property :foaf_name, predicate: RDF::FOAF.name
|
46
|
+
property :things, predicate: RDF::DC.relation, class_name: 'Thing'
|
47
|
+
end
|
43
48
|
end
|
44
|
-
end
|
45
|
-
|
46
|
-
shared_context 'with relations' do
|
47
|
-
before do
|
48
|
-
# related object
|
49
|
-
person.class.configure type: RDF::FOAF.Person
|
50
|
-
person.class.property :foaf_name, predicate: RDF::FOAF.name
|
51
|
-
person.foaf_name = 'Tove Jansson'
|
52
49
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
subject.people << person
|
57
|
-
end
|
50
|
+
after do
|
51
|
+
Object.send(:remove_const, 'Person') if Object
|
52
|
+
end
|
58
53
|
end
|
59
54
|
|
60
55
|
context 'with data' do
|
@@ -67,22 +62,19 @@ describe Ladder::Searchable::Background do
|
|
67
62
|
|
68
63
|
context 'with relations' do
|
69
64
|
let(:subject) { Thing.new }
|
70
|
-
let(:person) { Person.new }
|
71
|
-
|
72
|
-
before do
|
73
|
-
class Person
|
74
|
-
include Ladder::Resource
|
75
|
-
include Ladder::Searchable::Background
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
after do
|
80
|
-
Object.send(:remove_const, "Person") if Object
|
81
|
-
end
|
82
65
|
|
83
66
|
include_context 'with data'
|
84
67
|
include_context 'with relations'
|
85
68
|
|
69
|
+
before do
|
70
|
+
# many-to-many relation
|
71
|
+
Thing.property :people, predicate: RDF::DC.creator, class_name: 'Person'
|
72
|
+
|
73
|
+
# related object
|
74
|
+
person.foaf_name = 'Tove Jansson'
|
75
|
+
subject.people << person
|
76
|
+
end
|
77
|
+
|
86
78
|
it_behaves_like 'a Searchable'
|
87
79
|
it_behaves_like 'a Searchable with related'
|
88
80
|
end
|
@@ -97,8 +89,12 @@ describe Ladder::Searchable::Background do
|
|
97
89
|
end
|
98
90
|
|
99
91
|
context 'with data from string after creation' do
|
100
|
-
data =
|
101
|
-
|
92
|
+
data = 'And so Moomintroll was helplessly thrown out into a strange and dangerous world and dropped
|
93
|
+
up to his ears in the first snowdrift of his experience. It felt unpleasantly prickly to his
|
94
|
+
velvet skin, but at the same time his nose caught a new smell. It was a more serious smell
|
95
|
+
than any he had met before, and slightly frightening. But it made him wide awake and greatly
|
96
|
+
interested.'
|
97
|
+
|
102
98
|
let(:subject) { Datastream.new }
|
103
99
|
let(:source) { data } # UTF-8 (string)
|
104
100
|
|
@@ -108,5 +104,4 @@ describe Ladder::Searchable::Background do
|
|
108
104
|
|
109
105
|
it_behaves_like 'a Searchable File'
|
110
106
|
end
|
111
|
-
|
112
|
-
end
|
107
|
+
end
|
@@ -18,7 +18,7 @@ describe Ladder::Searchable::File do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
after do
|
21
|
-
Object.send(:remove_const,
|
21
|
+
Object.send(:remove_const, 'Datastream') if Object
|
22
22
|
end
|
23
23
|
|
24
24
|
context 'with data from file' do
|
@@ -32,8 +32,12 @@ describe Ladder::Searchable::File do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
context 'with data from string after creation' do
|
35
|
-
data =
|
36
|
-
|
35
|
+
data = 'And so Moomintroll was helplessly thrown out into a strange and dangerous world and dropped
|
36
|
+
up to his ears in the first snowdrift of his experience. It felt unpleasantly prickly to his
|
37
|
+
velvet skin, but at the same time his nose caught a new smell. It was a more serious smell
|
38
|
+
than any he had met before, and slightly frightening. But it made him wide awake and greatly
|
39
|
+
interested.'
|
40
|
+
|
37
41
|
let(:subject) { Datastream.new }
|
38
42
|
let(:source) { data } # UTF-8 (string)
|
39
43
|
|
@@ -44,5 +48,4 @@ describe Ladder::Searchable::File do
|
|
44
48
|
it_behaves_like 'a File'
|
45
49
|
it_behaves_like 'a Searchable File'
|
46
50
|
end
|
47
|
-
|
48
|
-
end
|
51
|
+
end
|
@@ -14,41 +14,36 @@ describe Ladder::Searchable::Resource do
|
|
14
14
|
class Thing
|
15
15
|
include Ladder::Resource
|
16
16
|
include Ladder::Searchable
|
17
|
+
configure type: RDF::DC.BibliographicResource
|
18
|
+
|
19
|
+
field :alt
|
20
|
+
property :alt, predicate: RDF::DC.alternative # non-localized literal
|
21
|
+
property :title, predicate: RDF::DC.title # localized literal
|
17
22
|
end
|
18
23
|
end
|
19
24
|
|
20
25
|
after do
|
21
26
|
Object.send(:remove_const, :LADDER_BASE_URI) if Object
|
22
|
-
Object.send(:remove_const,
|
27
|
+
Object.send(:remove_const, 'Thing') if Object
|
23
28
|
end
|
24
29
|
|
25
|
-
shared_context 'with
|
26
|
-
|
27
|
-
subject.class.configure type: RDF::DC.BibliographicResource
|
30
|
+
shared_context 'with relations' do
|
31
|
+
let(:person) { Person.new }
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
+
before do
|
34
|
+
class Person
|
35
|
+
include Ladder::Resource
|
36
|
+
include Ladder::Searchable
|
37
|
+
configure type: RDF::FOAF.Person
|
33
38
|
|
34
|
-
|
35
|
-
|
36
|
-
|
39
|
+
property :foaf_name, predicate: RDF::FOAF.name
|
40
|
+
property :things, predicate: RDF::DC.relation, class_name: 'Thing'
|
41
|
+
end
|
37
42
|
end
|
38
|
-
end
|
39
43
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
person.class.configure type: RDF::FOAF.Person
|
44
|
-
person.class.property :foaf_name, predicate: RDF::FOAF.name
|
45
|
-
person.foaf_name = 'Tove Jansson'
|
46
|
-
|
47
|
-
# many-to-many relation
|
48
|
-
person.class.property :things, predicate: RDF::DC.relation, class_name: 'Thing'
|
49
|
-
subject.class.property :people, predicate: RDF::DC.creator, class_name: 'Person'
|
50
|
-
subject.people << person
|
51
|
-
end
|
44
|
+
after do
|
45
|
+
Object.send(:remove_const, 'Person') if Object
|
46
|
+
end
|
52
47
|
end
|
53
48
|
|
54
49
|
context 'with data' do
|
@@ -61,23 +56,20 @@ describe Ladder::Searchable::Resource do
|
|
61
56
|
|
62
57
|
context 'with relations' do
|
63
58
|
let(:subject) { Thing.new }
|
64
|
-
let(:person) { Person.new }
|
65
|
-
|
66
|
-
before do
|
67
|
-
class Person
|
68
|
-
include Ladder::Resource
|
69
|
-
include Ladder::Searchable
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
after do
|
74
|
-
Object.send(:remove_const, "Person") if Object
|
75
|
-
end
|
76
59
|
|
77
60
|
include_context 'with data'
|
78
61
|
include_context 'with relations'
|
79
62
|
|
63
|
+
before do
|
64
|
+
# many-to-many relation
|
65
|
+
Thing.property :people, predicate: RDF::DC.creator, class_name: 'Person'
|
66
|
+
|
67
|
+
# related object
|
68
|
+
person.foaf_name = 'Tove Jansson'
|
69
|
+
subject.people << person
|
70
|
+
end
|
71
|
+
|
72
|
+
it_behaves_like 'a Searchable'
|
80
73
|
it_behaves_like 'a Searchable with related'
|
81
74
|
end
|
82
|
-
|
83
|
-
end
|
75
|
+
end
|
data/spec/shared/file.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'mimemagic'
|
2
2
|
|
3
3
|
shared_examples 'a File' do
|
4
|
-
|
5
4
|
describe 'LADDER_BASE_URI' do
|
6
5
|
it 'should automatically have a base URI' do
|
7
6
|
expect(subject.rdf_subject.parent).to eq RDF::URI('http://example.org/datastreams/')
|
@@ -34,7 +33,9 @@ shared_examples 'a File' do
|
|
34
33
|
end
|
35
34
|
|
36
35
|
describe '#grid' do
|
37
|
-
|
36
|
+
it 'should be an instance of a GridFS object' do
|
37
|
+
expect(subject.class.grid.parent.name).to eq 'Mongoid::GridFs'
|
38
|
+
end
|
38
39
|
end
|
39
40
|
|
40
41
|
describe '#attributes' do
|
@@ -42,18 +43,18 @@ shared_examples 'a File' do
|
|
42
43
|
subject.save
|
43
44
|
subject.reload
|
44
45
|
end
|
45
|
-
|
46
|
+
|
46
47
|
it 'should have a #length' do
|
47
48
|
expect(subject.length).to eq source.force_encoding(subject.data.encoding).length
|
48
49
|
end
|
49
|
-
|
50
|
+
|
50
51
|
it 'should have a #md5' do
|
51
52
|
expect(subject.md5).to eq Digest::MD5.hexdigest(source)
|
52
53
|
end
|
53
|
-
|
54
|
+
|
54
55
|
it 'should have a #content_type' do
|
55
56
|
source_type = MimeMagic.by_magic(source).to_s
|
56
|
-
expect(subject.content_type).to eq source_type.empty? ?
|
57
|
+
expect(subject.content_type).to eq source_type.empty? ? 'application/octet-stream' : source_type
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
@@ -64,7 +65,6 @@ shared_examples 'a File' do
|
|
64
65
|
|
65
66
|
it 'should not have any statements' do
|
66
67
|
expect(subject.update_resource.statements).to be_empty
|
67
|
-
end
|
68
|
+
end
|
68
69
|
end
|
69
|
-
|
70
|
-
end
|
70
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
{
|
2
|
+
"@context": {
|
3
|
+
"dc": "http://purl.org/dc/terms/",
|
4
|
+
"skos": "http://www.w3.org/2004/02/skos/core#"
|
5
|
+
},
|
6
|
+
"@id": "_:b1",
|
7
|
+
"@type": "dc:BibliographicResource",
|
8
|
+
"dc:alternative": "Mumintrollet pa kometjakt",
|
9
|
+
"dc:creator": {
|
10
|
+
"@id": "_:b2",
|
11
|
+
"@type": "dc:AgentClass",
|
12
|
+
"dc:relation": {
|
13
|
+
"@id": "_:b1"
|
14
|
+
}
|
15
|
+
},
|
16
|
+
"dc:hasPart": {
|
17
|
+
"@id": "_:b3",
|
18
|
+
"@type": "dc:PhysicalResource",
|
19
|
+
"dc:relation": {
|
20
|
+
"@id": "_:b1"
|
21
|
+
}
|
22
|
+
},
|
23
|
+
"dc:subject": {
|
24
|
+
"@id": "_:b4",
|
25
|
+
"@type": "skos:Concept"
|
26
|
+
},
|
27
|
+
"dc:title": {
|
28
|
+
"@value": "Comet in Moominland",
|
29
|
+
"@language": "en"
|
30
|
+
}
|
31
|
+
}
|
data/spec/shared/resource.rb
CHANGED
@@ -1,8 +1,164 @@
|
|
1
|
-
|
1
|
+
shared_context 'with data' do
|
2
|
+
before do
|
3
|
+
subject.alt = 'Mumintrollet pa kometjakt' # non-localized literal
|
4
|
+
subject.title = 'Comet in Moominland' # localized literal
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
shared_examples 'a Dynamic Resource' do
|
9
|
+
describe '#property' do
|
10
|
+
context 'with undefined property' do
|
11
|
+
before do
|
12
|
+
subject.property :description, predicate: RDF::DC.description
|
13
|
+
subject.description = "Second in Tove Jansson's series of Moomin books"
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should create a context' do
|
17
|
+
expect(subject._context).to eq(description: RDF::DC.description.to_uri.to_s)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should build an accessor' do
|
21
|
+
expect(subject.description).to eq "Second in Tove Jansson's series of Moomin books"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with conflicting property' do
|
26
|
+
before do
|
27
|
+
subject.property :title, predicate: RDF::DC11.title
|
28
|
+
subject.dc11_title = 'Kometjakten'
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should create a context' do
|
32
|
+
expect(subject._context).to eq(dc11_title: RDF::DC11.title.to_uri.to_s)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should build an accessor' do
|
36
|
+
expect(subject.dc11_title).to eq 'Kometjakten'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#update_resource' do
|
42
|
+
before do
|
43
|
+
# undefined property
|
44
|
+
subject.property :description, predicate: RDF::DC.description
|
45
|
+
subject.description = "Second in Tove Jansson's series of Moomin books"
|
46
|
+
|
47
|
+
# conflicting property
|
48
|
+
subject.property :title, predicate: RDF::DC11.title
|
49
|
+
subject.dc11_title = 'Kometjakten'
|
50
|
+
|
51
|
+
# defined field
|
52
|
+
subject << RDF::Statement(nil, RDF::DC.title, 'Kometen kommer')
|
53
|
+
|
54
|
+
# conflicting property
|
55
|
+
subject << RDF::Statement(nil, RDF::DC.alternative, 'Kometjakten')
|
56
|
+
|
57
|
+
# URI value
|
58
|
+
subject << RDF::Statement(nil, RDF::DC.identifier, RDF::URI('http://some.uri'))
|
59
|
+
|
60
|
+
# RDF type
|
61
|
+
subject << RDF::Statement(nil, RDF.type, RDF::DC.PhysicalResource)
|
62
|
+
|
63
|
+
subject.update_resource
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should have updated values' do
|
67
|
+
expect(subject.resource.statements.count).to eq 7
|
68
|
+
expect(subject.resource.query(predicate: RDF::DC.description, object: "Second in Tove Jansson's series of Moomin books").count).to eq 1
|
69
|
+
expect(subject.resource.query(predicate: RDF::DC11.title, object: 'Kometjakten').count).to eq 1
|
70
|
+
expect(subject.resource.query(predicate: RDF::DC.title, object: RDF::Literal.new('Kometen kommer', language: :en)).count).to eq 1
|
71
|
+
expect(subject.resource.query(predicate: RDF::DC.alternative, object: 'Kometjakten').count).to eq 1
|
72
|
+
expect(subject.resource.query(predicate: RDF::DC.identifier, object: RDF::URI('http://some.uri')).count).to eq 1
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should contain both class and dynamic types' do
|
76
|
+
expect(subject.type.count).to eq 2
|
77
|
+
expect(subject.type).to include RDF::DC.BibliographicResource
|
78
|
+
expect(subject.type).to include RDF::DC.PhysicalResource
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#<<' do
|
83
|
+
context 'with defined field' do
|
84
|
+
before do
|
85
|
+
subject << RDF::Statement(nil, RDF::DC.title, 'Kometen kommer')
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should not create a context' do
|
89
|
+
expect(subject._context).to be nil
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should update existing values' do
|
93
|
+
expect(subject.title).to eq 'Kometen kommer'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'with undefined field' do
|
98
|
+
before do
|
99
|
+
subject << RDF::Statement(nil, RDF::DC.description, "Second in Tove Jansson's series of Moomin books")
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should create a context' do
|
103
|
+
expect(subject._context).to eq(description: RDF::DC.description.to_uri.to_s)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should build an accessor' do
|
107
|
+
expect(subject.description).to eq "Second in Tove Jansson's series of Moomin books"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'with conflicting property' do
|
112
|
+
before do
|
113
|
+
subject << RDF::Statement(nil, RDF::DC11.title, 'Kometjakten')
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should create a context' do
|
117
|
+
expect(subject._context).to eq(dc11_title: RDF::DC11.title.to_uri.to_s)
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should build an accessor' do
|
121
|
+
expect(subject.dc11_title).to eq 'Kometjakten'
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'with a URI value' do
|
126
|
+
before do
|
127
|
+
subject << RDF::Statement(nil, RDF::DC.identifier, RDF::URI('http://some.uri'))
|
128
|
+
end
|
2
129
|
|
130
|
+
it 'should store the URI as a string' do
|
131
|
+
expect(subject.identifier).to eq 'http://some.uri'
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should cast a URI into the resource' do
|
135
|
+
subject.update_resource
|
136
|
+
query = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.identifier)
|
137
|
+
expect(query.first_object).to be_a_kind_of RDF::URI
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe '#resource_class' do
|
143
|
+
before do
|
144
|
+
subject.property :description, predicate: RDF::DC.description
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should have modified properties on the instance' do
|
148
|
+
expect(subject.resource.class.properties.keys).to include 'description'
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should not modify the global class properties' do
|
152
|
+
expect(subject.class.resource_class.properties.keys).to_not include 'description'
|
153
|
+
expect(subject.class.resource_class.properties).to eq subject.class.new.class.resource_class.properties
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
shared_examples 'a Resource' do
|
3
159
|
describe 'LADDER_BASE_URI' do
|
4
160
|
it 'should automatically have a base URI' do
|
5
|
-
expect(subject.rdf_subject.parent).to eq RDF::URI('http://example.org/things/')
|
161
|
+
expect(subject.resource.rdf_subject.parent).to eq RDF::URI('http://example.org/things/')
|
6
162
|
end
|
7
163
|
end
|
8
164
|
|
@@ -11,11 +167,11 @@ shared_examples 'a Resource' do
|
|
11
167
|
it 'should return non-localized value' do
|
12
168
|
expect(subject.alt).to eq 'Mumintrollet pa kometjakt'
|
13
169
|
end
|
14
|
-
|
170
|
+
|
15
171
|
it 'should not be a localized hash' do
|
16
172
|
expect(subject.attributes['alt']).to eq 'Mumintrollet pa kometjakt'
|
17
173
|
end
|
18
|
-
|
174
|
+
|
19
175
|
it 'should have a valid predicate' do
|
20
176
|
expect(subject.class.properties['alt'].predicate).to eq RDF::DC.alternative
|
21
177
|
end
|
@@ -25,16 +181,16 @@ shared_examples 'a Resource' do
|
|
25
181
|
expect(subject.class.properties['alt'].predicate).to eq RDF::DC.title
|
26
182
|
end
|
27
183
|
end
|
28
|
-
|
184
|
+
|
29
185
|
context 'with localized literal' do
|
30
186
|
it 'should return localized value' do
|
31
187
|
expect(subject.title).to eq 'Comet in Moominland'
|
32
188
|
end
|
33
|
-
|
189
|
+
|
34
190
|
it 'should return all locales' do
|
35
|
-
expect(subject.attributes['title']).to eq(
|
191
|
+
expect(subject.attributes['title']).to eq('en' => 'Comet in Moominland')
|
36
192
|
end
|
37
|
-
|
193
|
+
|
38
194
|
it 'should have a valid predicate' do
|
39
195
|
expect(subject.class.properties['title'].predicate).to eq RDF::DC.title
|
40
196
|
end
|
@@ -56,7 +212,7 @@ shared_examples 'a Resource' do
|
|
56
212
|
expect(s.object.to_s).to eq 'Comet in Moominland'
|
57
213
|
end
|
58
214
|
end
|
59
|
-
|
215
|
+
|
60
216
|
it 'should have a localized literal object' do
|
61
217
|
subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.alternative).each_statement do |s|
|
62
218
|
expect(s.object.to_s).to eq 'Mumintrollet pa kometjakt'
|
@@ -73,7 +229,7 @@ shared_examples 'a Resource' do
|
|
73
229
|
before do
|
74
230
|
subject << RDF::Statement(nil, RDF::DC.title, 'Kometen kommer')
|
75
231
|
end
|
76
|
-
|
232
|
+
|
77
233
|
it 'should update existing values' do
|
78
234
|
expect(subject.title).to eq 'Kometen kommer'
|
79
235
|
end
|
@@ -90,12 +246,22 @@ shared_examples 'a Resource' do
|
|
90
246
|
end
|
91
247
|
end
|
92
248
|
|
249
|
+
context 'with a RDF type' do
|
250
|
+
before do
|
251
|
+
subject << RDF::Statement(nil, RDF.type, RDF::DC.PhysicalResource)
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'should only contain types defined on the class' do
|
255
|
+
# expect(subject.type.count).to eq 1
|
256
|
+
expect(subject.type).to include RDF::DC.BibliographicResource
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
93
260
|
context 'with a URI value' do
|
94
261
|
before do
|
95
|
-
subject.class.property :identifier, predicate: RDF::DC.identifier
|
96
262
|
subject << RDF::Statement(nil, RDF::DC.identifier, RDF::URI('http://some.uri'))
|
97
263
|
end
|
98
|
-
|
264
|
+
|
99
265
|
it 'should store the URI as a string' do
|
100
266
|
expect(subject.identifier).to eq 'http://some.uri'
|
101
267
|
end
|
@@ -115,7 +281,6 @@ shared_examples 'a Resource' do
|
|
115
281
|
end
|
116
282
|
|
117
283
|
context 'a serializable' do
|
118
|
-
|
119
284
|
describe '#as_jsonld' do
|
120
285
|
it 'should output a valid jsonld representation of itself' do
|
121
286
|
graph = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_jsonld)
|
@@ -128,7 +293,225 @@ shared_examples 'a Resource' do
|
|
128
293
|
# TODO
|
129
294
|
end
|
130
295
|
end
|
296
|
+
end
|
297
|
+
|
298
|
+
describe '#new_from_graph' do
|
299
|
+
before do
|
300
|
+
subject.update_resource(related: true)
|
301
|
+
end
|
302
|
+
|
303
|
+
let(:new_subject) { subject.class.new_from_graph subject.resource }
|
304
|
+
|
305
|
+
it 'should create a new object of the same class' do
|
306
|
+
expect(new_subject.class).to eq subject.class
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'should populate the same properties' do
|
310
|
+
# TODO: clean this up
|
311
|
+
def remove_ids(x)
|
312
|
+
if x.is_a?(Hash)
|
313
|
+
x.reduce({}) do |m, (k, v)|
|
314
|
+
m[k] = remove_ids(v) unless k == '@id'
|
315
|
+
m
|
316
|
+
end
|
317
|
+
else
|
318
|
+
x
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
expect(remove_ids(new_subject.as_framed_jsonld)).to eq remove_ids(subject.as_framed_jsonld)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
shared_examples 'a Resource with relations' do
|
328
|
+
describe 'serializable' do
|
329
|
+
# TODO: contexts with relations and without
|
330
|
+
# expect(subject.as_jsonld(related: true)).to eq subject.as_jsonld
|
331
|
+
# expect(subject.as_qname(related: true)).to eq subject.as_qname
|
332
|
+
# expect(subject.as_framed_jsonld).to eq subject.as_jsonld
|
333
|
+
|
334
|
+
describe '#as_jsonld related: true' do
|
335
|
+
it 'should output a valid jsonld representation of itself and related' do
|
336
|
+
graph = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_jsonld related: true)
|
337
|
+
expect(subject.update_resource(related: true).to_hash).to eq graph.to_hash
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
describe '#as_qname related: true' do
|
342
|
+
it 'should output a valid qname representation of itself and related' do
|
343
|
+
# TODO
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
describe '#as_framed_jsonld' do
|
348
|
+
it 'should output a valid framed jsonld representation of itself and related' do
|
349
|
+
framed_graph = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_framed_jsonld)
|
350
|
+
related_graph = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_jsonld related: true)
|
351
|
+
expect(framed_graph.to_hash).to eq related_graph.to_hash
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
it 'should have relations' do
|
357
|
+
expect(subject.people.to_a).to include person
|
358
|
+
expect(subject.concepts.to_a).to include concept
|
359
|
+
expect(subject.part).to eq part
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'should have inverse relations' do
|
363
|
+
expect(person.things.to_a).to include subject
|
364
|
+
expect(concept.relations).to be_empty
|
365
|
+
expect(part.thing).to eq subject
|
366
|
+
end
|
367
|
+
|
368
|
+
describe 'with many-to-many' do
|
369
|
+
it 'should have a relation' do
|
370
|
+
expect(subject.relations['people'].relation).to eq Mongoid::Relations::Referenced::ManyToMany
|
371
|
+
expect(subject.people.to_a).to include person
|
372
|
+
end
|
131
373
|
|
374
|
+
it 'should have an inverse relation' do
|
375
|
+
expect(person.relations['things'].relation).to eq Mongoid::Relations::Referenced::ManyToMany
|
376
|
+
expect(person.things.to_a).to include subject
|
377
|
+
end
|
378
|
+
|
379
|
+
it 'should have a valid predicate' do
|
380
|
+
expect(subject.class.properties['people'].predicate).to eq RDF::DC.creator
|
381
|
+
end
|
382
|
+
|
383
|
+
it 'should have a valid inverse predicate' do
|
384
|
+
expect(person.class.properties['things'].predicate).to eq RDF::DC.relation
|
385
|
+
end
|
132
386
|
end
|
133
387
|
|
134
|
-
|
388
|
+
describe 'with one-sided has-many' do
|
389
|
+
it 'should have a relation' do
|
390
|
+
expect(subject.relations['concepts'].relation).to eq Mongoid::Relations::Referenced::ManyToMany
|
391
|
+
expect(subject.concepts.to_a).to include concept
|
392
|
+
end
|
393
|
+
|
394
|
+
it 'should not have an inverse relation' do
|
395
|
+
expect(subject.relations['concepts'].inverse_of).to be nil
|
396
|
+
expect(concept.relations).to be_empty
|
397
|
+
end
|
398
|
+
|
399
|
+
it 'should have a valid predicate' do
|
400
|
+
expect(subject.class.properties['concepts'].predicate).to eq RDF::DC.subject
|
401
|
+
end
|
402
|
+
|
403
|
+
it 'should not have an inverse predicate' do
|
404
|
+
expect(concept.class.properties).to be_empty
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
describe 'with embedded-one' do
|
409
|
+
it 'should have a relation' do
|
410
|
+
expect(subject.relations['part'].relation).to eq Mongoid::Relations::Embedded::One
|
411
|
+
expect(subject.part).to eq part
|
412
|
+
end
|
413
|
+
|
414
|
+
it 'should have an inverse relation' do
|
415
|
+
expect(part.relations['thing'].relation).to eq Mongoid::Relations::Embedded::In
|
416
|
+
expect(part.thing).to eq subject
|
417
|
+
end
|
418
|
+
|
419
|
+
it 'should have a valid predicate' do
|
420
|
+
expect(subject.class.properties['part'].predicate).to eq RDF::DC.hasPart
|
421
|
+
end
|
422
|
+
|
423
|
+
it 'should have a valid inverse predicate' do
|
424
|
+
expect(part.class.properties['thing'].predicate).to eq RDF::DC.relation
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
describe '#update_resource with related' do
|
429
|
+
# TODO: add tests for autosaved relations
|
430
|
+
before do
|
431
|
+
subject.update_resource(related: true)
|
432
|
+
end
|
433
|
+
|
434
|
+
it 'should have a literal object' do
|
435
|
+
query = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.title)
|
436
|
+
expect(query.first_object.to_s).to eq 'Comet in Moominland'
|
437
|
+
end
|
438
|
+
|
439
|
+
it 'should have an embedded object' do
|
440
|
+
query = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.hasPart)
|
441
|
+
expect(query.count).to eq 1
|
442
|
+
expect(query.first_object).to eq part.rdf_subject
|
443
|
+
end
|
444
|
+
|
445
|
+
it 'should have an embedded object relation' do
|
446
|
+
query = subject.resource.query(subject: part.rdf_subject, predicate: RDF::DC.relation)
|
447
|
+
expect(query.count).to eq 1
|
448
|
+
expect(query.first_object).to eq subject.rdf_subject
|
449
|
+
end
|
450
|
+
|
451
|
+
it 'should have related objects' do
|
452
|
+
# many-to-many
|
453
|
+
query_creator = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.creator)
|
454
|
+
expect(query_creator.count).to eq 1
|
455
|
+
expect(query_creator.first_object).to eq person.rdf_subject
|
456
|
+
|
457
|
+
# one-sided has-many
|
458
|
+
query_subject = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.subject)
|
459
|
+
expect(query_subject.count).to eq 1
|
460
|
+
expect(query_subject.first_object).to eq concept.rdf_subject
|
461
|
+
|
462
|
+
# embedded-one
|
463
|
+
query_part = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.hasPart)
|
464
|
+
expect(query_part.count).to eq 1
|
465
|
+
expect(query_part.first_object).to eq part.rdf_subject
|
466
|
+
end
|
467
|
+
|
468
|
+
it 'should have related object relations' do
|
469
|
+
# many-to-many
|
470
|
+
query = person.resource.query(subject: person.rdf_subject, predicate: RDF::DC.relation)
|
471
|
+
expect(query.count).to eq 1
|
472
|
+
expect(query.first_object).to eq subject.rdf_subject
|
473
|
+
|
474
|
+
# one-sided has-many
|
475
|
+
expect(concept.resource.query(object: subject.rdf_subject)).to be_empty
|
476
|
+
|
477
|
+
# embedded-one
|
478
|
+
query = part.resource.query(subject: part.rdf_subject, predicate: RDF::DC.relation)
|
479
|
+
expect(query.count).to eq 1
|
480
|
+
expect(query.first_object).to eq subject.rdf_subject
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
describe '#update_resource with related and then without related' do
|
485
|
+
# TODO: add tests for autosaved relations
|
486
|
+
before do
|
487
|
+
subject.update_resource(related: true)
|
488
|
+
subject.update_resource # implicit false
|
489
|
+
end
|
490
|
+
|
491
|
+
it 'should not have related objects' do
|
492
|
+
expect(subject.resource.query(subject: person.rdf_subject)).to be_empty
|
493
|
+
expect(subject.resource.query(subject: concept.rdf_subject)).to be_empty
|
494
|
+
end
|
495
|
+
|
496
|
+
it 'should have embedded object relations' do
|
497
|
+
query = subject.resource.query(subject: part.rdf_subject, predicate: RDF::DC.relation)
|
498
|
+
expect(query.count).to eq 1
|
499
|
+
expect(query.first_object).to eq subject.rdf_subject
|
500
|
+
end
|
501
|
+
|
502
|
+
it 'should have related object relations' do
|
503
|
+
# many-to-many
|
504
|
+
query = person.resource.query(subject: person.rdf_subject, predicate: RDF::DC.relation)
|
505
|
+
expect(query.count).to eq 1
|
506
|
+
expect(query.first_object).to eq subject.rdf_subject
|
507
|
+
|
508
|
+
# one-sided has-many
|
509
|
+
expect(concept.resource.query(object: subject.rdf_subject)).to be_empty
|
510
|
+
|
511
|
+
# embedded-one
|
512
|
+
query = part.resource.query(subject: part.rdf_subject, predicate: RDF::DC.relation)
|
513
|
+
expect(query.count).to eq 1
|
514
|
+
expect(query.first_object).to eq subject.rdf_subject
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|