active-triples 0.7.6 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +5 -3
- data/AUTHORS +1 -1
- data/CHANGES.md +12 -17
- data/Gemfile +2 -0
- data/README.md +2 -2
- data/active-triples.gemspec +6 -5
- data/lib/active_triples.rb +56 -5
- data/lib/active_triples/configurable.rb +13 -17
- data/lib/active_triples/list.rb +22 -21
- data/lib/active_triples/persistable.rb +100 -0
- data/lib/active_triples/persistence_strategies/parent_strategy.rb +89 -0
- data/lib/active_triples/persistence_strategies/persistence_strategy.rb +77 -0
- data/lib/active_triples/persistence_strategies/repository_strategy.rb +78 -0
- data/lib/active_triples/properties.rb +2 -1
- data/lib/active_triples/rdf_source.rb +94 -159
- data/lib/active_triples/relation.rb +17 -45
- data/lib/active_triples/repositories.rb +23 -9
- data/lib/active_triples/version.rb +1 -1
- data/spec/active_triples/configurable_spec.rb +5 -9
- data/spec/active_triples/identifiable_spec.rb +1 -18
- data/spec/active_triples/list_spec.rb +0 -4
- data/spec/active_triples/persistable_spec.rb +93 -0
- data/spec/active_triples/persistence_strategies/parent_strategy_spec.rb +93 -0
- data/spec/active_triples/persistence_strategies/persistence_strategy_spec.rb +24 -0
- data/spec/active_triples/persistence_strategies/repository_strategy_spec.rb +120 -0
- data/spec/active_triples/rdf_source_spec.rb +217 -9
- data/spec/active_triples/relation_spec.rb +51 -4
- data/spec/active_triples/repositories_spec.rb +17 -7
- data/spec/active_triples/resource_spec.rb +6 -53
- data/spec/active_triples_spec.rb +50 -1
- data/spec/spec_helper.rb +5 -1
- data/spec/support/active_model_lint.rb +6 -3
- data/spec/support/dummies/basic_persistable.rb +13 -0
- data/spec/support/shared_examples/persistence_strategy.rb +36 -0
- metadata +32 -9
- data/spec/active_triples/validations_spec.rb +0 -33
@@ -42,7 +42,7 @@ module ActiveTriples
|
|
42
42
|
.each_with_object([]) do |x, collector|
|
43
43
|
converted_object = convert_object(x.object)
|
44
44
|
collector << converted_object unless converted_object.nil?
|
45
|
-
|
45
|
+
end
|
46
46
|
end
|
47
47
|
|
48
48
|
def set(values)
|
@@ -52,14 +52,16 @@ module ActiveTriples
|
|
52
52
|
values.each do |val|
|
53
53
|
set_value(val)
|
54
54
|
end
|
55
|
-
parent.persist! if parent.
|
55
|
+
parent.persist! if parent.persistence_strategy.is_a? ParentStrategy
|
56
56
|
end
|
57
57
|
|
58
|
+
##
|
59
|
+
# Deletes the values for this relation. This removes all triples matching
|
60
|
+
# the basic graph pattern [:rdf_subject :predicate ?object] from the parent
|
61
|
+
# Enumerable.
|
58
62
|
def empty_property
|
59
63
|
parent.query([rdf_subject, predicate, nil]).each_statement do |statement|
|
60
|
-
|
61
|
-
parent.delete(statement)
|
62
|
-
end
|
64
|
+
parent.delete(statement)
|
63
65
|
end
|
64
66
|
end
|
65
67
|
|
@@ -133,8 +135,9 @@ module ActiveTriples
|
|
133
135
|
return
|
134
136
|
end
|
135
137
|
val = val.to_uri if val.respond_to? :to_uri
|
136
|
-
raise
|
137
|
-
|
138
|
+
raise "value must be an RDF URI, Node, Literal, or a valid datatype." \
|
139
|
+
" See RDF::Literal.\n\tYou provided #{val.inspect}" unless
|
140
|
+
val.kind_of? RDF::Term
|
138
141
|
parent.insert [rdf_subject, predicate, val]
|
139
142
|
end
|
140
143
|
|
@@ -144,9 +147,12 @@ module ActiveTriples
|
|
144
147
|
|
145
148
|
def add_child_node(resource,object=nil)
|
146
149
|
parent.insert [rdf_subject, predicate, resource.rdf_subject]
|
147
|
-
|
150
|
+
unless resource.frozen?
|
151
|
+
resource.set_persistence_strategy(ParentStrategy)
|
152
|
+
resource.parent = parent
|
153
|
+
end
|
148
154
|
self.node_cache[resource.rdf_subject] = (object ? object : resource)
|
149
|
-
resource.persist! if resource.
|
155
|
+
resource.persist! if resource.persistence_strategy.is_a? ParentStrategy
|
150
156
|
end
|
151
157
|
|
152
158
|
def predicate
|
@@ -163,6 +169,8 @@ module ActiveTriples
|
|
163
169
|
# Converts an object to the appropriate class.
|
164
170
|
def convert_object(value)
|
165
171
|
case value
|
172
|
+
when RDFSource
|
173
|
+
value
|
166
174
|
when RDF::Literal
|
167
175
|
return_literals? ? value : value.object
|
168
176
|
when RDF::Resource
|
@@ -235,41 +243,5 @@ module ActiveTriples
|
|
235
243
|
parent.rdf_subject
|
236
244
|
end
|
237
245
|
end
|
238
|
-
|
239
|
-
public
|
240
|
-
|
241
|
-
##
|
242
|
-
# An error class for unallowable values in relations.
|
243
|
-
class ValueError < ArgumentError
|
244
|
-
# @!attribute [r] value
|
245
|
-
attr_reader :value
|
246
|
-
|
247
|
-
##
|
248
|
-
# @param value [Object]
|
249
|
-
def initialize(value)
|
250
|
-
@value = value
|
251
|
-
end
|
252
|
-
|
253
|
-
##
|
254
|
-
# @return [String]
|
255
|
-
def message
|
256
|
-
'value must be an RDF URI, Node, Literal, or a valid datatype. '\
|
257
|
-
"See RDF::Literal.\n\tYou provided #{value.inspect}"
|
258
|
-
end
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
class Term < Relation
|
263
|
-
def self.inherited(*)
|
264
|
-
warn 'ActiveTriples::Term is deprecated! ' \
|
265
|
-
'Use ActiveTriples::Relation instead.'
|
266
|
-
super
|
267
|
-
end
|
268
|
-
|
269
|
-
def initialize(*)
|
270
|
-
warn 'ActiveTriples::Term is deprecated! ' \
|
271
|
-
'Use ActiveTriples::Relation instead.'
|
272
|
-
super
|
273
|
-
end
|
274
246
|
end
|
275
247
|
end
|
@@ -7,7 +7,9 @@ module ActiveTriples
|
|
7
7
|
# RDF::Repository implementation to be used for persistence of
|
8
8
|
# resources that will be shared between ActiveFedora::Base objects.
|
9
9
|
#
|
10
|
-
#
|
10
|
+
# @example registering a repository
|
11
|
+
#
|
12
|
+
# ActiveTriples::Repositories.add_repository :defaulst, RDF::Repository.new
|
11
13
|
#
|
12
14
|
# Multiple repositories can be registered to keep different kinds of
|
13
15
|
# resources seperate. This is configurable on subclasses of Resource
|
@@ -17,23 +19,32 @@ module ActiveTriples
|
|
17
19
|
module Repositories
|
18
20
|
|
19
21
|
##
|
20
|
-
#
|
21
|
-
#
|
22
|
+
# Register a repository to be configured by name
|
23
|
+
#
|
24
|
+
# @param name [Symbol]
|
25
|
+
# @param repo [RDF::Repository]
|
22
26
|
#
|
23
|
-
# @return [RDF::Repository]
|
24
|
-
#
|
27
|
+
# @return [RDF::Repository] gives the original repository on success
|
28
|
+
#
|
29
|
+
# @raise [RuntimeError] raised if the repository is not an `RDF::Repository`
|
25
30
|
def add_repository(name, repo)
|
26
|
-
raise
|
27
|
-
repo.kind_of? RDF::Repository
|
31
|
+
raise "Repositories must be an RDF::Repository" unless repo.kind_of? RDF::Repository
|
28
32
|
repositories[name] = repo
|
29
33
|
end
|
30
34
|
module_function :add_repository
|
31
35
|
|
36
|
+
##
|
37
|
+
# Delete existing name, repository pairs from the registry hash
|
38
|
+
#
|
39
|
+
# @return [Hash<Symbol, Repository>] the now empty repository registry hash
|
32
40
|
def clear_repositories!
|
33
41
|
@repositories = {}
|
34
42
|
end
|
35
43
|
module_function :clear_repositories!
|
36
44
|
|
45
|
+
##
|
46
|
+
# @return [Hash<Symbol, Repository>] a hash of currrently registered names
|
47
|
+
# and repositories
|
37
48
|
def repositories
|
38
49
|
@repositories ||= {}
|
39
50
|
end
|
@@ -42,9 +53,13 @@ module ActiveTriples
|
|
42
53
|
##
|
43
54
|
# Check for the specified rdf_subject in the specified repository
|
44
55
|
# defaulting to search all registered repositories.
|
56
|
+
#
|
45
57
|
# @param [String] rdf_subject
|
46
58
|
# @param [Symbol] repository name
|
47
|
-
|
59
|
+
#
|
60
|
+
# @return [Boolean] true if the repository contains at least one statement
|
61
|
+
# with the given subject term
|
62
|
+
def has_subject?(rdf_subject, repo_name=nil)
|
48
63
|
search_repositories = [repositories[repo_name]] if repo_name
|
49
64
|
search_repositories ||= repositories.values
|
50
65
|
found = false
|
@@ -55,6 +70,5 @@ module ActiveTriples
|
|
55
70
|
found
|
56
71
|
end
|
57
72
|
module_function :has_subject?
|
58
|
-
|
59
73
|
end
|
60
74
|
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
|
2
3
|
describe ActiveTriples::Configurable do
|
3
4
|
before do
|
4
5
|
class DummyConfigurable
|
5
6
|
extend ActiveTriples::Configurable
|
6
7
|
end
|
7
8
|
end
|
9
|
+
|
8
10
|
after do
|
9
11
|
Object.send(:remove_const, "DummyConfigurable")
|
10
12
|
end
|
@@ -12,10 +14,12 @@ describe ActiveTriples::Configurable do
|
|
12
14
|
it "should be okay if not configured" do
|
13
15
|
expect(DummyConfigurable.type).to eq nil
|
14
16
|
end
|
17
|
+
|
15
18
|
it "should be okay if configured to nil" do
|
16
19
|
DummyConfigurable.configure :type => nil
|
17
20
|
expect(DummyConfigurable.type).to eq []
|
18
21
|
end
|
22
|
+
|
19
23
|
describe '#configure' do
|
20
24
|
before do
|
21
25
|
DummyConfigurable.configure base_uri: "http://example.org/base", type: RDF::RDFS.Class, rdf_label: RDF::DC.title
|
@@ -37,18 +41,10 @@ describe ActiveTriples::Configurable do
|
|
37
41
|
it 'should set a type' do
|
38
42
|
expect(DummyConfigurable.type).to eq [RDF::RDFS.Class]
|
39
43
|
end
|
44
|
+
|
40
45
|
it "should be able to set multiple types" do
|
41
46
|
DummyConfigurable.configure type: RDF::RDFS.Container
|
42
47
|
expect(DummyConfigurable.type).to eq [RDF::RDFS.Class, RDF::RDFS.Container]
|
43
48
|
end
|
44
49
|
end
|
45
|
-
|
46
|
-
describe '#rdf_type' do
|
47
|
-
it "should set the type the old way" do
|
48
|
-
expect(DummyConfigurable).to receive(:configure).with(type: RDF::RDFS.Class).and_call_original
|
49
|
-
expect(Deprecation).to receive(:warn)
|
50
|
-
DummyConfigurable.rdf_type(RDF::RDFS.Class)
|
51
|
-
expect(DummyConfigurable.type).to eq [RDF::RDFS.Class]
|
52
|
-
end
|
53
|
-
end
|
54
50
|
end
|
@@ -3,24 +3,7 @@ require 'active_model'
|
|
3
3
|
|
4
4
|
describe ActiveTriples::Identifiable do
|
5
5
|
before do
|
6
|
-
class ActiveExample
|
7
|
-
include ActiveTriples::Identifiable
|
8
|
-
|
9
|
-
def self.property(*args)
|
10
|
-
prop = args.first
|
11
|
-
|
12
|
-
define_method prop.to_s do
|
13
|
-
resource.get_values(prop)
|
14
|
-
end
|
15
|
-
|
16
|
-
define_method "#{prop.to_s}=" do |*args|
|
17
|
-
resource.set_value(prop, *args)
|
18
|
-
end
|
19
|
-
|
20
|
-
resource_class.property(*args)
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
6
|
+
class ActiveExample; include ActiveTriples::Identifiable; end
|
24
7
|
end
|
25
8
|
|
26
9
|
after do
|
@@ -3,7 +3,6 @@ require 'nokogiri'
|
|
3
3
|
require 'linkeddata'
|
4
4
|
|
5
5
|
describe ActiveTriples::List do
|
6
|
-
|
7
6
|
subject { ActiveTriples::List.new }
|
8
7
|
|
9
8
|
context 'when empty' do
|
@@ -258,9 +257,6 @@ END
|
|
258
257
|
|
259
258
|
it "should be a valid list" do
|
260
259
|
list << "Val"
|
261
|
-
# TODO this is a workaround for https://github.com/projecthydra/active_fedora/issues/444
|
262
|
-
# remove the following line when #444 is closed.
|
263
|
-
list.resource.persist!
|
264
260
|
expect(RDF::List.new(list.rdf_subject, subject)).to be_valid
|
265
261
|
end
|
266
262
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveTriples::Persistable do
|
4
|
+
subject { klass.new }
|
5
|
+
|
6
|
+
let(:klass) { Class.new { include ActiveTriples::Persistable } }
|
7
|
+
|
8
|
+
let(:statement) { RDF::Statement(RDF::Node.new, RDF::DC.title, 'Moomin') }
|
9
|
+
|
10
|
+
it 'raises an error with no #graph implementation' do
|
11
|
+
expect { subject << statement }.to raise_error(NameError, /graph/)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'method delegation' do
|
15
|
+
context 'with a strategy' do
|
16
|
+
let(:strategy_class) do
|
17
|
+
object_double(ActiveTriples::ParentStrategy).as_stubbed_const
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:strategy) { instance_double('PersistenceStrategy') }
|
21
|
+
|
22
|
+
before do
|
23
|
+
allow(strategy_class).to receive(:new).and_return(strategy)
|
24
|
+
subject.set_persistence_strategy(strategy_class)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#persist!' do
|
28
|
+
before { allow(subject).to receive(:run_callbacks).and_yield }
|
29
|
+
|
30
|
+
it 'sends message to strategy' do
|
31
|
+
expect(strategy).to receive(:persist!)
|
32
|
+
subject.persist!
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#destroy' do
|
37
|
+
it 'sends message to strategy' do
|
38
|
+
expect(strategy).to receive(:destroy)
|
39
|
+
subject.destroy
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#persisted?' do
|
44
|
+
it 'sends message to strategy' do
|
45
|
+
expect(strategy).to receive(:persisted?)
|
46
|
+
subject.persisted?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#reload' do
|
51
|
+
it 'sends message to strategy' do
|
52
|
+
expect(strategy).to receive(:reload)
|
53
|
+
subject.reload
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#persistence_strategy' do
|
60
|
+
it 'defaults to RepositoryStrategy' do
|
61
|
+
expect(subject.persistence_strategy)
|
62
|
+
.to be_a ActiveTriples::RepositoryStrategy
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#set_persistence_strategy' do
|
67
|
+
let(:strategy_class) { double('strategy class') }
|
68
|
+
let(:strategy) { double('persistence strategy') }
|
69
|
+
|
70
|
+
before do
|
71
|
+
allow(strategy_class).to receive(:new).and_return(strategy)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'sets new persistence strategy as an instance of the given class' do
|
75
|
+
expect { subject.set_persistence_strategy(strategy_class) }
|
76
|
+
.to change { subject.persistence_strategy }
|
77
|
+
.from(an_instance_of(ActiveTriples::RepositoryStrategy))
|
78
|
+
.to(strategy)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'with graph implementation' do
|
83
|
+
before do
|
84
|
+
graph = RDF::Graph.new
|
85
|
+
allow(subject).to receive(:graph).and_return(graph)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'mirrors writes to graph' do
|
89
|
+
subject << statement
|
90
|
+
expect(subject.graph).to contain_exactly statement
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveTriples::ParentStrategy do
|
4
|
+
subject { described_class.new(rdf_source) }
|
5
|
+
let(:rdf_source) { BasicPersistable.new }
|
6
|
+
|
7
|
+
shared_context 'with a parent' do
|
8
|
+
before do
|
9
|
+
subject.parent = parent
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:parent) { BasicPersistable.new }
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'with a parent' do
|
16
|
+
include_context 'with a parent'
|
17
|
+
it_behaves_like 'a persistence strategy'
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#final_parent' do
|
21
|
+
it 'raises an error with no parent' do
|
22
|
+
expect { subject.final_parent }.to raise_error described_class::NilParentError
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with single parent' do
|
26
|
+
include_context 'with a parent'
|
27
|
+
|
28
|
+
it 'gives parent' do
|
29
|
+
expect(subject.final_parent).to eq subject.parent
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with parent chain' do
|
34
|
+
include_context 'with a parent'
|
35
|
+
let(:last) { double('last') }
|
36
|
+
|
37
|
+
it 'gives last parent terminating when no futher parents given' do
|
38
|
+
allow(parent).to receive(:parent).and_return(last)
|
39
|
+
expect(subject.final_parent).to eq last
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'gives last parent terminating parent is nil' do
|
43
|
+
allow(parent).to receive(:parent).and_return(last)
|
44
|
+
allow(last).to receive(:parentt).and_return(nil)
|
45
|
+
expect(subject.final_parent).to eq last
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'gives last parent terminating parent is same as current' do
|
49
|
+
allow(parent).to receive(:parent).and_return(last)
|
50
|
+
allow(last).to receive(:parentt).and_return(last)
|
51
|
+
expect(subject.final_parent).to eq last
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#parent' do
|
57
|
+
it { is_expected.to have_attributes(:parent => nil) }
|
58
|
+
|
59
|
+
it 'requires its parent to be RDF::Mutable' do
|
60
|
+
expect { subject.parent = Object.new }
|
61
|
+
.to raise_error described_class::UnmutableParentError
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'requires its parent to be #mutable?' do
|
65
|
+
immutable = double
|
66
|
+
allow(immutable).to receive(:mutable?).and_return(false)
|
67
|
+
expect { subject.parent = immutable }
|
68
|
+
.to raise_error described_class::UnmutableParentError
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with a parent' do
|
72
|
+
include_context 'with a parent'
|
73
|
+
it { is_expected.to have_attributes(:parent => parent) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '#persist!' do
|
78
|
+
it 'raises an error with no parent' do
|
79
|
+
expect { subject.persist! }.to raise_error described_class::NilParentError
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'with parent' do
|
83
|
+
include_context 'with a parent'
|
84
|
+
|
85
|
+
it 'writes to #final_parent graph' do
|
86
|
+
rdf_source << RDF::Statement.new(RDF::Node.new, RDF::DC.title, 'moomin')
|
87
|
+
subject.persist!
|
88
|
+
expect(subject.final_parent.statements)
|
89
|
+
.to contain_exactly *rdf_source.statements
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|