Class: RDF::RDFa::Writer

Inherits:
Writer
  • Object
show all
Includes:
Util::Logger
Defined in:
lib/rdf/rdfa/writer.rb,
lib/rdf/rdfa/writer/haml_templates.rb

Overview

An RDFa 1.1 serialiser in Ruby. The RDFa serializer makes use of Haml templates, allowing runtime-replacement with alternate templates. Note, however, that templates should be checked against the W3C test suite to ensure that valid RDFa is emitted.

Note that the natural interface is to write a whole graph at a time. Writing statements or Triples will create a graph to add them to and then serialize the graph.

The writer will add prefix definitions, and use them for creating @prefix definitions, and minting CURIEs

Examples:

Obtaining a RDFa writer class

RDF::Writer.for(:html)          => RDF::RDFa::Writer
RDF::Writer.for("etc/test.html")
RDF::Writer.for(file_name:      "etc/test.html")
RDF::Writer.for(file_extension: "html")
RDF::Writer.for(content_type:   "application/xhtml+xml")
RDF::Writer.for(content_type:   "text/html")

Serializing RDF graph into an XHTML+RDFa file

RDF::RDFa::Write.open("etc/test.html") do |writer|
  writer << graph
end

Serializing RDF statements into an XHTML+RDFa file

RDF::RDFa::Writer.open("etc/test.html") do |writer|
  graph.each_statement do |statement|
    writer << statement
  end
end

Serializing RDF statements into an XHTML+RDFa string

RDF::RDFa::Writer.buffer do |writer|
  graph.each_statement do |statement|
    writer << statement
  end
end

Creating @base and @prefix definitions in output

RDF::RDFa::Writer.buffer(base_uri: "http://example.com/", prefixes: {
    foaf: "http://xmlns.com/foaf/0.1/"}
) do |writer|
  graph.each_statement do |statement|
    writer << statement
  end
end

Author:

Constant Summary collapse

HAML_OPTIONS =
{
  format: :xhtml
}
BASE_HAML =

The default set of HAML templates used for RDFa code generation

{
 identifier: "base",
  # Document
  # Locals: language, title, prefix, base, subjects
  # Yield: subjects.each
  doc: %q(
    !!! XML
    !!! 5
    %html{xmlns: "http://www.w3.org/1999/xhtml", lang: lang, prefix: prefix}
      - if base || title
        %head
          - if base
            %base{href: base}
          - if title
            %title= title
      %body
        - subjects.map do |subject|
          != yield(subject)
  ),

  # Output for non-leaf resources
  # Note that @about may be omitted for Nodes that are not referenced
  #
  # If _rel_ and _resource_ are not nil, the tag will be written relative
  # to a previous subject. If _element_ is :li, the tag will be written
  # with <li> instead of <div>.
  #
  # Locals: subject, typeof, predicates, rel, element, inlist
  # Yield: predicates.each
  subject: %q(
    - if element == :li
      %li{rel: rel, resource: (about || resource), typeof: typeof, inlist: inlist}
        - if typeof
          %span.type!= typeof
        - predicates.each do |predicate|
          != yield(predicate)
    - else
      %div{rel: rel, resource: (about || resource), typeof: typeof, inlist: inlist}
        - if typeof
          %span.type!= typeof
        - predicates.each do |predicate|
          != yield(predicate)
  ),

  # Output for single-valued properties
  # Locals: predicate, object, inlist
  # Yields: object
  # If nil is returned, render as a leaf
  # Otherwise, render result
  property_value: %q(
    - if heading_predicates.include?(predicate) && object.literal?
      %h1{property: get_curie(predicate), content: get_content(object), lang: get_lang(object), datatype: get_dt_curie(object), inlist: inlist}= escape_entities(get_value(object))
    - else
      %div.property
        %span.label
          = get_predicate_name(predicate)
        - if res = yield(object)
          != res
        - elsif get_curie(object) == 'rdf:nil'
          %span{rel: get_curie(predicate), inlist: ''}
        - elsif object.node?
          %span{property: get_curie(predicate), resource: get_curie(object), inlist: inlist}= get_curie(object)
        - elsif object.uri?
          %a{property: get_curie(predicate), href: object.to_s, inlist: inlist}= object.to_s
        - elsif object.datatype == RDF.XMLLiteral
          %span{property: get_curie(predicate), lang: get_lang(object), datatype: get_dt_curie(object), inlist: inlist}<!= get_value(object)
        - else
          %span{property: get_curie(predicate), content: get_content(object), lang: get_lang(object), datatype: get_dt_curie(object), inlist: inlist}= escape_entities(get_value(object))
  ),

  # Output for multi-valued properties
  # Locals: predicate, :objects, :inlist
  # Yields: object for leaf resource rendering
  property_values:  %q(
    %div.property
      %span.label
        = get_predicate_name(predicate)
      %ul
        - objects.each do |object|
          - if res = yield(object)
            != res
          - elsif object.node?
            %li{property: get_curie(predicate), resource: get_curie(object), inlist: inlist}= get_curie(object)
          - elsif object.uri?
            %li
              %a{property: get_curie(predicate), href: object.to_s, inlist: inlist}= object.to_s
          - elsif object.datatype == RDF.XMLLiteral
            %li{property: get_curie(predicate), lang: get_lang(object), datatype: get_curie(object.datatype), inlist: inlist}<!= get_value(object)
          - else
            %li{property: get_curie(predicate), content: get_content(object), lang: get_lang(object), datatype: get_dt_curie(object), inlist: inlist}= escape_entities(get_value(object))
  ),
}
MIN_HAML =

