valkyrie 0.1.0 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +5 -5
  2. data/.ctags +2 -0
  3. data/.rubocop.yml +5 -0
  4. data/.rubocop_todo.yml +3 -0
  5. data/CHANGELOG.md +43 -0
  6. data/Gemfile +0 -4
  7. data/LICENSE +15 -0
  8. data/README.md +13 -8
  9. data/Rakefile +15 -1
  10. data/bin/jetty_wait +14 -0
  11. data/bin/rspec +29 -0
  12. data/browserslist +3 -0
  13. data/circle.yml +17 -0
  14. data/config/fedora.yml +10 -0
  15. data/db/migrate/20161007101725_create_orm_resources.rb +9 -1
  16. data/db/migrate/20171011224121_create_path_gin_index.rb +6 -0
  17. data/db/migrate/20171204224121_create_internal_resource_index.rb +6 -0
  18. data/db/migrate/20180212092225_create_updated_at_index.rb +6 -0
  19. data/lib/generators/valkyrie/templates/resource_spec.rb.erb +1 -1
  20. data/lib/valkyrie.rb +1 -7
  21. data/lib/valkyrie/change_set.rb +21 -7
  22. data/lib/valkyrie/engine.rb +2 -0
  23. data/lib/valkyrie/id.rb +1 -0
  24. data/lib/valkyrie/indexers/access_controls_indexer.rb +50 -6
  25. data/lib/valkyrie/metadata_adapter.rb +29 -1
  26. data/lib/valkyrie/persistence.rb +27 -0
  27. data/lib/valkyrie/persistence/buffered_persister.rb +17 -1
  28. data/lib/valkyrie/persistence/composite_persister.rb +14 -2
  29. data/lib/valkyrie/persistence/custom_query_container.rb +63 -0
  30. data/lib/valkyrie/persistence/delete_tracking_buffer.rb +8 -0
  31. data/lib/valkyrie/persistence/fedora.rb +3 -0
  32. data/lib/valkyrie/persistence/fedora/list_node.rb +5 -2
  33. data/lib/valkyrie/persistence/fedora/metadata_adapter.rb +13 -11
  34. data/lib/valkyrie/persistence/fedora/ordered_list.rb +2 -2
  35. data/lib/valkyrie/persistence/fedora/ordered_reader.rb +3 -2
  36. data/lib/valkyrie/persistence/fedora/permissive_schema.rb +75 -0
  37. data/lib/valkyrie/persistence/fedora/persister.rb +22 -0
  38. data/lib/valkyrie/persistence/fedora/persister/model_converter.rb +110 -25
  39. data/lib/valkyrie/persistence/fedora/persister/orm_converter.rb +49 -17
  40. data/lib/valkyrie/persistence/fedora/persister/resource_factory.rb +2 -0
  41. data/lib/valkyrie/persistence/fedora/query_service.rb +46 -3
  42. data/lib/valkyrie/persistence/memory.rb +5 -0
  43. data/lib/valkyrie/persistence/memory/metadata_adapter.rb +6 -1
  44. data/lib/valkyrie/persistence/memory/persister.rb +19 -1
  45. data/lib/valkyrie/persistence/memory/query_service.rb +49 -8
  46. data/lib/valkyrie/persistence/postgres.rb +2 -0
  47. data/lib/valkyrie/persistence/postgres/metadata_adapter.rb +8 -1
  48. data/lib/valkyrie/persistence/postgres/orm.rb +1 -0
  49. data/lib/valkyrie/persistence/postgres/orm/resource.rb +12 -0
  50. data/lib/valkyrie/persistence/postgres/orm_converter.rb +99 -74
  51. data/lib/valkyrie/persistence/postgres/persister.rb +16 -0
  52. data/lib/valkyrie/persistence/postgres/query_service.rb +94 -6
  53. data/lib/valkyrie/persistence/postgres/resource_converter.rb +3 -1
  54. data/lib/valkyrie/persistence/postgres/resource_factory.rb +5 -5
  55. data/lib/valkyrie/persistence/solr.rb +2 -0
  56. data/lib/valkyrie/persistence/solr/composite_indexer.rb +29 -0
  57. data/lib/valkyrie/persistence/solr/metadata_adapter.rb +26 -1
  58. data/lib/valkyrie/persistence/solr/model_converter.rb +38 -8
  59. data/lib/valkyrie/persistence/solr/orm_converter.rb +43 -20
  60. data/lib/valkyrie/persistence/solr/persister.rb +16 -0
  61. data/lib/valkyrie/persistence/solr/queries.rb +3 -0
  62. data/lib/valkyrie/persistence/solr/queries/default_paginator.rb +2 -0
  63. data/lib/valkyrie/persistence/solr/queries/find_all_query.rb +3 -1
  64. data/lib/valkyrie/persistence/solr/queries/find_by_id_query.rb +4 -2
  65. data/lib/valkyrie/persistence/solr/queries/find_inverse_references_query.rb +2 -0
  66. data/lib/valkyrie/persistence/solr/queries/find_many_by_ids_query.rb +21 -0
  67. data/lib/valkyrie/persistence/solr/queries/find_members_query.rb +13 -5
  68. data/lib/valkyrie/persistence/solr/queries/find_references_query.rb +7 -3
  69. data/lib/valkyrie/persistence/solr/query_service.rb +30 -2
  70. data/lib/valkyrie/persistence/solr/repository.rb +14 -2
  71. data/lib/valkyrie/persistence/solr/resource_factory.rb +3 -1
  72. data/lib/valkyrie/resource.rb +11 -4
  73. data/lib/valkyrie/resource/access_controls.rb +13 -0
  74. data/lib/valkyrie/specs/shared_specs.rb +1 -2
  75. data/lib/valkyrie/specs/shared_specs/change_set.rb +75 -0
  76. data/lib/valkyrie/specs/shared_specs/metadata_adapter.rb +3 -0
  77. data/lib/valkyrie/specs/shared_specs/persister.rb +145 -15
  78. data/lib/valkyrie/specs/shared_specs/queries.rb +153 -27
  79. data/lib/valkyrie/specs/shared_specs/storage_adapter.rb +8 -3
  80. data/lib/valkyrie/storage.rb +29 -0
  81. data/lib/valkyrie/storage/disk.rb +17 -5
  82. data/lib/valkyrie/storage/fedora.rb +14 -1
  83. data/lib/valkyrie/storage/memory.rb +15 -2
  84. data/lib/valkyrie/storage_adapter.rb +26 -4
  85. data/lib/valkyrie/types.rb +65 -7
  86. data/lib/valkyrie/version.rb +1 -1
  87. data/solr/config/_rest_managed.json +3 -0
  88. data/solr/config/admin-extra.html +31 -0
  89. data/solr/config/elevate.xml +36 -0
  90. data/solr/config/mapping-ISOLatin1Accent.txt +246 -0
  91. data/solr/config/protwords.txt +21 -0
  92. data/solr/config/schema.xml +366 -0
  93. data/solr/config/scripts.conf +24 -0
  94. data/solr/config/solrconfig.xml +322 -0
  95. data/solr/config/spellings.txt +2 -0
  96. data/solr/config/stopwords.txt +58 -0
  97. data/solr/config/stopwords_en.txt +58 -0
  98. data/solr/config/synonyms.txt +31 -0
  99. data/solr/config/xslt/example.xsl +132 -0
  100. data/solr/config/xslt/example_atom.xsl +67 -0
  101. data/solr/config/xslt/example_rss.xsl +66 -0
  102. data/solr/config/xslt/luke.xsl +337 -0
  103. data/solr/solr.xml +35 -0
  104. data/tasks/dev.rake +66 -0
  105. data/valkyrie.gemspec +6 -6
  106. metadata +58 -63
  107. data/lib/valkyrie/decorators/decorator_list.rb +0 -15
  108. data/lib/valkyrie/decorators/decorator_with_arguments.rb +0 -14
  109. data/lib/valkyrie/derivative_service.rb +0 -42
  110. data/lib/valkyrie/file_characterization_service.rb +0 -42
  111. data/lib/valkyrie/local_file_service.rb +0 -11
  112. data/lib/valkyrie/persist_derivatives.rb +0 -29
  113. data/lib/valkyrie/persistence/postgres/queries.rb +0 -8
  114. data/lib/valkyrie/persistence/postgres/queries/find_inverse_references_query.rb +0 -31
  115. data/lib/valkyrie/persistence/postgres/queries/find_members_query.rb +0 -33
  116. data/lib/valkyrie/persistence/postgres/queries/find_references_query.rb +0 -33
  117. data/lib/valkyrie/specs/shared_specs/derivative_service.rb +0 -30
  118. data/lib/valkyrie/specs/shared_specs/file_characterization_service.rb +0 -33
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr
3
3
  require 'valkyrie/persistence/solr/repository'
