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
@@ -1,30 +1,37 @@
|
|
1
|
-
module Ladder
|
2
|
-
|
1
|
+
module Ladder
|
2
|
+
module Searchable
|
3
|
+
module File
|
4
|
+
extend ActiveSupport::Concern
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
6
|
+
included do
|
7
|
+
# Index binary content using Elasticsearch mapper attachment plugin
|
8
|
+
# https://github.com/elasticsearch/elasticsearch-mapper-attachments
|
9
|
+
mapping _source: { enabled: false } do
|
10
|
+
indexes :file, type: 'attachment', fields: {
|
11
|
+
file: { store: true },
|
12
|
+
title: { store: true },
|
13
|
+
date: { store: true },
|
14
|
+
author: { store: true },
|
15
|
+
keywords: { store: true },
|
16
|
+
content_type: { store: true },
|
17
|
+
content_length: { store: true },
|
18
|
+
language: { store: true }
|
19
|
+
}
|
20
|
+
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
# Explicitly set mapping definition on index
|
23
|
+
__elasticsearch__.create_index!
|
24
|
+
end
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
##
|
27
|
+
# Serialize a Base64-encoded version of data for indexing
|
28
|
+
#
|
29
|
+
# @see Elasticsearch::Model::Serializing#as_indexed_json
|
30
|
+
#
|
31
|
+
# @return [Hash] a serialized version of the file
|
32
|
+
def as_indexed_json(*)
|
33
|
+
{ file: Base64.encode64(data) }
|
34
|
+
end
|
35
|
+
end
|
28
36
|
end
|
29
|
-
|
30
|
-
end
|
37
|
+
end
|
@@ -1,19 +1,30 @@
|
|
1
|
-
module Ladder
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
respond_to?(:serialized_json) ? serialized_json : as_json(except: [:id, :_id])
|
6
|
-
end
|
1
|
+
module Ladder
|
2
|
+
module Searchable
|
3
|
+
module Resource
|
4
|
+
extend ActiveSupport::Concern
|
7
5
|
|
8
|
-
|
6
|
+
##
|
7
|
+
# Serialize the resource as JSON for indexing
|
8
|
+
#
|
9
|
+
# @see Elasticsearch::Model::Serializing#as_indexed_json
|
10
|
+
#
|
11
|
+
# @return [Hash] a serialized version of the resource
|
12
|
+
def as_indexed_json(*)
|
13
|
+
respond_to?(:serialized_json) ? serialized_json : as_json(except: [:id, :_id])
|
14
|
+
end
|
9
15
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
module ClassMethods
|
17
|
+
##
|
18
|
+
# Specify type of serialization to use for indexing;
|
19
|
+
# if a block is provided, it is expected to return a Hash
|
20
|
+
# that will be used in lieu of {#as_indexed_json} for
|
21
|
+
# serializing the resource in the index
|
22
|
+
#
|
23
|
+
# @return [void]
|
24
|
+
def index_for_search(&block)
|
25
|
+
define_method(:serialized_json, block) if block_given?
|
26
|
+
end
|
27
|
+
end
|
15
28
|
end
|
16
|
-
|
17
29
|
end
|
18
|
-
|
19
|
-
end
|
30
|
+
end
|
data/lib/ladder/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Ladder
|
2
|
-
VERSION =
|
3
|
-
end
|
2
|
+
VERSION = '0.3.2'
|
3
|
+
end
|
data/spec/ladder/file_spec.rb
CHANGED
@@ -15,7 +15,7 @@ describe Ladder::File do
|
|
15
15
|
|
16
16
|
after do
|
17
17
|
Object.send(:remove_const, :LADDER_BASE_URI) if Object
|
18
|
-
Object.send(:remove_const,
|
18
|
+
Object.send(:remove_const, 'Datastream') if Object
|
19
19
|
end
|
20
20
|
|
21
21
|
shared_context 'with relations' do
|
@@ -42,7 +42,7 @@ describe Ladder::File do
|
|
42
42
|
|
43
43
|
context 'with one-sided has-many' do
|
44
44
|
it 'should have a relation' do
|
45
|
-
expect(thing.relations['files'].relation).to eq
|
45
|
+
expect(thing.relations['files'].relation).to eq Mongoid::Relations::Referenced::ManyToMany
|
46
46
|
expect(thing.files.to_a).to include subject
|
47
47
|
end
|
48
48
|
|
@@ -59,7 +59,6 @@ describe Ladder::File do
|
|
59
59
|
expect(subject.class.properties).to be_empty
|
60
60
|
end
|
61
61
|
end
|
62
|
-
|
63
62
|
end
|
64
63
|
|
65
64
|
context 'with data from file' do
|
@@ -73,8 +72,12 @@ describe Ladder::File do
|
|
73
72
|
end
|
74
73
|
|
75
74
|
context 'with data from string after creation' do
|
76
|
-
data =
|
77
|
-
|
75
|
+
data = 'And so Moomintroll was helplessly thrown out into a strange and dangerous world and dropped
|
76
|
+
up to his ears in the first snowdrift of his experience. It felt unpleasantly prickly to his
|
77
|
+
velvet skin, but at the same time his nose caught a new smell. It was a more serious smell
|
78
|
+
than any he had met before, and slightly frightening. But it made him wide awake and greatly
|
79
|
+
interested.'
|
80
|
+
|
78
81
|
let(:subject) { Datastream.new }
|
79
82
|
let(:source) { data } # UTF-8 (string)
|
80
83
|
|
@@ -85,5 +88,4 @@ describe Ladder::File do
|
|
85
88
|
include_context 'with relations'
|
86
89
|
it_behaves_like 'a File'
|
87
90
|
end
|
88
|
-
|
89
|
-
end
|
91
|
+
end
|
@@ -10,12 +10,17 @@ describe Ladder::Resource::Dynamic do
|
|
10
10
|
|
11
11
|
class Thing
|
12
12
|
include Ladder::Resource::Dynamic
|
13
|
+
configure type: RDF::DC.BibliographicResource
|
14
|
+
|
15
|
+
field :alt
|
16
|
+
property :alt, predicate: RDF::DC.alternative # non-localized literal
|
17
|
+
property :title, predicate: RDF::DC.title # localized literal
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
16
21
|
after do
|
17
22
|
Object.send(:remove_const, :LADDER_BASE_URI) if Object
|
18
|
-
Object.send(:remove_const,
|
23
|
+
Object.send(:remove_const, 'Thing') if Object
|
19
24
|
end
|
20
25
|
|
21
26
|
context 'with data' do
|
@@ -23,155 +28,20 @@ describe Ladder::Resource::Dynamic do
|
|
23
28
|
|
24
29
|
before do
|
25
30
|
# non-localized literal
|
26
|
-
subject.class.field :alt
|
27
|
-
subject.class.property :alt, predicate: RDF::DC.alternative
|
28
31
|
subject.alt = 'Mumintrollet pa kometjakt'
|
29
32
|
|
30
33
|
# localized literal
|
31
|
-
subject.class.property :title, predicate: RDF::DC.title
|
32
34
|
subject.title = 'Comet in Moominland'
|
33
35
|
end
|
34
36
|
|
35
37
|
it_behaves_like 'a Resource'
|
36
|
-
|
37
|
-
|
38
|
-
context 'with undefined property' do
|
39
|
-
before do
|
40
|
-
subject.property :description, predicate: RDF::DC.description
|
41
|
-
subject.description = "Second in Tove Jansson's series of Moomin books"
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'should create a context' do
|
45
|
-
expect(subject._context).to eq({description: RDF::DC.description.to_uri.to_s})
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'should build an accessor' do
|
49
|
-
expect(subject.description).to eq "Second in Tove Jansson's series of Moomin books"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
context 'with conflicting property' do
|
54
|
-
before do
|
55
|
-
subject.property :title, predicate: RDF::DC11.title
|
56
|
-
subject.dc11_title = "Kometjakten"
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'should create a context' do
|
60
|
-
expect(subject._context).to eq({dc11_title: RDF::DC11.title.to_uri.to_s})
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'should build an accessor' do
|
64
|
-
expect(subject.dc11_title).to eq "Kometjakten"
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
describe '#update_resource' do
|
70
|
-
before do
|
71
|
-
# undefined property
|
72
|
-
subject.property :description, predicate: RDF::DC.description
|
73
|
-
subject.description = "Second in Tove Jansson's series of Moomin books"
|
74
|
-
|
75
|
-
# conflicting property
|
76
|
-
subject.property :title, predicate: RDF::DC11.title
|
77
|
-
subject.dc11_title = "Kometjakten"
|
78
|
-
|
79
|
-
# defined field
|
80
|
-
subject << RDF::Statement(nil, RDF::DC.title, 'Kometen kommer')
|
81
|
-
|
82
|
-
# conflicting property
|
83
|
-
subject << RDF::Statement(nil, RDF::DC.alternative, "Kometjakten")
|
84
|
-
|
85
|
-
# URI value
|
86
|
-
subject << RDF::Statement(nil, RDF::DC.identifier, RDF::URI('http://some.uri'))
|
87
|
-
|
88
|
-
subject.update_resource
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'should have updated values' do
|
92
|
-
expect(subject.resource.statements.count).to eq 5
|
93
|
-
expect(subject.resource.query(predicate: RDF::DC.description, object: "Second in Tove Jansson's series of Moomin books").count).to eq 1
|
94
|
-
expect(subject.resource.query(predicate: RDF::DC11.title, object: "Kometjakten").count).to eq 1
|
95
|
-
expect(subject.resource.query(predicate: RDF::DC.title, object: RDF::Literal.new('Kometen kommer', language: :en)).count).to eq 1
|
96
|
-
expect(subject.resource.query(predicate: RDF::DC.alternative, object: "Kometjakten").count).to eq 1
|
97
|
-
expect(subject.resource.query(predicate: RDF::DC.identifier, object: RDF::URI('http://some.uri')).count).to eq 1
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
describe '#<<' do
|
102
|
-
context 'with defined field' do
|
103
|
-
before do
|
104
|
-
subject << RDF::Statement(nil, RDF::DC.title, 'Kometen kommer')
|
105
|
-
end
|
106
|
-
|
107
|
-
it 'should not create a context' do
|
108
|
-
expect(subject._context).to be nil
|
109
|
-
end
|
110
|
-
|
111
|
-
it 'should update existing values' do
|
112
|
-
expect(subject.title).to eq 'Kometen kommer'
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
context 'with undefined field' do
|
117
|
-
before do
|
118
|
-
subject << RDF::Statement(nil, RDF::DC.description, "Second in Tove Jansson's series of Moomin books")
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'should create a context' do
|
122
|
-
expect(subject._context).to eq({ description: RDF::DC.description.to_uri.to_s })
|
123
|
-
end
|
124
|
-
|
125
|
-
it 'should build an accessor' do
|
126
|
-
expect(subject.description).to eq "Second in Tove Jansson's series of Moomin books"
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
context 'with conflicting property' do
|
131
|
-
before do
|
132
|
-
subject << RDF::Statement(nil, RDF::DC11.title, "Kometjakten")
|
133
|
-
end
|
134
|
-
|
135
|
-
it 'should create a context' do
|
136
|
-
expect(subject._context).to eq({ dc11_title: RDF::DC11.title.to_uri.to_s })
|
137
|
-
end
|
138
|
-
|
139
|
-
it 'should build an accessor' do
|
140
|
-
expect(subject.dc11_title).to eq "Kometjakten"
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
context 'with a URI value' do
|
145
|
-
before do
|
146
|
-
subject << RDF::Statement(nil, RDF::DC.identifier, RDF::URI('http://some.uri'))
|
147
|
-
end
|
148
|
-
|
149
|
-
it 'should store the URI as a string' do
|
150
|
-
expect(subject.identifier).to eq 'http://some.uri'
|
151
|
-
end
|
38
|
+
it_behaves_like 'a Dynamic Resource'
|
39
|
+
end
|
152
40
|
|
153
|
-
|
154
|
-
|
155
|
-
query = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.identifier)
|
156
|
-
expect(query.first_object).to be_a_kind_of RDF::URI
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
describe '#resource_class' do
|
162
|
-
before do
|
163
|
-
subject.property :description, predicate: RDF::DC.description
|
164
|
-
end
|
41
|
+
context 'from JSON-LD' do
|
42
|
+
let(:subject) { Thing.new_from_graph(RDF::Graph.load './spec/shared/graph.jsonld') }
|
165
43
|
|
166
|
-
|
167
|
-
|
168
|
-
end
|
169
|
-
|
170
|
-
it 'should not modify the global class properties' do
|
171
|
-
expect(subject.class.resource_class.properties.keys).to_not include 'description'
|
172
|
-
expect(subject.class.resource_class.properties).to eq subject.class.new.class.resource_class.properties
|
173
|
-
end
|
174
|
-
end
|
44
|
+
it_behaves_like 'a Resource'
|
45
|
+
it_behaves_like 'a Dynamic Resource'
|
175
46
|
end
|
176
|
-
|
177
|
-
end
|
47
|
+
end
|
@@ -10,266 +10,63 @@ describe Ladder::Resource do
|
|
10
10
|
|
11
11
|
class Thing
|
12
12
|
include Ladder::Resource
|
13
|
+
configure type: RDF::DC.BibliographicResource
|
14
|
+
|
15
|
+
field :alt
|
16
|
+
property :alt, predicate: RDF::DC.alternative # non-localized literal
|
17
|
+
property :title, predicate: RDF::DC.title # localized literal
|
18
|
+
property :identifier, predicate: RDF::DC.identifier
|
13
19
|
end
|
14
20
|
end
|
15
21
|
|
16
22
|
after do
|
17
23
|
Object.send(:remove_const, :LADDER_BASE_URI) if Object
|
18
|
-
Object.send(:remove_const,
|
19
|
-
end
|
20
|
-
|
21
|
-
shared_context 'with data' do
|
22
|
-
before do
|
23
|
-
subject.class.configure type: RDF::DC.BibliographicResource
|
24
|
-
|
25
|
-
# non-localized literal
|
26
|
-
subject.class.field :alt
|
27
|
-
subject.class.property :alt, predicate: RDF::DC.alternative
|
28
|
-
subject.alt = 'Mumintrollet pa kometjakt'
|
29
|
-
|
30
|
-
# localized literal
|
31
|
-
subject.class.property :title, predicate: RDF::DC.title
|
32
|
-
subject.title = 'Comet in Moominland'
|
33
|
-
end
|
24
|
+
Object.send(:remove_const, 'Thing') if Object
|
34
25
|
end
|
35
26
|
|
36
27
|
shared_context 'with relations' do
|
37
28
|
let(:person) { Person.new }
|
38
29
|
let(:concept) { Concept.new }
|
39
30
|
let(:part) { Part.new }
|
40
|
-
|
31
|
+
|
41
32
|
before do
|
42
33
|
class Person
|
43
34
|
include Ladder::Resource
|
35
|
+
configure type: RDF::DC.AgentClass
|
36
|
+
|
37
|
+
property :things, predicate: RDF::DC.relation, class_name: 'Thing'
|
44
38
|
end
|
45
39
|
|
46
40
|
class Concept
|
47
41
|
include Ladder::Resource
|
42
|
+
configure type: RDF::SKOS.Concept
|
48
43
|
end
|
49
44
|
|
50
45
|
class Part
|
51
46
|
include Ladder::Resource
|
52
|
-
|
47
|
+
configure type: RDF::DC.PhysicalResource
|
53
48
|
|
54
|
-
|
55
|
-
|
56
|
-
|
49
|
+
embedded_in :thing
|
50
|
+
property :thing, predicate: RDF::DC.relation, class_name: 'Thing'
|
51
|
+
end
|
57
52
|
|
58
53
|
# many-to-many
|
59
|
-
|
60
|
-
subject.class.property :people, predicate: RDF::DC.creator, class_name: 'Person'
|
61
|
-
subject.people << person
|
54
|
+
Thing.property :people, predicate: RDF::DC.creator, class_name: 'Person'
|
62
55
|
|
63
56
|
# one-sided has-many
|
64
|
-
|
65
|
-
|
66
|
-
subject.concepts << concept
|
57
|
+
Thing.has_and_belongs_to_many :concepts, inverse_of: nil, autosave: true
|
58
|
+
Thing.property :concepts, predicate: RDF::DC.subject, class_name: 'Concept'
|
67
59
|
|
68
60
|
# embedded one
|
69
|
-
|
70
|
-
|
71
|
-
subject.class.embeds_one :part, cascade_callbacks: true
|
72
|
-
subject.class.property :part, predicate: RDF::DC.hasPart, class_name: 'Part'
|
73
|
-
subject.part = part
|
74
|
-
subject.save
|
61
|
+
Thing.embeds_one :part, cascade_callbacks: true
|
62
|
+
Thing.property :part, predicate: RDF::DC.hasPart, class_name: 'Part'
|
75
63
|
end
|
76
64
|
|
77
65
|
after do
|
78
|
-
Object.send(:remove_const,
|
66
|
+
Object.send(:remove_const, 'Person') if Object
|
79
67
|
Object.send(:remove_const, 'Concept') if Object
|
80
68
|
Object.send(:remove_const, 'Part') if Object
|
81
69
|
end
|
82
|
-
|
83
|
-
it 'should have relations' do
|
84
|
-
expect(subject.people.to_a).to include person
|
85
|
-
expect(subject.concepts.to_a).to include concept
|
86
|
-
expect(subject.part).to eq part
|
87
|
-
end
|
88
|
-
|
89
|
-
it 'should have inverse relations' do
|
90
|
-
expect(person.things.to_a).to include subject
|
91
|
-
expect(concept.relations).to be_empty
|
92
|
-
expect(part.thing).to eq subject
|
93
|
-
end
|
94
|
-
|
95
|
-
context 'with many-to-many' do
|
96
|
-
it 'should have a relation' do
|
97
|
-
expect(subject.relations['people'].relation).to eq (Mongoid::Relations::Referenced::ManyToMany)
|
98
|
-
expect(subject.people.to_a).to include person
|
99
|
-
end
|
100
|
-
|
101
|
-
it 'should have an inverse relation' do
|
102
|
-
expect(person.relations['things'].relation).to eq (Mongoid::Relations::Referenced::ManyToMany)
|
103
|
-
expect(person.things.to_a).to include subject
|
104
|
-
end
|
105
|
-
|
106
|
-
it 'should have a valid predicate' do
|
107
|
-
expect(subject.class.properties['people'].predicate).to eq RDF::DC.creator
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'should have a valid inverse predicate' do
|
111
|
-
expect(person.class.properties['things'].predicate).to eq RDF::DC.relation
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
context 'with one-sided has-many' do
|
116
|
-
it 'should have a relation' do
|
117
|
-
expect(subject.relations['concepts'].relation).to eq (Mongoid::Relations::Referenced::ManyToMany)
|
118
|
-
expect(subject.concepts.to_a).to include concept
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'should not have an inverse relation' do
|
122
|
-
expect(subject.relations['concepts'].inverse_of).to be nil
|
123
|
-
expect(concept.relations).to be_empty
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'should have a valid predicate' do
|
127
|
-
expect(subject.class.properties['concepts'].predicate).to eq RDF::DC.subject
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'should not have an inverse predicate' do
|
131
|
-
expect(concept.class.properties).to be_empty
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
context 'with embedded-one' do
|
136
|
-
it 'should have a relation' do
|
137
|
-
expect(subject.relations['part'].relation).to eq (Mongoid::Relations::Embedded::One)
|
138
|
-
expect(subject.part).to eq part
|
139
|
-
end
|
140
|
-
|
141
|
-
it 'should have an inverse relation' do
|
142
|
-
expect(part.relations['thing'].relation).to eq (Mongoid::Relations::Embedded::In)
|
143
|
-
expect(part.thing).to eq subject
|
144
|
-
end
|
145
|
-
|
146
|
-
it 'should have a valid predicate' do
|
147
|
-
expect(subject.class.properties['part'].predicate).to eq RDF::DC.hasPart
|
148
|
-
end
|
149
|
-
|
150
|
-
it 'should have a valid inverse predicate' do
|
151
|
-
expect(part.class.properties['thing'].predicate).to eq RDF::DC.relation
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
context '#update_resource with related' do
|
156
|
-
# TODO add tests for autosaved relations
|
157
|
-
before do
|
158
|
-
subject.update_resource(related: true)
|
159
|
-
end
|
160
|
-
|
161
|
-
it 'should have a literal object' do
|
162
|
-
query = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.title)
|
163
|
-
expect(query.first_object.to_s).to eq 'Comet in Moominland'
|
164
|
-
end
|
165
|
-
|
166
|
-
it 'should have an embedded object' do
|
167
|
-
query = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.hasPart)
|
168
|
-
expect(query.count).to eq 1
|
169
|
-
expect(query.first_object).to eq part.rdf_subject
|
170
|
-
end
|
171
|
-
|
172
|
-
it 'should have an embedded object relation' do
|
173
|
-
query = subject.resource.query(subject: part.rdf_subject, predicate: RDF::DC.relation)
|
174
|
-
expect(query.count).to eq 1
|
175
|
-
expect(query.first_object).to eq subject.rdf_subject
|
176
|
-
end
|
177
|
-
|
178
|
-
it 'should have related objects' do
|
179
|
-
# many-to-many
|
180
|
-
query_creator = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.creator)
|
181
|
-
expect(query_creator.count).to eq 1
|
182
|
-
expect(query_creator.first_object).to eq person.rdf_subject
|
183
|
-
|
184
|
-
# one-sided has-many
|
185
|
-
query_subject = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.subject)
|
186
|
-
expect(query_subject.count).to eq 1
|
187
|
-
expect(query_subject.first_object).to eq concept.rdf_subject
|
188
|
-
|
189
|
-
# embedded-one
|
190
|
-
query_part = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.hasPart)
|
191
|
-
expect(query_part.count).to eq 1
|
192
|
-
expect(query_part.first_object).to eq part.rdf_subject
|
193
|
-
end
|
194
|
-
|
195
|
-
it 'should have related object relations' do
|
196
|
-
# many-to-many
|
197
|
-
query = person.resource.query(subject: person.rdf_subject, predicate: RDF::DC.relation)
|
198
|
-
expect(query.count).to eq 1
|
199
|
-
expect(query.first_object).to eq subject.rdf_subject
|
200
|
-
|
201
|
-
# one-sided has-many
|
202
|
-
expect(concept.resource.query(object: subject.rdf_subject)).to be_empty
|
203
|
-
|
204
|
-
# embedded-one
|
205
|
-
query = part.resource.query(subject: part.rdf_subject, predicate: RDF::DC.relation)
|
206
|
-
expect(query.count).to eq 1
|
207
|
-
expect(query.first_object).to eq subject.rdf_subject
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
context '#update_resource with related and then without related' do
|
212
|
-
# TODO add tests for autosaved relations
|
213
|
-
before do
|
214
|
-
subject.update_resource(related: true)
|
215
|
-
subject.update_resource # implicit false
|
216
|
-
end
|
217
|
-
|
218
|
-
it 'should not have related objects' do
|
219
|
-
expect(subject.resource.query(subject: person.rdf_subject)).to be_empty
|
220
|
-
expect(subject.resource.query(subject: concept.rdf_subject)).to be_empty
|
221
|
-
end
|
222
|
-
|
223
|
-
it 'should have embedded object relations' do
|
224
|
-
query = subject.resource.query(subject: part.rdf_subject, predicate: RDF::DC.relation)
|
225
|
-
expect(query.count).to eq 1
|
226
|
-
expect(query.first_object).to eq subject.rdf_subject
|
227
|
-
end
|
228
|
-
|
229
|
-
it 'should have related object relations' do
|
230
|
-
# many-to-many
|
231
|
-
query = person.resource.query(subject: person.rdf_subject, predicate: RDF::DC.relation)
|
232
|
-
expect(query.count).to eq 1
|
233
|
-
expect(query.first_object).to eq subject.rdf_subject
|
234
|
-
|
235
|
-
# one-sided has-many
|
236
|
-
expect(concept.resource.query(object: subject.rdf_subject)).to be_empty
|
237
|
-
|
238
|
-
# embedded-one
|
239
|
-
query = part.resource.query(subject: part.rdf_subject, predicate: RDF::DC.relation)
|
240
|
-
expect(query.count).to eq 1
|
241
|
-
expect(query.first_object).to eq subject.rdf_subject
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
context 'serializable' do
|
246
|
-
# TODO: contexts with relations and without
|
247
|
-
# expect(subject.as_jsonld(related: true)).to eq subject.as_jsonld
|
248
|
-
# expect(subject.as_qname(related: true)).to eq subject.as_qname
|
249
|
-
# expect(subject.as_framed_jsonld).to eq subject.as_jsonld
|
250
|
-
|
251
|
-
describe '#as_jsonld related: true' do
|
252
|
-
it 'should output a valid jsonld representation of itself and related' do
|
253
|
-
graph = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_jsonld related: true)
|
254
|
-
expect(subject.update_resource(related: true).to_hash).to eq graph.to_hash
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
describe '#as_qname related: true' do
|
259
|
-
it 'should output a valid qname representation of itself and related' do
|
260
|
-
# TODO
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
describe '#as_framed_jsonld' do
|
265
|
-
it 'should output a valid framed jsonld representation of itself and related' do
|
266
|
-
framed_graph = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_framed_jsonld)
|
267
|
-
related_graph = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_jsonld related: true)
|
268
|
-
expect(framed_graph.to_hash).to eq related_graph.to_hash
|
269
|
-
end
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
70
|
end
|
274
71
|
|
275
72
|
context 'with data' do
|
@@ -286,7 +83,31 @@ describe Ladder::Resource do
|
|
286
83
|
include_context 'with data'
|
287
84
|
include_context 'with relations'
|
288
85
|
|
86
|
+
before do
|
87
|
+
subject.people << person # many-to-many
|
88
|
+
subject.concepts << concept # one-sided has-many
|
89
|
+
subject.part = part # embedded one
|
90
|
+
subject.save
|
91
|
+
end
|
92
|
+
|
289
93
|
it_behaves_like 'a Resource'
|
94
|
+
it_behaves_like 'a Resource with relations'
|
290
95
|
end
|
291
96
|
|
292
|
-
|
97
|
+
context 'from JSON-LD' do
|
98
|
+
let(:subject) { Thing.new_from_graph(RDF::Graph.load './spec/shared/graph.jsonld') }
|
99
|
+
|
100
|
+
include_context 'with relations'
|
101
|
+
|
102
|
+
let(:person) { subject.people.first }
|
103
|
+
let(:concept) { subject.concepts.first }
|
104
|
+
let(:part) { subject.part }
|
105
|
+
|
106
|
+
before do
|
107
|
+
subject.save
|
108
|
+
end
|
109
|
+
|
110
|
+
it_behaves_like 'a Resource'
|
111
|
+
it_behaves_like 'a Resource with relations'
|
112
|
+
end
|
113
|
+
end
|