lib/rdf/rdfa/writer.rb in rdf-rdfa-0.3.5.1 vs lib/rdf/rdfa/writer.rb in rdf-rdfa-0.3.6

- old
+ new

@@ -1,10 +1,8 @@ require 'haml' require 'cgi' -require 'rdf/rdfa/patches/graph_properties' - module RDF::RDFa ## # 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. @@ -49,17 +47,10 @@ # graph.each_statement do |statement| # writer << statement # end # end # - # @example Creating @profile definitions in output - # RDF::RDFa::Writer.buffer(:profile => "http://example.com/profile") do |writer| - # graph.each_statement do |statement| - # writer << statement - # end - # end - # # @author [Gregg Kellogg](http://kellogg-assoc.com/) class Writer < RDF::Writer format RDF::RDFa::Format # Defines rdf:type of subjects to be emitted at the beginning of the document. @@ -94,21 +85,16 @@ # any additional options # @option options [Boolean] :canonicalize (false) # whether to canonicalize literals when serializing # @option options [Hash] :prefixes (Hash.new) # the prefix mappings to use - # @option options [#to_a] :profiles (Array.new) - # List of profiles to add to document. This will use terms, prefix definitions and default-vocabularies - # identified within the profiles (taken in reverse order) to determine how to serialize terms # @option options [#to_s] :base_uri (nil) # the base URI to use when constructing relative URIs, set as html>head>base.href # @option options [#to_s] :lang (nil) # Output as root @lang attribute, and avoid generation _@lang_ where possible # @option options [Boolean] :standard_prefixes (false) # Add standard prefixes to _prefixes_, if necessary. - # @option options [Repository] :profile_repository (nil) - # Repository to find and save profile graphs. # @option options [Array<RDF::URI>] :top_classes ([RDF::RDFS.Class]) # Defines rdf:type of subjects to be emitted at the beginning of the document. # @option options [Array<RDF::URI>] :predicate_order ([RDF.type, RDF::RDFS.label, RDF::DC.title]) # Defines order of predicates to to emit at begninning of a resource description.. # @option options [Array<RDF::URI>] :heading_predicates ([RDF::RDFS.label, RDF::DC.title]) @@ -119,11 +105,10 @@ # Options to pass to Haml::Engine.new. Default options set :ugly => false # to ensure that whitespace in literals with newlines is properly preserved. # @yield [writer] # @yieldparam [RDF::Writer] writer def initialize(output = $stdout, options = {}, &block) - self.profile_repository = options[:profile_repository] if options[:profile_repository] 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] @@ -142,21 +127,10 @@ when Hash then @options[:haml] else DEFAULT_HAML end end - # @return [RDF::Repository] - def profile_repository - Profile.repository - end - - # @param [RDF::Repository] repo - # @return [RDF::Repository] - def profile_repository=(repo) - Profile.repository = repo - end - ## # Write whole graph # # @param [Graph] graph # @return [void] @@ -192,20 +166,17 @@ @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}" + add_debug {"\nserialize: graph size: #{@graph.size}"} preprocess - # Profiles - profile = @options[:profiles].join(" ") if @options[:profiles] - # Prefixes prefix = prefixes.keys.map {|pk| "#{pk}: #{prefixes[pk]}"}.sort.join(" ") unless prefixes.empty? - add_debug "\nserialize: prefixes: #{prefix.inspect}" + add_debug {"\nserialize: prefixes: #{prefix.inspect}"} subjects = order_subjects # Take title from first subject having a heading predicate doc_title = nil @@ -221,11 +192,10 @@ # Generate document doc = render_document(subjects, :lang => @lang, :base => @base_uri, :title => doc_title, - :profile => profile, :prefix => prefix) do |s| subject(s) end @output.write(doc) end @@ -236,11 +206,11 @@ # Yields each subject to be rendered separately. # # The default Haml template is: # !!! XML # !!! 5 - # %html{:xmlns => "http://www.w3.org/1999/xhtml", :lang => lang, :profile => profile, :prefix => prefix} + # %html{:xmlns => "http://www.w3.org/1999/xhtml", :lang => lang, :prefix => prefix} # - if base || title # %head # - if base # %base{:href => base} # - if title @@ -258,12 +228,10 @@ # @option options [Symbol, String] language (nil) # Value of @lang attribute in document, also allows included literals to omit # an @lang attribute if it is equivalent to that of the document. # @option options [String] title (nil) # Value of html>head>title element. - # @option options [String] profile (nil) - # Value of @profile attribute. # @option options [String] prefix (nil) # Value of @prefix attribute. # @option options [String] haml (haml_template[:doc]) # Haml template to render. # @yield [subject] @@ -274,11 +242,10 @@ # The rendered document is returned as a string def render_document(subjects, options = {}) template = options[:haml] || :doc options = { :prefix => nil, - :profile => nil, :subjects => subjects, :title => nil, }.merge(options) hamlify(template, options) do |subject| yield(subject) if block_given? @@ -458,24 +425,24 @@ end # Perform any preprocessing of statements required # @return [ignored] def preprocess - # Load profiles + # Load default profiles # Add terms and prefixes to local store for converting URIs # Keep track of vocabulary from left-most profile - [@options[:profiles]].flatten.compact.reverse.each do |uri| + [XML_RDFA_PROFILE, XHTML_RDFA_PROFILE].each do |uri| prof = Profile.find(uri) prof.prefixes.each_pair do |k, v| @uri_to_prefix[v] = k end prof.terms.each_pair do |k, v| - @uri_to_term_or_curie[v] = RDF::URI.intern(k) + @uri_to_term_or_curie[v] = k end - @vocabulary = prof.vocabulary.to_s + @vocabulary = prof.vocabulary.to_s if prof.vocabulary end # Load defined prefixes (@options[:prefixes] || {}).each_pair do |k, v| @uri_to_prefix[v.to_s] = k @@ -503,11 +470,11 @@ # Add distinguished classes top_classes. select {|s| !seen.include?(s)}. each do |class_uri| graph.query(:predicate => RDF.type, :object => class_uri).map {|st| st.subject}.sort.uniq.each do |subject| - #add_debug "order_subjects: #{subject.inspect}" + #add_debug {"order_subjects: #{subject.inspect}"} subjects << subject seen[subject] = true end end @@ -515,54 +482,44 @@ recursable = @subjects.keys. select {|s| !seen.include?(s)}. map {|r| [r.is_a?(RDF::Node) ? 1 : 0, ref_count(r), r]}. sort - add_debug "order_subjects: #{recursable.inspect}" + add_debug {"order_subjects: #{recursable.inspect}"} subjects += recursable.map{|r| r.last} end # Take a hash from predicate uris to lists of values. # Sort the lists of values. Return a sorted list of properties. # # @param [Hash{String => Array<Resource>}] properties A hash of Property to Resource mappings # @return [Array<String>}] Ordered list of properties. Uses predicate_order. def order_properties(properties) - properties.keys.each do |k| - properties[k] = properties[k].sort do |a, b| - a_li = a.is_a?(RDF::URI) && get_curie(a) && get_curie(a).to_s =~ /:_\d+$/ ? a.to_i : a.to_s - b_li = b.is_a?(RDF::URI) && get_curie(b) && get_curie(b).to_s =~ /:_\d+$/ ? b.to_i : b.to_s - - a_li <=> b_li - end - end - # Make sorted list of properties prop_list = [] predicate_order.each do |prop| - next unless properties[prop] + next unless properties[prop.to_s] prop_list << prop.to_s end properties.keys.sort.each do |prop| - prop = prop =~ /^_:(.*)$/ ? RDF::Node.intern($1) : RDF::URI.intern(prop) - next if prop_list.include?(prop) - prop_list << prop + next if prop_list.include?(prop.to_s) + prop_list << prop.to_s end - add_debug "order_properties: #{prop_list.inspect}" + add_debug {"order_properties: #{prop_list.join(', ')}"} prop_list end # Perform any statement preprocessing required. This is used to perform reference counts and determine required # prefixes. # @param [RDF::Statement] statement # @return [ignored] def preprocess_statement(statement) - #add_debug "preprocess: #{statement.inspect}" + #add_debug {"preprocess: #{statement.inspect}"} references = ref_count(statement.object) + 1 @references[statement.object] = references @subjects[statement.subject] = true get_curie(statement.subject) get_curie(statement.predicate) @@ -604,11 +561,15 @@ def subject(subject, options = {}) return if is_done?(subject) subject_done(subject) - properties = @graph.properties(subject) + properties = {} + @graph.query(:subject => subject) do |st| + properties[st.predicate.to_s] ||= [] + properties[st.predicate.to_s] << st.object + end prop_list = order_properties(properties) # Find appropriate template curie ||= case when subject.node? @@ -626,23 +587,23 @@ # Nodes without a curie need a blank @typeof to generate a subject typeof ||= "" unless curie prop_list -= [RDF.type.to_s] - add_debug "subject: found template #{tmpl[:identifier] || tmpl.inspect}" if tmpl - add_debug "subject: #{curie.inspect}, typeof: #{typeof.inspect}, props: #{prop_list.inspect}" + add_debug {"subject: found template #{tmpl[:identifier] || tmpl.inspect}"} if tmpl + add_debug {"subject: #{curie.inspect}, typeof: #{typeof.inspect}, props: #{prop_list.inspect}"} # Render this subject # If :rel is specified and :typeof is nil, use @resource instead of @about. # Pass other options from calling context render_opts = {:typeof => typeof}.merge(options) with_template(tmpl) do render_subject(subject, prop_list, render_opts) do |pred| depth do pred = RDF::URI(pred) if pred.is_a?(String) values = properties[pred.to_s] - add_debug "subject: #{get_curie(subject)}, pred: #{get_curie(pred)}, values: #{values.inspect}" + add_debug {"subject: #{get_curie(subject)}, pred: #{get_curie(pred)}, values: #{values.inspect}"} predicate(pred, values) end end end end @@ -654,15 +615,15 @@ # Predicate to serialize # @param [Array<RDF::Resource>] objects # Objects to serialize # @return [String] def predicate(predicate, objects) - add_debug "predicate: #{predicate.inspect}, objects: #{objects}" + add_debug {"predicate: #{predicate.inspect}, objects: #{objects}"} return if objects.to_a.empty? - add_debug("predicate: #{get_curie(predicate)}") + add_debug {"predicate: #{get_curie(predicate)}"} property = predicate if objects.any?(&:literal?) rel = predicate if objects.any?(&:uri?) || objects.any?(&:node?) render_property(predicate, objects, property, rel) do |o| # Yields each object, for potential recursive definition. # If nil is returned, a leaf is produced @@ -741,47 +702,39 @@ # @raise [RDF::WriterError] def get_curie(resource) raise RDF::WriterError, "Getting CURIE for #{resource.inspect}, which must be an RDF value" unless resource.is_a?(RDF::Value) return resource.to_s unless resource.uri? - @rdfcore_prefixes ||= RDF::RDFa::Profile.find(RDF::URI("http://www.w3.org/profile/rdfa-1.1")).prefixes - uri = resource.to_s curie = case when @uri_to_term_or_curie.has_key?(uri) - #add_debug("get_curie(#{uri}): uri_to_term_or_curie") + add_debug {"get_curie(#{uri}): uri_to_term_or_curie #{@uri_to_term_or_curie[uri].inspect}"} return @uri_to_term_or_curie[uri] when @base_uri && uri.index(@base_uri.to_s) == 0 - #add_debug("get_curie(#{uri}): base_uri (#{uri.sub(@base_uri.to_s, "")})") + add_debug {"get_curie(#{uri}): base_uri (#{uri.sub(@base_uri.to_s, "")})"} uri.sub(@base_uri.to_s, "") when @vocabulary && uri.index(@vocabulary) == 0 - #add_debug("get_curie(#{uri}): vocabulary") + add_debug {"get_curie(#{uri}): vocabulary"} uri.sub(@vocabulary, "") when u = @uri_to_prefix.keys.detect {|u| uri.index(u.to_s) == 0} - #add_debug("get_curie(#{uri}): uri_to_prefix") + add_debug {"get_curie(#{uri}): uri_to_prefix"} # Use a defined prefix prefix = @uri_to_prefix[u] prefix(prefix, u) # Define for output uri.sub(u.to_s, "#{prefix}:") - when u = @rdfcore_prefixes.values.detect {|u| uri.index(u.to_s) == 0} - #add_debug("get_curie(#{uri}): rdfcore_prefixes") - # Use standard profile prefixes - pfx = @rdfcore_prefixes.invert[u] - prefix(pfx, u) # Define for output - uri.sub(u.to_s, "#{pfx}:") when @options[:standard_prefixes] && vocab = RDF::Vocabulary.detect {|v| uri.index(v.to_uri.to_s) == 0} - #add_debug("get_curie(#{uri}): standard_prefixes") + add_debug {"get_curie(#{uri}): standard_prefixes"} prefix = vocab.__name__.to_s.split('::').last.downcase prefix(prefix, vocab.to_uri) # Define for output uri.sub(vocab.to_uri.to_s, "#{prefix}:") else - #add_debug("get_curie(#{uri}): none") + add_debug {"get_curie(#{uri}): none"} uri end - #add_debug("get_curie(#{resource}) => #{curie}") + #add_debug {"get_curie(#{resource}) => #{curie}"} @uri_to_term_or_curie[uri] = curie rescue Addressable::URI::InvalidURIError => e raise RDF::WriterError, "Invalid URI #{uri.inspect}: #{e.message}" end @@ -831,15 +784,15 @@ # @param [Hash{Symbol => Object}] locals # Locals to pass to render # @return [String] # @raise [RDF::WriterError] def hamlify(template, locals = {}) - add_debug "hamlify template: #{template}" + add_debug {"hamlify template: #{template}"} template = haml_template[template] if template.is_a?(Symbol) template = template.align_left - add_debug "hamlify locals: #{locals.inspect}" + add_debug {"hamlify locals: #{locals.inspect}"} Haml::Engine.new(template, @options[:haml_options] || HAML_OPTIONS).render(self, locals) do |*args| yield(*args) if block_given? end rescue Haml::Error => e @@ -872,10 +825,13 @@ end # Add debug event to debug array, if specified # # @param [String] message:: - def add_debug(message) + # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message + def add_debug(message = "") + return unless ::RDF::RDFa.debug? || @debug + message = message + yield if block_given? msg = "#{' ' * @depth}#{message}" STDERR.puts msg if ::RDF::RDFa.debug? @debug << msg if @debug.is_a?(Array) end end