4
+ # Persister for Solr MetadataAdapter.
5
+ #
6
+ # Most methods are delegated to {Valkyrie::Persistence::Solr::Repository}
4
7
  class Persister
5
8
  attr_reader :adapter
6
9
  delegate :connection, :resource_factory, to: :adapter
@@ -11,6 +14,14 @@ module Valkyrie::Persistence::Solr
11
14
  end
12
15
 
13
16
  # (see Valkyrie::Persistence::Memory::Persister#save)
17
+ #
18
+ # @note Fields are saved using Solr's dynamic fields functionality.
19
+ # If the text has length > 1000, it is stored as *_tsim
20
+ # otherwise it's stored as *_tsim, *_ssim, and *_tesim
21
+ # e.g., a field called 'title' would be stored as 3 solr fields:
22
+ # 'title_tsim'
23
+ # 'title_ssim'
24
+ # 'title_tesim'
14
25
  def save(resource:)
15
26
  repository([resource]).persist.first
16
27
  end
@@ -25,6 +36,11 @@ module Valkyrie::Persistence::Solr
25
36
  repository([resource]).delete.first
26
37
  end
27
38
 
39
+ def wipe!
40
+ connection.delete_by_query("*:*")
41
+ connection.commit
42
+ end
43
+
28
44
  def repository(resources)
