active_fedora-datastreams 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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +140 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/README.md +34 -0
- data/Rakefile +27 -0
- data/active_fedora-datastreams.gemspec +32 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config/fedora.yml +14 -0
- data/config/solr.yml +15 -0
- data/lib/active_fedora/datastreams.rb +21 -0
- data/lib/active_fedora/datastreams/nokogiri_datastreams.rb +119 -0
- data/lib/active_fedora/datastreams/version.rb +5 -0
- data/lib/active_fedora/nom_datastream.rb +71 -0
- data/lib/active_fedora/om_datastream.rb +112 -0
- data/lib/active_fedora/qualified_dublin_core_datastream.rb +158 -0
- data/lib/active_fedora/rdf/datastream_indexing.rb +49 -0
- data/lib/active_fedora/rdf/ntriples_rdf_datastream.rb +9 -0
- data/lib/active_fedora/rdf/rdf_datastream.rb +164 -0
- data/lib/active_fedora/rdf/rdfxml_datastream.rb +13 -0
- metadata +240 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
require "nom"
|
2
|
+
|
3
|
+
module ActiveFedora
|
4
|
+
class NomDatastream < File
|
5
|
+
include Datastreams::NokogiriDatastreams
|
6
|
+
|
7
|
+
def self.set_terminology(options = {}, &block)
|
8
|
+
@terminology_options = options || {}
|
9
|
+
@terminology = block
|
10
|
+
end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_reader :terminology_options
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
attr_reader :terminology
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.decorate_ng_xml(xml)
|
21
|
+
xml.set_terminology terminology_options, &terminology
|
22
|
+
xml.nom!
|
23
|
+
xml
|
24
|
+
end
|
25
|
+
|
26
|
+
def serialize!
|
27
|
+
self.content = @ng_xml.to_s if @ng_xml
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_solr
|
31
|
+
solr_doc = {}
|
32
|
+
|
33
|
+
ng_xml.terminology.flatten.select { |x| x.options[:index] }.each do |term|
|
34
|
+
term.values.each do |v|
|
35
|
+
Array(term.options[:index]).each do |index_as|
|
36
|
+
solr_doc[index_as] ||= []
|
37
|
+
solr_doc[index_as] << if v.is_a? Nokogiri::XML::Node
|
38
|
+
v.text
|
39
|
+
else
|
40
|
+
v
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
solr_doc
|
47
|
+
end
|
48
|
+
|
49
|
+
def method_missing(method, *args, &block)
|
50
|
+
if ng_xml.respond_to? method
|
51
|
+
ng_xml.send(method, *args, &block)
|
52
|
+
else
|
53
|
+
super
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def respond_to_missing?(*args)
|
58
|
+
ng_xml.respond_to?(*args)
|
59
|
+
end
|
60
|
+
|
61
|
+
def respond_to?(*args)
|
62
|
+
super || self.class.terminology.respond_to?(*args)
|
63
|
+
end
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
def default_mime_type
|
68
|
+
'text/xml'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require "om"
|
2
|
+
|
3
|
+
module ActiveFedora
|
4
|
+
class OmDatastream < File
|
5
|
+
# before_save do
|
6
|
+
# if content.blank?
|
7
|
+
# ActiveFedora::Base.logger.warn "Cowardly refusing to save a datastream with empty content: #{self.inspect}"
|
8
|
+
# false
|
9
|
+
# end
|
10
|
+
# end
|
11
|
+
|
12
|
+
include OM::XML::Document
|
13
|
+
include OM::XML::TerminologyBasedSolrizer # this adds support for calling .to_solr
|
14
|
+
include Datastreams::NokogiriDatastreams
|
15
|
+
|
16
|
+
alias om_term_values term_values unless method_defined?(:om_term_values)
|
17
|
+
alias om_update_values update_values unless method_defined?(:om_update_values)
|
18
|
+
|
19
|
+
def default_mime_type
|
20
|
+
'text/xml'
|
21
|
+
end
|
22
|
+
|
23
|
+
# Indicates that this datastream has metadata content.
|
24
|
+
# @return true
|
25
|
+
def metadata?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
# Return a hash suitable for indexing in solr. Every field name is prefixed with the
|
30
|
+
# value returned by the +prefix+ method.
|
31
|
+
def to_solr(solr_doc = {}, opts = {})
|
32
|
+
prefix = self.prefix(opts[:name])
|
33
|
+
solr_doc.merge super({}).each_with_object({}) { |(key, value), new| new[[prefix, key].join] = value }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Update field values within the current datastream using {#update_values}, which is a wrapper for {http://rdoc.info/gems/om/1.2.4/OM/XML/TermValueOperators#update_values-instance_method OM::TermValueOperators#update_values}
|
37
|
+
# Ignores any fields from params that this datastream's Terminology doesn't recognize
|
38
|
+
#
|
39
|
+
# @param [Hash] params The params specifying which fields to update and their new values. The syntax of the params Hash is the same as that expected by
|
40
|
+
# term_pointers must be a valid OM Term pointers (ie. [:name]). Strings will be ignored.
|
41
|
+
# @param [Hash] _opts This is not currently used by the datastream-level update_indexed_attributes method
|
42
|
+
#
|
43
|
+
# Example:
|
44
|
+
# @mods_ds.update_indexed_attributes( {[{":person"=>"0"}, "role"]=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"} })
|
45
|
+
# => {"person_0_role"=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"}}
|
46
|
+
#
|
47
|
+
# @mods_ds.to_xml # (the following is an approximation)
|
48
|
+
# <mods>
|
49
|
+
# <mods:name type="person">
|
50
|
+
# <mods:role>
|
51
|
+
# <mods:roleTerm>role1</mods:roleTerm>
|
52
|
+
# </mods:role>
|
53
|
+
# <mods:role>
|
54
|
+
# <mods:roleTerm>role2</mods:roleTerm>
|
55
|
+
# </mods:role>
|
56
|
+
# <mods:role>
|
57
|
+
# <mods:roleTerm>role3</mods:roleTerm>
|
58
|
+
# </mods:role>
|
59
|
+
# </mods:name>
|
60
|
+
# </mods>
|
61
|
+
def update_indexed_attributes(params = {}, _opts = {})
|
62
|
+
if self.class.terminology.nil?
|
63
|
+
raise "No terminology is set for this OmDatastream class. Cannot perform update_indexed_attributes"
|
64
|
+
end
|
65
|
+
# remove any fields from params that this datastream doesn't recognize
|
66
|
+
# make sure to make a copy of params so not to modify hash that might be passed to other methods
|
67
|
+
current_params = params.clone
|
68
|
+
current_params.delete_if do |term_pointer, new_values|
|
69
|
+
if term_pointer.is_a?(String)
|
70
|
+
ActiveFedora::Base.logger.warn "WARNING: #{self.class.name} ignoring {#{term_pointer.inspect} => #{new_values.inspect}} because #{term_pointer.inspect} is a String (only valid OM Term Pointers will be used). Make sure your html has the correct field_selector tags in it." if ActiveFedora::Base.logger
|
71
|
+
true
|
72
|
+
else
|
73
|
+
!self.class.terminology.has_term?(*OM.destringify(term_pointer))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
result = {}
|
78
|
+
result = update_values(current_params) unless current_params.empty?
|
79
|
+
|
80
|
+
result
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_values(field_key, _default = [])
|
84
|
+
term_values(*field_key)
|
85
|
+
end
|
86
|
+
|
87
|
+
def find_by_terms(*termpointer)
|
88
|
+
super
|
89
|
+
end
|
90
|
+
|
91
|
+
# Update values in the datastream's xml
|
92
|
+
# This wraps {http://rdoc.info/gems/om/1.2.4/OM/XML/TermValueOperators#update_values-instance_method OM::TermValueOperators#update_values} so that returns an error if we have loaded from solr since datastreams loaded that way should be read-only
|
93
|
+
#
|
94
|
+
# @example Updating multiple values with a Hash of Term pointers and values
|
95
|
+
# ds.update_values( {[{":person"=>"0"}, "role", "text"]=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"}, [{:person=>1}, :family_name]=>"Andronicus", [{"person"=>"1"},:given_name]=>["Titus"],[{:person=>1},:role,:text]=>["otherrole1","otherrole2"] } )
|
96
|
+
# => {"person_0_role_text"=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"}, "person_1_role_text"=>{"0"=>"otherrole1", "1"=>"otherrole2"}}
|
97
|
+
def update_values(params = {})
|
98
|
+
raise "can't modify frozen #{self.class}" if frozen?
|
99
|
+
ng_xml_will_change!
|
100
|
+
result = om_update_values(params)
|
101
|
+
result
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
# The string to prefix all solr fields with. Override this method if you want
|
107
|
+
# a prefix other than the default
|
108
|
+
def prefix(path)
|
109
|
+
path ? "#{path.underscore}__" : ''
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module ActiveFedora
|
2
|
+
# This class represents a Qualified Dublin Core Datastream. A special case of ActiveFedora::OmDatastream
|
3
|
+
# The implementation of this class defines the terms from the Qualified Dublin Core specification.
|
4
|
+
# This implementation features customized xml generators and deserialization routines to handle the
|
5
|
+
# Fedora Dublin Core XML datastreams structure.
|
6
|
+
#
|
7
|
+
# Fields can still be overridden if more specificity is desired (see ActiveFedora::File#fields method).
|
8
|
+
class QualifiedDublinCoreDatastream < OmDatastream
|
9
|
+
attr_accessor :fields
|
10
|
+
class_attribute :class_fields
|
11
|
+
self.class_fields = []
|
12
|
+
|
13
|
+
set_terminology do |t|
|
14
|
+
t.root(path: "dc", xmlns: "http://purl.org/dc/terms/")
|
15
|
+
end
|
16
|
+
|
17
|
+
define_template :creator do |xml, name|
|
18
|
+
xml.creator do
|
19
|
+
xml.text(name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# A frozen array of Dublincore Terms.
|
24
|
+
DCTERMS = [
|
25
|
+
:abstract,
|
26
|
+
:accessRights,
|
27
|
+
:accrualMethod,
|
28
|
+
:accrualPeriodicity,
|
29
|
+
:accrualPolicy,
|
30
|
+
:alternative,
|
31
|
+
:audience,
|
32
|
+
:available,
|
33
|
+
:bibliographicCitation,
|
34
|
+
:conformsTo,
|
35
|
+
:contributor,
|
36
|
+
:coverage,
|
37
|
+
:created,
|
38
|
+
:creator,
|
39
|
+
:date,
|
40
|
+
:dateAccepted,
|
41
|
+
:dateCopyrighted,
|
42
|
+
:dateSubmitted,
|
43
|
+
:description,
|
44
|
+
:educationLevel,
|
45
|
+
:extent,
|
46
|
+
:hasFormat,
|
47
|
+
:hasPart,
|
48
|
+
:hasVersion,
|
49
|
+
:identifier,
|
50
|
+
:instructionalMethod,
|
51
|
+
:isFormatOf,
|
52
|
+
:isPartOf,
|
53
|
+
:isReferencedBy,
|
54
|
+
:isReplacedBy,
|
55
|
+
:isRequiredBy,
|
56
|
+
:isVersionOf,
|
57
|
+
:issued,
|
58
|
+
:language,
|
59
|
+
:license,
|
60
|
+
:mediator,
|
61
|
+
:medium,
|
62
|
+
:modified,
|
63
|
+
:provenance,
|
64
|
+
:publisher,
|
65
|
+
:references,
|
66
|
+
:relation,
|
67
|
+
:replaces,
|
68
|
+
:requires,
|
69
|
+
:rights,
|
70
|
+
:rightsHolder,
|
71
|
+
:source,
|
72
|
+
:spatial,
|
73
|
+
:subject,
|
74
|
+
:tableOfContents,
|
75
|
+
:temporal,
|
76
|
+
:title,
|
77
|
+
:type,
|
78
|
+
:valid
|
79
|
+
].freeze # removed :format
|
80
|
+
DCTERMS.freeze
|
81
|
+
|
82
|
+
# Constructor. this class will call self.field for each DCTERM. In short, all DCTERMS fields will already exist
|
83
|
+
# when this method returns. Each term is marked as a multivalue string.
|
84
|
+
def initialize(string_or_url = nil)
|
85
|
+
super
|
86
|
+
self.fields = {}
|
87
|
+
DCTERMS.each do |el|
|
88
|
+
field el, :string, multiple: true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# This method generates the various accessor and mutator methods on self for the datastream metadata attributes.
|
93
|
+
# each field will have the 2 magic methods:
|
94
|
+
# name=(arg)
|
95
|
+
# name
|
96
|
+
#
|
97
|
+
#
|
98
|
+
# Calling any of the generated methods marks self as dirty.
|
99
|
+
#
|
100
|
+
# 'tupe' is a datatype, currently :string, :text and :date are supported.
|
101
|
+
#
|
102
|
+
# opts is an options hash, which will affect the generation of the xml representation of this datastream.
|
103
|
+
#
|
104
|
+
# Currently supported modifiers:
|
105
|
+
# For +QualifiedDublinCorDatastreams+:
|
106
|
+
# :element_attrs =>{:foo=>:bar} - hash of xml element attributes
|
107
|
+
# :xml_node => :nodename - The xml node to be used to represent this object (in dcterms namespace)
|
108
|
+
# :encoding=>foo, or encodings_scheme - causes an xsi:type attribute to be set to 'foo'
|
109
|
+
# :multiple=>true - mark this field as a multivalue field (on by default)
|
110
|
+
#
|
111
|
+
#
|
112
|
+
# There is quite a good example of this class in use in spec/examples/oral_history.rb
|
113
|
+
#
|
114
|
+
# !! Careful: If you declare two fields that correspond to the same xml node without any qualifiers to differentiate them,
|
115
|
+
# you will end up replicating the values in the underlying datastream, resulting in mysterious dubling, quadrupling, etc.
|
116
|
+
# whenever you edit the field's values.
|
117
|
+
def field(name, tupe = nil, opts = {})
|
118
|
+
@fields[name.to_s.to_sym] = { type: tupe, values: [] }.merge(opts)
|
119
|
+
# add term to template
|
120
|
+
self.class.class_fields << name.to_s
|
121
|
+
# add term to terminology
|
122
|
+
return if self.class.terminology.has_term?(name.to_sym)
|
123
|
+
om_term_opts = { xmlns: "http://purl.org/dc/terms/", namespace_prefix: "dcterms", path: opts[:path] }
|
124
|
+
term = OM::XML::Term.new(name.to_sym, om_term_opts, self.class.terminology)
|
125
|
+
self.class.terminology.add_term(term)
|
126
|
+
term.generate_xpath_queries!
|
127
|
+
end
|
128
|
+
|
129
|
+
def update_indexed_attributes(params = {}, opts = {})
|
130
|
+
# if the params are just keys, not an array, make then into an array.
|
131
|
+
new_params = {}
|
132
|
+
params.each do |key, val|
|
133
|
+
if key.is_a? Array
|
134
|
+
new_params[key] = val
|
135
|
+
else
|
136
|
+
new_params[[key.to_sym]] = val
|
137
|
+
end
|
138
|
+
end
|
139
|
+
super(new_params, opts)
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.xml_template
|
143
|
+
Nokogiri::XML::Document.parse("<dc xmlns:dcterms='http://purl.org/dc/terms/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'/>")
|
144
|
+
end
|
145
|
+
|
146
|
+
def to_solr(solr_doc = {}, _opts = {}) # :nodoc:
|
147
|
+
@fields.each do |field_key, field_info|
|
148
|
+
things = send(field_key)
|
149
|
+
next unless things
|
150
|
+
field_symbol = ActiveFedora.index_field_mapper.solr_name(field_key, type: field_info[:type])
|
151
|
+
things.val.each do |val|
|
152
|
+
::Solrizer::Extractor.insert_solr_field_value(solr_doc, field_symbol, val)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
solr_doc
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module ActiveFedora::RDF
|
2
|
+
module DatastreamIndexing
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def to_solr(solr_doc = {}, opts = {}) # :nodoc:
|
6
|
+
super.tap do |new_doc|
|
7
|
+
solrize_rdf_assertions(opts[:name], new_doc)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def indexer
|
13
|
+
ActiveFedora::RDF::IndexingService
|
14
|
+
end
|
15
|
+
|
16
|
+
def index_config
|
17
|
+
@index_config ||= ActiveFedora::Indexing::Map.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def indexing_service
|
24
|
+
@indexing_service ||= self.class.indexer.new(self)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Serialize the datastream's RDF relationships to solr
|
28
|
+
# @param [String] file_path used to prefix the keys in the solr document
|
29
|
+
# @param [Hash] solr_doc @default an empty Hash
|
30
|
+
def solrize_rdf_assertions(file_path, solr_doc = {})
|
31
|
+
solr_doc.merge! indexing_service.generate_solr_document(prefix_method(file_path))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns a function that takes field name and returns a solr document key
|
35
|
+
def prefix_method(file_path)
|
36
|
+
->(field_name) { apply_prefix(field_name, file_path) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def apply_prefix(name, file_path)
|
40
|
+
prefix(file_path) + name.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
# The string to prefix all solr fields with. Override this method if you want
|
44
|
+
# a prefix other than the default
|
45
|
+
def prefix(path)
|
46
|
+
path ? "#{path.underscore}__" : ''
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module ActiveFedora
|
2
|
+
class RDFDatastream < File
|
3
|
+
include ActiveTriples::NestedAttributes
|
4
|
+
include RDF::DatastreamIndexing
|
5
|
+
include ActiveTriples::Properties
|
6
|
+
include ActiveTriples::Reflection
|
7
|
+
|
8
|
+
delegate :rdf_subject, :set_value, :get_values, :attributes=, to: :resource
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def rdf_subject(&block)
|
12
|
+
return @subject_block = block if block_given?
|
13
|
+
|
14
|
+
@subject_block ||= ->(ds) { parent_uri(ds) }
|
15
|
+
end
|
16
|
+
|
17
|
+
# Trim the last segment off the URI to get the parents uri
|
18
|
+
def parent_uri(ds)
|
19
|
+
m = /^(.*)\/[^\/]*$/.match(ds.uri)
|
20
|
+
if m
|
21
|
+
m[1]
|
22
|
+
else
|
23
|
+
::RDF::URI.new(nil)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# @param [Class] klass an object to set as the resource class, Must be a descendant of
|
29
|
+
# ActiveTriples::Resource and include ActiveFedora::RDF::Persistence.
|
30
|
+
#
|
31
|
+
# @return [Class] the object resource class
|
32
|
+
def resource_class(klass = nil)
|
33
|
+
if klass
|
34
|
+
raise ArgumentError, "#{self} already has a resource_class #{@resource_class}, cannot redefine it to #{klass}" if @resource_class && klass != @resource_class
|
35
|
+
raise ArgumentError, "#{klass} must be a subclass of ActiveTriples::Resource" unless klass < ActiveTriples::Resource
|
36
|
+
end
|
37
|
+
|
38
|
+
@resource_class ||= begin
|
39
|
+
klass = Class.new(klass || ActiveTriples::Resource)
|
40
|
+
klass.send(:include, RDF::Persistence)
|
41
|
+
klass
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
before_save do
|
47
|
+
if content.blank?
|
48
|
+
ActiveFedora::Base.logger.warn "Cowardly refusing to save a datastream with empty content: #{inspect}" if ActiveFedora::Base.logger
|
49
|
+
if ActiveSupport.respond_to?(:halt_callback_chains_on_return_false)
|
50
|
+
# For Rails 5+
|
51
|
+
throw :abort
|
52
|
+
else
|
53
|
+
# For Rails <= 4
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def parent_uri
|
60
|
+
self.class.parent_uri(self)
|
61
|
+
end
|
62
|
+
|
63
|
+
def metadata?
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
def content
|
68
|
+
serialize
|
69
|
+
end
|
70
|
+
|
71
|
+
def content=(new_content)
|
72
|
+
resource.clear!
|
73
|
+
resource << deserialize(new_content)
|
74
|
+
content
|
75
|
+
end
|
76
|
+
|
77
|
+
def uri=(uri)
|
78
|
+
super
|
79
|
+
resource.set_subject!(parent_uri) if empty_or_blank_subject?
|
80
|
+
end
|
81
|
+
|
82
|
+
def content_changed?
|
83
|
+
return false unless instance_variable_defined? :@resource
|
84
|
+
return true if empty_or_blank_subject? # can't be serialized because a subject hasn't been assigned yet.
|
85
|
+
@content = serialize
|
86
|
+
super
|
87
|
+
end
|
88
|
+
|
89
|
+
def empty_or_blank_subject?
|
90
|
+
resource.rdf_subject.node? || resource.rdf_subject.value.blank?
|
91
|
+
end
|
92
|
+
|
93
|
+
def freeze
|
94
|
+
@resource.freeze
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# The resource is the RdfResource object that stores the graph for
|
99
|
+
# the datastream and is the central point for its relationship to
|
100
|
+
# other nodes.
|
101
|
+
#
|
102
|
+
# set_value, get_value, and property accessors are delegated to this object.
|
103
|
+
def resource
|
104
|
+
@resource ||= begin
|
105
|
+
klass = self.class.resource_class
|
106
|
+
klass.properties.merge(self.class.properties).each do |_prop, config|
|
107
|
+
klass.property(config.term,
|
108
|
+
predicate: config.predicate,
|
109
|
+
class_name: config.class_name)
|
110
|
+
end
|
111
|
+
klass.accepts_nested_attributes_for(*nested_attributes_options.keys) unless nested_attributes_options.blank?
|
112
|
+
uri_stub = self.class.rdf_subject.call(self)
|
113
|
+
|
114
|
+
r = klass.new(uri_stub)
|
115
|
+
r.datastream = self
|
116
|
+
r << deserialize
|
117
|
+
r
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
alias graph resource
|
122
|
+
|
123
|
+
def refresh_attributes
|
124
|
+
@resource = nil
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# This method allows for delegation.
|
129
|
+
# This patches the fact that there's no consistent API for allowing delegation - we're matching the
|
130
|
+
# OmDatastream implementation as our "consistency" point.
|
131
|
+
# @TODO: We may need to enable deep RDF delegation at one point.
|
132
|
+
def term_values(*values)
|
133
|
+
send(values.first)
|
134
|
+
end
|
135
|
+
|
136
|
+
def update_indexed_attributes(hash)
|
137
|
+
hash.each do |fields, value|
|
138
|
+
fields.each do |field|
|
139
|
+
send("#{field}=", value)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def serialize
|
145
|
+
resource.set_subject!(parent_uri) if parent_uri && rdf_subject.node?
|
146
|
+
resource.dump serialization_format
|
147
|
+
end
|
148
|
+
|
149
|
+
def deserialize(data = nil)
|
150
|
+
return ::RDF::Graph.new if new_record? && data.nil?
|
151
|
+
data ||= remote_content
|
152
|
+
|
153
|
+
# Because datastream_content can return nil, we should check that here.
|
154
|
+
return ::RDF::Graph.new if data.nil?
|
155
|
+
|
156
|
+
data.force_encoding('utf-8')
|
157
|
+
::RDF::Graph.new << ::RDF::Reader.for(serialization_format).new(data)
|
158
|
+
end
|
159
|
+
|
160
|
+
def serialization_format
|
161
|
+
raise "you must override the `serialization_format' method in a subclass"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|