speedy-af 0.2.0 → 0.3.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 +13 -5
- data/.rubocop_todo.yml +15 -2
- data/lib/speedy_af/base.rb +152 -37
- data/lib/speedy_af/version.rb +1 -1
- data/spec/integration/base_spec.rb +39 -0
- data/speedy-af.gemspec +1 -1
- metadata +2 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f90da5142141870db8a58733e7456af65d963a3de8ed26e40af1c1d23fba022a
|
4
|
+
data.tar.gz: a995000c4e447c04073f22884d7608c33fb9a6401d839ef613007818ebb4d648
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d07dc9fdfabc614b2ad7ba5960da463d102f07680626efa40e1446087ea027b243fe29ac28be4ad01866c5edbff3663ee9bc846297c3bd3815fc481dd7564b90
|
7
|
+
data.tar.gz: 445ce106eabd70703ad9b96ef3c91a657fd6500d82e8b7c9d8e3c52baf48030d01601fc21f95ac57a5832f721621db8aa535ddf2bac546381d2c0e0b9eacdb29
|
data/.circleci/config.yml
CHANGED
@@ -17,7 +17,7 @@ jobs:
|
|
17
17
|
executor:
|
18
18
|
name: 'samvera/ruby_fcrepo_solr'
|
19
19
|
ruby_version: << parameters.ruby_version >>
|
20
|
-
solr_version:
|
20
|
+
solr_version: 8-slim
|
21
21
|
environment:
|
22
22
|
RAILS_VERSION: << parameters.rails_version >>
|
23
23
|
working_directory: ~/project
|
@@ -37,11 +37,19 @@ jobs:
|
|
37
37
|
workflows:
|
38
38
|
ci:
|
39
39
|
jobs:
|
40
|
+
- bundle_and_test:
|
41
|
+
name: "ruby2-7_rails7-0"
|
42
|
+
ruby_version: "2.7.6"
|
43
|
+
rails_version: "7.0.4"
|
44
|
+
- bundle_and_test:
|
45
|
+
name: "ruby2-7_rails6-1"
|
46
|
+
ruby_version: "2.7.6"
|
47
|
+
rails_version: "6.1.7"
|
40
48
|
- bundle_and_test:
|
41
49
|
name: "ruby2-7_rails6-0"
|
42
|
-
ruby_version: "2.7.
|
43
|
-
rails_version: "6.0.
|
50
|
+
ruby_version: "2.7.6"
|
51
|
+
rails_version: "6.0.6"
|
44
52
|
- bundle_and_test:
|
45
53
|
name: "ruby2-7_rails5-2"
|
46
|
-
ruby_version: "2.7.
|
47
|
-
rails_version: "5.2.
|
54
|
+
ruby_version: "2.7.6"
|
55
|
+
rails_version: "5.2.8.1"
|
data/.rubocop_todo.yml
CHANGED
@@ -11,21 +11,34 @@
|
|
11
11
|
# ExcludedMethods: refine
|
12
12
|
Metrics/BlockLength:
|
13
13
|
Max: 143
|
14
|
+
Exclude:
|
15
|
+
- 'spec/**/*_spec.rb'
|
14
16
|
|
15
17
|
# Offense count: 1
|
16
18
|
# Configuration parameters: CountComments.
|
17
19
|
Metrics/ClassLength:
|
18
20
|
Max: 165
|
21
|
+
Exclude:
|
22
|
+
- 'lib/speedy_af/base.rb'
|
19
23
|
|
20
24
|
# Offense count: 1
|
21
25
|
# Configuration parameters: IgnoredMethods.
|
22
26
|
Metrics/CyclomaticComplexity:
|
23
|
-
Max:
|
27
|
+
Max: 11
|
28
|
+
|
29
|
+
# Offense count: 1
|
30
|
+
# Configuration parameters: IgnoredMethods.
|
31
|
+
Metrics/PerceivedComplexity:
|
32
|
+
Max: 11
|
33
|
+
|
34
|
+
# Offense count: 1
|
35
|
+
Metrics/AbcSize:
|
36
|
+
Max: 40
|
24
37
|
|
25
38
|
# Offense count: 1
|
26
39
|
# Configuration parameters: CountComments, ExcludedMethods.
|
27
40
|
Metrics/MethodLength:
|
28
|
-
Max:
|
41
|
+
Max: 20
|
29
42
|
|
30
43
|
# Offense count: 1
|
31
44
|
# Configuration parameters: ExpectMatchingDefinition, CheckDefinitionPathHierarchy, Regex, IgnoreExecutableScripts, AllowedAcronyms.
|
data/lib/speedy_af/base.rb
CHANGED
@@ -7,55 +7,151 @@ module SpeedyAF
|
|
7
7
|
|
8
8
|
SOLR_ALL = 10_000_000
|
9
9
|
|
10
|
+
class_attribute :model_reflections, :reflection_predicates
|
11
|
+
SpeedyAF::Base.model_reflections = { belongs_to: {}, has_many: {}, subresource: {} }
|
12
|
+
SpeedyAF::Base.reflection_predicates = {}
|
13
|
+
|
10
14
|
attr_reader :attrs, :model
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
class << self
|
17
|
+
def defaults
|
18
|
+
@defaults ||= {}
|
19
|
+
end
|
15
20
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
21
|
+
def defaults=(value)
|
22
|
+
raise ArgumentError unless value.respond_to?(:merge)
|
23
|
+
@defaults = value
|
24
|
+
end
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
def proxy_class_for(model)
|
27
|
+
klass = "::SpeedyAF::Proxy::#{model.name}".safe_constantize
|
28
|
+
if klass.nil?
|
29
|
+
namespace = model.name.deconstantize
|
30
|
+
name = model.name.demodulize
|
31
|
+
klass_module = namespace.split(/::/).inject(::SpeedyAF::Proxy) do |mod, ns|
|
32
|
+
mod.const_defined?(ns, false) ? mod.const_get(ns, false) : mod.const_set(ns, Module.new)
|
33
|
+
end
|
34
|
+
klass = klass_module.const_set(name, Class.new(self))
|
28
35
|
end
|
29
|
-
klass
|
36
|
+
klass
|
30
37
|
end
|
31
|
-
klass
|
32
|
-
end
|
33
38
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
39
|
+
def config(model, &block)
|
40
|
+
proxy_class = proxy_class_for(model) { Class.new(self) }
|
41
|
+
proxy_class.class_eval(&block) if block_given?
|
42
|
+
end
|
38
43
|
|
39
|
-
|
40
|
-
|
41
|
-
|
44
|
+
def find(id, opts = {})
|
45
|
+
where(%(id:"#{id}"), opts).first
|
46
|
+
end
|
42
47
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
48
|
+
def where(query, opts = {})
|
49
|
+
docs = ActiveFedora::SolrService.query(query, rows: SOLR_ALL)
|
50
|
+
from(docs, opts)
|
51
|
+
end
|
47
52
|
|
48
|
-
|
49
|
-
hash = docs.each_with_object({}) do |doc, h|
|
53
|
+
def for(doc, opts = {})
|
50
54
|
proxy = proxy_class_for(model_for(doc))
|
51
|
-
|
55
|
+
proxy.new(doc, opts[:defaults])
|
52
56
|
end
|
53
|
-
return hash.values if opts[:order].nil?
|
54
|
-
opts[:order].call.collect { |id| hash[id] }.to_a
|
55
|
-
end
|
56
57
|
|
57
|
-
|
58
|
-
|
58
|
+
def from(docs, opts = {})
|
59
|
+
hash = docs.each_with_object({}) do |doc, h|
|
60
|
+
h[doc['id']] = self.for(doc, opts)
|
61
|
+
end
|
62
|
+
|
63
|
+
if opts[:load_reflections]
|
64
|
+
reflections_hash = gather_reflections(hash, opts)
|
65
|
+
reflections_hash.each { |parent_id, reflections| hash[parent_id].attrs.merge!(reflections) }
|
66
|
+
end
|
67
|
+
|
68
|
+
return hash.values if opts[:order].nil?
|
69
|
+
opts[:order].call.collect { |id| hash[id] }.to_a
|
70
|
+
end
|
71
|
+
|
72
|
+
def model_for(solr_document)
|
73
|
+
solr_document[:has_model_ssim].first.safe_constantize
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
def predicate_for_reflection(reflection)
|
79
|
+
SpeedyAF::Base.reflection_predicates[reflection.name] ||= reflection.predicate_for_solr
|
80
|
+
end
|
81
|
+
|
82
|
+
def gather_reflections(proxy_hash, opts)
|
83
|
+
query = [query_for_belongs_to(proxy_hash, opts), query_for_has_many(proxy_hash, opts), query_for_subresources(proxy_hash, opts)].reject(&:blank?).join(" OR ")
|
84
|
+
docs = ActiveFedora::SolrService.query query, rows: SOLR_ALL
|
85
|
+
|
86
|
+
reflections = {}
|
87
|
+
reflections.deep_merge!(gather_belongs_to(docs, proxy_hash, opts))
|
88
|
+
reflections.deep_merge!(gather_has_many(docs, proxy_hash, opts))
|
89
|
+
reflections.deep_merge!(gather_subresources(docs, proxy_hash, opts))
|
90
|
+
reflections
|
91
|
+
end
|
92
|
+
|
93
|
+
def query_for_belongs_to(proxy_hash, _opts)
|
94
|
+
proxy_hash.collect do |_id, proxy|
|
95
|
+
proxy.belongs_to_reflections.collect { |_name, reflection| "id:#{proxy.attrs[predicate_for_reflection(reflection).to_sym]}" }
|
96
|
+
end.flatten.join(" OR ")
|
97
|
+
end
|
98
|
+
|
99
|
+
def gather_belongs_to(docs, proxy_hash, _opts)
|
100
|
+
hash = {}
|
101
|
+
proxy_hash.each do |proxy_id, proxy|
|
102
|
+
proxy.belongs_to_reflections.each do |name, reflection|
|
103
|
+
doc = docs.find { |d| d.id == proxy.attrs[predicate_for_reflection(reflection).to_sym] }
|
104
|
+
next unless doc
|
105
|
+
hash[proxy_id] ||= {}
|
106
|
+
hash[proxy_id][name] = doc
|
107
|
+
hash[proxy_id]["#{name}_id".to_sym] = doc.id
|
108
|
+
end
|
109
|
+
end
|
110
|
+
hash
|
111
|
+
end
|
112
|
+
|
113
|
+
def query_for_has_many(proxy_hash, _opts)
|
114
|
+
proxy_hash.collect do |id, proxy|
|
115
|
+
proxy.has_many_reflections.collect { |_name, reflection| "#{predicate_for_reflection(reflection)}_ssim:#{id}" }
|
116
|
+
end.flatten.join(" OR ")
|
117
|
+
end
|
118
|
+
|
119
|
+
def gather_has_many(docs, proxy_hash, _opts)
|
120
|
+
hash = {}
|
121
|
+
has_many_reflections = SpeedyAF::Base.model_reflections[:has_many].values.reduce(:merge)
|
122
|
+
docs.each do |doc|
|
123
|
+
has_many_reflections.each do |name, reflection|
|
124
|
+
Array(doc["#{predicate_for_reflection(reflection)}_ssim"]).each do |proxy_id|
|
125
|
+
next unless proxy_hash.key?(proxy_id)
|
126
|
+
hash[proxy_id] ||= {}
|
127
|
+
hash[proxy_id][name] ||= []
|
128
|
+
hash[proxy_id][name] << doc
|
129
|
+
hash[proxy_id]["#{name.to_s.singularize}_ids".to_sym] ||= []
|
130
|
+
hash[proxy_id]["#{name.to_s.singularize}_ids".to_sym] << doc.id
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
hash
|
135
|
+
end
|
136
|
+
|
137
|
+
def query_for_subresources(proxy_hash, _opts)
|
138
|
+
proxy_hash.collect do |id, proxy|
|
139
|
+
proxy.subresource_reflections.collect { |name, _reflection| "id:#{id}/#{name}" }
|
140
|
+
end.flatten.join(" OR ")
|
141
|
+
end
|
142
|
+
|
143
|
+
def gather_subresources(docs, proxy_hash, _opts)
|
144
|
+
docs.each_with_object({}) do |doc, hash|
|
145
|
+
doc_id = doc.id
|
146
|
+
parent_id = proxy_hash.keys.find { |id| doc_id.start_with? id }
|
147
|
+
next unless parent_id
|
148
|
+
subresource_id = proxy_hash[parent_id].subresource_reflections.keys.find { |name| doc_id == "#{parent_id}/#{name}" }
|
149
|
+
next unless subresource_id
|
150
|
+
hash[parent_id] ||= {}
|
151
|
+
hash[parent_id][subresource_id.to_sym] = doc
|
152
|
+
hash[parent_id]["#{subresource_id}_id".to_sym] = doc_id
|
153
|
+
end
|
154
|
+
end
|
59
155
|
end
|
60
156
|
|
61
157
|
def initialize(solr_document, instance_defaults = {})
|
@@ -101,7 +197,12 @@ module SpeedyAF
|
|
101
197
|
def method_missing(sym, *args)
|
102
198
|
return real_object.send(sym, *args) if real?
|
103
199
|
|
104
|
-
|
200
|
+
if @attrs.key?(sym)
|
201
|
+
# Lazy convert the solr document into a speedy_af proxy object
|
202
|
+
@attrs[sym] = SpeedyAF::Base.for(@attrs[sym]) if @attrs[sym].is_a?(ActiveFedora::SolrHit)
|
203
|
+
@attrs[sym] = @attrs[sym].map { |doc| SpeedyAF::Base.for(doc) } if @attrs[sym].is_a?(Array) && @attrs[sym].all? { |d| d.is_a?(ActiveFedora::SolrHit) }
|
204
|
+
return @attrs[sym]
|
205
|
+
end
|
105
206
|
|
106
207
|
reflection = reflection_for(sym)
|
107
208
|
unless reflection.nil?
|
@@ -119,6 +220,20 @@ module SpeedyAF
|
|
119
220
|
super
|
120
221
|
end
|
121
222
|
|
223
|
+
def subresource_reflections
|
224
|
+
SpeedyAF::Base.model_reflections[:subresource][model] ||= model.reflections.select { |_name, reflection| reflection.is_a? ActiveFedora::Reflection::HasSubresourceReflection }
|
225
|
+
end
|
226
|
+
|
227
|
+
# rubocop:disable Naming/PredicateName
|
228
|
+
def has_many_reflections
|
229
|
+
SpeedyAF::Base.model_reflections[:has_many][model] ||= model.reflections.select { |_name, reflection| reflection.has_many? && reflection.respond_to?(:predicate_for_solr) }
|
230
|
+
end
|
231
|
+
# rubocop:enable Naming/PredicateName
|
232
|
+
|
233
|
+
def belongs_to_reflections
|
234
|
+
SpeedyAF::Base.model_reflections[:belongs_to][model] ||= model.reflections.select { |_name, reflection| reflection.belongs_to? && reflection.respond_to?(:predicate_for_solr) }
|
235
|
+
end
|
236
|
+
|
122
237
|
protected
|
123
238
|
|
124
239
|
def reflection_for(sym)
|
data/lib/speedy_af/version.rb
CHANGED
@@ -104,6 +104,45 @@ describe SpeedyAF::Base do
|
|
104
104
|
expect(book_presenter.library.model).to eq(library.class)
|
105
105
|
expect(book_presenter).not_to be_real
|
106
106
|
end
|
107
|
+
|
108
|
+
context 'preloaded subresources' do
|
109
|
+
let(:book_presenter) { described_class.find(book.id, load_reflections: true) }
|
110
|
+
|
111
|
+
it 'has already loaded indexed subresources' do
|
112
|
+
expect(book_presenter.attrs).to include :indexed_file
|
113
|
+
allow(ActiveFedora::SolrService).to receive(:query).and_call_original
|
114
|
+
ipsum_presenter = book_presenter.indexed_file
|
115
|
+
expect(ipsum_presenter.model).to eq(IndexedFile)
|
116
|
+
expect(ipsum_presenter.content).to eq(indexed_content)
|
117
|
+
expect(ipsum_presenter).not_to be_real
|
118
|
+
expect(ActiveFedora::SolrService).not_to have_received(:query)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'has already loaded has_many reflections' do
|
122
|
+
library.books.create(title: 'Ordered Things II')
|
123
|
+
library.save
|
124
|
+
book_ids = library.book_ids
|
125
|
+
library_presenter = described_class.find(library.id, load_reflections: true)
|
126
|
+
expect(library_presenter.attrs).to include :books
|
127
|
+
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
|
131
|
+
expect(library_presenter.book_ids).to match_array(book_ids)
|
132
|
+
expect(library_presenter).not_to be_real
|
133
|
+
expect(ActiveFedora::SolrService).not_to have_received(:query)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'has already loaded belongs_to reflections' do
|
137
|
+
expect(book_presenter.attrs).to include :library
|
138
|
+
allow(ActiveFedora::SolrService).to receive(:query).and_call_original
|
139
|
+
expect(book_presenter.library_id).to eq(library.id)
|
140
|
+
expect(book_presenter.library).to be_a(described_class)
|
141
|
+
expect(book_presenter.library.model).to eq(library.class)
|
142
|
+
expect(book_presenter).not_to be_real
|
143
|
+
expect(ActiveFedora::SolrService).not_to have_received(:query)
|
144
|
+
end
|
145
|
+
end
|
107
146
|
end
|
108
147
|
|
109
148
|
context 'configuration' do
|
data/speedy-af.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
25
25
|
|
26
26
|
s.add_dependency 'active-fedora', '>= 11.0.0'
|
27
|
-
s.add_dependency 'activesupport', '> 5.2'
|
27
|
+
s.add_dependency 'activesupport', '> 5.2'
|
28
28
|
s.add_development_dependency 'solr_wrapper'
|
29
29
|
s.add_development_dependency 'fcrepo_wrapper'
|
30
30
|
s.add_development_dependency 'simplecov'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: speedy-af
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael B. Klein
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: active-fedora
|
@@ -31,9 +31,6 @@ dependencies:
|
|
31
31
|
- - ">"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '5.2'
|
34
|
-
- - "<"
|
35
|
-
- !ruby/object:Gem::Version
|
36
|
-
version: '6.1'
|
37
34
|
type: :runtime
|
38
35
|
prerelease: false
|
39
36
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -41,9 +38,6 @@ dependencies:
|
|
41
38
|
- - ">"
|
42
39
|
- !ruby/object:Gem::Version
|
43
40
|
version: '5.2'
|
44
|
-
- - "<"
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '6.1'
|
47
41
|
- !ruby/object:Gem::Dependency
|
48
42
|
name: solr_wrapper
|
49
43
|
requirement: !ruby/object:Gem::Requirement
|