29
45
  Valkyrie::Persistence::Solr::Repository.new(resources: resources, connection: connection, resource_factory: resource_factory)
30
46
  end
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr
3
3
  module Queries
4
+ MEMBER_IDS = 'member_ids_ssim'
5
+ MODEL = 'internal_resource_ssim'
4
6
  require 'valkyrie/persistence/solr/queries/default_paginator'
5
7
  require 'valkyrie/persistence/solr/queries/find_all_query'
6
8
  require 'valkyrie/persistence/solr/queries/find_by_id_query'
9
+ require 'valkyrie/persistence/solr/queries/find_many_by_ids_query'
7
10
  require 'valkyrie/persistence/solr/queries/find_inverse_references_query'
8
11
  require 'valkyrie/persistence/solr/queries/find_members_query'
9
12
  require 'valkyrie/persistence/solr/queries/find_references_query'
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr::Queries
3
+ # Acts as a null object representing the default case for paginating over solr
4
+ # results. Often only used for the first iteration of a loop.
3
5
  class DefaultPaginator
4
6
  def next_page
5
7
  1
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr::Queries
3
+ # Responsible for efficiently returning all objects in the solr repository as
4
+ # {Valkyrie::Resource}s
3
5
  class FindAllQuery
4
6
  attr_reader :connection, :resource_factory, :model
5
7
  def initialize(connection:, resource_factory:, model: nil)
@@ -26,7 +28,7 @@ module Valkyrie::Persistence::Solr::Queries
26
28
  if !model
27
29
  "*:*"
28
30
  else
29
- "internal_resource_ssim:#{model}"
31
+ "#{Valkyrie::Persistence::Solr::Queries::MODEL}:#{model}"
30
32
  end
31
33
  end
32
34
  end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr::Queries
3
+ # Responsible for returning a single resource identified by an ID.
3
4
  class FindByIdQuery
4
- attr_reader :id, :connection, :resource_factory
5
+ attr_reader :connection, :resource_factory
6
+ attr_writer :id
5
7
  def initialize(id, connection:, resource_factory:)
6
8
  @id = id
7
9
  @connection = connection
@@ -14,7 +16,7 @@ module Valkyrie::Persistence::Solr::Queries
14
16
  end
15
17
 
16
18
  def id
17
- "id-#{@id}"
19
+ @id.to_s
18
20
  end
19
21
 
