active-fedora 9.10.4 → 9.11.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/.rubocop.yml +3 -0
- data/History.txt +0 -13
- data/lib/active_fedora.rb +7 -0
- data/lib/active_fedora/aggregation.rb +11 -0
- data/lib/active_fedora/aggregation/base_extension.rb +17 -0
- data/lib/active_fedora/aggregation/list_source.rb +103 -0
- data/lib/active_fedora/aggregation/ordered_reader.rb +27 -0
- data/lib/active_fedora/aggregation/proxy.rb +18 -0
- data/lib/active_fedora/associations.rb +40 -0
- data/lib/active_fedora/associations/builder/aggregation.rb +51 -0
- data/lib/active_fedora/associations/builder/filter.rb +18 -0
- data/lib/active_fedora/associations/builder/orders.rb +63 -0
- data/lib/active_fedora/associations/filter_association.rb +71 -0
- data/lib/active_fedora/associations/orders_association.rb +149 -0
- data/lib/active_fedora/attached_files.rb +2 -1
- data/lib/active_fedora/attribute_methods.rb +4 -4
- data/lib/active_fedora/autosave_association.rb +14 -0
- data/lib/active_fedora/base.rb +1 -0
- data/lib/active_fedora/cleaner.rb +1 -1
- data/lib/active_fedora/errors.rb +3 -0
- data/lib/active_fedora/fedora.rb +17 -7
- data/lib/active_fedora/file/streaming.rb +13 -4
- data/lib/active_fedora/orders.rb +12 -0
- data/lib/active_fedora/orders/collection_proxy.rb +8 -0
- data/lib/active_fedora/orders/list_node.rb +161 -0
- data/lib/active_fedora/orders/ordered_list.rb +264 -0
- data/lib/active_fedora/orders/target_proxy.rb +60 -0
- data/lib/active_fedora/reflection.rb +215 -42
- data/lib/active_fedora/validations.rb +1 -1
- data/lib/active_fedora/version.rb +1 -1
- data/lib/generators/active_fedora/config/solr/templates/solr/config/schema.xml +1 -1
- data/solr/config/schema.xml +1 -1
- data/spec/integration/attributes_spec.rb +8 -0
- data/spec/integration/file_spec.rb +22 -0
- data/spec/integration/has_many_associations_spec.rb +23 -0
- data/spec/integration/versionable_spec.rb +6 -6
- data/spec/unit/active_fedora_spec.rb +1 -1
- data/spec/unit/aggregation/list_source_spec.rb +134 -0
- data/spec/unit/aggregation/ordered_reader_spec.rb +43 -0
- data/spec/unit/fedora_spec.rb +1 -1
- data/spec/unit/filter_spec.rb +133 -0
- data/spec/unit/ordered_spec.rb +369 -0
- data/spec/unit/orders/list_node_spec.rb +151 -0
- data/spec/unit/orders/ordered_list_spec.rb +335 -0
- data/spec/unit/orders/reflection_spec.rb +22 -0
- data/spec/unit/reflection_spec.rb +2 -4
- metadata +25 -3
@@ -103,7 +103,7 @@
|
|
103
103
|
http://wiki.apache.org/solr/SolrAdaptersForLuceneSpatial4
|
104
104
|
-->
|
105
105
|
<fieldType name="location_rpt" class="solr.SpatialRecursivePrefixTreeFieldType"
|
106
|
-
geo="true" distErrPct="0.025" maxDistErr="0.000009"
|
106
|
+
geo="true" distErrPct="0.025" maxDistErr="0.000009" distanceUnits="degrees" />
|
107
107
|
|
108
108
|
<fieldType name="text" class="solr.TextField" omitNorms="false">
|
109
109
|
<analyzer>
|
data/solr/config/schema.xml
CHANGED
@@ -103,7 +103,7 @@
|
|
103
103
|
http://wiki.apache.org/solr/SolrAdaptersForLuceneSpatial4
|
104
104
|
-->
|
105
105
|
<fieldType name="location_rpt" class="solr.SpatialRecursivePrefixTreeFieldType"
|
106
|
-
geo="true" distErrPct="0.025" maxDistErr="0.000009"
|
106
|
+
geo="true" distErrPct="0.025" maxDistErr="0.000009" distanceUnits="degrees" />
|
107
107
|
|
108
108
|
<fieldType name="text" class="solr.TextField" omitNorms="false">
|
109
109
|
<analyzer>
|
@@ -147,4 +147,12 @@ describe "delegating attributes" do
|
|
147
147
|
end
|
148
148
|
end
|
149
149
|
end
|
150
|
+
|
151
|
+
describe 'dangerous attributes' do
|
152
|
+
it 'raises an exception if a dangerous attribute is defined' do
|
153
|
+
Deprecation.silence(ActiveFedora::Attributes) do
|
154
|
+
expect { TitledObject.has_attributes :save, datastream: 'foo', multiple: false }.to raise_error ActiveFedora::DangerousAttributeError
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
150
158
|
end
|
@@ -149,6 +149,28 @@ describe ActiveFedora::File do
|
|
149
149
|
it { should eq 'two2thre' }
|
150
150
|
end
|
151
151
|
end
|
152
|
+
|
153
|
+
context "when the request results in a redirect" do
|
154
|
+
before do
|
155
|
+
test_object.add_file('one1two2threfour', path: 'webm', mime_type: 'video/webm')
|
156
|
+
test_object.add_file('', path: 'redirector', mime_type: "message/external-body; access-type=URL; url=\"#{test_object.webm.uri}\"")
|
157
|
+
test_object.save!
|
158
|
+
end
|
159
|
+
subject { str = ''; test_object.redirector.stream.each { |chunk| str << chunk }; str }
|
160
|
+
it { should eq 'one1two2threfour' }
|
161
|
+
end
|
162
|
+
|
163
|
+
context "when there are more than 3 requests because of redirects" do
|
164
|
+
before do
|
165
|
+
test_object.add_file('', path: 'one', mime_type: "message/external-body; access-type=URL; url=\"#{test_object.attached_files[path].uri}\"")
|
166
|
+
test_object.add_file('', path: 'two', mime_type: "message/external-body; access-type=URL; url=\"#{test_object.one.uri}\"")
|
167
|
+
test_object.add_file('', path: 'three', mime_type: "message/external-body; access-type=URL; url=\"#{test_object.two.uri}\"")
|
168
|
+
test_object.save!
|
169
|
+
end
|
170
|
+
it "raises a HTTP redirect too deep Error" do
|
171
|
+
expect { test_object.three.stream.each { |chunk| chunk } }.to raise_error('HTTP redirect too deep')
|
172
|
+
end
|
173
|
+
end
|
152
174
|
end
|
153
175
|
end
|
154
176
|
end
|
@@ -423,4 +423,27 @@ describe ActiveFedora::Associations::HasManyAssociation do
|
|
423
423
|
end
|
424
424
|
end
|
425
425
|
end
|
426
|
+
|
427
|
+
describe "when destroying the owner" do
|
428
|
+
before do
|
429
|
+
class Book < ActiveFedora::Base
|
430
|
+
has_many :permissions, dependent: :destroy
|
431
|
+
end
|
432
|
+
|
433
|
+
class Permission < ActiveFedora::Base
|
434
|
+
belongs_to :book, predicate: ActiveFedora::RDF::Fcrepo::RelsExt.hasConstituent
|
435
|
+
end
|
436
|
+
end
|
437
|
+
let(:book) { Book.create! }
|
438
|
+
let!(:permissions) { Permission.create!(book: book) }
|
439
|
+
|
440
|
+
after do
|
441
|
+
Object.send(:remove_const, :Book)
|
442
|
+
Object.send(:remove_const, :Permission)
|
443
|
+
end
|
444
|
+
|
445
|
+
it "the object is destroyed" do
|
446
|
+
expect { book.destroy }.to change { Permission.count }.by(-1)
|
447
|
+
end
|
448
|
+
end
|
426
449
|
end
|
@@ -179,8 +179,8 @@ describe ActiveFedora::Versionable do
|
|
179
179
|
@original_size = subject.size
|
180
180
|
end
|
181
181
|
|
182
|
-
it "
|
183
|
-
expect(subject.
|
182
|
+
it "links to versions endpoint" do
|
183
|
+
expect(subject.metadata.ldp_source.graph.query(predicate: ::RDF::Vocab::Fcrepo4.hasVersions).objects).to_not be_empty
|
184
184
|
end
|
185
185
|
|
186
186
|
it "has one version" do
|
@@ -305,8 +305,8 @@ describe ActiveFedora::Versionable do
|
|
305
305
|
@original_size = subject.size
|
306
306
|
end
|
307
307
|
|
308
|
-
it "
|
309
|
-
expect(subject.
|
308
|
+
it "links to versions endpoint" do
|
309
|
+
expect(subject.metadata.ldp_source.graph.query(predicate: ::RDF::Vocab::Fcrepo4.hasVersions).objects).to_not be_empty
|
310
310
|
end
|
311
311
|
|
312
312
|
it "has one version" do
|
@@ -427,8 +427,8 @@ describe ActiveFedora::Versionable do
|
|
427
427
|
subject.create_version
|
428
428
|
end
|
429
429
|
|
430
|
-
it "
|
431
|
-
expect(subject.
|
430
|
+
it "links to versions endpoint" do
|
431
|
+
expect(subject.metadata.ldp_source.graph.query(predicate: ::RDF::Vocab::Fcrepo4.hasVersions).objects).to_not be_empty
|
432
432
|
end
|
433
433
|
|
434
434
|
it "has one version" do
|
@@ -28,7 +28,7 @@ describe ActiveFedora do
|
|
28
28
|
it "does not connect and warn" do
|
29
29
|
expect(ActiveFedora::Base.logger).to receive(:warn)
|
30
30
|
expect {
|
31
|
-
ActiveFedora::Fedora.new(url: bad_url, base_path: '/test', user: user, password: password)
|
31
|
+
ActiveFedora::Fedora.new(url: bad_url, base_path: '/test', user: user, password: password).connection
|
32
32
|
}.to raise_error Ldp::HttpError
|
33
33
|
end
|
34
34
|
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe ActiveFedora::Aggregation::ListSource do
|
4
|
+
subject { described_class.new }
|
5
|
+
|
6
|
+
describe "#head" do
|
7
|
+
it "is nil by default" do
|
8
|
+
expect(subject.head).to eq nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it "is settable" do
|
12
|
+
subject.head = RDF::URI("test.org")
|
13
|
+
|
14
|
+
expect(subject.head_id.first).to eq RDF::URI("test.org")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "maps to IANA.first" do
|
18
|
+
expect(subject.class.properties["head"].predicate).to eq ::RDF::Vocab::IANA["first"]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#order_will_change!" do
|
23
|
+
it "marks it as changed" do
|
24
|
+
expect(subject).not_to be_changed
|
25
|
+
subject.order_will_change!
|
26
|
+
expect(subject).to be_changed
|
27
|
+
expect(subject.ordered_self).to be_changed
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#tail" do
|
32
|
+
it "is nil by default" do
|
33
|
+
expect(subject.tail).to eq nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "is settable" do
|
37
|
+
subject.tail = RDF::URI("test.org")
|
38
|
+
|
39
|
+
expect(subject.tail_id.first).to eq RDF::URI("test.org")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "maps to IANA.last" do
|
43
|
+
expect(subject.class.properties["tail"].predicate).to eq ::RDF::Vocab::IANA["last"]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#changed?" do
|
48
|
+
context "when nothing has changed" do
|
49
|
+
it "is false" do
|
50
|
+
expect(subject).not_to be_changed
|
51
|
+
end
|
52
|
+
end
|
53
|
+
context "when the ordered list is changed" do
|
54
|
+
it "is true" do
|
55
|
+
allow(subject.ordered_self).to receive(:changed?).and_return(true)
|
56
|
+
|
57
|
+
expect(subject).to be_changed
|
58
|
+
end
|
59
|
+
end
|
60
|
+
context "when the ordered list is not changed" do
|
61
|
+
it "is false" do
|
62
|
+
allow(subject.ordered_self).to receive(:changed?).and_return(false)
|
63
|
+
|
64
|
+
expect(subject).not_to be_changed
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#save" do
|
70
|
+
context "when nothing has changed" do
|
71
|
+
it "does not persist ordered_self" do
|
72
|
+
allow(subject.ordered_self).to receive(:to_graph)
|
73
|
+
|
74
|
+
subject.save
|
75
|
+
|
76
|
+
expect(subject.ordered_self).not_to have_received(:to_graph)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
context "when attributes have changed, but not ordered list" do
|
80
|
+
it "does not persist ordered self" do
|
81
|
+
allow(subject.ordered_self).to receive(:to_graph)
|
82
|
+
subject.nodes += [RDF::URI("http://test.org")]
|
83
|
+
|
84
|
+
subject.save
|
85
|
+
|
86
|
+
expect(subject.ordered_self).not_to have_received(:to_graph)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
context "when ordered list has changed" do
|
90
|
+
it "persists it" do
|
91
|
+
allow(subject.ordered_self).to receive(:to_graph).and_call_original
|
92
|
+
allow(subject.ordered_self).to receive(:changed?).and_return(true)
|
93
|
+
|
94
|
+
subject.save
|
95
|
+
|
96
|
+
expect(subject.ordered_self).to have_received(:to_graph)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "#serializable_hash" do
|
102
|
+
it "does not serialize nodes" do
|
103
|
+
subject.nodes += [RDF::URI("http://test.org")]
|
104
|
+
|
105
|
+
expect(subject.serializable_hash).not_to have_key "nodes"
|
106
|
+
end
|
107
|
+
it "does not serialize head" do
|
108
|
+
subject.head = RDF::URI("http://test.org")
|
109
|
+
|
110
|
+
expect(subject.serializable_hash).not_to have_key "head"
|
111
|
+
end
|
112
|
+
it "does not serialize tail" do
|
113
|
+
subject.tail = RDF::URI("http://test.org")
|
114
|
+
|
115
|
+
expect(subject.serializable_hash).not_to have_key "tail"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#to_solr" do
|
120
|
+
before do
|
121
|
+
class Member < ActiveFedora::Base
|
122
|
+
end
|
123
|
+
end
|
124
|
+
after do
|
125
|
+
Object.send(:remove_const, :Member)
|
126
|
+
end
|
127
|
+
it "can index" do
|
128
|
+
m = Member.create
|
129
|
+
proxy_in = RDF::URI(ActiveFedora::Base.translate_id_to_uri.call("banana"))
|
130
|
+
subject.ordered_self.append_target m, proxy_in: proxy_in
|
131
|
+
expect(subject.to_solr).to include ordered_targets_ssim: [m.id], proxy_in_ssi: "banana"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe ActiveFedora::Aggregation::OrderedReader do
|
4
|
+
subject { described_class.new(root) }
|
5
|
+
let(:root) { instance_double(ActiveFedora::Aggregation::ListSource) }
|
6
|
+
|
7
|
+
describe "#each" do
|
8
|
+
it "iterates a linked list" do
|
9
|
+
head = build_node
|
10
|
+
tail = build_node(prev_node: head)
|
11
|
+
allow(head).to receive(:next).and_return(tail)
|
12
|
+
allow(root).to receive(:head).and_return(head)
|
13
|
+
expect(subject.to_a).to eq [head, tail]
|
14
|
+
end
|
15
|
+
it "only goes as deep as necessary" do
|
16
|
+
head = build_node
|
17
|
+
tail = build_node(prev_node: head)
|
18
|
+
allow(head).to receive(:next).and_return(tail)
|
19
|
+
allow(root).to receive(:head).and_return(head)
|
20
|
+
expect(subject.first).to eq head
|
21
|
+
expect(head).not_to have_received(:next)
|
22
|
+
end
|
23
|
+
context "when the prev is wrong" do
|
24
|
+
it "fixes it up" do
|
25
|
+
head = build_node
|
26
|
+
bad_node = build_node
|
27
|
+
tail = build_node(prev_node: bad_node)
|
28
|
+
allow(head).to receive(:next).and_return(tail)
|
29
|
+
allow(root).to receive(:head).and_return(head)
|
30
|
+
allow(tail).to receive(:prev=)
|
31
|
+
expect(subject.to_a).to eq [head, tail]
|
32
|
+
expect(tail).to have_received(:prev=).with(head)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_node(prev_node: nil, next_node: nil)
|
38
|
+
node = instance_double(ActiveFedora::Orders::ListNode)
|
39
|
+
allow(node).to receive(:next).and_return(next_node)
|
40
|
+
allow(node).to receive(:prev).and_return(prev_node)
|
41
|
+
node
|
42
|
+
end
|
43
|
+
end
|
data/spec/unit/fedora_spec.rb
CHANGED
@@ -12,7 +12,7 @@ describe ActiveFedora::Fedora do
|
|
12
12
|
}
|
13
13
|
}
|
14
14
|
specify {
|
15
|
-
expect(Faraday).to receive(:new).with("https://example.com", ssl: { ca_path: '/path/to/certs' }).
|
15
|
+
expect(Faraday).to receive(:new).with("https://example.com", ssl: { ca_path: '/path/to/certs' }).and_call_original
|
16
16
|
subject.authorized_connection
|
17
17
|
}
|
18
18
|
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveFedora::Associations::FilterAssociation do
|
4
|
+
before do
|
5
|
+
class Image < ActiveFedora::Base
|
6
|
+
ordered_aggregation :members, through: :list_source, class_name: 'ActiveFedora::Base'
|
7
|
+
|
8
|
+
filters_association :members, as: :child_objects, condition: :pcdm_object?
|
9
|
+
filters_association :members, as: :child_collections, condition: :pcdm_collection?
|
10
|
+
end
|
11
|
+
|
12
|
+
class TestObject < ActiveFedora::Base
|
13
|
+
def pcdm_object?
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def pcdm_collection?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class TestCollection < ActiveFedora::Base
|
23
|
+
def pcdm_object?
|
24
|
+
false
|
25
|
+
end
|
26
|
+
|
27
|
+
def pcdm_collection?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
after do
|
34
|
+
Object.send(:remove_const, :Image)
|
35
|
+
Object.send(:remove_const, :TestObject)
|
36
|
+
Object.send(:remove_const, :TestCollection)
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:image) { Image.new }
|
40
|
+
let(:test_object) { TestObject.new }
|
41
|
+
let(:test_collection) { TestCollection.new }
|
42
|
+
|
43
|
+
describe "setting" do
|
44
|
+
context "when an incorrect object type is sent" do
|
45
|
+
it "raises an error" do
|
46
|
+
image.child_collections
|
47
|
+
expect { image.child_collections = [test_object] }.to raise_error ArgumentError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when the parent is already loaded" do
|
52
|
+
let(:another_collection) { TestCollection.new }
|
53
|
+
before do
|
54
|
+
image.members = [test_object, test_collection]
|
55
|
+
image.child_collections = [another_collection]
|
56
|
+
end
|
57
|
+
it "overwrites existing matches" do
|
58
|
+
expect(image.members).to eq [test_object, another_collection]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "appending" do
|
64
|
+
context "when an incorrect object type is sent" do
|
65
|
+
it "raises an error" do
|
66
|
+
expect { image.child_collections << test_object }.to raise_error ArgumentError
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when the parent is already loaded" do
|
71
|
+
let(:another_collection) { TestCollection.new }
|
72
|
+
before do
|
73
|
+
image.members = [test_object, test_collection]
|
74
|
+
image.child_collections << [another_collection]
|
75
|
+
end
|
76
|
+
|
77
|
+
it "updates the parent" do
|
78
|
+
expect(image.members).to eq [test_object, test_collection, another_collection]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "#size" do
|
84
|
+
it "returns the size" do
|
85
|
+
# Need to persist so that count_records will be called.
|
86
|
+
image.save
|
87
|
+
test_object.save
|
88
|
+
image.members = [test_object]
|
89
|
+
|
90
|
+
expect(image.reload.child_objects.size).to eq 1
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "reading" do
|
95
|
+
before do
|
96
|
+
image.members = [test_object, test_collection]
|
97
|
+
end
|
98
|
+
|
99
|
+
it "returns the objects of the correct type" do
|
100
|
+
expect(image.child_objects).to eq [test_object]
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "when the parent association is changed" do
|
104
|
+
before do
|
105
|
+
image.child_objects = [test_object]
|
106
|
+
image.child_objects.to_a # this would cause the @target of the association to be populated
|
107
|
+
image.members = [test_collection]
|
108
|
+
end
|
109
|
+
|
110
|
+
it "updates the filtered relation" do
|
111
|
+
expect(image.child_objects).to eq []
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "#_ids" do
|
116
|
+
it "returns just the ids" do
|
117
|
+
expect(image.child_object_ids).to eq [test_object.id]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "#delete" do
|
123
|
+
let(:another_object) { TestObject.new }
|
124
|
+
before do
|
125
|
+
image.members = [test_object, test_collection, another_object]
|
126
|
+
image.child_objects.delete(test_object)
|
127
|
+
end
|
128
|
+
|
129
|
+
subject { image.members }
|
130
|
+
|
131
|
+
it { is_expected.to eq [test_collection, another_object] }
|
132
|
+
end
|
133
|
+
end
|