An alternative, minimal HAML template for RDFa code generation. This version does not perform recursive object generation and does not attempt to create human readable output.

{
  identifier: "min",
  # Document
  # Locals: language, title, prefix, base, subjects
  # Yield: subjects.each
  doc: %q(
    !!! XML
    !!! 5
    %html{xmlns: "http://www.w3.org/1999/xhtml", lang: lang, prefix: prefix}
      - if base
        %head
          %base{href: base}
      %body
        - subjects.each do |subject|
          != yield(subject)
  ),

  # Output for non-leaf resources
  # Note that @about may be omitted for Nodes that are not referenced
  #
  # Locals: about, typeof, predicates, :inlist
  # Yield: predicates.each
  subject: %q(
    %div{rel: rel, resource: (about || resource), typeof: typeof}
      - predicates.each do |predicate|
        != yield(predicate)
  ),

  # Output for single-valued properties.
  # This version does not perform a recursive call, and renders all objects as leafs.
  # Locals: predicate, object, inlist
  # Yields: object
  # If nil is returned, render as a leaf
  # Otherwise, render result
  property_value: %q(
  - if res = yield(object)
    != res
  - elsif get_curie(object) == 'rdf:nil'
    %span{rel: get_curie(predicate), inlist: ''}
  - elsif object.node?
    %span{property: get_curie(predicate), resource: get_curie(object), inlist: inlist}= get_curie(object)
  - elsif object.uri?
    %a{property: get_curie(predicate), href: object.to_s, inlist: inlist}= object.to_s
  - elsif object.datatype == RDF.XMLLiteral
    %span{property: get_curie(predicate), lang: get_lang(object), datatype: get_dt_curie(object), inlist: inlist}<!= get_value(object)
  - else
    %span{property: get_curie(predicate), content: get_content(object), lang: get_lang(object), datatype: get_dt_curie(object), inlist: inlist}= escape_entities(get_value(object))
  )
}
DISTILLER_HAML =
{
  identifier: "distiller",
  # Document
  # Locals: language, title, prefix, base, subjects
  # Yield: subjects.each
  doc: %q(
    !!! XML
    !!! 5
    %html{xmlns: "http://www.w3.org/1999/xhtml", lang: lang, prefix: prefix}
      - if base || title
        %head
          - if base
            %base{href: base}
          - if title
            %title= title
          %link{rel: "stylesheet", href: "http://rdf.kellogg-assoc.com/css/distiller.css", type: "text/css"}
          %script{src: "https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js", type: "text/javascript"}
          %script{src: "http://rdf.kellogg-assoc.com/js/distiller.js", type: "text/javascript"}
      %body
        - if base
          %p= "RDFa serialization URI base: &lt;#{base}&gt;"
        - subjects.each do |subject|
          != yield(subject)
        %footer
          %p= "Written by <a href='http://rubygems.org/gems/rdf-rdfa'>RDF::RDFa</a> version #{RDF::RDFa::VERSION}"
  ),

  # Output for non-leaf resources
  # Note that @about may be omitted for Nodes that are not referenced
  #
  # If _rel_ and _resource_ are not nil, the tag will be written relative
  # to a previous subject. If _element_ is :li, the tag will be written
  # with <li> instead of <div>.
  #
  # Note that @rel and @resource can be used together, or @about and @typeof, but
  # not both.
  #
  # Locals: subject, typeof, predicates, rel, element, inlist
  # Yield: predicates.each
  subject: %q(
    - if element == :li
      %li{rel: rel, resource: (about || resource), typeof: typeof, inlist: inlist}
        - if typeof
          %span.type!= typeof
        %table.properties
          - predicates.each do |predicate|
            != yield(predicate)
    - elsif rel
      %td{rel: rel, resource: (about || resource), typeof: typeof, inlist: inlist}
        - if typeof
          %span.type!= typeof
        %table.properties
          - predicates.each do |predicate|
            != yield(predicate)
    - else
      %div{resource: (about || resource), typeof: typeof, inlist: inlist}
        - if typeof
          %span.type!= typeof
        %table.properties
          - predicates.each do |predicate|
            != yield(predicate)
  ),

  # Output for single-valued properties
  # Locals: predicate, object, inlist
  # Yields: object
  # If nil is returned, render as a leaf
  # Otherwise, render result
  property_value: %q(
    - if heading_predicates.include?(predicate) && object.literal?
      %h1{property: get_curie(predicate), content: get_content(object), lang: get_lang(object), datatype: get_dt_curie(object), inlist: inlist}= escape_entities(get_value(object))
    - else
      %tr.property
        %td.label
          = get_predicate_name(predicate)
        - if res = yield(object)
          != res
        - elsif get_curie(object) == 'rdf:nil'
          %td{rel: get_curie(predicate), inlist: ''}= "Empty"
        - elsif object.node?
          %td{property: get_curie(predicate), resource: get_curie(object), inlist: inlist}= get_curie(object)
        - elsif object.uri?
          %td
            %a{property: get_curie(predicate), href: object.to_s, inlist: inlist}= object.to_s
        - elsif object.datatype == RDF.XMLLiteral
          %td{property: get_curie(predicate), lang: get_lang(object), datatype: get_dt_curie(object), inlist: inlist}<!= get_value(object)
        - else
          %td{property: get_curie(predicate), content: get_content(object), lang: get_lang(object), datatype: get_dt_curie(object), inlist: inlist}= escape_entities(get_value(object))
  ),

  # Output for multi-valued properties
  # Locals: predicate, objects, inliste
  # Yields: object for leaf resource rendering
  property_values:  %q(
    %tr.property
      %td.label
        = get_predicate_name(predicate)
      %td
        %ul
          - objects.each do |object|
            - if res = yield(object)
              != res
            - elsif object.node?
              %li{property: get_curie(predicate), resource: get_curie(object), inlist: inlist}= get_curie(object)
            - elsif object.uri?
              %li
                %a{property: get_curie(predicate), href: object.to_s, inlist: inlist}= object.to_s
            - elsif object.datatype == RDF.XMLLiteral
              %li{property: get_curie(predicate), lang: get_lang(object), datatype: get_curie(object.datatype), inlist: inlist}<!= get_value(object)
            - else
              %li{property: get_curie(predicate), content: get_content(object), lang: get_lang(object), datatype: get_dt_curie(object), inlist: inlist}= escape_entities(get_value(object))
  ),
}
HAML_TEMPLATES =
{base: BASE_HAML, min: MIN_HAML, distiller: DISTILLER_HAML}
DEFAULT_HAML =
BASE_HAML

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(output = $stdout, options = {}) {|writer| ... } ⇒ Writer