20
22
  def resource
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr::Queries
3
+ # Responsible for efficiently returning all {Valkyrie::Resource}s which
4
+ # reference a {Valkyrie::Resource} in a given property.
3
5
  class FindInverseReferencesQuery
4
6
  attr_reader :resource, :property, :connection, :resource_factory
5
7
  def initialize(resource:, property:, connection:, resource_factory:)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence::Solr::Queries
3
+ class FindManyByIdsQuery
4
+ attr_reader :connection, :resource_factory
5
+ attr_accessor :ids
6
+ def initialize(ids, connection:, resource_factory:)
7
+ @ids = ids
8
+ @connection = connection
9
+ @resource_factory = resource_factory
10
+ end
11
+
12
+ def run
13
+ resources.map { |solr_resource| resource_factory.to_resource(object: solr_resource) }
14
+ end
15
+
16
+ def resources
17
+ id_query = ids.map { |id| "\"#{id}\"" }.join(' OR ')
18
+ @resources ||= connection.get("select", params: { q: "id:(#{id_query})", fl: "*", defType: 'lucene', rows: 1_000_000_000 })["response"]["docs"]
19
+ end
20
+ end
21
+ end
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr::Queries
3
+ # Responsible for returning all members of a given resource as
4
+ # {Valkyrie::Resource}s
3
5
  class FindMembersQuery
4
- attr_reader :resource, :connection, :resource_factory
5
- def initialize(resource:, connection:, resource_factory:)
6
+ attr_reader :resource, :connection, :resource_factory, :model
7
+ def initialize(resource:, connection:, resource_factory:, model:)
6
8
  @resource = resource
7
9
  @connection = connection
8
10
  @resource_factory = resource_factory
11
+ @model = model
9
12
  end
10
13
 
11
14
  def run
@@ -13,6 +16,7 @@ module Valkyrie::Persistence::Solr::Queries
13
16
  end
14
17
 
15
18
  def each
19
+ return [] unless resource.id.present?
16
20
  unordered_members.sort_by { |x| member_ids.index(x.id) }.each do |member|
17
21
  yield member
18
22
  end
@@ -25,7 +29,11 @@ module Valkyrie::Persistence::Solr::Queries
25
29
  end
26
30
 
27
31
  def docs
28
- connection.get("select", params: { q: query, rows: 1_000_000_000 })["response"]["docs"]
32
+ options = { q: query, rows: 1_000_000_000 }
33
+ options[:fq] = "{!raw f=internal_resource_ssim}#{model}" if model
34
+ options[:defType] = 'lucene'
35
+ result = connection.get("select", params: options)
36
+ result["response"]["docs"]
29
37
  end
30
38
 
31
39
  def member_ids
@@ -33,11 +41,11 @@ module Valkyrie::Persistence::Solr::Queries
33
41
  end
34
42
 
35
43
  def query
36
- "{!join from=member_ids_ssim to=id}id:#{id}"
44
+ "{!join from=#{MEMBER_IDS} to=join_id_ssi}id:#{id}"
37
45
  end
38
46
 
39
47
  def id
40
- "id-#{resource.id}"
48
+ resource.id.to_s
41
49
  end
42
50
  end
43
51
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr::Queries
3
+ # Responsible for efficently returning all {Valkyrie::Resource}s which are referenced in
4
+ # a given {Valkyrie::Resource}'s property.
3
5
  class FindReferencesQuery
4
6
  attr_reader :resource, :property, :connection, :resource_factory
5
7
  def initialize(resource:, property:, connection:, resource_factory:)
@@ -16,7 +18,9 @@ module Valkyrie::Persistence::Solr::Queries
16
18
  def each
17
19
  docs = DefaultPaginator.new
18
20
  while docs.has_next?
19
- docs = connection.paginate(docs.next_page, docs.per_page, "select", params: { q: query })["response"]["docs"]
21
+ params = { q: query, defType: 'lucene' }
22
+ result = connection.paginate(docs.next_page, docs.per_page, 'select', params: params)
23
+ docs = result.fetch('response').fetch('docs')
20
24
  docs.each do |doc|
21
25
  yield resource_factory.to_resource(object: doc)
22
26
  end
@@ -24,11 +28,11 @@ module Valkyrie::Persistence::Solr::Queries
24
28
  end
