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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -3
  3. data/AUTHORS +1 -1
  4. data/CHANGES.md +12 -17
  5. data/Gemfile +2 -0
  6. data/README.md +2 -2
  7. data/active-triples.gemspec +6 -5
  8. data/lib/active_triples.rb +56 -5
  9. data/lib/active_triples/configurable.rb +13 -17
  10. data/lib/active_triples/list.rb +22 -21
  11. data/lib/active_triples/persistable.rb +100 -0
  12. data/lib/active_triples/persistence_strategies/parent_strategy.rb +89 -0
  13. data/lib/active_triples/persistence_strategies/persistence_strategy.rb +77 -0
  14. data/lib/active_triples/persistence_strategies/repository_strategy.rb +78 -0
  15. data/lib/active_triples/properties.rb +2 -1
  16. data/lib/active_triples/rdf_source.rb +94 -159
  17. data/lib/active_triples/relation.rb +17 -45
  18. data/lib/active_triples/repositories.rb +23 -9
  19. data/lib/active_triples/version.rb +1 -1
  20. data/spec/active_triples/configurable_spec.rb +5 -9
  21. data/spec/active_triples/identifiable_spec.rb +1 -18
  22. data/spec/active_triples/list_spec.rb +0 -4
  23. data/spec/active_triples/persistable_spec.rb +93 -0
  24. data/spec/active_triples/persistence_strategies/parent_strategy_spec.rb +93 -0
  25. data/spec/active_triples/persistence_strategies/persistence_strategy_spec.rb +24 -0
  26. data/spec/active_triples/persistence_strategies/repository_strategy_spec.rb +120 -0
  27. data/spec/active_triples/rdf_source_spec.rb +217 -9
  28. data/spec/active_triples/relation_spec.rb +51 -4
  29. data/spec/active_triples/repositories_spec.rb +17 -7
  30. data/spec/active_triples/resource_spec.rb +6 -53
  31. data/spec/active_triples_spec.rb +50 -1
  32. data/spec/spec_helper.rb +5 -1
  33. data/spec/support/active_model_lint.rb +6 -3
  34. data/spec/support/dummies/basic_persistable.rb +13 -0
  35. data/spec/support/shared_examples/persistence_strategy.rb +36 -0
  36. metadata +32 -9
  37. 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
- end
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.class.repository == :parent && parent.send(:repository)
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
- if !uri_class(statement.object) || uri_class(statement.object) == class_for_property
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 ValueError, val unless
137
- val.kind_of? RDF::Value or val.kind_of? RDF::Literal
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
- resource.parent = parent unless resource.frozen?
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.class.repository == :parent
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
- # ActiveTriples::Repositories.add_repository :blah, RDF::Repository.new
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
- # @param name [Symbol]
21
- # @param repo [RDF::Repository]
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
- # @raise [ArgumentError] if a non-repository is passed
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 ArgumentError, "Repositories must be an RDF::Repository" unless
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
- def has_subject?(rdf_subject,repo_name=nil)
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,3 +1,3 @@
1
1
  module ActiveTriples
2
- VERSION = '0.7.6'.freeze
2
+ VERSION = "0.8.0".freeze
3
3
  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