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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bce277ca040d2e5d2b6f20ad12ded43a0d09a81e
|
4
|
+
data.tar.gz: 41626c552b9571d1993bc95f3719a0b12a24eb5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b48ed0579be52f0683f6293adc4ff4908706d125f924119cabc2e01076bfcf2501e7732ef9b057948f6aeffd0f10679626d033aab52313523839020005d543ac
|
7
|
+
data.tar.gz: 2dfa7a8abb57bc8dc376da0767fd5d70ecf13bc9bbcb6f0542faaad5b8d70973367c1f81776aa8e4e704655029676b9ec124836f3736d1940c36a7e470b3092d
|
data/.rubocop.yml
CHANGED
@@ -87,6 +87,7 @@ Metrics/ClassLength:
|
|
87
87
|
- 'lib/active_fedora/associations/collection_proxy.rb'
|
88
88
|
- 'lib/active_fedora/associations/collection_association.rb'
|
89
89
|
- 'lib/active_fedora/reflection.rb'
|
90
|
+
- 'lib/active_fedora/orders/ordered_list.rb'
|
90
91
|
|
91
92
|
Metrics/MethodLength:
|
92
93
|
Enabled: false
|
@@ -123,6 +124,8 @@ Style/PredicateName:
|
|
123
124
|
- 'lib/active_fedora/attached_files.rb'
|
124
125
|
- 'lib/active_fedora/associations.rb'
|
125
126
|
- 'lib/active_fedora/association_hash.rb'
|
127
|
+
- 'lib/active_fedora/aggregation/list_source.rb'
|
128
|
+
- 'lib/active_fedora/associations/builder/aggregation.rb'
|
126
129
|
|
127
130
|
Style/GuardClause:
|
128
131
|
Exclude:
|
data/History.txt
CHANGED
@@ -1,16 +1,3 @@
|
|
1
|
-
v9.10.4
|
2
|
-
2016-03-28: Use a version of activesupport >= 4.2.4 [Justin Coyne]
|
3
|
-
|
4
|
-
v9.10.3
|
5
|
-
2016-03-28: Revert "ActiveFedora::File.mime_type should be updatable" [Justin
|
6
|
-
Coyne]
|
7
|
-
|
8
|
-
2016-03-28: Pin to rubocop 0.38 [Justin Coyne]
|
9
|
-
|
10
|
-
v9.10.2
|
11
|
-
2016-03-26: Pass the block from find_in_batches to search_in_batches [Justin
|
12
|
-
Coyne]
|
13
|
-
|
14
1
|
v9.10.1
|
15
2
|
2016-03-26: Fix #reflect_on_association. [Trey Pendragon]
|
16
3
|
|
data/lib/active_fedora.rb
CHANGED
@@ -36,6 +36,7 @@ module ActiveFedora #:nodoc:
|
|
36
36
|
extend ActiveSupport::Autoload
|
37
37
|
|
38
38
|
eager_autoload do
|
39
|
+
autoload :Aggregation
|
39
40
|
autoload :AssociationHash
|
40
41
|
autoload :AssociationRelation
|
41
42
|
autoload :Associations
|
@@ -76,6 +77,7 @@ module ActiveFedora #:nodoc:
|
|
76
77
|
autoload :FilePersistence
|
77
78
|
autoload :FileRelation
|
78
79
|
autoload :FilesHash
|
80
|
+
autoload :Filter
|
79
81
|
autoload :FixityService
|
80
82
|
autoload :Identifiable
|
81
83
|
autoload :Indexers
|
@@ -94,6 +96,7 @@ module ActiveFedora #:nodoc:
|
|
94
96
|
autoload :NomDatastream
|
95
97
|
autoload :NullRelation
|
96
98
|
autoload :OmDatastream
|
99
|
+
autoload :Orders
|
97
100
|
autoload :Pathing
|
98
101
|
autoload :Persistence
|
99
102
|
autoload :ProfileIndexingService
|
@@ -227,6 +230,10 @@ module ActiveFedora #:nodoc:
|
|
227
230
|
ActiveFedora::SolrService.instance
|
228
231
|
end
|
229
232
|
|
233
|
+
def reset_fedora!
|
234
|
+
@fedora = nil
|
235
|
+
end
|
236
|
+
|
230
237
|
def fedora
|
231
238
|
@fedora ||= Fedora.new(fedora_config.credentials)
|
232
239
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveFedora::Aggregation
|
2
|
+
module BaseExtension
|
3
|
+
def ordered_by
|
4
|
+
ordered_by_ids.lazy.map { |x| ActiveFedora::Base.find(x) }
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def ordered_by_ids
|
10
|
+
if id.present?
|
11
|
+
ActiveFedora::SolrService.query("{!join from=proxy_in_ssi to=id}ordered_targets_ssim:#{id}").map { |x| x["id"] }
|
12
|
+
else
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module ActiveFedora
|
2
|
+
module Aggregation
|
3
|
+
class ListSource < ActiveFedora::Base
|
4
|
+
delegate :order_will_change!, to: :ordered_self
|
5
|
+
property :head, predicate: ::RDF::Vocab::IANA['first'], multiple: false
|
6
|
+
property :tail, predicate: ::RDF::Vocab::IANA.last, multiple: false
|
7
|
+
property :nodes, predicate: ::RDF::Vocab::DC.hasPart
|
8
|
+
|
9
|
+
def save(*args)
|
10
|
+
return true if has_unpersisted_proxy_for? || !changed?
|
11
|
+
persist_ordered_self if ordered_self.changed?
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
# Overriding so that we don't track previously_changed, which was
|
16
|
+
# rather expensive.
|
17
|
+
def clear_changed_attributes
|
18
|
+
@changed_attributes.clear
|
19
|
+
end
|
20
|
+
|
21
|
+
def changed?
|
22
|
+
super || ordered_self.changed?
|
23
|
+
end
|
24
|
+
|
25
|
+
# Ordered list representation of proxies in graph.
|
26
|
+
# @return [Array<ListNode>]
|
27
|
+
def ordered_self
|
28
|
+
@ordered_self ||= ordered_list_factory.new(resource, head_subject, tail_subject)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Allow this to be set so that -=, += will work.
|
32
|
+
# @param [ActiveFedora::Orders::OrderedList] An ordered list object this
|
33
|
+
# graph should contain.
|
34
|
+
attr_writer :ordered_self
|
35
|
+
|
36
|
+
# Serializing head/tail/nodes slows things down CONSIDERABLY, and is not
|
37
|
+
# useful.
|
38
|
+
# @note This method is used by ActiveFedora::Base upstream for indexing,
|
39
|
+
# at https://github.com/projecthydra/active_fedora/blob/master/lib/active_fedora/profile_indexing_service.rb.
|
40
|
+
def serializable_hash(_options = nil)
|
41
|
+
{}
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_solr(solr_doc = {})
|
45
|
+
super.merge(ordered_targets_ssim: ordered_self.target_ids,
|
46
|
+
proxy_in_ssi: ordered_self.proxy_in.to_s)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Not useful and slows down indexing.
|
50
|
+
def create_date
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Not useful, slows down indexing.
|
55
|
+
def modified_date
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
# Not useful, slows down indexing.
|
60
|
+
def has_model
|
61
|
+
["ActiveFedora::Aggregation::ListSource"]
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def persist_ordered_self
|
67
|
+
nodes_will_change!
|
68
|
+
# Delete old statements
|
69
|
+
subj = resource.subjects.to_a.select { |x| x.to_s.split("/").last.to_s.include?("#g") }
|
70
|
+
subj.each do |s|
|
71
|
+
resource.delete [s, nil, nil]
|
72
|
+
end
|
73
|
+
# Assert head and tail
|
74
|
+
self.head = ordered_self.head.next.rdf_subject
|
75
|
+
self.tail = ordered_self.tail.prev.rdf_subject
|
76
|
+
graph = ordered_self.to_graph
|
77
|
+
resource << graph
|
78
|
+
# Set node subjects to a term in AF JUST so that AF will persist the
|
79
|
+
# sub-graphs.
|
80
|
+
# TODO: Find a way to fix this.
|
81
|
+
self.nodes = nil
|
82
|
+
self.nodes += graph.subjects.to_a
|
83
|
+
ordered_self.changes_committed!
|
84
|
+
end
|
85
|
+
|
86
|
+
def has_unpersisted_proxy_for?
|
87
|
+
ordered_self.select(&:new_record?).map(&:target).find { |x| x.respond_to?(:uri) }
|
88
|
+
end
|
89
|
+
|
90
|
+
def head_subject
|
91
|
+
head_id.first
|
92
|
+
end
|
93
|
+
|
94
|
+
def tail_subject
|
95
|
+
tail_id.first
|
96
|
+
end
|
97
|
+
|
98
|
+
def ordered_list_factory
|
99
|
+
ActiveFedora::Orders::OrderedList
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ActiveFedora::Aggregation
|
2
|
+
##
|
3
|
+
# Lazily iterates over a doubly linked list, fixing up nodes if necessary.
|
4
|
+
class OrderedReader
|
5
|
+
include Enumerable
|
6
|
+
attr_reader :root
|
7
|
+
def initialize(root)
|
8
|
+
@root = root
|
9
|
+
end
|
10
|
+
|
11
|
+
def each
|
12
|
+
proxy = first_head
|
13
|
+
while proxy
|
14
|
+
yield proxy unless proxy.nil?
|
15
|
+
next_proxy = proxy.next
|
16
|
+
next_proxy.try(:prev=, proxy) if next_proxy && next_proxy.prev != proxy
|
17
|
+
proxy = next_proxy
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def first_head
|
24
|
+
root.head
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActiveFedora::Aggregation
|
2
|
+
class Proxy < ActiveFedora::Base
|
3
|
+
belongs_to :container, predicate: ::RDF::Vocab::ORE.proxyIn, class_name: 'ActiveFedora::Base'
|
4
|
+
belongs_to :target, predicate: ::RDF::Vocab::ORE.proxyFor, class_name: 'ActiveFedora::Base'
|
5
|
+
belongs_to :next, predicate: ::RDF::Vocab::IANA.next, class_name: 'ActiveFedora::Aggregation::Proxy'
|
6
|
+
belongs_to :prev, predicate: ::RDF::Vocab::IANA.prev, class_name: 'ActiveFedora::Aggregation::Proxy'
|
7
|
+
|
8
|
+
type ::RDF::Vocab::ORE.Proxy
|
9
|
+
|
10
|
+
def as_list
|
11
|
+
if self.next
|
12
|
+
[self] + self.next.as_list
|
13
|
+
else
|
14
|
+
[self]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -29,6 +29,8 @@ module ActiveFedora
|
|
29
29
|
autoload :DirectlyContainsOneAssociation
|
30
30
|
autoload :IndirectlyContainsAssociation
|
31
31
|
autoload :ContainsAssociation
|
32
|
+
autoload :FilterAssociation
|
33
|
+
autoload :OrdersAssociation
|
32
34
|
autoload :DeleteProxy
|
33
35
|
autoload :ContainedFinder
|
34
36
|
autoload :RecordComposite
|
@@ -50,6 +52,10 @@ module ActiveFedora
|
|
50
52
|
|
51
53
|
autoload :Property, 'active_fedora/associations/builder/property'
|
52
54
|
autoload :SingularProperty, 'active_fedora/associations/builder/singular_property'
|
55
|
+
|
56
|
+
autoload :Aggregation, 'active_fedora/associations/builder/aggregation'
|
57
|
+
autoload :Filter, 'active_fedora/associations/builder/filter'
|
58
|
+
autoload :Orders, 'active_fedora/associations/builder/orders'
|
53
59
|
end
|
54
60
|
|
55
61
|
eager_autoload do
|
@@ -280,6 +286,40 @@ module ActiveFedora
|
|
280
286
|
Builder::HasAndBelongsToMany.build(self, name, options)
|
281
287
|
Builder::Property.build(self, name, options.slice(:class_name, :predicate))
|
282
288
|
end
|
289
|
+
|
290
|
+
##
|
291
|
+
# Allows ordering of an association
|
292
|
+
# @example
|
293
|
+
# class Image < ActiveFedora::Base
|
294
|
+
# contains :list_resource, class_name:
|
295
|
+
# "ActiveFedora::Aggregation::ListSource"
|
296
|
+
# orders :generic_files, through: :list_resource
|
297
|
+
# end
|
298
|
+
def orders(name, options = {})
|
299
|
+
Builder::Orders.build(self, name, options)
|
300
|
+
end
|
301
|
+
|
302
|
+
##
|
303
|
+
# Convenience method for building an ordered aggregation.
|
304
|
+
# @example
|
305
|
+
# class Image < ActiveFedora::Base
|
306
|
+
# ordered_aggregation :members, through: :list_source
|
307
|
+
# end
|
308
|
+
def ordered_aggregation(name, options = {})
|
309
|
+
Builder::Aggregation.build(self, name, options)
|
310
|
+
end
|
311
|
+
|
312
|
+
##
|
313
|
+
# Create an association filter on the class
|
314
|
+
# @example
|
315
|
+
# class Image < ActiveFedora::Base
|
316
|
+
# aggregates :generic_files
|
317
|
+
# filters_association :generic_files, as: :large_files, condition: :big_file?
|
318
|
+
# end
|
319
|
+
def filters_association(extending_from, options = {})
|
320
|
+
name = options.delete(:as)
|
321
|
+
Builder::Filter.build(self, name, options.merge(extending_from: extending_from))
|
322
|
+
end
|
283
323
|
end
|
284
324
|
end
|
285
325
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ActiveFedora::Associations::Builder
|
2
|
+
class Aggregation < ActiveFedora::Associations::Builder::Association
|
3
|
+
def self.valid_options(_options)
|
4
|
+
[:through, :class_name, :has_member_relation, :type_validator]
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.build(model, name, options)
|
8
|
+
model.indirectly_contains name, { has_member_relation: has_member_relation(options), through: proxy_class, foreign_key: proxy_foreign_key, inserted_content_relation: inserted_content_relation }.merge(indirect_options(options))
|
9
|
+
model.contains contains_key(options), class_name: list_source_class
|
10
|
+
model.orders name, through: contains_key(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.indirect_options(options)
|
14
|
+
{
|
15
|
+
class_name: options[:class_name],
|
16
|
+
type_validator: options[:type_validator]
|
17
|
+
}.select { |_k, v| v.present? }
|
18
|
+
end
|
19
|
+
private_class_method :indirect_options
|
20
|
+
|
21
|
+
def self.has_member_relation(options)
|
22
|
+
options[:has_member_relation] || ::RDF::DC.hasPart
|
23
|
+
end
|
24
|
+
private_class_method :has_member_relation
|
25
|
+
|
26
|
+
def self.inserted_content_relation
|
27
|
+
::RDF::Vocab::ORE.proxyFor
|
28
|
+
end
|
29
|
+
private_class_method :inserted_content_relation
|
30
|
+
|
31
|
+
def self.proxy_class
|
32
|
+
"ActiveFedora::Aggregation::Proxy"
|
33
|
+
end
|
34
|
+
private_class_method :proxy_class
|
35
|
+
|
36
|
+
def self.proxy_foreign_key
|
37
|
+
:target
|
38
|
+
end
|
39
|
+
private_class_method :proxy_foreign_key
|
40
|
+
|
41
|
+
def self.contains_key(options)
|
42
|
+
options[:through]
|
43
|
+
end
|
44
|
+
private_class_method :contains_key
|
45
|
+
|
46
|
+
def self.list_source_class
|
47
|
+
"ActiveFedora::Aggregation::ListSource"
|
48
|
+
end
|
49
|
+
private_class_method :list_source_class
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActiveFedora::Associations::Builder
|
2
|
+
class Filter < ActiveFedora::Associations::Builder::CollectionAssociation
|
3
|
+
def self.valid_options(options)
|
4
|
+
super + [:extending_from, :condition]
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.macro
|
8
|
+
:filter
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.define_readers(mixin, name)
|
12
|
+
super
|
13
|
+
mixin.redefine_method("#{name.to_s.singularize}_ids") do
|
14
|
+
association(name).ids_reader
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module ActiveFedora::Associations::Builder
|
2
|
+
class Orders < ActiveFedora::Associations::Builder::CollectionAssociation
|
3
|
+
include ActiveFedora::AutosaveAssociation::AssociationBuilderExtension
|
4
|
+
def self.macro
|
5
|
+
:orders
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.valid_options(options)
|
9
|
+
super + [:through, :unordered_reflection]
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.define_readers(mixin, name)
|
13
|
+
super
|
14
|
+
mixin.redefine_method(target_accessor(name)) do
|
15
|
+
association(name).target_reader
|
16
|
+
end
|
17
|
+
mixin.redefine_method("#{target_accessor(name)}=") do |nodes|
|
18
|
+
association(name).target_writer(nodes)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.build(model, name, options)
|
23
|
+
options = { unordered_reflection: unordered_reflection(model, name) }.merge(options)
|
24
|
+
name = :"ordered_#{name.to_s.singularize}_proxies"
|
25
|
+
model.property :head, predicate: ::RDF::Vocab::IANA['first']
|
26
|
+
model.property :tail, predicate: ::RDF::Vocab::IANA.last
|
27
|
+
model.send(:define_method, :apply_first_and_last) do
|
28
|
+
source = send(options[:through])
|
29
|
+
source.save
|
30
|
+
return if head.map(&:rdf_subject) == source.head_id && tail.map(&:rdf_subject) == source.tail_id
|
31
|
+
self.head = source.head_id
|
32
|
+
self.tail = source.tail_id
|
33
|
+
save! if changed?
|
34
|
+
end
|
35
|
+
model.include ActiveFedora::Associations::Builder::Orders::FixFirstLast
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
module FixFirstLast
|
40
|
+
def save(*args)
|
41
|
+
super.tap do |result|
|
42
|
+
apply_first_and_last if result
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def save!(*args)
|
47
|
+
super.tap do |result|
|
48
|
+
apply_first_and_last if result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.target_accessor(name)
|
54
|
+
name.to_s.gsub("_proxies", "").pluralize
|
55
|
+
end
|
56
|
+
private_class_method :target_accessor
|
57
|
+
|
58
|
+
def self.unordered_reflection(model, original_name)
|
59
|
+
model._reflect_on_association(original_name)
|
60
|
+
end
|
61
|
+
private_class_method :unordered_reflection
|
62
|
+
end
|
63
|
+
end
|