25
29
 
26
30
  def query
27
- "{!join from=#{property}_ssim to=id}id:#{id}"
31
+ "{!join from=#{property}_ssim to=join_id_ssi}id:#{id}"
28
32
  end
29
33
 
30
34
  def id
31
- "id-#{resource.id}"
35
+ resource.id.to_s
32
36
  end
33
37
  end
34
38
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr
3
3
  require 'valkyrie/persistence/solr/queries'
4
+ # Query Service for Solr MetadataAdapter.
4
5
  class QueryService
5
6
  attr_reader :connection, :resource_factory
6
7
  # @param connection [RSolr::Client]
@@ -12,9 +13,21 @@ module Valkyrie::Persistence::Solr
12
13
 
13
14
  # (see Valkyrie::Persistence::Memory::QueryService#find_by)
14
15
  def find_by(id:)
16
+ id = Valkyrie::ID.new(id.to_s) if id.is_a?(String)
17
+ validate_id(id)
15
18
  Valkyrie::Persistence::Solr::Queries::FindByIdQuery.new(id, connection: connection, resource_factory: resource_factory).run
16
19
  end
17
20
 
21
+ # (see Valkyrie::Persistence::Memory::QueryService#find_many_by_ids)
22
+ def find_many_by_ids(ids:)
23
+ ids.map! do |id|
24
+ id = Valkyrie::ID.new(id.to_s) if id.is_a?(String)
25
+ validate_id(id)
26
+ id
27
+ end
28
+ Valkyrie::Persistence::Solr::Queries::FindManyByIdsQuery.new(ids, connection: connection, resource_factory: resource_factory).run
29
+ end
30
+
18
31
  # (see Valkyrie::Persistence::Memory::QueryService#find_all)
19
32
  def find_all
20
33
  Valkyrie::Persistence::Solr::Queries::FindAllQuery.new(connection: connection, resource_factory: resource_factory).run
@@ -31,8 +44,8 @@ module Valkyrie::Persistence::Solr
31
44
  end
32
45
 
33
46
  # (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
47
+ def find_members(resource:, model: nil)
48
+ Valkyrie::Persistence::Solr::Queries::FindMembersQuery.new(resource: resource, model: model, connection: connection, resource_factory: resource_factory).run
36
49
  end
37
50
 
38
51
  # (see Valkyrie::Persistence::Memory::QueryService#find_references_by)
@@ -42,7 +55,22 @@ module Valkyrie::Persistence::Solr
42
55
 
43
56
  # (see Valkyrie::Persistence::Memory::QueryService#find_inverse_references_by)
44
57
  def find_inverse_references_by(resource:, property:)
58
+ ensure_persisted(resource)
45
59
  Valkyrie::Persistence::Solr::Queries::FindInverseReferencesQuery.new(resource: resource, property: property, connection: connection, resource_factory: resource_factory).run
46
60
  end
61
+
62
+ def custom_queries
63
+ @custom_queries ||= ::Valkyrie::Persistence::CustomQueryContainer.new(query_service: self)
64
+ end
65
+
66
+ private
67
+
68
+ def validate_id(id)
69
+ raise ArgumentError, 'id must be a Valkyrie::ID' unless id.is_a? Valkyrie::ID
70
+ end
71
+
72
+ def ensure_persisted(resource)
73
+ raise ArgumentError, 'resource is not saved' unless resource.persisted?
74
+ end
47
75
  end
48
76
  end
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr
3
+ # Responsible for handling the logic for persisting or deleting multiple
4
+ # objects into or out of solr.
3
5
  class Repository
6
+ COMMIT_PARAMS = { softCommit: true }.freeze
7
+
4
8
  attr_reader :resources, :connection, :resource_factory
5
9
  def initialize(resources:, connection:, resource_factory:)
6
10
  @resources = resources
@@ -11,16 +15,17 @@ module Valkyrie::Persistence::Solr
11
15
  def persist
12
16
  documents = resources.map do |resource|
13
17
  generate_id(resource) if resource.id.blank?
18
+ ensure_multiple_values!(resource)
14
19
  solr_document(resource)
15
20
  end
