require 'rdf/turtle' module Chronicle module Schema module RDFParsing PREFIXES = { schemaorg: 'https://schema.org/', owl: 'http://www.w3.org/2002/07/owl#', dc: 'http://purl.org/dc/terms/', rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', rdfs: 'http://www.w3.org/2000/01/rdf-schema#', xml: 'http://www.w3.org/XML/1998/namespace', xsd: 'http://www.w3.org/2001/XMLSchema#' }.freeze # Take a graph and serialize it as a ttl string class RDFSerializer attr_reader :graph def initialize(graph) raise ArgumentError, 'graph must be a SchemaGraph' unless graph.is_a?(Chronicle::Schema::SchemaGraph) @graph = graph end def self.serialize(graph, include_generator_comment: true) new(graph).serialize(include_generator_comment:) end def serialize(include_generator_comment: true) schema_graph = RDF::Graph.new schema_graph << ontology_triple schema_graph << version_triple graph.types.each do |klass| serialize_class(klass).each do |triple| # binding.pry schema_graph << triple end end graph.properties.each do |property| serialize_property(property).each do |triple| schema_graph << triple end end prefixes = { '': default_namespace }.merge(PREFIXES) output_str = '' output_str += generation_header if include_generator_comment output_str + schema_graph.dump(:ttl, prefixes:) end private def default_namespace @graph.default_namespace end def generation_header <<~TTL # This file was generated from schema/chronicle_schema_v#{graph.version}.rb. # # Do not edit this file directly, as it will be overwritten. # # To generate a new version, run `rake generate` TTL end def ontology_triple RDF::Statement(RDF::URI.new('https://schema.chronicle.app'), RDF.type, RDF::OWL.Ontology) end def version_triple RDF::Statement(RDF::URI.new('https://schema.chronicle.app'), RDF::OWL.versionInfo, graph.version.to_s) end def serialize_class(klass) statements = [] statements << RDF::Statement(RDF::URI.new(klass.id), RDF.type, RDF::RDFS.Class) statements << RDF::Statement(RDF::URI.new(klass.id), RDF::RDFS.comment, klass.comment) if klass.comment klass.subtype_ids.each do |subtype_id| statements << RDF::Statement(RDF::URI.new(subtype_id), RDF::RDFS.subClassOf, RDF::URI.new(klass.id)) end if klass.see_also statements << RDF::Statement(RDF::URI.new(klass.id), RDF::RDFS.seeAlso, RDF::URI.new(klass.see_also)) end statements end def serialize_property(property) statements = [] statements << RDF::Statement(RDF::URI.new(property.id), RDF.type, RDF::RDFV.Property) property.range.each do |range| statements << RDF::Statement(RDF::URI.new(property.id), RDF::URI.new("#{@graph.default_namespace}rangeIncludes"), RDF::URI.new(range)) end property.domain.each do |domain| statements << RDF::Statement(RDF::URI.new(property.id), RDF::URI.new("#{@graph.default_namespace}domainIncludes"), RDF::URI.new(domain)) end if property.comment statements << RDF::Statement(RDF::URI.new(property.id), RDF::RDFS.comment, property.comment) end if property.see_also statements << RDF::Statement(RDF::URI.new(property.id), RDF::RDFS.seeAlso, RDF::URI.new(property.see_also)) end if property.required? statements << RDF::Statement(RDF::URI.new(property.id), RDF::OWL.minCardinality, RDF::Literal.new(1, datatype: RDF::XSD.integer)) end unless property.many statements << RDF::Statement(RDF::URI.new(property.id), RDF::OWL.maxCardinality, RDF::Literal.new(1, datatype: RDF::XSD.integer)) end statements end end end end end