active-triples 0.7.6 → 0.8.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/.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
|