Initializes the RDFa writer instance.

Parameters:

  • output (IO, File) (defaults to: $stdout)

    the output stream

  • options (Hash{Symbol => Object}) (defaults to: {})

    any additional options

Options Hash (options):

  • :canonicalize (Boolean) — default: false

    whether to canonicalize literals when serializing

  • :prefixes (Hash) — default: Hash.new

    the prefix mappings to use

  • :base_uri (#to_s) — default: nil

    the base URI to use when constructing relative URIs, set as html>head>base.href

  • :validate (Boolean) — default: false

    whether to validate terms when serializing

  • :lang (#to_s) — default: nil

    Output as root @lang attribute, and avoid generation [email protected]_ where possible

  • :standard_prefixes (Boolean) — default: false

    Add standard prefixes to prefixes, if necessary.

  • :top_classes (Array<RDF::URI>) — default: [RDF::RDFS.Class]

    Defines rdf:type of subjects to be emitted at the beginning of the document.

  • :predicate_order (Array<RDF::URI>) — default: [RDF.type, RDF::RDFS.label, RDF::Vocab::DC.title]

    Defines order of predicates to to emit at begninning of a resource description..

  • :heading_predicates (Array<RDF::URI>) — default: [RDF::RDFS.label, RDF::Vocab::DC.title]

    Defines order of predicates to use in heading.

  • :haml (String, Symbol, Hash{Symbol => String}) — default: DEFAULT_HAML

    HAML templates used for generating code

  • :haml_options (Hash) — default: HAML_OPTIONS

    Options to pass to Haml::Engine.new.

Yields:

  • (writer)

Yield Parameters:

  • writer (RDF::Writer)

119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/rdf/rdfa/writer.rb', line 119

def initialize(output = $stdout, options = {}, &block)
  super do
    @uri_to_term_or_curie = {}
    @uri_to_prefix = {}
    @top_classes = options[:top_classes] || [RDF::RDFS.Class]
    @predicate_order = options[:predicate_order] || [RDF.type, RDF::RDFS.label, RDF::URI("http://purl.org/dc/terms/title")]
    @heading_predicates = options[:heading_predicates] || [RDF::RDFS.label, RDF::URI("http://purl.org/dc/terms/title")]
    @graph = RDF::Graph.new

    block.call(self) if block_given?
  end
end

Instance Attribute Details

#base_uriRDF::URI

Returns Base URI used for relativizing URIs

Returns:

  • (RDF::URI)

    Base URI used for relativizing URIs


74
75
76
# File 'lib/rdf/rdfa/writer.rb', line 74

def base_uri
  @base_uri
end

#graphGraph

Returns Graph of statements serialized

Returns:

  • (Graph)

    Graph of statements serialized


71
72
73
# File 'lib/rdf/rdfa/writer.rb', line 71

def graph
  @graph
end

#heading_predicatesArray<URI> (readonly)

Defines order of predicates to use in heading.

Returns:

  • (Array<URI>)

64
65
66
# File 'lib/rdf/rdfa/writer.rb', line 64

def heading_predicates
  @heading_predicates
end

#predicate_orderArray<URI> (readonly)

Defines order of predicates to to emit at begninning of a resource description. Defaults to `[rdf:type, rdfs:label, dc:title]`

Returns:

  • (Array<URI>)

60
61
62
# File 'lib/rdf/rdfa/writer.rb', line 60

def predicate_order
  @predicate_order
end

#top_classesArray<URI> (readonly)

Defines rdf:type of subjects to be emitted at the beginning of the document.

Returns:

  • (Array<URI>)

56
57
58
# File 'lib/rdf/rdfa/writer.rb', line 56

def top_classes
  @top_classes
end

Class Method Details

.optionsObject

RDFa Writer options


79
80
81
82
83
84
85
86
87
# File 'lib/rdf/rdfa/writer.rb', line 79

def self.options
  super + [
    RDF::CLI::Option.new(
      symbol: :lang,
      datatype: String,
      on: ["--lang"],
      description: "Output as root @lang attribute, and avoid generation [email protected]_ where possible."),
  ]
end

Instance Method Details

#haml_templateHash<Symbol => String>

Returns:


133
134
135
136
137
138
139
140
# File 'lib/rdf/rdfa/writer.rb', line 133

def haml_template
  return @haml_template if @haml_template
  case @options[:haml]
  when Symbol, String   then HAML_TEMPLATES.fetch(@options[:haml].to_sym, DEFAULT_HAML)
  when Hash             then @options[:haml]
  else                       DEFAULT_HAML
  end
end

#write_epiloguevoid

This method returns an undefined value.

Outputs the XHTML+RDFa representation of all stored triples.


159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/rdf/rdfa/writer.rb', line 159

def write_epilogue
  @base_uri = RDF::URI(@options[:base_uri]) if @options[:base_uri]
  @lang = @options[:lang]
  self.reset

  log_debug {"\nserialize: graph size: #{@graph.size}"}

  preprocess

  # Prefixes
  prefix = prefixes.keys.map {|pk| "#{pk}: #{prefixes[pk]}"}.sort.join(" ") unless prefixes.empty?
  log_debug {"\nserialize: prefixes: #{prefix.inspect}"}

  subjects = order_subjects

  # Take title from first subject having a heading predicate
  doc_title = nil
  titles = {}
  heading_predicates.each do |pred|
    @graph.query(predicate: pred) do |statement|
      titles[statement.subject] ||= statement.object
    end
  end
  title_subject = subjects.detect {|subject| titles[subject]}
  doc_title = titles[title_subject]

  # Generate document
  doc = render_document(subjects,
    lang:     @lang,
    base:     base_uri,
    title:    doc_title,
    prefix:   prefix) do |s|
    subject(s)
  end
  @output.write(doc)

  super
end

#write_triple(subject, predicate, object) ⇒ void

This method is abstract.

This method returns an undefined value.

Addes a triple to be serialized

Parameters:

  • subject (RDF::Resource)
  • predicate (RDF::URI)
  • object (RDF::Value)

Raises:

  • (NotImplementedError)

    unless implemented in subclass

  • (RDF::WriterError)

    if validating and attempting to write an invalid Term.


151
152
153
# File 'lib/rdf/rdfa/writer.rb', line 151

def write_triple(subject, predicate, object)
  @graph.insert(RDF::Statement(subject, predicate, object))
end