Skip to content

Instantly share code, notes, and snippets.

@no-reply
Last active August 29, 2015 13:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save no-reply/9519740 to your computer and use it in GitHub Desktop.
Save no-reply/9519740 to your computer and use it in GitHub Desktop.
A Demo of ActiveFedora::Rdf in 7.x
# RdfResource is a subclass of RDF::Graph with property configuration, accessors, and some other methods
# for managing "resources" as discrete subgraphs which can be managed by a Hydra datastream model.
bnode = ActiveFedora::Rdf::Resource.new
bnode.rdf_subject
# => #<RDF::Node:0x3f967a58c1b8(_:g69915530281400)>
bnode << RDF::Statement.new(bnode.rdf_subject, RDF::DC.title, RDF::Literal('A blank node'))
bnode << RDF::Statement.new(bnode.rdf_subject, RDF::DC.subject, RDF::Literal('RDF'))
bnode.dump :ntriples
# => "_:g69915530281400 <http://purl.org/dc/terms/title> \"A blank node\" .\n_:g69915530281400 <http://purl.org/dc/terms/subject> \"RDF\" .\n"
bnode.set_subject!(RDF::URI('http://example.org/1'))
bnode.rdf_subject
# => #<RDF::URI:0x3f967b832a9c URI:http://example.org/1>
bnode.dump :ntriples
# => "<http://example.org/1> <http://purl.org/dc/terms/title> \"A blank node\" .\n<http://example.org/1> <http://purl.org/dc/terms/subject> \"RDF\" .\n"
# Define subclasses of Resource to configure and register properties for types of resources
class License < ActiveFedora::Rdf::Resource
configure :base_uri => 'http://example.org/license', :type => RDF::DC.LicenseDocument
property :title, :predicate => RDF::DC.title do |index|
index.as :displayable, :facetable
end
end
cc = License.new('cc')
cc.rdf_subject
# => #<RDF::URI:0x3f967b9546b4 URI:http://example.org/license/cc>
# cc = License.new; cc.set_subject!('cc'); would accomplish the same thing
cc.title = "Creative Commons"
cc.dump :ntriples
# => "<http://example.org/license/cc> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://purl.org/dc/terms/LicenseDocument> .\n<http://example.org/license/cc> <http://purl.org/dc/terms/title> \"Creative Commons\" .\n"
class Document < ActiveFedora::Rdf::Resource
configure :base_uri => 'http://example.org/document', :type => RDF::URI('http://purl.org/ontology/bibo/Document')
property :title, :predicate => RDF::DC.title do |index|
index.as :displayable, :facetable, :searchable
end
property :license, :predicate => RDF::DC.license, :class_name => License do |index|
index.as :displayable, :facetable
end
end
doc = Document.new('1')
doc.title = 'A Document'
doc.license = cc
doc.license
# => [#<License:0x47b22f4(default)>]
doc.license.first.title
# => ["Creative Commons"]
doc.dump :ntriples
# => "<http://example.org/document/1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://purl.org/ontology/bibo/Document> .\n<http://example.org/document/1> <http://purl.org/dc/terms/title> \"A Document\" .\n<http://example.org/document/1> <http://purl.org/dc/terms/license> <http://example.org/license/cc> .\n<http://example.org/license/cc> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://purl.org/dc/terms/LicenseDocument> .\n<http://example.org/license/cc> <http://purl.org/dc/terms/title> \"Creative Commons\" .\n"
# Note that you can also manage the graph manually; but do so with care!
# It matters which statements are in the graph for the object being called.
doc2 = Document.new('2')
doc2 << RDF::Statement.new(doc2.rdf_subject, RDF::DC.license, cc.rdf_subject)
cc.title
# => ["Creative Commons"]
# We have added the statement specifying the license, but none of the License object's graph
doc2.license.first.rdf_subject
# => #<RDF::URI:0x3f967b9546b4 URI:http://example.org/license/cc>
doc2.license.first.dump :ntriples
# => "<http://example.org/license/cc> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://purl.org/dc/terms/LicenseDocument> .\n"
doc2.license.first.title
# => []
# If we add the Licenses' graph in full, all is well.
doc2 << cc
doc2.license.first.reload # rebuilds the cached License object from the manually edited graph
doc2.license.first.title
# => ["Creative Commons"]
# By default, resources persist to their 'parent' object.
doc2.license.first.title = "Creative Commons BY-SA"
doc2.license.first.title
# => ["Creative Commons BY-SA"]
cc.title
# => ["Creative Commons"]
# Persistence to any RDF::Repository is configurable. There are RDF::Repository implementations
# for all the major triplestores and a variety of RDBMS and NoSQL databases.
ActiveFedora::Rdf::Repositories.add_repository :repo, RDF::Repository.new
class CCLicense < ActiveFedora::Rdf::Resource
configure :base_uri => 'http://creativecommons.org/licenses/', :type => RDF::DC.LicenseDocument, :repository => :repo
property :title, :predicate => RDF::DC.title
end
by = CCLicense.new('by/4.0/')
by.title = 'By Attribution'
by.dump :ntriples
# =>"<http://creativecommons.org/licenses/by/4.0/> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://purl.org/dc/terms/LicenseDocument> .\n<http://creativecommons.org/licenses/by/4.0/> <http://purl.org/dc/terms/title> \"By Attribution\" .\n"
by.persist!
ActiveFedora::Rdf::Repositories.repositories[:repo].dump :ntriples
# =>"<http://creativecommons.org/licenses/by/4.0/> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://purl.org/dc/terms/LicenseDocument> .\n<http://creativecommons.org/licenses/by/4.0/> <http://purl.org/dc/terms/title> \"By Attribution\" .\n"
# The license is now in the repository, so any Resource initialized with that URI will load its data.
# Resources persisted to a repository can be shared across repository objects.
by2 = CCLicense.new('by/4.0/')
by2.title
# => ["By Attribution"]
# RDF Datastreams now implement the normal AF::Datastream interface as a wrapper around a Resource object.
# They will also pass down properties defined at the datastream level to the resource they wrap.
class GenericResourceDatastream < ActiveFedora::NtriplesRDFDatastream
property :title, :predicate => RDF::DC[:title] do |index|
index.as :searchable, :displayable
end
property :creator, :predicate => RDF::DC[:creator] do |index|
index.as :searchable, :displayable, :facetable
end
property :contributor, :predicate => RDF::DC[:contributor] do |index|
index.as :searchable, :displayable, :facetable
end
property :license, :predicate => RDF::DC[:license], :class_name => License do |index|
index.as :displayable
end
end
# The resource that powers the datastream is of class ObjectResource.
# This class has a default base_uri of 'info:fedora/' and implements a few tweaks
# to persistence to store its graph in the datastream.
GenericResourceDatastream.resource_class
# => ActiveFedora::Rdf::ObjectResource
# Until a resource has a PID, it will be a blank node
ds = GenericResourceDatastream.new
ds.resource.rdf_subject
# => #<RDF::Node:0x3f967bc3e08c(_:g69915554078860)>
class DummyAsset < ActiveFedora::Base
has_metadata 'descMetadata', type: GenericResourceDatastream
has_attributes :title, :license, datastream: 'descMetadata', :multiple => true
end
asset = DummyAsset.new
asset.title = "Comet in Moominland"
asset.license = cc
asset.save
asset.descMetadata.content
asset.descMetadata.content
# => "<info:fedora/changeme:1> <http://purl.org/dc/terms/title> \"Comet in Moominland\" .\n<info:fedora/changeme:1> <http://purl.org/dc/terms/license> <http://example.org/license/cc> .\n<http://example.org/license/cc> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://purl.org/dc/terms/LicenseDocument> .\n"
# Objects that persist to an RDF::Repository will bypass the datastream. This avoids
# duplication of data across objects and issues with managing the same graph in many datastreams.
# Data in the RDF::Repository is only 'persisted' to the extent that underlying
# triplestore is trustworthy.
GenericResourceDatastream.property :license, :predicate => RDF::DC[:license], :class_name => CCLicense
asset = DummyAsset.new
asset.save
asset.license = by
asset.descMetadata.content
# => "<http://oregondigital.org/resource/changeme:66> <http://purl.org/dc/terms/license> <http://creativecommons.org/licenses/by/4.0/> .\n"
# Of course, the object still builds its graph.
asset.license.first.title
# => ["By Attribution"]
# Resources with URIs also respond to #fetch, which retrieves the data at their rdf_subject URI
# and loads it into the triplestore or parent.
by.fetch
by.title
# => ["By Attribution", "Attribution 4.0 International"]
# Resource responds to #rdf_label, which returns values from the first of the following
# predicates to respond non-empty:
#
# [RDF::SKOS.prefLabel, RDF::DC.title, RDF::RDFS.label, RDF::SKOS.altLabel, RDF::SKOS.hiddenLabel]
by.rdf_label
# => ["By Attribution", "Attribution 4.0 International"]
# #rdf_label is configurable on the class level. Configured labels are prepended to the default
# labels, and take priority.
CCLicense.configure :rdf_label => RDF::DC.title
# ActiveFedora::Rdf::Identifiable is a mixin module included in ActiveFedora::Base, allowing
# objects to be treated as deep nodes. This lets you create complex networks of relationships
# between objects in your repository.
class ChildAsset < ActiveFedora::Base
has_metadata 'descMetadata', type: GenericResourceDatastream
has_attributes :title, datastream: 'descMetadata', multiple: true
end
GenericResourceDatastream.property :related, :predicate => RDF::DC[:relation], :class_name => ChildAsset
class DummyAsset < ActiveFedora::Base
has_metadata 'descMetadata', type: GenericResourceDatastream
has_attributes :title, :license, :related, datastream: 'descMetadata', :multiple => true
end
child = ChildAsset.new
child.title = "Moomins the Movie"
child.save
asset = DummyAsset.new
asset.title = "Comet in Moominland"
asset.related = child
asset.related.first.title
# => ["Moomins the Movie"]
asset.descMetadata.content
# => "<info:fedora/changeme:11> <http://purl.org/dc/terms/title> \"Comet in Moominland\" .\n<info:fedora/changeme:11> <http://purl.org/dc/terms/relation> <info:fedora/changeme:10> .\n"
asset.related.first.descMetadata.content
# => "<info:fedora/changeme:10> <http://purl.org/dc/terms/title> \"Moomins the Movie\" .\n"
# The following methods have the same output:
# asset.related.first.dump :ntriples
# child.descMetadata.content
# child.dump :ntriples
# This also works with objects that do not have an RDFDatastream
class DummyOmAsset < ActiveFedora::Base
has_metadata 'descMetadata', type: ActiveFedora::OmDatastream
end
GenericResourceDatastream.property :related, :predicate => RDF::DC[:relation], :class_name => DummyOmAsset
child = DummyOmAsset.new
child.save
asset = DummyAsset.new
asset.title = "Comet in Moominland"
asset.related = child
asset.related
# => [#<DummyOmAsset pid: "changeme:4">]
@jcoyne
Copy link

jcoyne commented Mar 13, 2014

I'd suggest you declare your datastreams using 1.9.3 hashes and no "name" parameter

has_metadata  'descMetadata', type: GenericResourceDatastream

@no-reply
Copy link
Author

Thanks. I didn't know you could skip the 'name' parameter.

(also: bah! but I hate 1.9.3 hashes!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment