Class: RDF::RDFa::Writer

Inherits:
Writer
  • Object
show all
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

HAML_OPTIONS =
{
  :ugly => false, # to preserve whitespace without using entities
}
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.each 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://rdf.rubyforge.org/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)
    - else
      %td{:rel => rel, :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)

Instance Method Summary (collapse)

Constructor Details

- (Writer) initialize(output = $stdout, options = {}) {|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

  • :lang (#to_s) — default: nil

    Output as root @lang attribute, and avoid generation _@lang_ 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::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::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. Default options set :ugly => false to ensure that whitespace in literals with newlines is properly preserved.

Yields:

  • (writer)

Yield Parameters:

  • writer (RDF::Writer)


109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/rdf/rdfa/writer.rb', line 109

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::DC.title]
    @heading_predicates = options[:heading_predicates] || [RDF::RDFS.label, RDF::DC.title]
    @graph = RDF::Graph.new

    block.call(self) if block_given?
  end
end

Instance Attribute Details

- (RDF::URI) base_uri

Base URI used for relativizing URIs

Returns:

  • (RDF::URI)

    Base URI used for relativizing URIs



77
78
79
# File 'lib/rdf/rdfa/writer.rb', line 77

def base_uri
  @base_uri
end

- (Graph) graph

Graph of statements serialized

Returns:

  • (Graph)

    Graph of statements serialized



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

def graph
  @graph
end

- (Array<URI>) heading_predicates (readonly)

Defines order of predicates to use in heading.

Returns:

  • (Array<URI>)


67
68
69
# File 'lib/rdf/rdfa/writer.rb', line 67

def heading_predicates
  @heading_predicates
end

- (Array<URI>) predicate_order (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>)


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

def predicate_order
  @predicate_order
end

- (Array<URI>) top_classes (readonly)

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

Returns:

  • (Array<URI>)


58
59
60
# File 'lib/rdf/rdfa/writer.rb', line 58

def top_classes
  @top_classes
end

Instance Method Details

- (Hash<Symbol => String>) haml_template

Returns:



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

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

- (void) write_epilogue

This method returns an undefined value.

Outputs the XHTML+RDFa representation of all stored triples.



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
197
198
199
200
201
# File 'lib/rdf/rdfa/writer.rb', line 165

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

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

  preprocess

  # Prefixes
  prefix = prefixes.keys.map {|pk| "#{pk}: #{prefixes[pk]}"}.sort.join(" ") unless prefixes.empty?
  add_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)
end

- (void) write_graph(graph)

This method returns an undefined value.

Write whole graph

Parameters:

  • graph (Graph)


137
138
139
# File 'lib/rdf/rdfa/writer.rb', line 137

def write_graph(graph)
  @graph = graph
end

- (void) write_statement(statement)

This method returns an undefined value.

Addes a statement to be serialized

Parameters:

  • statement (RDF::Statement)


145
146
147
# File 'lib/rdf/rdfa/writer.rb', line 145

def write_statement(statement)
  @graph.insert(statement)
end

- (void) write_triple(subject, predicate, object)

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



157
158
159
# File 'lib/rdf/rdfa/writer.rb', line 157

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