16
- connection.add documents, params: { softCommit: true }
21
+ connection.add documents, params: COMMIT_PARAMS
17
22
  documents.map do |document|
18
23
  resource_factory.to_resource(object: document.stringify_keys)
19
24
  end
20
25
  end
21
26
 
22
27
  def delete
23
- connection.delete_by_id resources.map { |resource| "id-#{resource.id}" }, params: { softCommit: true }
28
+ connection.delete_by_id resources.map { |resource| resource.id.to_s }, params: COMMIT_PARAMS
24
29
  resources
25
30
  end
26
31
 
@@ -32,5 +37,12 @@ module Valkyrie::Persistence::Solr
32
37
  Valkyrie.logger.warn "The Solr adapter is not meant to persist new resources, but is now generating an ID."
33
38
  resource.id = SecureRandom.uuid
34
39
  end
40
+
41
+ def ensure_multiple_values!(resource)
42
+ bad_keys = resource.attributes.except(:internal_resource, :created_at, :updated_at, :new_record, :id).select do |_k, v|
43
+ !v.nil? && !v.is_a?(Array)
44
+ end
45
+ raise ::Valkyrie::Persistence::UnsupportedDatatype, "#{resource}: #{bad_keys.keys} have non-array values, which can not be persisted by Valkyrie. Cast to arrays." unless bad_keys.keys.empty?
46
+ end
35
47
  end
36
48
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie::Persistence::Solr
3
+ # Provides access to generic methods for converting to/from
4
+ # {Valkyrie::Resource} and hashes for persistence into Solr.
3
5
  class ResourceFactory
4
6
  require 'valkyrie/persistence/solr/orm_converter'
5
7
  require 'valkyrie/persistence/solr/model_converter'
@@ -8,7 +10,7 @@ module Valkyrie::Persistence::Solr
8
10
  @resource_indexer = resource_indexer
9
11
  end
10
12
 
11
- # @param solr_document [Hash] The solr document in a hash to convert to a
13
+ # @param object [Hash] The solr document in a hash to convert to a
12
14
  # resource.
13
15
  # @return [Valkyrie::Resource]
14
16
  def to_resource(object:)
@@ -8,6 +8,12 @@ module Valkyrie
8
8
  # attribute :member_ids, Valkyrie::Types::Array
9
9
  # attribute :author
10
10
  # end
11
+ #
12
+ # @see https://github.com/samvera-labs/valkyrie/wiki/Persistence Resources are persisted by metadata persisters
13
+ # @see https://github.com/samvera-labs/valkyrie/wiki/Queries Resources are retrieved by query adapters
14
+ # @see https://github.com/samvera-labs/valkyrie/wiki/ChangeSets-and-Dirty-Tracking Validation and change tracking is provided by change sets
15
+ #
16
+ # @see lib/valkyrie/specs/shared_specs/resource.rb
11
17
  class Resource < Dry::Struct
12
18
  include Draper::Decoratable
13
19
  constructor_type :schema
@@ -15,16 +21,17 @@ module Valkyrie
15
21
  # Overridden to provide default attributes.
16
22
  # @note The current theory is that we should use this sparingly.
17
23
  def self.inherited(subclass)
18
- ::Dry::Struct.inherited(subclass)
24
+ super(subclass)
19
25
  subclass.constructor_type :schema
20
26
  subclass.attribute :internal_resource, Valkyrie::Types::Any.default(subclass.to_s)
21
27
  subclass.attribute :created_at, Valkyrie::Types::DateTime.optional
22
28
  subclass.attribute :updated_at, Valkyrie::Types::DateTime.optional
29
+ subclass.attribute :new_record, Types::Bool.default(true)
23
30
  end
24
31
 
25
32
  # @return [Array<Symbol>] Array of fields defined for this class.
26
33
  def self.fields
27
- schema.keys
34
+ schema.keys.without(:new_record)
28
35
  end
29
36
 
30
37
  # Define an attribute.
@@ -75,7 +82,7 @@ module Valkyrie
75
82
 
76
83
  # @return [Boolean]
77
84
  def persisted?
78
- to_param.present?
85
+ @new_record == false
79
86
  end
80
87
 
81
88
  def to_key
@@ -83,7 +90,7 @@ module Valkyrie
83
90
  end
84
91
 
85
92
  def to_param
86
- id
93
+ to_key.map(&:to_s).join('-')
87
94
  end
88
95
 
89
96
  # @note Added for ActiveModel compatibility
@@ -1,6 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie
3
3
  class Resource
4
+ # Provides an optional interface consistent with Hydra::AccessControls
5
+ #
6
+ # @example
7
+ # class CustomResource < Valkyrie::Resource
8
+ # include Valkyrie::Resource::AccessControls
9
+ # attribute :id, Valkyrie::Types::ID.optional
10
+ # attribute :title
11
+ # attribute :member_ids
12
+ # attribute :nested_resource
13
+ # end
14
+ #
15
+ # @see https://github.com/samvera/hydra-head/tree/master/hydra-access-controls
16
+ # @see lib/valkyrie/indexers/access_controls_indexer/rb
4
17
  module AccessControls
5
18
  def self.included(klass)
6
19
  klass.attribute :read_groups, Valkyrie::Types::Set
@@ -4,7 +4,6 @@ require 'valkyrie/specs/shared_specs/queries.rb'
4
4
  require 'valkyrie/specs/shared_specs/metadata_adapter'
5
5
  require 'valkyrie/specs/shared_specs/resource.rb'
6
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
7
  require 'valkyrie/specs/shared_specs/change_set_persister.rb'
10
8
  require 'valkyrie/specs/shared_specs/file.rb'
9
+ require 'valkyrie/specs/shared_specs/change_set.rb'
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+ RSpec.shared_examples 'a Valkyrie::ChangeSet' do |*_flags|
3
+ before do
4
+ raise 'adapter must be set with `let(:change_set)`' unless defined? change_set
5
+ raise 'change_set must have at least one field' if change_set.fields.empty?
6
+ end
7
+
8
+ subject { change_set }
9
+
10
+ it { is_expected.to respond_to :append_id }
11
+ it { is_expected.to respond_to :fields }
12
+ it { is_expected.to respond_to :fields= }
13
+ it { is_expected.to respond_to :multiple? }
14
+ it { is_expected.to respond_to :prepopulate! }
15
+ it { is_expected.to respond_to :required? }
16
+ it { is_expected.to respond_to :validate }
17
+ it { is_expected.to respond_to :valid? }
18
+
19
+ it "can set an append_id" do
20
+ change_set.append_id = Valkyrie::ID.new("test")
21
+ expect(change_set.append_id).to eq Valkyrie::ID.new("test")
22
+ expect(change_set[:append_id]).to eq Valkyrie::ID.new("test")
23
+ end
24
+
25
+ describe "#fields" do
26
+ it "returns an hash" do
27
+ expect(change_set.fields).to be_a Hash
28
+ end
29
+ end
30
+
31
+ describe "#fields=" do
32
+ it "sets fields" do
33
+ change_set.fields = { "totally_a_field" => [] }
34
+ expect(change_set.fields).to eq("totally_a_field" => [])
35
+ end
36
+ end
37
+
38
+ describe "#multiple?" do
39
+ it "returns a boolean" do
40
+ expect(change_set.multiple?(change_set.fields.keys.first)).to be_in [true, false]
41
+ end
42
+ end
43
+
44
+ describe "#prepopulate!" do
45
+ it "doesn't make it look changed" do
46
+ expect(change_set).not_to be_changed
47
+ change_set.prepopulate!
48
+ expect(change_set).not_to be_changed
49
+ end
50
+ end
51
+
52
+ describe "#required?" do
53
+ it "returns a boolean" do
54
+ expect(change_set.required?(change_set.fields.keys.first)).to be_in [true, false]
55
+ end
56
+ end
57
+
58
+ describe "#valid?" do
59
+ it "returns a boolean" do
60
+ expect(change_set.valid?).to be_in [true, false]
61
+ end
62
+ end
63
+
64
+ describe "#validate" do
65
+ it "returns a change_set" do
66
+ expect(change_set.validate(change_set.fields)).to be_in [true, false]
67
+ end
68
+ end
69
+
70
+ describe ".validators_on" do
71
+ it "the class responds to validators_on" do
72
+ expect(described_class).to respond_to(:validators_on)
73
+ end
74
+ end
75
+ end