valkyrie 1.2.1 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2f8a25a4907164098b326abda6efecd2e8312c44
4
- data.tar.gz: 527a724741b2be29ae313179b0d9d44acee1588c
2
+ SHA256:
3
+ metadata.gz: f665fe8da6906565cf3773a62c028e0528d62074c50fd109bbd6dd438196f4fb
4
+ data.tar.gz: f0db72ec609cfa390a1cdaef4e03d8dd0834c25e51cbac8845a061dda7458281
5
5
  SHA512:
6
- metadata.gz: 3e5d8ab1685035c009f3aece230c97189f51c5382e33bd45677afaf8464da3893fab299ea3c3cd7a35d71943db4c292379ac9531dbbf6f33df4c7e51bf6ddb9e
7
- data.tar.gz: 532b506eed5ffd247c6dca88d01893190a56f5dcdd88ad6d38775ff93de8e2aa647d3397006d9aab7be4bba610fafae5523707c3135218d8b0a312fdbe236d56
6
+ metadata.gz: 797105358eece5769534d6c62cfd6599d1d3ceeb66e36b0a9ded3750f266fae69d57ec0e8929cc33b579066e7ecad862a249510c1fac551093723cfdbe66a711
7
+ data.tar.gz: 96ac6d47efa9dc4d00235ac4e0ffb00a63141f13ab89bc622bc0a7bd6161200df7622e6e03419840b3ae003a3f53b045ca8e33737c72ddd00e419e66927aafc0
@@ -1,6 +1,7 @@
1
1
  Metrics/ClassLength:
2
2
  Exclude:
3
3
  - 'lib/valkyrie/persistence/fedora/persister.rb'
4
+ - 'lib/valkyrie/persistence/fedora/query_service.rb'
4
5
  - 'lib/valkyrie/persistence/postgres/query_service.rb'
5
6
 
6
7
  Metrics/MethodLength:
