Last active
August 29, 2015 14:06
-
-
Save no-reply/fce0771254a3a25817dd to your computer and use it in GitHub Desktop.
Hydra Connect RDF Workshop Walkthrough
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# lib/models/agent.rb | |
class Agent < ActiveTriples::Resource | |
configure base_uri: 'http://example.org/agent/', type: 'http://example.org/ns/Agent' | |
property :name, predicate: RDF::FOAF.name | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# lib/models/cho.rb | |
class CHO < ActiveTriples::Resource | |
configure base_uri: 'http://example.org/resource/', type: 'http://example.org/ns/CHO' | |
property :title, predicate: RDF::DC.title | |
property :creator, predicate: RDF::DC.creator | |
property :date, predicate: RDF::DC.date | |
property :location, predicate: RDF::DC.spatial | |
en |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# lib/models/datastream.rb | |
class CHODatastream < ActiveFedora::NtriplesRDFDatastream | |
property :title, predicate: RDF::DC.title | |
property :creator, predicate: RDF::DC.creator | |
property :date, predicate: RDF::DC.date | |
property :location, predicate: RDF::DC.spatial | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# lib/models/collection.rb | |
class Collection < ActiveTriples::Resource | |
configure base_uri: 'http://example.org/collection/', type: 'http://example.org/ns/Collection' | |
property :name, predicate: RDF::DC.title | |
property :members, predicate: RDF::DC.hasPart | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# lib/models/my_object.rb | |
class MyObject < ActiveFedora::Base | |
has_metadata 'descMetadata', type: CHODatastream | |
has_attributes :title, :creator, :date, :location, datastream: 'descMetadata', multiple: true | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# lib/models/place.rb | |
class Place < ActiveTriples::Resource | |
configure base_uri: 'http://sws.geonames.org/', type: 'http://www.geonames.org/ontology#Feature' | |
property :name, predicate: RDF::URI('http://www.geonames.org/ontology#name') | |
property :lat, predicate: RDF::GEO.lat | |
property :long, predicate: RDF::GEO.long | |
property :parentFeature, predicate: RDF::URI('http://www.geonames.org/ontology#parentFeature'), class_name: 'Place' | |
property :parentCountry, predicate: RDF::URI('http://www.geonames.org/ontology#parentCountry'), class_name: 'Place' | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# My First Resource | |
# ================== | |
# | |
# $ mkdir rdf-tutorial | |
# $ cd rdf-tutorial | |
# | |
# $ echo "source 'http://rubygems.org'" > Gemfile | |
# $ echo "gem 'active-fedora', '~> 7.1.1'" >> Gemfile | |
# | |
# $ bundle install | |
# | |
# $ mkdir -p lib/models | |
# | |
# create cho.rb as below in lib/models/ | |
class CHO < ActiveTriples::Resource | |
property :title, predicate: RDF::DC.title | |
end | |
# | |
# $ bundle console | |
require './lib/models/cho' | |
cho = CHO.new('http://example.org/resources/1') | |
cho.title = 'my cultural heritage object' | |
cho.dump :ntriples | |
# => "<http://example.org/resources/1> <http://purl.org/dc/terms/title> \"my cultural heritage object\" .\n" | |
# your Resource object is a an RDF::Graph scoped specifically to your data. You can operate on it directly, | |
# adding statements with `<<`, `query`, and `delete`, call other Graph methods, and use `dump` to serialize | |
# in any RDF format. | |
require 'linkeddata' | |
cho.dump :ttl | |
# => "\n<http://example.org/resources/1> <http://purl.org/dc/terms/title> \"my cultural heritage object\" .\n" | |
# | |
# Adding a base_uri | |
# ----------------- | |
# exit your ruby console and | |
# edit lib/models/cho.rb as follows: | |
class CHO < ActiveTriples::Resource | |
configure base_uri: 'http://example.org/resource' | |
property :title, predicate: RDF::DC.title | |
end | |
# $ bundle console | |
require './lib/models/cho' | |
cho = CHO.new('1') | |
cho.rdf_subject | |
# => #<RDF::URI:0x3f8326f14224 URI:http://example.org/resource/1> | |
# | |
# Adding an RDF::type | |
# ------------------- | |
# exit your ruby console and | |
# edit lib/models/cho.rb as follows: | |
class CHO < ActiveTriples::Resource | |
configure base_uri: 'http://example.org/resource', type: 'http://example.org/ns/CHO' | |
property :title, predicate: RDF::DC.title | |
end | |
# $ bundle console | |
require './lib/models/cho' | |
cho = CHO.new('1') | |
cho.type | |
# => [#<RDF::URI:0x3f8326f8ae60 URI:http://example.org/ns/CHO>] | |
cho.dump :ntriples | |
# => "<http://example.org/resource/1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/ns/CHO> .\n" | |
# | |
# Adding additional properties | |
# ---------------------------- | |
# exit your ruby console and | |
# edit lib/models/cho.rb as follows: | |
class CHO < ActiveTriples::Resource | |
configure base_uri: 'http://example.org/resource/', type: 'http://example.org/ns/CHO' | |
property :title, predicate: RDF::DC.title | |
property :creator, predicate: RDF::DC.creator | |
property :date, predicate: RDF::DC.date | |
end | |
# $ bundle console | |
require './lib/models/cho' | |
cho = CHO.new('1') | |
cho.title = 'My Resource' | |
cho.creator = 'Me' | |
cho.date = DateTime.now | |
puts cho.dump :ntriples | |
# <http://example.org/resource/1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/ns/CHO> . | |
# <http://example.org/resource/1> <http://purl.org/dc/terms/title> "My Resource" . | |
# <http://example.org/resource/1> <http://purl.org/dc/terms/creator> "Me" . | |
# <http://example.org/resource/1> <http://purl.org/dc/terms/date> "2014-09-11T14:34:28-07:00"^^<http://www.w3.org/2001/XMLSchema#dateTime> . | |
# => nil | |
# Note that the `date` property is encoded as a typed literal. | |
# When typed data is passed to a property, ActiveTriples serialises it correctly | |
# and returns the appropriate datatype when accessed. This is handled through | |
# RDF.rb's Literal class. | |
# | |
# For more about typed literals in RDF.rb, see: http://rdf.greggkellogg.net/yard/RDF/Literal.html | |
# For more about data types in RDF in general, see: http://www.w3.org/TR/rdf11-concepts/#section-Graph-Literal | |
cho.date | |
# => [Thu, 11 Sep 2014 14:34:28 -0700] | |
# | |
# Defining a Domain Model | |
# ======================= | |
# | |
# First Relationship | |
# ------------------ | |
# create models.rb as below in lib/ | |
require 'linkeddata' | |
Dir["./lib/models/*.rb"].each {|file| require file } | |
# create collection.rb as below in lib/models/ | |
class Collection < ActiveTriples::Resource | |
configure base_uri: 'http://example.org/collection/', type: 'http://example.org/ns/Collection' | |
property :name, predicate: RDF::DC.title | |
property :members, predicate: RDF::DC.hasPart | |
end | |
# $ bundle console | |
require './lib/models' | |
coll = Collection.new('1') | |
coll.name = 'Hydra Connect Photos' | |
coll.members = CHO.new('abc') | |
puts coll.dump :ttl | |
# <http://example.org/collection/1> a <http://example.org/ns/Collection>; | |
# <http://purl.org/dc/terms/title> "Hydra Connect Photos"; | |
# <http://purl.org/dc/terms/hasPart> <http://example.org/resource/abc> . | |
# | |
# <http://example.org/resource/abc> a <http://example.org/ns/CHO> . | |
# => nil | |
coll.members | |
# => [#<CHO:0x3f9f7120ba84(default)> | |
coll.members.first.title = 'Rock & Roll Hall of Fame' | |
puts coll.dump :ttl | |
# <http://example.org/collection/1> a <http://example.org/ns/Collection>; | |
# <http://purl.org/dc/terms/title> "Hydra Connect Photos"; | |
# <http://purl.org/dc/terms/hasPart> <http://example.org/resource/abc> . | |
# <http://example.org/resource/abc> a <http://example.org/ns/CHO>; | |
# <http://purl.org/dc/terms/title> "Rock & Roll Hall of Fame" . | |
# => nil | |
# Resource objects loaded from the graph will build in the class associated with | |
# their rdf:type. But what happens if no type is given? | |
coll.members << ActiveTriples::Resource.new('http://example.org/noType/1') | |
coll.members | |
# => [#<CHO:0x3f9f7120ba84(default)>, #<ActiveTriples::Resource:0x3f9f710314d4(default)>] | |
# | |
# Expanding the Domain Model with Agents and Places | |
# ------------------------------------------------- | |
# create agent.rb as below in lib/models/ | |
class Agent < ActiveTriples::Resource | |
configure base_uri: 'http://example.org/agent/', type: 'http://example.org/ns/Agent' | |
property :name, predicate: RDF::FOAF.name | |
end | |
# create place.rb as below in lib/models/ | |
class Place < ActiveTriples::Resource | |
configure base_uri: 'http://example.org/place/', type: 'http://example.org/ns/Place' | |
property :name, predicate: RDF::URI('http://www.geonames.org/ontology#name') | |
property :lat, predicate: RDF::GEO.lat | |
property :long, predicate: RDF::GEO.long | |
end | |
# update lib/models/cho.rb as below: | |
class CHO < ActiveTriples::Resource | |
configure base_uri: 'http://example.org/resource/', type: 'http://example.org/ns/CHO' | |
property :title, predicate: RDF::DC.title | |
property :creator, predicate: RDF::DC.creator | |
property :date, predicate: RDF::DC.date | |
property :location, predicate: RDF::DC.spatial | |
end | |
# $ bundle console | |
require './lib/models' | |
cho = CHO.new('1') | |
cho.title = 'RDF Tutorial' | |
cho.date = Date.today | |
tom = Agent.new('tj') | |
tom.name = 'Tom' | |
karen = Agent.new('ke') | |
karen.name = 'Karen' | |
place = Place.new('cleveland') | |
place.name = 'Cleveland' | |
cho.location = place | |
cho.creator = [tom, karen] | |
puts cho.dump :ttl | |
# <http://example.org/resource/1> a <http://example.org/ns/CHO>; | |
# <http://purl.org/dc/terms/title> "RDF Tutorial"; | |
# <http://purl.org/dc/terms/creator> <http://example.org/agent/tj>, | |
# <http://example.org/agent/ke>; | |
# <http://purl.org/dc/terms/date> "2014-09-29"^^<http://www.w3.org/2001/XMLSchema#date>; | |
# <http://purl.org/dc/terms/spatial> <http://example.org/place/cleveland> . | |
# | |
# <http://example.org/agent/ke> a <http://example.org/ns/Agent>; | |
# <http://xmlns.com/foaf/0.1/name> "Karen" . | |
# | |
# <http://example.org/agent/tj> a <http://example.org/ns/Agent>; | |
# <http://xmlns.com/foaf/0.1/name> "Tom" . | |
# <http://example.org/place/cleveland> a <http://example.org/ns/Place>; | |
# <http://www.geonames.org/ontology#name> "Cleveland" . | |
# => nil | |
# | |
# Using External Data | |
# ------------------- | |
# update place.rb as follows | |
class Place < ActiveTriples::Resource | |
configure base_uri: 'http://sws.geonames.org/', type: 'http://example.org/ns/Place' | |
property :name, predicate: RDF::URI('http://www.geonames.org/ontology#name') | |
property :lat, predicate: RDF::GEO.lat | |
property :long, predicate: RDF::GEO.long | |
property :parentFeature, predicate: RDF::URI('http://www.geonames.org/ontology#parentFeature') | |
property :parentCountry, predicate: RDF::URI('http://www.geonames.org/ontology#parentCountry') | |
end | |
# $ bundle console | |
require './lib/models' | |
cw = Place.new('5149374/') | |
cw.fetch | |
cw.name | |
# => ["Case Western Reserve University"] | |
cw.lat | |
# => ["41.5045"] | |
cw.long | |
# => ["-81.59707"] | |
cw.parentFeature.first.fetch | |
cw.parentFeature.first.name | |
# => ["Cuyahoga County"] | |
cw.parentFeature.first.parentFeature.first.fetch | |
# => #<Place:0x3fdfd736ef9c(default)> | |
cw.parentFeature.first.parentFeature.first.name | |
# => ["Ohio"] | |
# ...and so on. | |
# | |
# Closing the Gap to Fedora | |
# ========================= | |
# Everything so far has been persisting only to memory. | |
# ActiveTriples::Resources persist to an in-memory RDF::Repository | |
# by default, and can be configured to persist to a persistent | |
# Repository instance, but for most Hydra use cases, we want to associate | |
# a graph with an Fedora object and serialize it to a datastream. | |
# We can achieve this with ActiveFedora::RDFDatastream (and subclasses). | |
# | |
# Create a Datastream and Object | |
# ------------------------------- | |
# in lib/models/datastream.rb | |
class CHODatastream < ActiveFedora::NtriplesRDFDatastream | |
property :title, predicate: RDF::DC.title | |
property :creator, predicate: RDF::DC.creator | |
property :date, predicate: RDF::DC.date | |
property :location, predicate: RDF::DC.spatial | |
end | |
# in lib/models/my_object.rb | |
class MyObject < ActiveFedora::Base | |
has_metadata 'descMetadata', type: CHODatastream | |
has_attributes :title, :creator, :date, :location, datastream: 'descMetadata', multiple: true | |
end | |
# $ bundle console | |
obj = MyObject.new | |
obj.descMetadata.title = 'my object' | |
obj.descMetadata.creator = Agent.new('tj') | |
puts obj.descMetadata.content | |
# _:g69927657577780 <http://purl.org/dc/terms/title> "my object" . | |
# _:g69927657577780 <http://purl.org/dc/terms/creator> <http://example.org/agent/tj> . | |
# <http://example.org/agent/tj> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/ns/Agent> . | |
# => nil | |
# | |
# Extras & Asides | |
# ================ | |
# | |
# Check Out the DPLA MAP | |
# ---------------------- | |
# DPLA is drafting it's Metadata Application Profile v4.0 | |
# we have been prototyping the new model in ActiveTriples | |
# as we go. The prototype validates compliance with the model | |
# and uses the LinkedVocabs gem to implement controlled | |
# vocabularies. | |
# See: https://github.com/dpla/dpla_map/tree/map-4.0 | |
# $ git clone git@github.com:dpla/dpla_map.git | |
# $ cd dpla_map | |
# $ git checkout map-4.0 | |
# $ bundle install | |
# $ bundle console | |
sr = DPLA::SourceResource.new('123') | |
sr.valid # => false | |
sr.errors | |
# => #<ActiveModel::Errors:0x007f95b7a9aef8 | |
# @base=#<DPLA::SourceResource:0x3fcadbdadf78(default)>, | |
# @messages={:rights=>["can't be blank"], :title=>["can't be blank"]}> | |
sr.title = 'my resource' | |
sr.rights = 'cc by-sa' | |
sr.valid? # => true | |
sr.dctype = 'IMG' | |
sr.valid? # => false | |
sr.errors | |
# => #<ActiveModel::Errors:0x007f95b82306b8 | |
# @base=#<DPLA::SourceResource:0x3fcadbf7e550(default)>, | |
# @messages={:base=>["value `IMG for `dctype` property is not a term in a controlled vocabulary dcmitype"]}> | |
sr.dctype = DPLA::Controlled::DCMIType.new('Image') | |
sr.valid? # => true | |
# also look at our factories: | |
# https://github.com/dpla/dpla_map/blob/map-4.0/spec/factories.rb | |
# | |
# What is a Blank node/bnode/RDF::Node? | |
# ------------------------------------- | |
# These are all terms for a Resource that is not identified | |
# by a URI. ActiveTriples creates bnodes for Resources that aren't | |
# passed a URI (or stub) at initialization. | |
cho = CHO.new | |
cho.rdf_subject | |
# => #<RDF::Node:0x3f98d5bb1a04(_:g69925653387780)> | |
# Blank nodes are identified by a UUID, which won't be stable from | |
# load to load/serialization to serialization. It identifies the | |
# resource only the context of a specific graph expression. | |
# | |
# ActiveTriples will let you set a URI for a bnode, but only once! | |
cho.set_subject!('abc') | |
cho.rdf_subject | |
# => #<RDF::URI:0x3f98d5ba4160 URI:http://example.org/resource/abc> | |
cho.set_subject!('123') | |
# RuntimeError: Refusing update URI when one is already assigned! | |
# | |
# Look Out: Lists are Unordered! | |
# ------------------------------ | |
coll = Collection.new('1') | |
10.times do | |
coll.members << CHO.new | |
end | |
coll.members[4].rdf_subject | |
# => #<RDF::Node:0x3f98d544be54(_:g69925645631060)> | |
puts coll.dump :ttl | |
# <http://example.org/collection/1> a <http://example.org/ns/Collection>; | |
# <http://purl.org/dc/terms/title> "Hydra Connect Photos"; | |
# <http://purl.org/dc/terms/hasPart> [ a <http://example.org/ns/CHO>], [ a <http://example.org/ns/CHO>], [ a <http://example.org/ns/CHO>], [ a <http://example.org/ns/CHO>], [ a <http://example.org/ns/CHO>], [ a <http://example.org/ns/CHO>], [ a <http://example.org/ns/CHO>], [ a <http://example.org/ns/CHO>], [ a <http://example.org/ns/CHO>], [ a <http://example.org/ns/CHO>] . | |
# => nil | |
# Though these `coll.members` returns a list, that list is unordered; | |
# it is a reflection on the underlying graph, which assigns no order | |
# to its elements. | |
# | |
# The order your objects load depends on where you serialize them and | |
# how RDF.rb reads them back. | |
coll.members[4].title = 'key 4' | |
coll.reload! # The object 'key 4' could be anywhere in the list! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment