valkyrie 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,11 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie
3
+ class LocalFileService
4
+ # @param [String] file_name path to the file
5
+ # @param [Hash] _options
6
+ # @yield [File] opens the file and yields it to the block
7
+ def self.call(file_name, _options)
8
+ yield File.open(file_name)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie
3
+ class MetadataAdapter
4
+ class_attribute :adapters
5
+ self.adapters = {}
6
+ class << self
7
+ # Register an adapter by a short name.
8
+ # @param adapter [#persister,#query_service] Adapter to register.
9
+ # @param short_name [Symbol] Name to register it under.
10
+ def register(adapter, short_name)
11
+ adapters[short_name.to_sym] = adapter
12
+ end
13
+
14
+ # Find an adapter by its short name.
15
+ # @param short_name [Symbol]
16
+ # @return [#persister,#query_service]
17
+ def find(short_name)
18
+ adapters[short_name.to_sym]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie
3
+ class PersistDerivatives < Hydra::Derivatives::PersistOutputFileService
4
+ # Persists a derivative to the local file system.
5
+ # This Service conforms to the signature of `Hydra::Derivatives::PersistOutputFileService`.
6
+ # This service is an alternative to the default Hydra::Derivatives::PersistOutputFileService.
7
+ # This service will always update existing and does not do versioning of persisted files.
8
+ #
9
+ # @param [#read] stream the derivative filestream
10
+ # @param [Hash] directives
11
+ # @option directives [String] :url a url to the file destination
12
+ def self.call(stream, directives)
13
+ output_file(directives) do |output|
14
+ IO.copy_stream(stream, output)
15
+ end
16
+ end
17
+
18
+ # Open the output file to write and yield the block to the
19
+ # file. It makes the directories in the path if necessary.
20
+ def self.output_file(directives, &blk)
21
+ raise ArgumentError, "No :url was provided in the transcoding directives" unless directives.key?(:url)
22
+ uri = URI(directives.fetch(:url))
23
+ raise ArgumentError, "Must provide a file uri" unless uri.scheme == 'file'
24
+ output_file_dir = File.dirname(uri.path)
25
+ FileUtils.mkdir_p(output_file_dir) unless File.directory?(output_file_dir)
26
+ File.open(uri.path, 'wb', &blk)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie
3
+ module Persistence
4
+ require 'valkyrie/persistence/memory'
5
+ require 'valkyrie/persistence/postgres'
6
+ require 'valkyrie/persistence/solr'
7
+ require 'valkyrie/persistence/fedora'
8
+ require 'valkyrie/persistence/composite_persister'
9
+ require 'valkyrie/persistence/delete_tracking_buffer'
10
+ require 'valkyrie/persistence/buffered_persister'
11
+ class ObjectNotFoundError < StandardError
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence
3
+ class BufferedPersister
4
+ attr_reader :persister, :buffer_class
5
+ delegate :adapter, to: :persister
6
+ def initialize(persister, buffer_class: Valkyrie::Persistence::DeleteTrackingBuffer)
7
+ @persister = persister
8
+ @buffer_class = buffer_class
9
+ end
10
+
11
+ def save(resource:)
12
+ persister.save(resource: resource)
13
+ end
14
+
15
+ def save_all(resources:)
16
+ persister.save_all(resources: resources)
17
+ end
18
+
19
+ def delete(resource:)
20
+ persister.delete(resource: resource)
21
+ end
22
+
23
+ def with_buffer
24
+ memory_buffer = buffer_class.new
25
+ yield [Valkyrie::Persistence::CompositePersister.new(self, memory_buffer.persister), memory_buffer]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence
3
+ ##
4
+ # Wrap up multiple persisters under a common interface, to transparently
5
+ # persist to multiple places at once.
6
+ class CompositePersister
7
+ attr_reader :persisters
8
+ def initialize(*persisters)
9
+ @persisters = persisters
10
+ end
11
+
12
+ # (see Valkyrie::Persistence::Memory::Persister#save)
13
+ def save(resource:)
14
+ persisters.inject(resource) { |m, persister| persister.save(resource: m) }
15
+ end
16
+
17
+ # (see Valkyrie::Persistence::Memory::Persister#save_all)
18
+ def save_all(resources:)
19
+ resources.map do |resource|
20
+ save(resource: resource)
21
+ end
22
+ end
23
+
24
+ # (see Valkyrie::Persistence::Memory::Persister#delete)
25
+ def delete(resource:)
26
+ persisters.inject(resource) { |m, persister| persister.delete(resource: m) }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence
3
+ class DeleteTrackingBuffer < Valkyrie::Persistence::Memory::MetadataAdapter
4
+ def persister
5
+ @persister ||= DeleteTrackingBuffer::Persister.new(self)
6
+ end
7
+
8
+ class Persister < Valkyrie::Persistence::Memory::Persister
9
+ attr_reader :deletes
10
+ def initialize(*args)
11
+ @deletes = []
12
+ super
13
+ end
14
+
15
+ def delete(resource:)
16
+ @deletes << resource
17
+ super
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence
3
+ module Fedora
4
+ require 'valkyrie/persistence/fedora/metadata_adapter'
5
+ require 'valkyrie/persistence/fedora/persister'
6
+ require 'valkyrie/persistence/fedora/query_service'
7
+ require 'valkyrie/persistence/fedora/ordered_list'
8
+ require 'valkyrie/persistence/fedora/ordered_reader'
9
+ require 'valkyrie/persistence/fedora/list_node'
10
+ end
11
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence::Fedora
3
+ class ListNode
4
+ attr_reader :rdf_subject, :graph
5
+ attr_accessor :prev, :next, :target
6
+ attr_writer :next_uri, :prev_uri
7
+ attr_accessor :proxy_in, :proxy_for
8
+ attr_reader :adapter
9
+ def initialize(node_cache, rdf_subject, adapter, graph = RDF::Repository.new)
10
+ @rdf_subject = rdf_subject
11
+ @graph = graph
12
+ @node_cache = node_cache
13
+ @adapter = adapter
14
+ Builder.new(rdf_subject, graph).populate(self)
15
+ end
16
+
17
+ # Returns the next proxy or a tail sentinel.
18
+ # @return [ActiveFedora::Orders::ListNode]
19
+ def next
20
+ @next ||=
21
+ if next_uri
22
+ node_cache.fetch(next_uri) do
23
+ node = self.class.new(node_cache, next_uri, adapter, graph)
24
+ node.prev = self
25
+ node
26
+ end
27
+ end
28
+ end
29
+
30
+ # Returns the previous proxy or a head sentinel.
31
+ # @return [ActiveFedora::Orders::ListNode]
32
+ def prev
33
+ @prev ||= node_cache.fetch(prev_uri) if prev_uri
34
+ end
35
+
36
+ # Graph representation of node.
37
+ # @return [ActiveFedora::Orders::ListNode::Resource]
38
+ def to_graph
39
+ return RDF::Graph.new if target_id.blank?
40
+ g = Resource.new(rdf_subject)
41
+ g.proxy_for = target_uri
42
+ g.proxy_in = proxy_in.try(:uri)
43
+ g.next = self.next.try(:rdf_subject)
44
+ g.prev = prev.try(:rdf_subject)
45
+ g
46
+ end
47
+
48
+ def target_uri
49
+ adapter.id_to_uri(target_id.to_s)
50
+ end
51
+
52
+ def target_id
53
+ adapter.uri_to_id(proxy_for)
54
+ end
55
+
56
+ private
57
+
58
+ attr_reader :next_uri, :prev_uri, :graph, :node_cache
59
+
60
+ class Builder
61
+ attr_reader :uri, :graph
62
+ def initialize(uri, graph)
63
+ @uri = uri
64
+ @graph = graph
65
+ end
66
+
67
+ def populate(instance)
68
+ instance.proxy_for = resource.proxy_for.first
69
+ instance.proxy_in = resource.proxy_in.first
70
+ instance.next_uri = resource.next.first
71
+ instance.prev_uri = resource.prev.first
72
+ end
73
+
74
+ private
75
+
76
+ def resource
77
+ @resource ||= Resource.new(uri, data: graph)
78
+ end
79
+ end
80
+
81
+ class Resource < ActiveTriples::Resource
82
+ property :proxy_for, predicate: ::RDF::Vocab::ORE.proxyFor, cast: false
83
+ property :proxy_in, predicate: ::RDF::Vocab::ORE.proxyIn, cast: false
84
+ property :next, predicate: ::RDF::Vocab::IANA.next, cast: false
85
+ property :prev, predicate: ::RDF::Vocab::IANA.prev, cast: false
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence::Fedora
3
+ class MetadataAdapter
4
+ attr_reader :connection, :base_path
5
+ def initialize(connection:, base_path: "/")
6
+ @connection = connection
7
+ @base_path = base_path
8
+ end
9
+
10
+ def query_service
11
+ Valkyrie::Persistence::Fedora::QueryService.new(adapter: self)
12
+ end
13
+
14
+ def persister
15
+ Valkyrie::Persistence::Fedora::Persister.new(adapter: self)
16
+ end
17
+
18
+ def resource_factory
19
+ Valkyrie::Persistence::Fedora::Persister::ResourceFactory.new(adapter: self)
20
+ end
21
+
22
+ def uri_to_id(uri)
23
+ Valkyrie::ID.new(uri.to_s.gsub(/^.*\//, ''))
24
+ end
25
+
26
+ def id_to_uri(id)
27
+ RDF::URI("#{connection_prefix}/#{pair_path(id)}/#{id}")
28
+ end
29
+
30
+ def pair_path(id)
31
+ id.to_s.split("-").first.split("").each_slice(2).map(&:join).join("/")
32
+ end
33
+
34
+ def connection_prefix
35
+ "#{connection.options}/#{base_path}"
36
+ end
37
+
38
+ def wipe!
39
+ connection.delete(base_path)
40
+ connection.delete("#{base_path}/fcr:tombstone")
41
+ rescue => error
42
+ Valkyrie.logger.debug("Failed to wipe Fedora for some reason.") unless error.is_a?(::Ldp::NotFound)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie::Persistence::Fedora
3
+ ##
4
+ # Ruby object representation of an ORE doubly linked list.
5
+ class OrderedList
6
+ include Enumerable
7
+ attr_reader :graph, :head_subject, :tail_subject, :adapter
8
+ attr_writer :head, :tail
9
+ delegate :each, to: :ordered_reader
10
+ delegate :length, to: :to_a
11
+ # @param [::RDF::Enumerable] graph Enumerable where ORE statements are
12
+ # stored.
13
+ # @param [::RDF::URI] head_subject URI of head node in list.
14
+ # @param [::RDF::URI] tail_subject URI of tail node in list.
15
+ def initialize(graph, head_subject, tail_subject, adapter)
16
+ @graph = graph
17
+ @head_subject = head_subject
18
+ @tail_subject = tail_subject
19
+ @node_cache ||= NodeCache.new
20
+ @adapter = adapter
21
+ @changed = false
22
+ tail
23
+ end
24
+
25
+ # @return [HeadSentinel] Sentinel for the top of the list. If not empty,
26
+ # head.next is the first element.
27
+ def head
28
+ @head ||= HeadSentinel.new(self, next_node: build_node(head_subject))
29
+ end
30
+
31
+ # @return [TailSentinel] Sentinel for the bottom of the list. If not
32
+ # empty, tail.prev is the first element.
33
+ def tail
34
+ @tail ||=
35
+ begin
36
+ if tail_subject
37
+ TailSentinel.new(self, prev_node: build_node(tail_subject))
38
+ else
39
+ head.next
40
+ end
41
+ end
42
+ end
43
+
44
+ # @param [Integer] loc Location to insert target at
45
+ # @param [String] proxy_for proxyFor to add
46
+ def insert_proxy_for_at(loc, proxy_for, proxy_in: nil)
47
+ node = build_node(new_node_subject)
48
+ node.proxy_for = proxy_for
49
+ node.proxy_in = proxy_in
50
+ if loc.zero?
51
+ append_to(node, head)
52
+ else
53
+ append_to(node, ordered_reader.take(loc).last)
54
+ end
55
+ end
56
+
57
+ # @return [::RDF::Graph] Graph representation of this list.
58
+ def to_graph
59
+ ::RDF::Graph.new.tap do |g|
60
+ array = to_a
61
+ array.map(&:to_graph).each do |resource_graph|
62
+ g << resource_graph
63
+ end
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ attr_reader :node_cache
70
+
71
+ def append_to(source, append_node)
72
+ source.prev = append_node
73
+ append_node.next.prev = source
74
+ source.next = append_node.next
75
+ append_node.next = source
76
+ @changed = true
77
+ end
78
+
79
+ def ordered_reader
80
+ OrderedReader.new(self)
81
+ end
82
+
83
+ def build_node(subject = nil)
84
+ return nil unless subject
85
+ node_cache.fetch(subject) do
86
+ ListNode.new(node_cache, subject, adapter, graph)
87
+ end
88
+ end
89
+
90
+ def new_node_subject
91
+ node = ::RDF::URI("##{::RDF::Node.new.id}")
92
+ node = ::RDF::URI("##{::RDF::Node.new.id}") while node_cache.key?(node)
93
+ node
94
+ end
95
+
96
+ class NodeCache
97
+ def initialize
98
+ @cache ||= {}
99
+ end
100
+
101
+ def fetch(uri)
102
+ @cache[uri] ||= yield if block_given?
103
+ end
104
+
105
+ def key?(key)
106
+ @cache.key?(key)
107
+ end
108
+ end
109
+
110
+ class Sentinel
111
+ attr_reader :parent
112
+ attr_writer :next, :prev
113
+ def initialize(parent, next_node: nil, prev_node: nil)
114
+ @parent = parent
115
+ @next = next_node
116
+ @prev = prev_node
117
+ end
118
+
119
+ attr_reader :next
120
+
121
+ attr_reader :prev
122
+
123
+ def nil?
124
+ true
125
+ end
126
+
127
+ def rdf_subject
128
+ nil
129
+ end
130
+ end
131
+
132
+ class HeadSentinel < Sentinel
133
+ def initialize(*args)
134
+ super
135
+ @next ||= TailSentinel.new(parent, prev_node: self)
136
+ end
137
+ end
138
+
139
+ class TailSentinel < Sentinel
140
+ def initialize(*args)
141
+ super
142
+ prev.next = self if prev && prev.next != self
143
+ end
144
+ end
145
+ end
146
+ end