@@ -1,3 +1,10 @@
1
+ # v1.2.2 2018-10-05
2
+
3
+ ## Changes since last release
4
+
5
+ * Fix consistency in adapter's responses to queries.
6
+ [ojlyytinen](https://github.com/ojlyytinen)
7
+
1
8
  # v1.2.1 2018-09-25
2
9
 
3
10
  ## Changes since last release
@@ -34,10 +34,20 @@ module Valkyrie
34
34
  require 'valkyrie/engine' if defined?(Rails)
35
35
  def config
36
36
  @config ||= Config.new(
37
- YAML.safe_load(ERB.new(File.read(config_root_path.join("config", "valkyrie.yml"))).result)[environment]
37
+ config_hash
38
38
  )
39
39
  end
40
40
 
41
+ def config_file
42
+ return unless File.exist?(config_root_path.join("config", "valkyrie.yml"))
43
+ File.read(config_root_path.join("config", "valkyrie.yml"))
44
+ end
45
+
46
+ def config_hash
47
+ return {} unless config_file
48
+ YAML.safe_load(ERB.new(config_file).result)[environment]
49
+ end
50
+
41
51
  def environment
42
52
  Rails.env
43
53
  end
@@ -58,7 +68,16 @@ module Valkyrie
58
68
  @logger = logger
59
69
  end
60
70
 
71
+ def warn_about_standard_queries!
72
+ warn "[DEPRECATION] Please enable query normalization to avoid inconsistent results between different adapters by adding `standardize_query_results: true` to your environment block" \
73
+ " in config\/valkyrie.yml. This will be the behavior in Valkyrie 2.0."
74
+ end
75
+
61
76
  class Config < OpenStruct
77
+ def initialize(hsh = {})
78
+ super(defaults.merge(hsh))
79
+ end
80
+
62
81
  def metadata_adapter
63
82
  Valkyrie::MetadataAdapter.find(super.to_sym)
64
83
  end
@@ -66,7 +85,15 @@ module Valkyrie
66
85
  def storage_adapter
67
86
  Valkyrie::StorageAdapter.find(super.to_sym)
68
87
  end
88
+
89
+ private
90
+
91
+ def defaults
92
+ {
93
+ standardize_query_result: false
94
+ }
95
+ end
69
96
  end
70
97
 
71
- module_function :config, :logger, :logger=, :config_root_path, :environment
98
+ module_function :config, :logger, :logger=, :config_root_path, :environment, :warn_about_standard_queries!, :config_file, :config_hash
72
99
  end
@@ -73,5 +73,10 @@ module Valkyrie::Persistence::Fedora
73
73
  def connection_prefix
74
74
  "#{connection.http.url_prefix}/#{base_path}"
75
75
  end
76
+
77
+ def standardize_query_result?
78
+ Valkyrie.warn_about_standard_queries! if Valkyrie.config.standardize_query_result != true
79
+ Valkyrie.config.standardize_query_result == true
80
+ end
76
81
  end
77
82
  end
@@ -238,7 +238,7 @@ module Valkyrie::Persistence::Fedora
238
238
  obj = calling_mapper.for(property.property).result
239
239
  # Append value directly if possible.
240
240
  if obj.respond_to?(:value)
241
- ordered_list.insert_proxy_for_at(index, obj.value)
241
+ ordered_list.insert_proxy_for_at(index, proxy_for_value(obj.value))
242
242
  # If value is a nested object, take its graph and append it.
243
243
  elsif obj.respond_to?(:graph)
244
244
  append_to_graph(obj: obj, index: index, property: property.property)
@@ -247,6 +247,14 @@ module Valkyrie::Persistence::Fedora
247
247
  end
248
248
  end
249
249
 
250
+ def proxy_for_value(value)
251
+ if value.is_a?(RDF::Literal) && value.datatype == PermissiveSchema.valkyrie_id
252
+ ordered_list.adapter.id_to_uri(value)
253
+ else
254
+ value
255
+ end
256
+ end
257
+
250
258
  class NestedProperty
251
259
  attr_reader :value, :scope
252
260
  def initialize(value:, scope:)
@@ -29,6 +29,7 @@ module Valkyrie::Persistence::Fedora
29
29
 
30
30
  # (see Valkyrie::Persistence::Memory::QueryService#find_many_by_ids)
31
31
  def find_many_by_ids(ids:)
32
+ ids = ids.uniq if adapter.standardize_query_result?
32
33
  ids.map do |id|
33
34
  begin
34
35
  find_by(id: id)
@@ -42,6 +43,7 @@ module Valkyrie::Persistence::Fedora
42
43
  def find_parents(resource:)
43
44
  content = content_with_inbound(id: resource.id)
44
45
  parent_ids = content.graph.query([nil, RDF::Vocab::ORE.proxyFor, nil]).map(&:subject).map { |x| x.to_s.gsub(/#.*/, '') }.map { |x| adapter.uri_to_id(x) }
46
+ parent_ids.uniq! if adapter.standardize_query_result?
45
47
  parent_ids.lazy.map do |id|
46
48
  find_by(id: id)
47
49
  end
@@ -111,11 +113,10 @@ module Valkyrie::Persistence::Fedora
111
113
  # *Also, an initial request is made to find the URIs of the resources referencing the resource in the query*
112
114
  def find_inverse_references_by(resource:, property:)
113
115
  ensure_persisted(resource)
114
- content = content_with_inbound(id: resource.id)
115
- property_uri = adapter.schema.predicate_for(property: property, resource: nil)
116
- ids = content.graph.query([nil, property_uri, nil]).map(&:subject).map { |x| x.to_s.gsub(/#.*/, '') }.map { |x| adapter.uri_to_id(x) }
117
- ids.lazy.map do |id|
118
- find_by(id: id)
116
+ if ordered_property?(resource: resource, property: property)
117
+ find_inverse_references_by_ordered(resource: resource, property: property)
118
+ else
119
+ find_inverse_references_by_unordered(resource: resource, property: property)
119
120
  end
120
121
  end
121
122
 
@@ -126,6 +127,21 @@ module Valkyrie::Persistence::Fedora
126
127
 
127
128
  private
128
129
 
130
+ def find_inverse_references_by_unordered(resource:, property:)
131
+ content = content_with_inbound(id: resource.id)
132
+ property_uri = adapter.schema.predicate_for(property: property, resource: nil)
133
+ ids = content.graph.query([nil, property_uri, adapter.id_to_uri(resource.id)]).map(&:subject).map { |x| x.to_s.gsub(/#.*/, '') }.map { |x| adapter.uri_to_id(x) }
134
+ ids.uniq! if adapter.standardize_query_result?
135
+ ids.lazy.map { |id| find_by(id: id) }
136
+ end
137
+
138
+ def find_inverse_references_by_ordered(resource:, property:)
139
+ content = content_with_inbound(id: resource.id)
140
+ ids = content.graph.query([nil, ::RDF::Vocab::ORE.proxyFor, adapter.id_to_uri(resource.id)]).map(&:subject).map { |x| x.to_s.gsub(/#.*/, '') }.map { |x| adapter.uri_to_id(x) }
141
+ ids.uniq! if adapter.standardize_query_result?
142
+ ids.lazy.map { |id| find_by(id: id) }.select { |o| o[property].include?(resource.id) }
143
+ end
144
+
129
145
  # Ensures that an object is (or can be cast into a) Valkyrie::ID
130
146
  # @return [Valkyrie::ID]
131
147
  # @raise [ArgumentError]
@@ -151,5 +167,9 @@ module Valkyrie::Persistence::Fedora
151
167
  def ensure_persisted(resource)
152
168
  raise ArgumentError, 'resource is not saved' unless resource.persisted?
153
169
  end
170
+
171
+ def ordered_property?(resource:, property:)
172
+ resource.class.schema[property].meta.try(:[], :ordered)
173
+ end
154
174
  end
155
175
  end
@@ -29,5 +29,10 @@ module Valkyrie::Persistence::Memory
29
29
  def id
30
30
  @id ||= Valkyrie::ID.new(Digest::MD5.hexdigest(self.class.to_s))
31
31
  end
32
+
33
+ def standardize_query_result?
34
+ Valkyrie.warn_about_standard_queries! if Valkyrie.config.standardize_query_result != true
35
+ Valkyrie.config.standardize_query_result == true
36
+ end
32
37
  end
33
38
  end
@@ -44,6 +44,7 @@ module Valkyrie::Persistence::Memory
44
44
  # @raise [ArgumentError] Raised when any ID is not a String or a Valkyrie::ID
45
45
  # @return [Array<Valkyrie::Resource>] All requested objects that were found
46
46
  def find_many_by_ids(ids:)
47
+ ids = ids.uniq if adapter.standardize_query_result?
47
48
  ids.map do |id|
48
49
  begin
49
50
  find_by(id: id)
@@ -89,9 +90,15 @@ module Valkyrie::Persistence::Memory
89
90
  # @return [Array<Valkyrie::Resource>] All objects which are referenced by the
90
91
  # `property` property on `resource`. Not necessarily in order.
91
92
  def find_references_by(resource:, property:)
92
- Array.wrap(resource[property]).map do |id|
93
- find_by(id: id)
94
- end
93
+ refs = Array.wrap(resource[property]).map do |id|
94
+ begin
95
+ find_by(id: id)
96
+ rescue ::Valkyrie::Persistence::ObjectNotFoundError
97
+ nil
98
+ end
99
+ end.reject(&:nil?)
100
+ refs.uniq! if adapter.standardize_query_result? && !ordered_property?(resource: resource, property: property)
101
+ refs
95
102
  end
96
103
 
97
104
  # Get all resources which link to a resource with a given property.
@@ -147,5 +154,9 @@ module Valkyrie::Persistence::Memory
147
154
  def ensure_persisted(resource)
148
155
  raise ArgumentError, 'resource is not saved' unless resource.persisted?
149
156
  end
157
+
158
+ def ordered_property?(resource:, property:)
159
+ resource.class.schema[property].meta.try(:[], :ordered)
160
+ end
150
161
  end
151
162
  end
@@ -17,7 +17,8 @@ module Valkyrie::Persistence::Postgres
17
17
  # @return [Class] {Valkyrie::Persistence::Postgres::QueryService}
18
18
  def query_service
19
19
  @query_service ||= Valkyrie::Persistence::Postgres::QueryService.new(
20
- resource_factory: resource_factory
20
+ resource_factory: resource_factory,
21
+ adapter: self
21
22
  )
22
23
  end
23
24
 
@@ -34,5 +35,10 @@ module Valkyrie::Persistence::Postgres
34
35
  Valkyrie::ID.new(Digest::MD5.hexdigest(to_hash))
35
36
  end
36
37
  end
38
+
39
+ def standardize_query_result?
40
+ Valkyrie.warn_about_standard_queries! if Valkyrie.config.standardize_query_result != true
41
+ Valkyrie.config.standardize_query_result == true
42
+ end
37
43
  end
38
44
  end
@@ -7,12 +7,13 @@ module Valkyrie::Persistence::Postgres
7
7
  #
8
8
  # @see Valkyrie::Persistence::Postgres::MetadataAdapter
9
9
  class QueryService
10
- attr_reader :resource_factory
10
+ attr_reader :resource_factory, :adapter
11
11
  delegate :orm_class, to: :resource_factory
12
12
 
13
13
  # @param [ResourceFactory] resource_factory
14
- def initialize(resource_factory:)
14
+ def initialize(adapter:, resource_factory:)
15
15
  @resource_factory = resource_factory
16
+ @adapter = adapter
16
17
  end
17
18
 
18
19
  # Retrieve all records for the resource and construct Valkyrie Resources
@@ -188,7 +189,7 @@ module Valkyrie::Persistence::Postgres
188
189
  # @return [String]
189
190
  def find_references_query
190
191
  <<-SQL
191
- SELECT member.* FROM orm_resources a,
192
+ SELECT #{adapter.standardize_query_result? ? 'DISTINCT' : ''} member.* FROM orm_resources a,
192
193
  jsonb_array_elements(a.metadata->?) AS b(member)
193
194
  JOIN orm_resources member ON (b.member->>'id')::#{id_type} = member.id WHERE a.id = ?
194
195
  SQL
@@ -46,7 +46,8 @@ module Valkyrie::Persistence::Solr
46
46
  def query_service
47
47
  @query_service ||= Valkyrie::Persistence::Solr::QueryService.new(
48
48
  connection: connection,
49
- resource_factory: resource_factory
49
+ resource_factory: resource_factory,
50
+ adapter: self
50
51
  )
51
52
  end
52
53
 
@@ -63,6 +64,11 @@ module Valkyrie::Persistence::Solr
63
64
  Valkyrie::Persistence::Solr::ResourceFactory.new(resource_indexer: resource_indexer, adapter: self)
64
65
  end
65
66
 
67
+ def standardize_query_result?
68
+ Valkyrie.warn_about_standard_queries! if Valkyrie.config.standardize_query_result != true
69
+ Valkyrie.config.standardize_query_result == true
70
+ end
71
+
66
72
  # Class modeling the indexer for cases where indexing is *not* performed
67
73
  class NullIndexer
68
74
  # @note this is a no-op
@@ -3,17 +3,18 @@ module Valkyrie::Persistence::Solr::Queries
3
3
  # Responsible for returning all members of a given resource as
4
4
  # {Valkyrie::Resource}s
5
5
  class FindMembersQuery
6
- attr_reader :resource, :connection, :resource_factory, :model
6
+ attr_reader :resource, :connection, :resource_factory, :model, :standardize_query_result
7
7
 
8
8
  # @param [Valkyrie::Resource] resource
9
9
  # @param [RSolr::Client] connection
10
10
  # @param [ResourceFactory] resource_factory
11
11
  # @param [Class] model
12
- def initialize(resource:, connection:, resource_factory:, model:)
12
+ def initialize(resource:, connection:, resource_factory:, model:, standardize_query_result:)
13
13
  @resource = resource
14
14
  @connection = connection
15
15
  @resource_factory = resource_factory
16
16
  @model = model
17
+ @standardize_query_result = standardize_query_result
17
18
  end
18
19
 
19
20
  # Iterate over each Solr Document and convert each Document into a Valkyrie Resource
@@ -28,15 +29,21 @@ module Valkyrie::Persistence::Solr::Queries
28
29
  # @yield [Valkyrie::Resource]
29
30
  def each
30
31
  return [] unless resource.id.present?
31
- unordered_members.sort_by { |x| member_ids.index(x.id) }.each do |member|
32
- yield member
32
+ if standardize_query_result
33
+ member_ids.map { |id| unordered_members.find { |member| member.id == id } }.reject(&:nil?).each do |member|
34
+ yield member
35
+ end
36
+ else
37
+ unordered_members.sort_by { |x| member_ids.index(x.id) }.each do |member|
38
+ yield member
39
+ end
33
40
  end
34
41
  end
35
42
 
36
43
  # Retrieving the Solr Documents for the member resources, construct Valkyrie Resources for each
37
44
  # @return [Array<Valkyrie::Resource>]
38
45
  def unordered_members
39
- docs.map do |doc|
46
+ @unordered_members ||= docs.map do |doc|
40
47
  resource_factory.to_resource(object: doc)
41
48
  end
42
49
  end
@@ -55,7 +62,7 @@ module Valkyrie::Persistence::Solr::Queries
55
62
  # Access the IDs of the members for the Valkyrie Resource
56
63
  # @return [Array<Valkyrie::ID>]
57
64
  def member_ids
58
- Array.wrap(resource.member_ids)
65
+ resource.respond_to?(:member_ids) ? Array.wrap(resource.member_ids) : []
59
66
  end
60
67
 
61
68
  # Generate the Solr join query using the id_ssi field
@@ -17,13 +17,13 @@ module Valkyrie::Persistence::Solr::Queries
17
17
 
18
18
  def each
19
19
  # map them off of the property to fix solr's deduplication
20
- property_values.map { |id| unordered_members.find { |member| member.id == id } } .each do |value|
20
+ property_values.map { |id| unordered_members.find { |member| member.id == id } } .reject(&:nil?).each do |value|
21
21
  yield value
22
22
  end
23
23
  end
24
24
 
25
25
  def unordered_members
26
- docs.map do |doc|
26
+ @unordered_members ||= docs.map do |doc|
27
27
  resource_factory.to_resource(object: doc)
28
28
  end
29
29
  end
@@ -3,12 +3,13 @@ module Valkyrie::Persistence::Solr
3
3
  require 'valkyrie/persistence/solr/queries'
4
4
  # Query Service for Solr MetadataAdapter.
5
5
  class QueryService
6
- attr_reader :connection, :resource_factory
6
+ attr_reader :connection, :resource_factory, :adapter
7
7
  # @param [RSolr::Client] connection
8
8
  # @param [Valkyrie::Persistence::Solr::ResourceFactory] resource_factory
9
- def initialize(connection:, resource_factory:)
9
+ def initialize(connection:, resource_factory:, adapter:)
10
10
  @connection = connection
11
11
  @resource_factory = resource_factory
12
+ @adapter = adapter
12
13
  end
13
14
 
14
15
  # Find resources by Valkyrie ID
@@ -65,7 +66,13 @@ module Valkyrie::Persistence::Solr
65
66
  # @param [Valkyrie::Resource] parent resource
66
67
  # @return [Array<Valkyrie::Resource>] member resources
67
68
  def find_members(resource:, model: nil)
68
- Valkyrie::Persistence::Solr::Queries::FindMembersQuery.new(resource: resource, model: model, connection: connection, resource_factory: resource_factory).run
69
+ Valkyrie::Persistence::Solr::Queries::FindMembersQuery.new(
70
+ resource: resource,
71
+ model: model,
72
+ connection: connection,
73
+ resource_factory: resource_factory,
74
+ standardize_query_result: adapter.standardize_query_result?
75
+ ).run
69
76
  end
70
77
 
71
78
  # Find all of the resources referenced by a given Valkyrie Resource using a specific property
@@ -7,7 +7,7 @@ RSpec.shared_examples 'a Valkyrie query provider' do
7
7
  attribute :alternate_ids, Valkyrie::Types::Array
8
8
  attribute :title
9
9
  attribute :member_ids, Valkyrie::Types::Array
10
- attribute :a_member_of
10
+ attribute :a_member_of, Valkyrie::Types::Array
11
11
  attribute :an_ordered_member_of, Valkyrie::Types::Array.meta(ordered: true)
12
12
  end
13
13
  class SecondResource < Valkyrie::Resource
@@ -22,6 +22,8 @@ RSpec.shared_examples 'a Valkyrie query provider' do
22
22
  let(:persister) { adapter.persister }
23
23
  subject { adapter.query_service }
24
24
 
25
+ before { allow(Valkyrie.config).to receive(:standardize_query_result).and_return(true) }
26
+
25
27
  it { is_expected.to respond_to(:find_all).with(0).arguments }
26
28
  it { is_expected.to respond_to(:find_all_of_model).with_keywords(:model) }
27
29
  it { is_expected.to respond_to(:find_by).with_keywords(:id) }
@@ -139,6 +141,11 @@ RSpec.shared_examples 'a Valkyrie query provider' do
139
141
  it 'raises an error if any id is not a Valkyrie::ID or a string' do
140
142
  expect { query_service.find_many_by_ids(ids: [resource.id, 123]) }.to raise_error ArgumentError
141
143
  end
144
+
145
+ it "removes duplicates" do
146
+ found = query_service.find_many_by_ids(ids: [resource.id, resource2.id, resource.id])
147
+ expect(found.map(&:id)).to contain_exactly resource.id, resource2.id
148
+ end
142
149
  end
143
150
 
144
151
  describe ".find_members" do
@@ -153,6 +160,13 @@ RSpec.shared_examples 'a Valkyrie query provider' do
153
160
  it "returns all a resource's members in order" do
154
161
  expect(subject.map(&:id).to_a).to eq [child2.id, child1.id]
155
162
  end
163
+
164
+ context "when something is member more than once" do
165
+ let(:parent) { persister.save(resource: resource_class.new(member_ids: [child1.id, child2.id, child1.id])) }
166
+ it "includes duplicates" do
167
+ expect(subject.map(&:id).to_a).to eq [child1.id, child2.id, child1.id]
168
+ end
169
+ end
156
170
  end
157
171
 
158
172
  context "when there's no resource ID" do
@@ -207,20 +221,39 @@ RSpec.shared_examples 'a Valkyrie query provider' do
207
221
  end
208
222
 
209
223
  describe ".find_references_by" do
210
- it "returns all references given in a property" do
211
- parent = persister.save(resource: resource_class.new)
212
- child = persister.save(resource: resource_class.new(a_member_of: [parent.id]))
213
- persister.save(resource: resource_class.new)
224
+ context "when the property is unordered" do
225
+ it "returns all references given in a property" do
226
+ parent = persister.save(resource: resource_class.new)
227
+ parent2 = persister.save(resource: resource_class.new)
228
+ child = persister.save(resource: resource_class.new(a_member_of: [parent.id, parent2.id]))
229
+ persister.save(resource: resource_class.new)
214
230
 
215
- expect(query_service.find_references_by(resource: child, property: :a_member_of).map(&:id).to_a).to eq [parent.id]
216
- end
217
- it "returns an empty array if there are none" do
218
- child = persister.save(resource: resource_class.new)
219
- expect(query_service.find_references_by(resource: child, property: :a_member_of).to_a).to eq []
231
+ expect(query_service.find_references_by(resource: child, property: :a_member_of).map(&:id).to_a).to contain_exactly parent.id, parent2.id
232
+ end
233
+
234
+ it "returns an empty array if there are none" do
235
+ child = persister.save(resource: resource_class.new)
236
+ expect(query_service.find_references_by(resource: child, property: :a_member_of).to_a).to eq []
237
+ end
238
+
239
+ it "removes duplicates" do
240
+ parent = persister.save(resource: resource_class.new)
241
+ child = persister.save(resource: resource_class.new(a_member_of: [parent.id, parent.id]))
242
+ persister.save(resource: resource_class.new)
243
+
244
+ expect(query_service.find_references_by(resource: child, property: :a_member_of).map(&:id).to_a).to contain_exactly parent.id
245
+ end
246
+
247
+ it "returns nothing if reference not found" do
248
+ child = persister.save(resource: resource_class.new(a_member_of: ["123123123"]))
249
+ persister.save(resource: resource_class.new)
250
+
251
+ expect(query_service.find_references_by(resource: child, property: :a_member_of).map(&:id).to_a).to eq []
252
+ end
220
253
  end
221
254
 
222
255
  context "when the property is ordered" do
223
- it "returns all references in order" do
256
+ it "returns all references in order including duplicates" do
224
257
  parent = persister.save(resource: resource_class.new)
225
258
  parent2 = persister.save(resource: resource_class.new)
226
259
  child = persister.save(resource: resource_class.new(an_ordered_member_of: [parent.id, parent2.id, parent.id]))
@@ -228,23 +261,47 @@ RSpec.shared_examples 'a Valkyrie query provider' do
228
261
 
229
262
  expect(query_service.find_references_by(resource: child, property: :an_ordered_member_of).map(&:id).to_a).to eq [parent.id, parent2.id, parent.id]
230
263
  end
264
+
265
+ it "returns nothing if reference not found" do
266
+ child = persister.save(resource: resource_class.new(an_ordered_member_of: ["123123123"]))
267
+ persister.save(resource: resource_class.new)
268
+
269
+ expect(query_service.find_references_by(resource: child, property: :an_ordered_member_of).map(&:id).to_a).to eq []
270
+ end
231
271
  end
232
272
  end
233
273
 
234
274
  describe ".find_inverse_references_by" do
235
275
  context "when the resource is saved" do
236
- it "returns everything which references the given resource by the given property" do
237
- parent = persister.save(resource: resource_class.new)
238
- child = persister.save(resource: resource_class.new(a_member_of: [parent.id]))
239
- persister.save(resource: resource_class.new)
240
- persister.save(resource: SecondResource.new)
276
+ context "when the property is unordered" do
277
+ it "returns everything which references the given resource by the given property" do
278
+ parent = persister.save(resource: resource_class.new)
279
+ parent2 = persister.save(resource: resource_class.new)
280
+ child = persister.save(resource: resource_class.new(a_member_of: [parent.id]))
281
+ child2 = persister.save(resource: resource_class.new(a_member_of: [parent.id, parent2.id, parent.id]))
282
+ persister.save(resource: resource_class.new)
283
+ persister.save(resource: SecondResource.new)
284
+
285
+ expect(query_service.find_inverse_references_by(resource: parent, property: :a_member_of).map(&:id).to_a).to contain_exactly child.id, child2.id
286
+ end
241
287
 
242
- expect(query_service.find_inverse_references_by(resource: parent, property: :a_member_of).map(&:id).to_a).to eq [child.id]
288
+ it "returns an empty array if there are none" do
289
+ parent = persister.save(resource: resource_class.new)
290
+
291
+ expect(query_service.find_inverse_references_by(resource: parent, property: :a_member_of).to_a).to eq []
292
+ end
243
293
  end
244
- it "returns an empty array if there are none" do
245
- parent = persister.save(resource: resource_class.new)
246
294
 
247
- expect(query_service.find_inverse_references_by(resource: parent, property: :a_member_of).to_a).to eq []
295
+ context "when the property is ordered" do
296
+ it "returns everything which references the given resource by the given property" do
297
+ parent = persister.save(resource: resource_class.new)
298
+ child = persister.save(resource: resource_class.new(an_ordered_member_of: [parent.id]))
299
+ child2 = persister.save(resource: resource_class.new(an_ordered_member_of: [parent.id, parent.id]))
300
+ persister.save(resource: resource_class.new)
301
+ persister.save(resource: SecondResource.new)
302
+
303
+ expect(query_service.find_inverse_references_by(resource: parent, property: :an_ordered_member_of).map(&:id).to_a).to contain_exactly child.id, child2.id
304
+ end
248
305
  end
249
306
  end
250
307
  context "when the resource is not saved" do
@@ -265,12 +322,21 @@ RSpec.shared_examples 'a Valkyrie query provider' do
265
322
 
266
323
  expect(query_service.find_parents(resource: child1).map(&:id).to_a).to contain_exactly parent.id, parent2.id
267
324
  end
325
+
268
326
  it "returns an empty array if there are none" do
269
327
  child1 = persister.save(resource: resource_class.new)
270
328
 
271
329
  expect(query_service.find_parents(resource: child1).to_a).to eq []
272
330
  end
273
331
 
332
+ it "doesn't return same parent twice" do
333
+ child1 = persister.save(resource: resource_class.new)
334
+ parent = persister.save(resource: resource_class.new(member_ids: [child1.id, child1.id]))
335
+ parent2 = persister.save(resource: resource_class.new(member_ids: [child1.id]))
336
+
337
+ expect(query_service.find_parents(resource: child1).map(&:id).to_a).to contain_exactly parent.id, parent2.id
338
+ end
339
+
274
340
  context "when the model doesn't have member_ids" do
275
341
  let(:child1) { persister.save(resource: SecondResource.new) }
276
342
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie
3
- VERSION = "1.2.1"
3
+ VERSION = "1.2.2"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: valkyrie
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Trey Pendragon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-09-25 00:00:00.000000000 Z
11
+ date: 2018-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-struct
@@ -589,7 +589,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
589
589
  version: '0'
590
590
  requirements: []
591
591
  rubyforge_project:
592
- rubygems_version: 2.6.14
592
+ rubygems_version: 2.7.7
593
593
  signing_key:
594
594
  specification_version: 4
595
595
  summary: An ORM using the Data Mapper pattern, specifically built to solve Digital