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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f90da5142141870db8a58733e7456af65d963a3de8ed26e40af1c1d23fba022a
4
- data.tar.gz: a995000c4e447c04073f22884d7608c33fb9a6401d839ef613007818ebb4d648
3
+ metadata.gz: 0ff0f3d554f80151433f700cfae02d61d3c271fbcfb1cefe132d8a9e0e518734
4
+ data.tar.gz: 01433ba5beca3308fa708191c76a2be9df2b5682be1ae41a3dc08f6ad6dd42af
5
5
  SHA512:
6
- metadata.gz: d07dc9fdfabc614b2ad7ba5960da463d102f07680626efa40e1446087ea027b243fe29ac28be4ad01866c5edbff3663ee9bc846297c3bd3815fc481dd7564b90
7
- data.tar.gz: 445ce106eabd70703ad9b96ef3c91a657fd6500d82e8b7c9d8e3c52baf48030d01601fc21f95ac57a5832f721621db8aa535ddf2bac546381d2c0e0b9eacdb29
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.0.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: "ruby2-7_rails7-0"
42
- ruby_version: "2.7.6"
43
- rails_version: "7.0.4"
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: "ruby2-7_rails6-1"
46
- ruby_version: "2.7.6"
47
- rails_version: "6.1.7"
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: "ruby2-7_rails6-0"
50
- ruby_version: "2.7.6"
51
- rails_version: "6.0.6"
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: "ruby2-7_rails5-2"
54
- ruby_version: "2.7.6"
55
- rails_version: "5.2.8.1"
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|
@@ -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
+
@@ -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, _opts)
93
+ def query_for_belongs_to(proxy_hash, opts)
94
94
  proxy_hash.collect do |_id, proxy|
95
- proxy.belongs_to_reflections.collect { |_name, reflection| "id:#{proxy.attrs[predicate_for_reflection(reflection).to_sym]}" }
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| d.id == proxy.attrs[predicate_for_reflection(reflection).to_sym] }
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, _opts)
116
+ def query_for_has_many(proxy_hash, opts)
114
117
  proxy_hash.collect do |id, proxy|
115
- proxy.has_many_reflections.collect { |_name, reflection| "#{predicate_for_reflection(reflection)}_ssim:#{id}" }
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, _opts)
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.predicate_for_solr, ids_only) if reflection.belongs_to? && reflection.respond_to?(:predicate_for_solr)
271
- return load_has_many_reflection(reflection.predicate_for_solr, ids_only) if reflection.has_many? && reflection.respond_to?(:predicate_for_solr)
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(predicate, ids_only = false)
303
- id = @attrs[predicate.to_sym]
304
- return id if ids_only
305
- Base.find(id)
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(predicate, ids_only = false)
309
- query = %(#{predicate}_ssim:#{id})
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'] }
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module SpeedyAF
3
- VERSION = '0.3.0'
3
+ VERSION = '0.5.0'
4
4
  end
@@ -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 DowncaseBehavior
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 DowncaseBehavior
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
- def uppercase_title
38
- title.upcase
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.encode(book.id, /[^\-_.!~*'()a-zA-Z\d;?:@&=+$,\[\]]/)}")
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
- presenter = library_presenter.books
95
- expect(presenter.length).to eq(2)
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
- expect(book_presenter.library_id).to eq(library.id)
103
- expect(book_presenter.library).to be_a(described_class)
104
- expect(book_presenter.library.model).to eq(library.class)
105
- expect(book_presenter).not_to be_real
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
- presenter = library_presenter.books
129
- expect(presenter.length).to eq(2)
130
- expect(presenter.all? { |bp| bp.is_a?(described_class) }).to be_truthy
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 DowncaseBehavior
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.3.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: 2022-10-18 00:00:00.000000000 Z
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.1.6
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: []