valkyrie 1.2.1 → 1.2.2

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
- 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