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