valkyrie 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -3
  3. data/.rubocop.yml +28 -0
  4. data/Gemfile +6 -1
  5. data/README.md +145 -10
  6. data/Rakefile +59 -1
  7. data/bin/console +1 -1
  8. data/config/valkyrie.yml +8 -0
  9. data/db/config.yml +17 -0
  10. data/db/migrate/20160111215816_enable_uuid_extension.rb +6 -0
  11. data/db/migrate/20161007101725_create_orm_resources.rb +10 -0
  12. data/db/migrate/20170124135846_add_model_type_to_orm_resources.rb +6 -0
  13. data/db/migrate/20170531004548_change_model_type_to_internal_model.rb +6 -0
  14. data/db/schema.rb +65 -0
  15. data/db/seeds.rb +8 -0
  16. data/lib/config/database_connection.rb +15 -0
  17. data/lib/generators/valkyrie/resource_generator.rb +27 -0
  18. data/lib/generators/valkyrie/templates/resource.rb.erb +9 -0
  19. data/lib/generators/valkyrie/templates/resource_spec.rb.erb +13 -0
  20. data/lib/valkyrie.rb +76 -1
  21. data/lib/valkyrie/adapter_container.rb +12 -0
  22. data/lib/valkyrie/change_set.rb +84 -0
  23. data/lib/valkyrie/decorators/decorator_list.rb +15 -0
  24. data/lib/valkyrie/decorators/decorator_with_arguments.rb +14 -0
  25. data/lib/valkyrie/derivative_service.rb +42 -0
  26. data/lib/valkyrie/engine.rb +10 -0
  27. data/lib/valkyrie/file_characterization_service.rb +42 -0
  28. data/lib/valkyrie/id.rb +32 -0
  29. data/lib/valkyrie/indexers/access_controls_indexer.rb +19 -0
  30. data/lib/valkyrie/local_file_service.rb +11 -0
  31. data/lib/valkyrie/metadata_adapter.rb +22 -0
  32. data/lib/valkyrie/persist_derivatives.rb +29 -0
  33. data/lib/valkyrie/persistence.rb +14 -0
  34. data/lib/valkyrie/persistence/buffered_persister.rb +28 -0
  35. data/lib/valkyrie/persistence/composite_persister.rb +29 -0
  36. data/lib/valkyrie/persistence/delete_tracking_buffer.rb +21 -0
  37. data/lib/valkyrie/persistence/fedora.rb +11 -0
  38. data/lib/valkyrie/persistence/fedora/list_node.rb +88 -0
  39. data/lib/valkyrie/persistence/fedora/metadata_adapter.rb +45 -0
  40. data/lib/valkyrie/persistence/fedora/ordered_list.rb +146 -0
  41. data/lib/valkyrie/persistence/fedora/ordered_reader.rb +28 -0
  42. data/lib/valkyrie/persistence/fedora/persister.rb +47 -0
  43. data/lib/valkyrie/persistence/fedora/persister/model_converter.rb +199 -0
  44. data/lib/valkyrie/persistence/fedora/persister/orm_converter.rb +338 -0
  45. data/lib/valkyrie/persistence/fedora/persister/resource_factory.rb +21 -0
  46. data/lib/valkyrie/persistence/fedora/query_service.rb +80 -0
  47. data/lib/valkyrie/persistence/memory.rb +8 -0
  48. data/lib/valkyrie/persistence/memory/metadata_adapter.rb +22 -0
  49. data/lib/valkyrie/persistence/memory/persister.rb +58 -0
  50. data/lib/valkyrie/persistence/memory/query_service.rb +86 -0
  51. data/lib/valkyrie/persistence/postgres.rb +6 -0
  52. data/lib/valkyrie/persistence/postgres/metadata_adapter.rb +20 -0
  53. data/lib/valkyrie/persistence/postgres/orm.rb +9 -0
  54. data/lib/valkyrie/persistence/postgres/orm/resource.rb +7 -0
  55. data/lib/valkyrie/persistence/postgres/orm_converter.rb +118 -0
  56. data/lib/valkyrie/persistence/postgres/persister.rb +33 -0
  57. data/lib/valkyrie/persistence/postgres/queries.rb +8 -0
  58. data/lib/valkyrie/persistence/postgres/queries/find_inverse_references_query.rb +31 -0
  59. data/lib/valkyrie/persistence/postgres/queries/find_members_query.rb +33 -0
  60. data/lib/valkyrie/persistence/postgres/queries/find_references_query.rb +33 -0
  61. data/lib/valkyrie/persistence/postgres/query_service.rb +53 -0
  62. data/lib/valkyrie/persistence/postgres/resource_converter.rb +18 -0
  63. data/lib/valkyrie/persistence/postgres/resource_factory.rb +30 -0
  64. data/lib/valkyrie/persistence/solr.rb +6 -0
  65. data/lib/valkyrie/persistence/solr/metadata_adapter.rb +42 -0
  66. data/lib/valkyrie/persistence/solr/model_converter.rb +270 -0
  67. data/lib/valkyrie/persistence/solr/orm_converter.rb +252 -0
  68. data/lib/valkyrie/persistence/solr/persister.rb +32 -0
  69. data/lib/valkyrie/persistence/solr/queries.rb +11 -0
  70. data/lib/valkyrie/persistence/solr/queries/default_paginator.rb +16 -0
  71. data/lib/valkyrie/persistence/solr/queries/find_all_query.rb +33 -0
  72. data/lib/valkyrie/persistence/solr/queries/find_by_id_query.rb +24 -0
  73. data/lib/valkyrie/persistence/solr/queries/find_inverse_references_query.rb +30 -0
  74. data/lib/valkyrie/persistence/solr/queries/find_members_query.rb +43 -0
  75. data/lib/valkyrie/persistence/solr/queries/find_references_query.rb +34 -0
  76. data/lib/valkyrie/persistence/solr/query_service.rb +48 -0
  77. data/lib/valkyrie/persistence/solr/repository.rb +36 -0
  78. data/lib/valkyrie/persistence/solr/resource_factory.rb +24 -0
  79. data/lib/valkyrie/rdf_patches.rb +17 -0
  80. data/lib/valkyrie/resource.rb +106 -0
  81. data/lib/valkyrie/resource/access_controls.rb +13 -0
  82. data/lib/valkyrie/specs/shared_specs.rb +10 -0
  83. data/lib/valkyrie/specs/shared_specs/change_set_persister.rb +60 -0
  84. data/lib/valkyrie/specs/shared_specs/derivative_service.rb +30 -0
  85. data/lib/valkyrie/specs/shared_specs/file.rb +12 -0
  86. data/lib/valkyrie/specs/shared_specs/file_characterization_service.rb +33 -0
  87. data/lib/valkyrie/specs/shared_specs/metadata_adapter.rb +10 -0
  88. data/lib/valkyrie/specs/shared_specs/persister.rb +154 -0
  89. data/lib/valkyrie/specs/shared_specs/queries.rb +128 -0
  90. data/lib/valkyrie/specs/shared_specs/resource.rb +71 -0
  91. data/lib/valkyrie/specs/shared_specs/storage_adapter.rb +44 -0
  92. data/lib/valkyrie/storage.rb +8 -0
  93. data/lib/valkyrie/storage/disk.rb +55 -0
  94. data/lib/valkyrie/storage/fedora.rb +71 -0
  95. data/lib/valkyrie/storage/memory.rb +31 -0
  96. data/lib/valkyrie/storage_adapter.rb +100 -0
  97. data/lib/valkyrie/types.rb +34 -0
  98. data/lib/valkyrie/value_mapper.rb +67 -0
  99. data/lib/valkyrie/version.rb +2 -1
  100. data/lib/valkyrie/vocab/pcdm_use.rb +73 -0
  101. data/valkyrie.gemspec +33 -7
  102. metadata +462 -7
  103. data/.travis.yml +0 -5
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence::Solr::Queries
3
+ class FindReferencesQuery
4
+ attr_reader :resource, :property, :connection, :resource_factory
5
+ def initialize(resource:, property:, connection:, resource_factory:)
6
+ @resource = resource
7
+ @property = property
8
+ @connection = connection
9
+ @resource_factory = resource_factory
10
+ end
11
+
12
+ def run
13
+ enum_for(:each)
14
+ end
15
+
16
+ def each
17
+ docs = DefaultPaginator.new
18
+ while docs.has_next?
19
+ docs = connection.paginate(docs.next_page, docs.per_page, "select", params: { q: query })["response"]["docs"]
20
+ docs.each do |doc|
21
+ yield resource_factory.to_resource(object: doc)
22
+ end
23
+ end
24
+ end
25
+
26
+ def query
27
+ "{!join from=#{property}_ssim to=id}id:#{id}"
28
+ end
29
+
30
+ def id
31
+ "id-#{resource.id}"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence::Solr
3
+ require 'valkyrie/persistence/solr/queries'
4
+ class QueryService
5
+ attr_reader :connection, :resource_factory
6
+ # @param connection [RSolr::Client]
7
+ # @param resource_factory [Valkyrie::Persistence::Solr::ResourceFactory]
8
+ def initialize(connection:, resource_factory:)
9
+ @connection = connection
10
+ @resource_factory = resource_factory
11
+ end
12
+
13
+ # (see Valkyrie::Persistence::Memory::QueryService#find_by)
14
+ def find_by(id:)
15
+ Valkyrie::Persistence::Solr::Queries::FindByIdQuery.new(id, connection: connection, resource_factory: resource_factory).run
16
+ end
17
+
18
+ # (see Valkyrie::Persistence::Memory::QueryService#find_all)
19
+ def find_all
20
+ Valkyrie::Persistence::Solr::Queries::FindAllQuery.new(connection: connection, resource_factory: resource_factory).run
21
+ end
22
+
23
+ # (see Valkyrie::Persistence::Memory::QueryService#find_all_of_model)
24
+ def find_all_of_model(model:)
25
+ Valkyrie::Persistence::Solr::Queries::FindAllQuery.new(connection: connection, resource_factory: resource_factory, model: model).run
26
+ end
27
+
28
+ # (see Valkyrie::Persistence::Memory::QueryService#find_parents)
29
+ def find_parents(resource:)
30
+ find_inverse_references_by(resource: resource, property: :member_ids)
31
+ end
32
+
33
+ # (see Valkyrie::Persistence::Memory::QueryService#find_members)
34
+ def find_members(resource:)
35
+ Valkyrie::Persistence::Solr::Queries::FindMembersQuery.new(resource: resource, connection: connection, resource_factory: resource_factory).run
36
+ end
37
+
38
+ # (see Valkyrie::Persistence::Memory::QueryService#find_references_by)
39
+ def find_references_by(resource:, property:)
40
+ Valkyrie::Persistence::Solr::Queries::FindReferencesQuery.new(resource: resource, property: property, connection: connection, resource_factory: resource_factory).run
41
+ end
42
+
43
+ # (see Valkyrie::Persistence::Memory::QueryService#find_inverse_references_by)
44
+ def find_inverse_references_by(resource:, property:)
45
+ Valkyrie::Persistence::Solr::Queries::FindInverseReferencesQuery.new(resource: resource, property: property, connection: connection, resource_factory: resource_factory).run
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence::Solr
3
+ class Repository
4
+ attr_reader :resources, :connection, :resource_factory
5
+ def initialize(resources:, connection:, resource_factory:)
6
+ @resources = resources
7
+ @connection = connection
8
+ @resource_factory = resource_factory
9
+ end
10
+
11
+ def persist
12
+ documents = resources.map do |resource|
13
+ generate_id(resource) if resource.id.blank?
14
+ solr_document(resource)
15
+ end
16
+ connection.add documents, params: { softCommit: true }
17
+ documents.map do |document|
18
+ resource_factory.to_resource(object: document.stringify_keys)
19
+ end
20
+ end
21
+
22
+ def delete
23
+ connection.delete_by_id resources.map { |resource| "id-#{resource.id}" }, params: { softCommit: true }
24
+ resources
25
+ end
26
+
27
+ def solr_document(resource)
28
+ resource_factory.from_resource(resource: resource).to_h
29
+ end
30
+
31
+ def generate_id(resource)
32
+ Valkyrie.logger.warn "The Solr adapter is not meant to persist new resources, but is now generating an ID."
33
+ resource.id = SecureRandom.uuid
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence::Solr
3
+ class ResourceFactory
4
+ require 'valkyrie/persistence/solr/orm_converter'
5
+ require 'valkyrie/persistence/solr/model_converter'
6
+ attr_reader :resource_indexer
7
+ def initialize(resource_indexer:)
8
+ @resource_indexer = resource_indexer
9
+ end
10
+
11
+ # @param solr_document [Hash] The solr document in a hash to convert to a
12
+ # resource.
13
+ # @return [Valkyrie::Resource]
14
+ def to_resource(object:)
15
+ ORMConverter.new(object).convert!
16
+ end
17
+
18
+ # @param resource [Valkyrie::Resource] The resource to convert to a solr hash.
19
+ # @return [Hash] The solr document represented as a hash.
20
+ def from_resource(resource:)
21
+ Valkyrie::Persistence::Solr::ModelConverter.new(resource, resource_factory: self).convert!
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ ##
3
+ # These patches are necessary for the postgres adapter to build JSON-LD versions
4
+ # of RDF objects when `to_json` is called on them - that way they're stored in
5
+ # the database as a standard format.
6
+ module RDF
7
+ class Literal
8
+ def as_json(*_args)
9
+ JSON::LD::API.fromRdf([RDF::Statement.new(RDF::URI(""), RDF::URI(""), self)])[0][""][0]
10
+ end
11
+ end
12
+ class URI
13
+ def as_json(*_args)
14
+ JSON::LD::API.fromRdf([RDF::Statement.new(RDF::URI(""), RDF::URI(""), self)])[0][""][0]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie
3
+ ##
4
+ # The base resource class for all Valkyrie metadata objects.
5
+ # @example Define a resource
6
+ # class Book < Valkyrie::Resource
7
+ # attribute :id, Valkyrie::Types::ID.optional
8
+ # attribute :member_ids, Valkyrie::Types::Array
9
+ # attribute :author
10
+ # end
11
+ class Resource < Dry::Struct
12
+ include Draper::Decoratable
13
+ constructor_type :schema
14
+
15
+ # Overridden to provide default attributes.
16
+ # @note The current theory is that we should use this sparingly.
17
+ def self.inherited(subclass)
18
+ ::Dry::Struct.inherited(subclass)
19
+ subclass.constructor_type :schema
20
+ subclass.attribute :internal_resource, Valkyrie::Types::Any.default(subclass.to_s)
21
+ subclass.attribute :created_at, Valkyrie::Types::DateTime.optional
22
+ subclass.attribute :updated_at, Valkyrie::Types::DateTime.optional
23
+ end
24
+
25
+ # @return [Array<Symbol>] Array of fields defined for this class.
26
+ def self.fields
27
+ schema.keys
28
+ end
29
+
30
+ # Define an attribute.
31
+ # @param name [Symbol]
32
+ # @param type [Dry::Types::Type]
33
+ # @note Overridden from {Dry::Struct} to make the default type
34
+ # {Valkyrie::Types::Set}
35
+ def self.attribute(name, type = Valkyrie::Types::Set.optional)
36
+ define_method("#{name}=") do |value|
37
+ instance_variable_set("@#{name}", self.class.schema[name].call(value))
38
+ end
39
+ super
40
+ end
41
+
42
+ # @return [ActiveModel::Name]
43
+ # @note Added for ActiveModel compatibility.
44
+ def self.model_name
45
+ @model_name ||= ::ActiveModel::Name.new(self)
46
+ end
47
+
48
+ delegate :model_name, to: :class
49
+
50
+ def self.human_readable_type
51
+ @_human_readable_type ||= name.demodulize.titleize
52
+ end
53
+
54
+ def self.human_readable_type=(val)
55
+ @_human_readable_type = val
56
+ end
57
+
58
+ # @return [Hash] Hash of attributes
59
+ def attributes
60
+ to_h
61
+ end
62
+
63
+ # @param name [Symbol] Attribute name
64
+ # @return [Boolean]
65
+ def has_attribute?(name)
66
+ respond_to?(name)
67
+ end
68
+
69
+ # @param name [Symbol]
70
+ # @return [Symbol]
71
+ # @note Added for ActiveModel compatibility.
72
+ def column_for_attribute(name)
73
+ name
74
+ end
75
+
76
+ # @return [Boolean]
77
+ def persisted?
78
+ to_param.present?
79
+ end
80
+
81
+ def to_key
82
+ [id]
83
+ end
84
+
85
+ def to_param
86
+ id
87
+ end
88
+
89
+ # @note Added for ActiveModel compatibility
90
+ def to_model
91
+ self
92
+ end
93
+
94
+ # @return [String]
95
+ def to_s
96
+ "#{self.class}: #{id}"
97
+ end
98
+
99
+ ##
100
+ # Provide a human readable name for the resource
101
+ # @return [String]
102
+ def human_readable_type
103
+ self.class.human_readable_type
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie
3
+ class Resource
4
+ module AccessControls
5
+ def self.included(klass)
6
+ klass.attribute :read_groups, Valkyrie::Types::Set
7
+ klass.attribute :read_users, Valkyrie::Types::Set
8
+ klass.attribute :edit_users, Valkyrie::Types::Set
9
+ klass.attribute :edit_groups, Valkyrie::Types::Set
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ require 'valkyrie/specs/shared_specs/persister.rb'
3
+ require 'valkyrie/specs/shared_specs/queries.rb'
4
+ require 'valkyrie/specs/shared_specs/metadata_adapter'
5
+ require 'valkyrie/specs/shared_specs/resource.rb'
6
+ require 'valkyrie/specs/shared_specs/storage_adapter.rb'
7
+ require 'valkyrie/specs/shared_specs/derivative_service.rb'
8
+ require 'valkyrie/specs/shared_specs/file_characterization_service.rb'
9
+ require 'valkyrie/specs/shared_specs/change_set_persister.rb'
10
+ require 'valkyrie/specs/shared_specs/file.rb'
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ RSpec.shared_examples 'a Valkyrie::ChangeSetPersister' do |*_flags|
3
+ before do
4
+ raise 'adapter must be set with `let(:change_set_persister)`' unless defined? change_set_persister
5
+ class CustomResource < Valkyrie::Resource
6
+ include Valkyrie::Resource::AccessControls
7
+ attribute :id, Valkyrie::Types::ID.optional
8
+ attribute :title
9
+ attribute :member_ids
10
+ attribute :nested_resource
11
+ end
12
+ class CustomChangeSet < Valkyrie::ChangeSet
13
+ self.fields = [:title]
14
+ end
15
+ end
16
+ after do
17
+ Object.send(:remove_const, :CustomResource)
18
+ Object.send(:remove_const, :CustomChangeSet)
19
+ end
20
+
21
+ subject { change_set_persister }
22
+ let(:resource_class) { CustomResource }
23
+ let(:resource) { resource_class.new }
24
+ let(:change_set) { CustomChangeSet.new(resource) }
25
+
26
+ it { is_expected.to respond_to(:save).with_keywords(:change_set) }
27
+ it { is_expected.to respond_to(:save_all).with_keywords(:change_sets) }
28
+ it { is_expected.to respond_to(:delete).with_keywords(:change_set) }
29
+ it { is_expected.to respond_to(:metadata_adapter) }
30
+ it { is_expected.to respond_to(:storage_adapter) }
31
+
32
+ describe "#save" do
33
+ it "saves a resource and returns it" do
34
+ output = subject.save(change_set: change_set)
35
+
36
+ expect(output).to be_kind_of CustomResource
37
+ expect(output).to be_persisted
38
+ end
39
+ end
40
+
41
+ describe "#delete" do
42
+ it "deletes a resource" do
43
+ output = subject.save(change_set: change_set)
44
+ subject.delete(change_set: CustomChangeSet.new(output))
45
+
46
+ expect do
47
+ subject.metadata_adapter.query_service.find_by(id: output.id)
48
+ end.to raise_error Valkyrie::Persistence::ObjectNotFoundError
49
+ end
50
+ end
51
+
52
+ describe "#save_all" do
53
+ it "saves multiple change_sets and returns them" do
54
+ change_set2 = CustomChangeSet.new(resource_class.new)
55
+ output = subject.save_all(change_sets: [change_set, change_set2])
56
+
57
+ expect(output.map(&:id).compact.length).to eq 2
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ RSpec.shared_examples 'a Valkyrie::DerivativeService' do
3
+ before do
4
+ raise 'valid_change_set must be set with `let(:valid_change_set)`' unless
5
+ defined? valid_change_set
6
+ raise 'derivative_service must be set with `let(:derivative_service)`' unless
7
+ defined? derivative_service
8
+ end
9
+
10
+ subject { derivative_service.new(valid_change_set) }
11
+
12
+ it { is_expected.to respond_to(:create_derivatives).with(0).arguments }
13
+
14
+ it { is_expected.to respond_to(:cleanup_derivatives).with(0).arguments }
15
+
16
+ it { is_expected.to respond_to(:change_set) }
17
+
18
+ it { is_expected.to respond_to(:mime_type) }
19
+
20
+ describe "#valid?" do
21
+ context "when given a resource it handles" do
22
+ it { is_expected.to be_valid }
23
+ end
24
+ end
25
+
26
+ it "takes a change_set as an argument" do
27
+ obj = derivative_service.new(valid_change_set)
28
+ expect(obj.change_set.resource).to eq valid_change_set.resource
29
+ end
30
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ RSpec.shared_examples 'a Valkyrie::StorageAdapter::File' do
3
+ before do
4
+ raise 'adapter must be set with `let(:file)`' unless defined? file
5
+ end
6
+
7
+ subject { file }
8
+
9
+ it { is_expected.to respond_to(:read) }
10
+ it { is_expected.to respond_to(:rewind) }
11
+ it { is_expected.to respond_to(:id) }
12
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'a Valkyrie::FileCharacterizationService' do
4
+ before do
5
+ raise 'valid_file_node must be set with `let(:valid_file_node)`' unless
6
+ defined? valid_file_node
7
+ raise 'persister must be set with `let(:persister)`' unless
8
+ defined? persister
9
+ raise 'file_characterization_service must be set with `let(:file_characterization_service)`' unless
10
+ defined? file_characterization_service
11
+ end
12
+
13
+ let(:file_node) { valid_file_node }
14
+ subject { file_characterization_service.new(file_node: file_node, persister: persister) }
15
+
16
+ it { is_expected.to respond_to(:characterize).with(0).arguments }
17
+ it 'returns a file node' do
18
+ expect(subject.characterize).to be_a(FileNode)
19
+ end
20
+
21
+ describe '#valid?' do
22
+ context 'when given a file_node it handles' do
23
+ let(:file_node) { valid_file_node }
24
+ it { is_expected.to be_valid }
25
+ end
26
+ end
27
+
28
+ it 'takes a file_node and a persister as arguments' do
29
+ obj = file_characterization_service.new(file_node: file_node, persister: persister)
30
+ expect(obj.file_node).to eq valid_file_node
31
+ expect(obj.persister).to eq persister
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ RSpec.shared_examples 'a Valkyrie::MetadataAdapter' do |passed_adapter|
3
+ before do
4
+ raise 'adapter must be set with `let(:adapter)`' unless
5
+ defined? adapter
6
+ end
7
+ subject { passed_adapter || adapter }
8
+ it { is_expected.to respond_to(:persister).with(0).arguments }
9
+ it { is_expected.to respond_to(:query_service).with(0).arguments }
10
+ end