speedy-af 0.3.0 → 0.5.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/.circleci/config.yml +25 -13
- data/README.md +6 -0
- data/Rakefile +9 -0
- data/docker-compose.yml +26 -0
- data/lib/speedy_af/base.rb +53 -23
- data/lib/speedy_af/version.rb +1 -1
- data/spec/fixture_classes.rb +28 -5
- data/spec/integration/base_spec.rb +101 -13
- metadata +4 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ff0f3d554f80151433f700cfae02d61d3c271fbcfb1cefe132d8a9e0e518734
|
4
|
+
data.tar.gz: 01433ba5beca3308fa708191c76a2be9df2b5682be1ae41a3dc08f6ad6dd42af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '09e918cd65f5a0ee8a1cf5365f268f68fb265c39da957265598cfd8fa1c363ce5447b0523b7e938264505f49ac420e80af38c331390fb8c547eb5621a519e3d8'
|
7
|
+
data.tar.gz: e900999b19accf9c9d35c8b33480bfdd32b79375c34bd9d8108bfaf92105d67984aafdcb2b8196906d91ccb3aa7357ae5f8de373b1162898406fca6e8f378c1a
|
data/.circleci/config.yml
CHANGED
@@ -10,7 +10,7 @@ jobs:
|
|
10
10
|
type: string
|
11
11
|
bundler_version:
|
12
12
|
type: string
|
13
|
-
default: 2.
|
13
|
+
default: 2.5.18
|
14
14
|
ffmpeg_version:
|
15
15
|
type: string
|
16
16
|
default: 4.1.4
|
@@ -38,18 +38,30 @@ workflows:
|
|
38
38
|
ci:
|
39
39
|
jobs:
|
40
40
|
- bundle_and_test:
|
41
|
-
name: "
|
42
|
-
ruby_version: "
|
43
|
-
rails_version: "
|
41
|
+
name: "ruby3-4_rails8-0"
|
42
|
+
ruby_version: "3.4.5"
|
43
|
+
rails_version: "8.0.1"
|
44
44
|
- bundle_and_test:
|
45
|
-
name: "
|
46
|
-
ruby_version: "
|
47
|
-
rails_version: "
|
45
|
+
name: "ruby3-3_rails8-0"
|
46
|
+
ruby_version: "3.3.4"
|
47
|
+
rails_version: "8.0.1"
|
48
48
|
- bundle_and_test:
|
49
|
-
name: "
|
50
|
-
ruby_version: "
|
51
|
-
rails_version: "
|
49
|
+
name: "ruby3-3_rails7-2"
|
50
|
+
ruby_version: "3.3.4"
|
51
|
+
rails_version: "7.2.1"
|
52
52
|
- bundle_and_test:
|
53
|
-
name: "
|
54
|
-
ruby_version: "2.
|
55
|
-
rails_version: "
|
53
|
+
name: "ruby3-2_rails7-1"
|
54
|
+
ruby_version: "3.2.5"
|
55
|
+
rails_version: "7.1.4"
|
56
|
+
- bundle_and_test:
|
57
|
+
name: "ruby3-1_rails7-1"
|
58
|
+
ruby_version: "3.1.6"
|
59
|
+
rails_version: "7.1.4"
|
60
|
+
- bundle_and_test:
|
61
|
+
name: "ruby3-2_rails7-0"
|
62
|
+
ruby_version: "3.2.5"
|
63
|
+
rails_version: "7.0.8.4"
|
64
|
+
- bundle_and_test:
|
65
|
+
name: "ruby3-1_rails7-0"
|
66
|
+
ruby_version: "3.1.6"
|
67
|
+
rails_version: "7.0.8.4"
|
data/README.md
CHANGED
@@ -91,6 +91,12 @@ If you have questions or need help, please email [the Hydra community tech list]
|
|
91
91
|
be more mindful of both local resources and Solr request limits.
|
92
92
|
* `Base` may not play nicely with language-tagged RDF literals, as ActiveFedora does not
|
93
93
|
currently index/encode the language tag into Solr.
|
94
|
+
* belongs_to relationships do not respect `include_reflections` filtering and will fetch all
|
95
|
+
belongs_to reflections when the `include_reflections` is not false.
|
96
|
+
|
97
|
+
# Development
|
98
|
+
|
99
|
+
To run tests locally, spin up Fedora and Solr using `docker-compose up` then run tests using `bundle exec rspec`.
|
94
100
|
|
95
101
|
# Acknowledgments
|
96
102
|
|
data/Rakefile
CHANGED
@@ -5,6 +5,15 @@ require 'active_fedora/rake_support'
|
|
5
5
|
require 'bundler'
|
6
6
|
Bundler::GemHelper.install_tasks
|
7
7
|
|
8
|
+
# This is to fix a breaking change with Ruby 3.2 where some methods
|
9
|
+
# in fcrepo_wrapper use the removed `File.exists?` alias, preventing
|
10
|
+
# tests from being set up.
|
11
|
+
class File
|
12
|
+
class << self
|
13
|
+
alias exists? exist?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
8
17
|
require 'rspec/core/rake_task'
|
9
18
|
desc 'Run tests only'
|
10
19
|
RSpec::Core::RakeTask.new(:rspec) do |spec|
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
volumes:
|
2
|
+
fedora:
|
3
|
+
solr:
|
4
|
+
|
5
|
+
services:
|
6
|
+
fedora:
|
7
|
+
image: ghcr.io/samvera/fcrepo4:4.7.5
|
8
|
+
volumes:
|
9
|
+
- fedora:/data
|
10
|
+
ports:
|
11
|
+
- '8986:8080'
|
12
|
+
|
13
|
+
solr:
|
14
|
+
image: solr:9
|
15
|
+
volumes:
|
16
|
+
- ./solr/conf:/opt/solr/core_conf
|
17
|
+
- solr:/var/solr
|
18
|
+
command:
|
19
|
+
- solr-precreate
|
20
|
+
- hydra-test
|
21
|
+
- /opt/solr/core_conf
|
22
|
+
environment:
|
23
|
+
- SOLR_MODULES=analysis-extras,extraction
|
24
|
+
ports:
|
25
|
+
- '8985:8983'
|
26
|
+
|
data/lib/speedy_af/base.rb
CHANGED
@@ -42,11 +42,11 @@ module SpeedyAF
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def find(id, opts = {})
|
45
|
-
where(%(id:"#{id}"), opts).first
|
45
|
+
where(%(id:"#{id}"), opts.merge(rows: 1)).first
|
46
46
|
end
|
47
47
|
|
48
48
|
def where(query, opts = {})
|
49
|
-
docs = ActiveFedora::SolrService.query(query, rows: SOLR_ALL)
|
49
|
+
docs = ActiveFedora::SolrService.query(query, rows: opts[:rows] || SOLR_ALL)
|
50
50
|
from(docs, opts)
|
51
51
|
end
|
52
52
|
|
@@ -90,9 +90,12 @@ module SpeedyAF
|
|
90
90
|
reflections
|
91
91
|
end
|
92
92
|
|
93
|
-
def query_for_belongs_to(proxy_hash,
|
93
|
+
def query_for_belongs_to(proxy_hash, opts)
|
94
94
|
proxy_hash.collect do |_id, proxy|
|
95
|
-
proxy.belongs_to_reflections.collect
|
95
|
+
proxy.belongs_to_reflections(only: opts[:load_reflections]).collect do |_name, reflection|
|
96
|
+
ids = proxy.attrs[predicate_for_reflection(reflection).to_sym]
|
97
|
+
ids.blank? ? nil : Array(ids).collect { |id| "id:#{id}" }.join(" OR ")
|
98
|
+
end.compact
|
96
99
|
end.flatten.join(" OR ")
|
97
100
|
end
|
98
101
|
|
@@ -100,7 +103,7 @@ module SpeedyAF
|
|
100
103
|
hash = {}
|
101
104
|
proxy_hash.each do |proxy_id, proxy|
|
102
105
|
proxy.belongs_to_reflections.each do |name, reflection|
|
103
|
-
doc = docs.find { |d|
|
106
|
+
doc = docs.find { |d| proxy.attrs[predicate_for_reflection(reflection).to_sym].include?(d.id) && d["has_model_ssim"].include?(reflection.class_name) }
|
104
107
|
next unless doc
|
105
108
|
hash[proxy_id] ||= {}
|
106
109
|
hash[proxy_id][name] = doc
|
@@ -110,9 +113,13 @@ module SpeedyAF
|
|
110
113
|
hash
|
111
114
|
end
|
112
115
|
|
113
|
-
def query_for_has_many(proxy_hash,
|
116
|
+
def query_for_has_many(proxy_hash, opts)
|
114
117
|
proxy_hash.collect do |id, proxy|
|
115
|
-
proxy.has_many_reflections.collect
|
118
|
+
proxy.has_many_reflections(only: opts[:load_reflections]).collect do |_name, reflection|
|
119
|
+
query = "#{predicate_for_reflection(reflection)}_ssim:#{id}"
|
120
|
+
query = "(#{query} AND has_model_ssim:#{reflection.class_name})"
|
121
|
+
query
|
122
|
+
end
|
116
123
|
end.flatten.join(" OR ")
|
117
124
|
end
|
118
125
|
|
@@ -122,7 +129,7 @@ module SpeedyAF
|
|
122
129
|
docs.each do |doc|
|
123
130
|
has_many_reflections.each do |name, reflection|
|
124
131
|
Array(doc["#{predicate_for_reflection(reflection)}_ssim"]).each do |proxy_id|
|
125
|
-
next unless proxy_hash.key?(proxy_id)
|
132
|
+
next unless proxy_hash.key?(proxy_id) && doc["has_model_ssim"].include?(reflection.class_name)
|
126
133
|
hash[proxy_id] ||= {}
|
127
134
|
hash[proxy_id][name] ||= []
|
128
135
|
hash[proxy_id][name] << doc
|
@@ -134,9 +141,9 @@ module SpeedyAF
|
|
134
141
|
hash
|
135
142
|
end
|
136
143
|
|
137
|
-
def query_for_subresources(proxy_hash,
|
144
|
+
def query_for_subresources(proxy_hash, opts)
|
138
145
|
proxy_hash.collect do |id, proxy|
|
139
|
-
proxy.subresource_reflections.collect { |name, _reflection| "id:#{id}/#{name}" }
|
146
|
+
proxy.subresource_reflections(only: opts[:load_reflections]).collect { |name, _reflection| "id:#{id}/#{name}" }
|
140
147
|
end.flatten.join(" OR ")
|
141
148
|
end
|
142
149
|
|
@@ -196,7 +203,6 @@ module SpeedyAF
|
|
196
203
|
|
197
204
|
def method_missing(sym, *args)
|
198
205
|
return real_object.send(sym, *args) if real?
|
199
|
-
|
200
206
|
if @attrs.key?(sym)
|
201
207
|
# Lazy convert the solr document into a speedy_af proxy object
|
202
208
|
@attrs[sym] = SpeedyAF::Base.for(@attrs[sym]) if @attrs[sym].is_a?(ActiveFedora::SolrHit)
|
@@ -220,18 +226,33 @@ module SpeedyAF
|
|
220
226
|
super
|
221
227
|
end
|
222
228
|
|
223
|
-
def subresource_reflections
|
229
|
+
def subresource_reflections(only: nil)
|
224
230
|
SpeedyAF::Base.model_reflections[:subresource][model] ||= model.reflections.select { |_name, reflection| reflection.is_a? ActiveFedora::Reflection::HasSubresourceReflection }
|
231
|
+
if only.is_a?(Array) && only.present?
|
232
|
+
SpeedyAF::Base.model_reflections[:subresource][model].select { |name, _reflection| only.include? name }
|
233
|
+
else
|
234
|
+
SpeedyAF::Base.model_reflections[:subresource][model]
|
235
|
+
end
|
225
236
|
end
|
226
237
|
|
227
238
|
# rubocop:disable Naming/PredicateName
|
228
|
-
def has_many_reflections
|
239
|
+
def has_many_reflections(only: nil)
|
229
240
|
SpeedyAF::Base.model_reflections[:has_many][model] ||= model.reflections.select { |_name, reflection| reflection.has_many? && reflection.respond_to?(:predicate_for_solr) }
|
241
|
+
if only.is_a?(Array) && only.present?
|
242
|
+
SpeedyAF::Base.model_reflections[:has_many][model].select { |name, _reflection| only.include? name }
|
243
|
+
else
|
244
|
+
SpeedyAF::Base.model_reflections[:has_many][model]
|
245
|
+
end
|
230
246
|
end
|
231
247
|
# rubocop:enable Naming/PredicateName
|
232
248
|
|
233
|
-
def belongs_to_reflections
|
249
|
+
def belongs_to_reflections(only: nil)
|
234
250
|
SpeedyAF::Base.model_reflections[:belongs_to][model] ||= model.reflections.select { |_name, reflection| reflection.belongs_to? && reflection.respond_to?(:predicate_for_solr) }
|
251
|
+
if only.is_a?(Array) && only.present?
|
252
|
+
SpeedyAF::Base.model_reflections[:belongs_to][model].select { |name, _reflection| only.include? name }
|
253
|
+
else
|
254
|
+
SpeedyAF::Base.model_reflections[:belongs_to][model]
|
255
|
+
end
|
235
256
|
end
|
236
257
|
|
237
258
|
protected
|
@@ -257,7 +278,7 @@ module SpeedyAF
|
|
257
278
|
attr_name, type, _stored, _indexed, _multi = k.scan(/^(.+)_(.+)(s)(i?)(m?)$/).first
|
258
279
|
return [k, v] if attr_name.nil?
|
259
280
|
value = Array(v).map { |m| transforms.fetch(type, transforms[nil]).call(m) }
|
260
|
-
value = value.first unless @model.respond_to?(:properties) && multiple?(@model.properties[attr_name])
|
281
|
+
value = value.first unless reflection_solr_fields(@model).include?(attr_name) || (@model.respond_to?(:properties) && multiple?(@model.properties[attr_name]))
|
261
282
|
[attr_name, value]
|
262
283
|
end
|
263
284
|
|
@@ -265,10 +286,15 @@ module SpeedyAF
|
|
265
286
|
prop.present? && prop.respond_to?(:multiple?) && prop.multiple?
|
266
287
|
end
|
267
288
|
|
289
|
+
def reflection_solr_fields(model)
|
290
|
+
return [] unless model.respond_to?(:reflections)
|
291
|
+
model.reflections.values.select { |v| v.belongs_to? && v.respond_to?(:predicate_for_solr) }.collect(&:predicate_for_solr)
|
292
|
+
end
|
293
|
+
|
268
294
|
def load_from_reflection(reflection, ids_only = false)
|
269
295
|
return load_through_reflection(reflection, ids_only) if reflection.options.key?(:through)
|
270
|
-
return load_belongs_to_reflection(reflection
|
271
|
-
return load_has_many_reflection(reflection
|
296
|
+
return load_belongs_to_reflection(reflection, ids_only) if reflection.belongs_to? && reflection.respond_to?(:predicate_for_solr)
|
297
|
+
return load_has_many_reflection(reflection, ids_only) if reflection.has_many? && reflection.respond_to?(:predicate_for_solr)
|
272
298
|
return load_subresource_content(reflection) if reflection.is_a?(ActiveFedora::Reflection::HasSubresourceReflection)
|
273
299
|
# :nocov:
|
274
300
|
raise NotAvailable, "`#{reflection.name}' cannot be quick-loaded. Falling back to model."
|
@@ -299,14 +325,18 @@ module SpeedyAF
|
|
299
325
|
ids
|
300
326
|
end
|
301
327
|
|
302
|
-
def load_belongs_to_reflection(
|
303
|
-
id = @attrs[
|
304
|
-
return id if ids_only
|
305
|
-
|
328
|
+
def load_belongs_to_reflection(reflection, ids_only = false)
|
329
|
+
id = @attrs[reflection.predicate_for_solr.to_sym]
|
330
|
+
return id.first if ids_only && Array(id).size == 1
|
331
|
+
return if id.blank?
|
332
|
+
query = Array(id).collect { |i| "id:#{i}" }.join(" OR ")
|
333
|
+
query = "(#{query}) AND has_model_ssim:#{reflection.class_name}"
|
334
|
+
obj = Base.where(query, rows: 1).first
|
335
|
+
ids_only ? obj&.id : obj
|
306
336
|
end
|
307
337
|
|
308
|
-
def load_has_many_reflection(
|
309
|
-
query =
|
338
|
+
def load_has_many_reflection(reflection, ids_only = false)
|
339
|
+
query = "#{reflection.predicate_for_solr}_ssim:#{id} AND has_model_ssim:#{reflection.class_name}"
|
310
340
|
return Base.where(query) unless ids_only
|
311
341
|
docs = ActiveFedora::SolrService.query query, rows: SOLR_ALL
|
312
342
|
docs.collect { |doc| doc['id'] }
|
data/lib/speedy_af/version.rb
CHANGED
data/spec/fixture_classes.rb
CHANGED
@@ -3,6 +3,10 @@ class IndexedFile < ActiveFedora::File
|
|
3
3
|
include SpeedyAF::IndexedContent
|
4
4
|
end
|
5
5
|
|
6
|
+
class SampleResource < ActiveFedora::File
|
7
|
+
include SpeedyAF::IndexedContent
|
8
|
+
end
|
9
|
+
|
6
10
|
class Chapter < ActiveFedora::Base
|
7
11
|
property :title, predicate: ::RDF::Vocab::DC.title, multiple: false do |index|
|
8
12
|
index.as :stored_searchable
|
@@ -12,18 +16,23 @@ class Chapter < ActiveFedora::Base
|
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
15
|
-
module
|
19
|
+
module TitleBehavior
|
16
20
|
def lowercase_title
|
17
21
|
title.downcase
|
18
22
|
end
|
23
|
+
|
24
|
+
def uppercase_title
|
25
|
+
title.upcase
|
26
|
+
end
|
19
27
|
end
|
20
28
|
|
21
29
|
class Book < ActiveFedora::Base
|
22
|
-
include
|
30
|
+
include TitleBehavior
|
23
31
|
include SpeedyAF::OrderedAggregationIndex
|
24
32
|
|
25
33
|
belongs_to :library, predicate: ::RDF::Vocab::DC.isPartOf
|
26
34
|
has_subresource 'indexed_file', class_name: 'IndexedFile'
|
35
|
+
has_subresource 'descMetadata', class_name: 'SampleResource'
|
27
36
|
has_subresource 'unindexed_file', class_name: 'ActiveFedora::File'
|
28
37
|
property :title, predicate: ::RDF::Vocab::DC.title, multiple: false do |index|
|
29
38
|
index.as :stored_searchable
|
@@ -33,14 +42,28 @@ class Book < ActiveFedora::Base
|
|
33
42
|
end
|
34
43
|
ordered_aggregation :chapters, through: :list_source
|
35
44
|
indexed_ordered_aggregation :chapters
|
45
|
+
end
|
36
46
|
|
37
|
-
|
38
|
-
|
47
|
+
class Comic < ActiveFedora::Base
|
48
|
+
include TitleBehavior
|
49
|
+
|
50
|
+
belongs_to :library, predicate: ::RDF::Vocab::DC.isPartOf
|
51
|
+
belongs_to :comic_shop, predicate: ::RDF::Vocab::DC.isPartOf
|
52
|
+
property :title, predicate: ::RDF::Vocab::DC.title, multiple: false do |index|
|
53
|
+
index.as :stored_searchable
|
54
|
+
end
|
55
|
+
property :publisher, predicate: ::RDF::Vocab::DC.publisher, multiple: false do |index|
|
56
|
+
index.as :stored_searchable
|
39
57
|
end
|
40
58
|
end
|
41
59
|
|
42
60
|
class Library < ActiveFedora::Base
|
43
|
-
has_many :books, predicate: ::RDF::Vocab::DC.isPartOf
|
61
|
+
has_many :books, class_name: 'Book', predicate: ::RDF::Vocab::DC.isPartOf
|
62
|
+
has_many :comics, class_name: 'Comic', predicate: ::RDF::Vocab::DC.isPartOf
|
63
|
+
end
|
64
|
+
|
65
|
+
class ComicShop < ActiveFedora::Base
|
66
|
+
has_many :comics, predicate: ::RDF::Vocab::DC.isPartOf
|
44
67
|
end
|
45
68
|
|
46
69
|
module SpeedySpecs
|
@@ -3,11 +3,13 @@ require 'spec_helper'
|
|
3
3
|
require 'rdf/vocab/dc'
|
4
4
|
|
5
5
|
describe SpeedyAF::Base do
|
6
|
-
before { load_fixture_classes!
|
6
|
+
before { load_fixture_classes! }
|
7
7
|
after { unload_fixture_classes! }
|
8
8
|
|
9
9
|
let!(:library) { Library.create }
|
10
|
+
let!(:comic_shop) { ComicShop.create }
|
10
11
|
let!(:book) { Book.new title: 'Ordered Things', publisher: 'ActiveFedora Performance LLC', library: library }
|
12
|
+
let!(:comic) { Comic.new title: 'Vestibulum velit nulla', publisher: 'SpeedyAF LLC', library: library, comic_shop: comic_shop }
|
11
13
|
let!(:chapters) do
|
12
14
|
[
|
13
15
|
Chapter.create(title: 'Chapter 3', contributor: ['Hopper', 'Lovelace', 'Johnson']),
|
@@ -31,12 +33,22 @@ describe SpeedyAF::Base do
|
|
31
33
|
Nigh tofth eliv ingdead.
|
32
34
|
IPSUM
|
33
35
|
end
|
36
|
+
let!(:metadata) do
|
37
|
+
<<-IPSUM
|
38
|
+
In scelerisque volutpat rutrum. Phasellus dictum velit at orci luctus convallis. Nulla rutrum
|
39
|
+
eget libero at sodales. Suspendisse potenti. Donec ut lobortis mi, ut venenatis diam. Phasellus
|
40
|
+
mi felis, cursus at lacinia vitae, aliquam vitae eros. Suspendisse tincidunt a massa in
|
41
|
+
condimentum. Nulla finibus risus quam, vel elementum enim sodales at.
|
42
|
+
IPSUM
|
43
|
+
end
|
34
44
|
let(:book_presenter) { described_class.find(book.id) }
|
45
|
+
let(:comic_presenter) { described_class.find(comic.id) }
|
35
46
|
|
36
47
|
context 'lightweight presenter' do
|
37
48
|
before do
|
38
49
|
book.indexed_file.content = indexed_content
|
39
50
|
book.unindexed_file.content = unindexed_content
|
51
|
+
book.descMetadata.content = metadata
|
40
52
|
book.chapters = chapters
|
41
53
|
book.ordered_chapters = chapters.sort_by(&:title)
|
42
54
|
book.save!
|
@@ -61,11 +73,12 @@ describe SpeedyAF::Base do
|
|
61
73
|
end
|
62
74
|
|
63
75
|
it '.to_query' do
|
64
|
-
expect(book.to_query('book_id')).to eq("book_id=#{URI.
|
76
|
+
expect(book.to_query('book_id')).to eq("book_id=#{URI::Parser.new.escape(book.id, /[^\-_.!~*'()a-zA-Z\d;?:@&=+$,\[\]]/)}")
|
65
77
|
end
|
66
78
|
|
67
79
|
context 'reflections' do
|
68
80
|
let!(:library_presenter) { described_class.find(library.id) }
|
81
|
+
let!(:comic_shop_presenter) { described_class.find(comic_shop.id) }
|
69
82
|
|
70
83
|
it 'loads via indexed proxies' do
|
71
84
|
expect(book_presenter.chapter_ids).to match_array(book.chapter_ids)
|
@@ -82,27 +95,47 @@ describe SpeedyAF::Base do
|
|
82
95
|
|
83
96
|
it 'loads indexed subresources' do
|
84
97
|
ipsum_presenter = book_presenter.indexed_file
|
98
|
+
lorem_presenter = book_presenter.descMetadata
|
85
99
|
expect(ipsum_presenter.model).to eq(IndexedFile)
|
100
|
+
expect(lorem_presenter.model).to eq(SampleResource)
|
86
101
|
expect(ipsum_presenter.content).to eq(indexed_content)
|
102
|
+
expect(lorem_presenter.content).to eq(metadata)
|
87
103
|
expect(book_presenter).not_to be_real
|
88
104
|
expect(ipsum_presenter).not_to be_real
|
105
|
+
expect(lorem_presenter).not_to be_real
|
89
106
|
end
|
90
107
|
|
91
108
|
it 'loads has_many reflections' do
|
92
109
|
library.books.create(title: 'Ordered Things II')
|
110
|
+
library.comics.create(title: 'Comet in Moominland')
|
93
111
|
library.save
|
94
|
-
|
95
|
-
expect(
|
96
|
-
expect(presenter.all? { |bp| bp.is_a?(described_class) }).to be_truthy
|
112
|
+
expect(library_presenter.books.length).to eq(2)
|
113
|
+
expect(library_presenter.books.all? { |bp| bp.is_a?(described_class) }).to be_truthy
|
97
114
|
expect(library_presenter.book_ids).to match_array(library.book_ids)
|
115
|
+
expect(library_presenter.comics.length).to eq(1)
|
116
|
+
expect(library_presenter.comics.all? { |bp| bp.is_a?(described_class) }).to be_truthy
|
117
|
+
expect(library_presenter.comic_ids).to match_array(library.comic_ids)
|
98
118
|
expect(library_presenter).not_to be_real
|
99
119
|
end
|
100
120
|
|
101
121
|
it 'loads belongs_to reflections' do
|
102
|
-
|
103
|
-
expect(
|
104
|
-
expect(
|
105
|
-
expect(
|
122
|
+
comic.save!
|
123
|
+
expect(comic_presenter.library_id).to eq(library.id)
|
124
|
+
expect(comic_presenter.library).to be_a(described_class)
|
125
|
+
expect(comic_presenter.library.model).to eq(library.class)
|
126
|
+
expect(comic_presenter.comic_shop_id).to eq(comic_shop.id)
|
127
|
+
expect(comic_presenter.comic_shop).to be_a(described_class)
|
128
|
+
expect(comic_presenter.comic_shop.model).to eq(comic_shop.class)
|
129
|
+
expect(comic_presenter).not_to be_real
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'missing parent id' do
|
133
|
+
let(:book) { Book.new title: 'Ordered Things', publisher: 'ActiveFedora Performance LLC', library: nil }
|
134
|
+
it 'does not cause belongs_to reflections to error' do
|
135
|
+
expect(book_presenter.library_id).to be_nil
|
136
|
+
expect(book_presenter.library).to be_nil
|
137
|
+
expect(book_presenter).not_to be_real
|
138
|
+
end
|
106
139
|
end
|
107
140
|
|
108
141
|
context 'preloaded subresources' do
|
@@ -120,15 +153,22 @@ describe SpeedyAF::Base do
|
|
120
153
|
|
121
154
|
it 'has already loaded has_many reflections' do
|
122
155
|
library.books.create(title: 'Ordered Things II')
|
156
|
+
library.comics.create(title: 'Comet in Moominland')
|
123
157
|
library.save
|
124
158
|
book_ids = library.book_ids
|
159
|
+
comic_ids = library.comic_ids
|
125
160
|
library_presenter = described_class.find(library.id, load_reflections: true)
|
126
161
|
expect(library_presenter.attrs).to include :books
|
162
|
+
expect(library_presenter.attrs).to include :comics
|
127
163
|
allow(ActiveFedora::SolrService).to receive(:query).and_call_original
|
128
|
-
|
129
|
-
|
130
|
-
expect(
|
164
|
+
books_presenter = library_presenter.books
|
165
|
+
comics_presenter = library_presenter.comics
|
166
|
+
expect(books_presenter.length).to eq(2)
|
167
|
+
expect(books_presenter.all? { |bp| bp.is_a?(described_class) }).to be_truthy
|
168
|
+
expect(comics_presenter.length).to eq(1)
|
169
|
+
expect(comics_presenter.all? { |bp| bp.is_a?(described_class) }).to be_truthy
|
131
170
|
expect(library_presenter.book_ids).to match_array(book_ids)
|
171
|
+
expect(library_presenter.comic_ids).to match_array(comic_ids)
|
132
172
|
expect(library_presenter).not_to be_real
|
133
173
|
expect(ActiveFedora::SolrService).not_to have_received(:query)
|
134
174
|
end
|
@@ -142,13 +182,61 @@ describe SpeedyAF::Base do
|
|
142
182
|
expect(book_presenter).not_to be_real
|
143
183
|
expect(ActiveFedora::SolrService).not_to have_received(:query)
|
144
184
|
end
|
185
|
+
|
186
|
+
context 'filtering' do
|
187
|
+
let(:book_presenter) { described_class.find(book.id, load_reflections: [:indexed_file]) }
|
188
|
+
let(:comic_presenter) { described_class.find(comic.id, load_reflections: [:comic_shop]) }
|
189
|
+
before { comic.save! }
|
190
|
+
|
191
|
+
it 'has already loaded filtered subresources' do
|
192
|
+
expect(book_presenter.attrs).to include :indexed_file
|
193
|
+
expect(book_presenter.attrs).to_not include :descMetadata
|
194
|
+
allow(ActiveFedora::SolrService).to receive(:query).and_call_original
|
195
|
+
ipsum_presenter = book_presenter.indexed_file
|
196
|
+
expect(ipsum_presenter.model).to eq(IndexedFile)
|
197
|
+
expect(ipsum_presenter.content).to eq(indexed_content)
|
198
|
+
expect(ipsum_presenter).not_to be_real
|
199
|
+
expect(ActiveFedora::SolrService).not_to have_received(:query)
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'has already loaded filtered has_many reflections' do
|
203
|
+
library.books.create(title: 'Ordered Things II')
|
204
|
+
library.save
|
205
|
+
book_ids = library.book_ids
|
206
|
+
library_presenter = described_class.find(library.id, load_reflections: [:books])
|
207
|
+
expect(library_presenter.attrs).to include :books
|
208
|
+
expect(library_presenter.attrs).to_not include :comics
|
209
|
+
allow(ActiveFedora::SolrService).to receive(:query).and_call_original
|
210
|
+
presenter = library_presenter.books
|
211
|
+
expect(presenter.length).to eq(2)
|
212
|
+
expect(presenter.all? { |bp| bp.is_a?(described_class) }).to be_truthy
|
213
|
+
expect(library_presenter.book_ids).to match_array(book_ids)
|
214
|
+
expect(library_presenter).not_to be_real
|
215
|
+
expect(ActiveFedora::SolrService).not_to have_received(:query)
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'has already loaded belongs_to reflections and does not filter' do
|
219
|
+
expect(comic_presenter.attrs).to include :comic_shop
|
220
|
+
expect(comic_presenter.attrs).to include :library
|
221
|
+
allow(ActiveFedora::SolrService).to receive(:query).and_call_original
|
222
|
+
expect(comic_presenter.comic_shop_id).to eq(comic_shop.id)
|
223
|
+
expect(comic_presenter.comic_shop).to be_a(described_class)
|
224
|
+
expect(comic_presenter.comic_shop.model).to eq(comic_shop.class)
|
225
|
+
expect(comic_presenter).not_to be_real
|
226
|
+
expect(comic_presenter.library_id).to eq(library.id)
|
227
|
+
expect(comic_presenter.library).to be_a(described_class)
|
228
|
+
expect(comic_presenter.library.model).to eq(library.class)
|
229
|
+
expect(comic_presenter).not_to be_real
|
230
|
+
expect(ActiveFedora::SolrService).not_to have_received(:query)
|
231
|
+
end
|
232
|
+
end
|
145
233
|
end
|
146
234
|
end
|
147
235
|
|
148
236
|
context 'configuration' do
|
149
237
|
before do
|
150
238
|
described_class.config Book do
|
151
|
-
include
|
239
|
+
include TitleBehavior
|
152
240
|
self.defaults = { foo: 'bar!' }
|
153
241
|
end
|
154
242
|
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: speedy-af
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael B. Klein
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-08-06 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: active-fedora
|
@@ -196,6 +195,7 @@ files:
|
|
196
195
|
- LICENSE
|
197
196
|
- README.md
|
198
197
|
- Rakefile
|
198
|
+
- docker-compose.yml
|
199
199
|
- lib/speedy-af.rb
|
200
200
|
- lib/speedy_af.rb
|
201
201
|
- lib/speedy_af/base.rb
|
@@ -211,7 +211,6 @@ homepage: http://github.com/samvera-labs/speedy_af
|
|
211
211
|
licenses:
|
212
212
|
- APACHE2
|
213
213
|
metadata: {}
|
214
|
-
post_install_message:
|
215
214
|
rdoc_options: []
|
216
215
|
require_paths:
|
217
216
|
- lib
|
@@ -226,8 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
226
225
|
- !ruby/object:Gem::Version
|
227
226
|
version: '0'
|
228
227
|
requirements: []
|
229
|
-
rubygems_version: 3.
|
230
|
-
signing_key:
|
228
|
+
rubygems_version: 3.6.5
|
231
229
|
specification_version: 4
|
232
230
|
summary: Performance enhancements for ActiveFedora
|
233
231
|
test_files: []
|