rdf-rdfxml 3.2.0 → 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a96523958077fe5163fbef8f4b68daabdd795f311b213c23a509f4847ff5ca8e
4
- data.tar.gz: 6c5180a2cc088857c4e0b984ca32d43451d85ff906f84d9bf52a001445fd50c9
3
+ metadata.gz: 1e99dd30e3fe9c3457ef54ed1bb66f455e16082e7c0ad72fc30414c0e38a7fad
4
+ data.tar.gz: ce09170384f2015247954df866fad90a12a305683a5161d10c9c8764b48b9653
5
5
  SHA512:
6
- metadata.gz: 73b19eaa58dfa6c7263112ffc64bbc204b2d28c5afc00ddedc9a0d8b99edc736da11d3d00faa2e6b98b840cccee5e5d3e59f5aa090210b368e09c5d73c3f288f
7
- data.tar.gz: 8e71648aa0ca0d4a5b182f60bd9e958975cde89b2604af6c1a89c9c20fafa1f36599a69628421c9400052ae72ce7fd4b08aa556f4b70cd082a75e59e64fe641c
6
+ metadata.gz: cbda846c7dbfd709d03af8164b4d8a89c2f38f464e5867faa6b517bb7392794749e1a692c7f609055fd4af95d8378f65ae3416ee31de7f78e934bd066bccdb21
7
+ data.tar.gz: 9a3e918e57823b6cf8c16ade45c8d0a47e914448f7efcb94a347e3d6f555ff9ba80ace97cba53f52d229e25f2bf0e3ed6465628d3dc93208401ed495f12a89df
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [RDF/XML][] reader/writer for [RDF.rb][].
4
4
 
5
- [![Gem Version](https://badge.fury.io/rb/rdf-rdfxml.png)](https://badge.fury.io/rb/rdf-rdfxml)
5
+ [![Gem Version](https://badge.fury.io/rb/rdf-rdfxml.svg)](https://badge.fury.io/rb/rdf-rdfxml)
6
6
  [![Build Status](https://github.com/ruby-rdf/rdf-rdfxml/workflows/CI/badge.svg?branch=develop)](https://github.com/ruby-rdf/rdf-rdfxml/actions?query=workflow%3ACI)
7
7
  [![Coverage Status](https://coveralls.io/repos/ruby-rdf/rdf-rdfxml/badge.svg?branch=develop)](https://coveralls.io/github/ruby-rdf/rdf-rdfxml?branch=develop)
8
8
  [![Gitter chat](https://badges.gitter.im/ruby-rdf/rdf.png)](https://gitter.im/ruby-rdf/rdf)
@@ -43,7 +43,8 @@ Write a graph to a file:
43
43
 
44
44
  ## Dependencies
45
45
  * [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.2)
46
- * Soft dependency on [Nokogiri](https://rubygems.org/gems/nokogiri) (>= 1.12)
46
+ * [Builder](https://rubygems.org/gems/builder) (~>- 3.2)
47
+ * Soft dependency on [Nokogiri](https://rubygems.org/gems/nokogiri) (>= 1.13)
47
48
 
48
49
  ## Documentation
49
50
  Full documentation available on [Rubydoc.info][RDF/XML doc])
@@ -103,5 +104,5 @@ see <https://unlicense.org/> or the accompanying {file:UNLICENSE} file.
103
104
  [YARD]: https://yardoc.org/
104
105
  [YARD-GS]: https://rubydoc.info/docs/yard/file/docs/GettingStarted.md
105
106
  [PDD]: https://unlicense.org/#unlicensing-contributions
106
- [RDF/XML doc]: https://rubydoc.info/github/ruby-rdf/rdf-rdfxml/master/frames
107
+ [RDF/XML doc]: https://ruby-rdf.github.io/rdf-rdfxml/master/frames
107
108
  [RDF-star]: https://w3c.github.io/rdf-star/rdf-star-cg-spec.html
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0
1
+ 3.2.2
@@ -0,0 +1,54 @@
1
+ # Extend builder to allow for better control of whitespace in XML Literals
2
+
3
+ require 'builder'
4
+
5
+ module Builder
6
+ # Extends XmlMarkup#tag! to better control whitespace when adding content from a block
7
+ #
8
+ class RdfXml < Builder::XmlMarkup
9
+ # Create a tag named +sym+. Other than the first argument which
10
+ # is the tag name, the arguments are the same as the tags
11
+ # implemented via <tt>method_missing</tt>.
12
+ #
13
+ # @see https://github.com/jimweirich/builder/blob/master/lib/builder/xmlbase.rb
14
+ def tag!(sym, *args, &block)
15
+ text = nil
16
+ attrs = args.last.is_a?(::Hash) ? args.last : {}
17
+ return super unless block && attrs[:no_whitespace]
18
+ attrs.delete(:no_whitespace)
19
+
20
+ sym = "#{sym}:#{args.shift}".to_sym if args.first.kind_of?(::Symbol)
21
+
22
+ args.each do |arg|
23
+ case arg
24
+ when ::Hash
25
+ attrs.merge!(arg)
26
+ when nil
27
+ attrs.merge!({:nil => true}) if explicit_nil_handling?
28
+ else
29
+ text ||= ''
30
+ text << arg.to_s
31
+ end
32
+ end
33
+
34
+ unless text.nil?
35
+ ::Kernel::raise ::ArgumentError,
36
+ "XmlMarkup cannot mix a text argument with a block"
37
+ end
38
+
39
+ # Indent
40
+ _indent
41
+ #unless @indent == 0 || @level == 0
42
+ # text!(" " * (@level * @indent))
43
+ #end
44
+
45
+ _start_tag(sym, attrs)
46
+ begin
47
+ _nested_structures(block)
48
+ ensure
49
+ _end_tag(sym)
50
+ _newline
51
+ end
52
+ end
53
+ end
54
+ end
@@ -20,7 +20,9 @@ module RDF::RDFXML
20
20
  #
21
21
  # @see http://www.w3.org/TR/rdf-testcases/#ntriples
22
22
  class Format < RDF::Format
23
- content_type 'application/rdf+xml', extensions: [:rdf, :owl]
23
+ content_type 'application/rdf+xml',
24
+ extensions: [:rdf, :owl],
25
+ uri: 'http://www.w3.org/ns/formats/RDF_XML'
24
26
  content_encoding 'utf-8'
25
27
 
26
28
  reader { RDF::RDFXML::Reader }
@@ -25,7 +25,7 @@ module RDF::RDFXML
25
25
 
26
26
  # Create a new element child of an existing node
27
27
  def create_node(name, children)
28
- native = ::Nokogiri::XML::Element.new(name, @node)
28
+ native = ::Nokogiri::XML::Element.new(name, @node.document)
29
29
  children.each do |c|
30
30
  native.add_child(c.node)
31
31
  end
@@ -1,6 +1,6 @@
1
1
  begin
2
2
  require 'nokogiri'
3
- rescue LoadError => e
3
+ rescue LoadError
4
4
  :rexml
5
5
  end
6
6
  require 'rdf/xsd'
@@ -91,7 +91,7 @@ module RDF::RDFXML
91
91
  # Produce the next list entry for this context
92
92
  def li_next
93
93
  @li_counter += 1
94
- predicate = RDF["_#{@li_counter}"]
94
+ RDF["_#{@li_counter}"]
95
95
  end
96
96
 
97
97
  # Set XML base. Ignore any fragment
@@ -328,7 +328,6 @@ module RDF::RDFXML
328
328
  end
329
329
 
330
330
  # Handle the propertyEltList children events in document order
331
- li_counter = 0 # this will increase for each li we iterate through
332
331
  el.children.each do |child|
333
332
  log_fatal "child must be a proxy not a #{child.class}" unless child.is_a?(@implementation::NodeProxy)
334
333
  next unless child.element?
@@ -1,4 +1,4 @@
1
- require 'rdf/rdfa'
1
+ require_relative 'extensions'
2
2
 
3
3
  module RDF::RDFXML
4
4
  ##
@@ -47,15 +47,25 @@ module RDF::RDFXML
47
47
  # end
48
48
  #
49
49
  # @author [Gregg Kellogg](http://greggkellogg.net/)
50
- class Writer < RDF::RDFa::Writer
50
+ class Writer < RDF::Writer
51
51
  format RDF::RDFXML::Format
52
52
  include RDF::Util::Logger
53
53
 
54
54
  VALID_ATTRIBUTES = [:none, :untyped, :typed]
55
55
 
56
+ # Defines rdf:type of subjects to be emitted at the beginning of the document.
57
+ # @return [Array<URI>]
58
+ attr :top_classes
59
+
60
+ # @return [Graph] Graph of statements serialized
61
+ attr_accessor :graph
62
+
63
+ # @return [RDF::URI] Base URI used for relativizing URIs
64
+ attr_accessor :base_uri
65
+
56
66
  ##
57
67
  # RDF/XML Writer options
58
- # @see http://www.rubydoc.info/github/ruby-rdf/rdf/RDF/Writer#options-class_method
68
+ # @see https://ruby-rdf.github.io/rdf/RDF/Writer#options-class_method
59
69
  def self.options
60
70
  super + [
61
71
  RDF::CLI::Option.new(
@@ -71,8 +81,8 @@ module RDF::RDFXML
71
81
  RDF::CLI::Option.new(
72
82
  symbol: :lang,
73
83
  datatype: String,
74
- on: ["--lang"],
75
- description: "Output as root @lang attribute, and avoid generation _@lang_ where possible."),
84
+ on: ["--lang LANG", :REQUIRED],
85
+ description: "Output as root xml:lang attribute, and avoid generation xml:lang, where possible.") {|arg| RDF::URI(arg)},
76
86
  RDF::CLI::Option.new(
77
87
  symbol: :max_depth,
78
88
  datatype: Integer,
@@ -93,71 +103,152 @@ module RDF::RDFXML
93
103
  # the output stream
94
104
  # @param [Hash{Symbol => Object}] options
95
105
  # any additional options
96
- # @option options [Boolean] :canonicalize (false)
97
- # whether to canonicalize literals when serializing
98
- # @option options [Hash] :prefixes (Hash.new)
99
- # the prefix mappings to use (not supported by all writers)
106
+ # @option options [Symbol] :attributes (nil)
107
+ # How to use XML attributes when serializing, one of :none, :untyped, :typed. The default is :none.
100
108
  # @option options [#to_s] :base_uri (nil)
101
109
  # the base URI to use when constructing relative URIs
102
- # @option options [Integer] :max_depth (10)
103
- # Maximum depth for recursively defining resources
110
+ # @option options [Boolean] :canonicalize (false)
111
+ # whether to canonicalize literals when serializing
112
+ # @option options [String] :default_namespace (nil)
113
+ # URI to use as default namespace, same as prefix(nil)
104
114
  # @option options [#to_s] :lang (nil)
105
115
  # Output as root xml:lang attribute, and avoid generation _xml:lang_ where possible
106
- # @option options [Symbol] :attributes (nil)
107
- # How to use XML attributes when serializing, one of :none, :untyped, :typed. The default is :none.
116
+ # @option options [Integer] :max_depth (10)
117
+ # Maximum depth for recursively defining resources
118
+ # @option options [Hash] :prefixes (Hash.new)
119
+ # the prefix mappings to use (not supported by all writers)
108
120
  # @option options [Boolean] :standard_prefixes (false)
109
121
  # Add standard prefixes to _prefixes_, if necessary.
110
- # @option options [String] :default_namespace (nil)
111
- # URI to use as default namespace, same as prefix(nil)
112
122
  # @option options [String] :stylesheet (nil)
113
123
  # URI to use as @href for output stylesheet processing instruction.
124
+ # @option options [Array<RDF::URI>] :top_classes ([RDF::RDFS.Class])
125
+ # Defines rdf:type of subjects to be emitted at the beginning of the document.
114
126
  # @yield [writer]
115
127
  # @yieldparam [RDF::Writer] writer
116
128
  def initialize(output = $stdout, **options, &block)
117
- super
118
- end
129
+ super do
130
+ @graph = RDF::Graph.new
131
+ @uri_to_prefix = {}
132
+ @uri_to_qname = {}
133
+ @top_classes = options[:top_classes] || [RDF::RDFS.Class]
119
134
 
120
- # @return [Hash<Symbol => String>]
121
- def haml_template
122
- return @haml_template if @haml_template
123
- case @options[:haml]
124
- when Hash then @options[:haml]
125
- else DEFAULT_HAML
135
+ block.call(self) if block_given?
126
136
  end
127
137
  end
128
138
 
139
+ ##
140
+ # Addes a triple to be serialized
141
+ # @param [RDF::Resource] subject
142
+ # @param [RDF::URI] predicate
143
+ # @param [RDF::Value] object
144
+ # @return [void]
145
+ # @raise [NotImplementedError] unless implemented in subclass
146
+ # @abstract
147
+ # @raise [RDF::WriterError] if validating and attempting to write an invalid {RDF::Term}.
148
+ def write_triple(subject, predicate, object)
149
+ @graph.insert(RDF::Statement(subject, predicate, object))
150
+ end
151
+
129
152
  def write_epilogue
130
- @force_RDF_about = {}
131
153
  @max_depth = @options.fetch(:max_depth, 10)
132
154
  @attributes = @options.fetch(:attributes, :none)
155
+ @base_uri = RDF::URI(@options[:base_uri]) if @options[:base_uri]
156
+ @lang = @options[:lang]
157
+ self.reset
158
+
159
+ log_debug {"\nserialize: graph size: #{@graph.size}"}
160
+
161
+ preprocess
162
+ # Prefixes
163
+ prefix = prefixes.keys.map {|pk| "#{pk}: #{prefixes[pk]}"}.sort.join(" ") unless prefixes.empty?
164
+ log_debug {"\nserialize: prefixes: #{prefix.inspect}"}
165
+
166
+ @subjects = order_subjects
167
+
168
+ # Generate document
169
+ doc = render_document(@subjects,
170
+ lang: @lang,
171
+ base: base_uri,
172
+ prefix: prefix,
173
+ stylesheet: @options[:stylesheet]) do |s|
174
+ subject(s)
175
+ end
176
+ @output.write(doc)
133
177
 
134
178
  super
135
179
  end
136
180
 
137
181
  protected
182
+
183
+ # Reset parser to run again
184
+ def reset
185
+ @options[:log_depth] = 0
186
+ @references = {}
187
+ @serialized = {}
188
+ @subjects = {}
189
+ end
190
+
191
+ # Render document using `haml_template[:doc]`. Yields each subject to be rendered separately.
192
+ #
193
+ # @param [Array<RDF::Resource>] subjects
194
+ # Ordered list of subjects. Template must yield to each subject, which returns
195
+ # the serialization of that subject (@see #subject_template)
196
+ # @param [Hash{Symbol => Object}] options Rendering options passed to Haml render.
197
+ # @option options [RDF::URI] base (nil)
198
+ # Base URI added to document, used for shortening URIs within the document.
199
+ # @option options [Symbol, String] language (nil)
200
+ # Value of @lang attribute in document, also allows included literals to omit
201
+ # an @lang attribute if it is equivalent to that of the document.
202
+ # @option options [String] title (nil)
203
+ # Value of html>head>title element.
204
+ # @option options [String] prefix (nil)
205
+ # Value of @prefix attribute.
206
+ # @option options [String] haml (haml_template[:doc])
207
+ # Haml template to render.
208
+ # @yield [subject]
209
+ # Yields each subject
210
+ # @yieldparam [RDF::URI] subject
211
+ # @yieldparam [Builder::RdfXml] builder
212
+ # @yieldreturn [:ignored]
213
+ # @return String
214
+ # The rendered document is returned as a string
215
+ def render_document(subjects, lang: nil, base: nil, **options, &block)
216
+ builder = Builder::RdfXml.new(indent: 2)
217
+ builder.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
218
+ builder.instruct! :'xml-stylesheet', type: 'text/xsl', href: options[:stylesheet] if options[:stylesheet]
219
+ attrs = prefix_attrs
220
+ attrs[:"xml:lang"] = lang if lang
221
+ attrs[:"xml:base"] = base if base
222
+
223
+ builder.rdf(:RDF, **attrs) do |b|
224
+ subjects.each do |subject|
225
+ render_subject(subject, b, **options)
226
+ end
227
+ end
228
+ end
229
+
138
230
  # Render a subject using `haml_template[:subject]`.
139
231
  #
140
232
  # The _subject_ template may be called either as a top-level element, or recursively under another element if the _rel_ local is not nil.
141
233
  #
142
234
  # For RDF/XML, removes from predicates those that can be rendered as attributes, and adds the `:attr_props` local for the Haml template, which includes all attributes to be rendered as properties.
143
235
  #
144
- # Yields each predicate/property to be rendered separately (@see #render_property_value and `#render_property_values`).
236
+ # Yields each property to be rendered separately.
145
237
  #
146
238
  # @param [Array<RDF::Resource>] subject
147
239
  # Subject to render
148
- # @param [Array<RDF::Resource>] predicates
149
- # Predicates of subject. Each property is yielded for separate rendering.
240
+ # @param [Builder::RdfXml] builder
150
241
  # @param [Hash{Symbol => Object}] options Rendering options passed to Haml render.
151
242
  # @option options [String] about (nil)
152
- # About description, a CURIE, URI or Node definition.
243
+ # About description, a QName, URI or Node definition.
153
244
  # May be nil if no @about is rendered (e.g. unreferenced Nodes)
154
245
  # @option options [String] resource (nil)
155
- # Resource description, a CURIE, URI or Node definition.
246
+ # Resource description, a QName, URI or Node definition.
156
247
  # May be nil if no @resource is rendered
157
248
  # @option options [String] rel (nil)
158
- # Optional @rel property description, a CURIE, URI or Node definition.
249
+ # Optional @rel property description, a QName, URI or Node definition.
159
250
  # @option options [String] typeof (nil)
160
- # RDF type as a CURIE, URI or Node definition.
251
+ # RDF type as a QName, URI or Node definition.
161
252
  # If :about is nil, this defaults to the empty string ("").
162
253
  # @option options [:li, nil] element (nil)
163
254
  # Render with &lt;li&gt;, otherwise with template default.
@@ -167,102 +258,54 @@ module RDF::RDFXML
167
258
  # Yields each predicate
168
259
  # @yieldparam [RDF::URI] predicate
169
260
  # @yieldreturn [:ignored]
170
- # @return String
171
- # The rendered document is returned as a string
172
- # Return Haml template for document from `haml_template[:subject]`
173
- def render_subject(subject, predicates, **options, &block)
174
- # extract those properties that can be rendered as attributes
175
- attr_props = if [:untyped, :typed].include?(@attributes)
176
- options[:property_values].inject({}) do |memo, (prop, values)|
177
- object = values.first
178
- if values.length == 1 &&
179
- object.literal? &&
180
- (object.plain? || @attributes == :typed) &&
181
- get_lang(object).nil?
261
+ # @return Builder::RdfXml
262
+ def render_subject(subject, builder, **options, &block)
263
+ return nil if is_done?(subject)
264
+
265
+ attr_props, embed_props, types = prop_partition(properties_for_subject(subject))
266
+
267
+ # The first type is used for
268
+ first_type = types.shift
269
+ type_qname = get_qname(first_type) if first_type && !first_type.node?
270
+ type_qname = nil unless type_qname.is_a?(String)
271
+ types.unshift(first_type) if first_type && !type_qname
272
+ type_qname ||= "rdf:Description"
273
+
274
+ attr_props = attr_props.merge("rdf:nodeID": subject.id) if subject.node? && ref_count(subject) >= 1
275
+ attr_props = attr_props.merge("rdf:about": subject.relativize(base_uri)) if subject.uri?
276
+
277
+ log_debug {"render_subject(#{subject.inspect})"}
278
+ subject_done(subject)
182
279
 
183
- memo[get_qname(RDF::URI(prop))] = object.value
280
+ builder.tag!(type_qname, **attr_props) do |b|
281
+ types.each do |type|
282
+ if type.node?
283
+ b.tag!("rdf:type", "rdf:nodeID": type.id)
284
+ else
285
+ b.tag!("rdf:type", "rdf:resource": type.to_s)
184
286
  end
185
- memo
186
287
  end
187
- else
188
- {}
189
- end
190
-
191
- predicates -= attr_props.keys.map {|k| expand_curie(k).to_s}
192
- super(subject, predicates, **options.merge(attr_props: attr_props), &block)
193
- end
194
- # See if we can serialize as attribute.
195
- # * untyped attributes that aren't duplicated where xml:lang == @lang
196
- # * typed attributes that aren't duplicated if @dt_as_attr is true
197
- # * rdf:type
198
- def predicate_as_attribute?(prop, object)
199
- [:untyped, :typed].include?(@attributes) && (
200
- prop == RDF.type ||
201
- [:typed].include?(@attributes) && object.literal? && object.typed? ||
202
- (object.literal? && object.simple? || @lang && object.language.to_s == @lang.to_s)
203
- )
204
- end
205
288
 
206
- # Render document using `haml_template[:doc]`. Yields each subject to be rendered separately.
207
- #
208
- # For RDF/XML pass along a stylesheet option.
209
- #
210
- # @param [Array<RDF::Resource>] subjects
211
- # Ordered list of subjects. Template must yield to each subject, which returns
212
- # the serialization of that subject (@see #subject_template)
213
- # @param [Hash{Symbol => Object}] options Rendering options passed to Haml render.
214
- # @option options [RDF::URI] base (nil)
215
- # Base URI added to document, used for shortening URIs within the document.
216
- # @option options [Symbol, String] language (nil)
217
- # Value of @lang attribute in document, also allows included literals to omit
218
- # an @lang attribute if it is equivalent to that of the document.
219
- # @option options [String] title (nil)
220
- # Value of html>head>title element.
221
- # @option options [String] prefix (nil)
222
- # Value of @prefix attribute.
223
- # @option options [String] haml (haml_template[:doc])
224
- # Haml template to render.
225
- # @yield [subject]
226
- # Yields each subject
227
- # @yieldparam [RDF::URI] subject
228
- # @yieldreturn [:ignored]
229
- # @return String
230
- # The rendered document is returned as a string
231
- def render_document(subjects, **options, &block)
232
- super(subjects, **options.merge(stylesheet: @options[:stylesheet]), &block)
289
+ log_depth do
290
+ embed_props.each do |p, objects|
291
+ render_property(p, objects, b, **options)
292
+ end
293
+ end
294
+ end
233
295
  end
234
296
 
235
- # Render a single- or multi-valued predicate using `haml_template[:property_value]` or `haml_template[:property_values]`. Yields each object for optional rendering. The block should only render for recursive subject definitions (i.e., where the object is also a subject and is rendered underneath the first referencing subject).
236
- #
237
- # For RDF/XML, pass the `:no_list_literals` option onto the `RDFa` implementation because of special considerations for lists in RDF/XML.
297
+ # Render a single- or multi-valued property. Yields each object for optional rendering. The block should only render for recursive subject definitions (i.e., where the object is also a subject and is rendered underneath the first referencing subject).
238
298
  #
239
299
  # If a multi-valued property definition is not found within the template, the writer will use the single-valued property definition multiple times.
240
300
  #
241
- # @param [Array<RDF::Resource>] predicate
242
- # Predicate to render.
301
+ # @param [String] property
302
+ # Property to render, already in QName form.
243
303
  # @param [Array<RDF::Resource>] objects
244
304
  # List of objects to render. If the list contains only a single element, the :property_value template will be used. Otherwise, the :property_values template is used.
305
+ # @param [Builder::RdfXml] builder
245
306
  # @param [Hash{Symbol => Object}] options Rendering options passed to Haml render.
246
- # @option options [String] :haml (haml_template[:property_value], haml_template[:property_values])
247
- # Haml template to render. Otherwise, uses `haml_template[:property_value] or haml_template[:property_values]`
248
- # depending on the cardinality of objects.
249
- # @option options [Boolean] :no_list_literals
250
- # Do not serialize as a list if any elements are literal.
251
- # @yield object, inlist
252
- # Yields object and if it is contained in a list.
253
- # @yieldparam [RDF::Resource] object
254
- # @yieldparam [Boolean] inlist
255
- # @yieldreturn [String, nil]
256
- # The block should only return a string for recursive object definitions.
257
- # @return String
258
- # The rendered document is returned as a string
259
- def render_property(predicate, objects, **options, &block)
260
- log_debug {"render_property(#{predicate}): #{objects.inspect}, #{options.inspect}"}
261
- # If there are multiple objects, and no :property_values is defined, call recursively with
262
- # each object
263
-
264
- template = options[:haml]
265
- template ||= haml_template[:property_value]
307
+ def render_property(property, objects, builder, **options)
308
+ log_debug {"render_property(#{property}): #{objects.inspect}"}
266
309
 
267
310
  # Separate out the objects which are lists and render separately
268
311
  lists = objects.
@@ -270,38 +313,57 @@ module RDF::RDFXML
270
313
  map {|o| RDF::List.new(subject: o, graph: @graph)}.
271
314
  select {|l| l.valid? && l.none?(&:literal?)}
272
315
 
316
+ objects = objects - lists.map(&:subject)
317
+
273
318
  unless lists.empty?
274
319
  # Render non-list objects
275
- log_debug {"properties with lists: #{lists} non-lists: #{objects - lists.map(&:subject)}"}
276
- nl = log_depth {render_property(predicate, objects - lists.map(&:subject), options, &block)} unless objects == lists.map(&:subject)
277
- return nl.to_s + lists.map do |list|
320
+ log_debug(depth: log_depth + 1) {"properties with lists: #{lists} non-lists: #{objects - lists.map(&:subject)}"}
321
+
322
+ unless objects.empty?
323
+ render_property(property, objects, builder, **options)
324
+ end
325
+
326
+ # Render each list
327
+ lists.each do |list|
278
328
  # Render each list as multiple properties and set :inlist to true
279
329
  list.each_statement {|st| subject_done(st.subject)}
280
330
 
281
- log_debug {"list: #{list.inspect} #{list.to_a}"}
282
331
  log_depth do
283
- render_collection(predicate, list, **options) do |object|
284
- yield(object, true) if block_given?
285
- end
332
+ log_debug {"list: #{list.inspect} #{list.to_a}"}
333
+ render_collection(property, list, builder, **options)
286
334
  end
287
- end.join(" ")
335
+ end
288
336
  end
289
337
 
290
- if objects.length > 1
291
- # Render each property using property_value template
292
- objects.map do |object|
293
- log_depth {render_property(predicate, [object], **options, &block)}
294
- end.join(" ")
338
+ if objects.length == 1
339
+ recurse = log_depth <= @max_depth
340
+ object = objects.first
341
+
342
+ if recurse && !is_done?(object)
343
+ builder.tag!(property) do |b|
344
+ render_subject(object, b, **options)
345
+ end
346
+ elsif object.literal? && object.datatype == RDF.XMLLiteral
347
+ builder.tag!(property, "rdf:parseType": "Literal", no_whitespace: true) do |b|
348
+ b << object.value
349
+ end
350
+ elsif object.literal?
351
+ attrs = {}
352
+ attrs[:"xml:lang"] = object.language if object.language?
353
+ attrs[:"rdf:datatype"] = object.datatype if object.datatype?
354
+ builder.tag!(property, object.value.to_s, **attrs)
355
+ elsif object.node?
356
+ builder.tag!(property, "rdf:nodeID": object.id)
357
+ else
358
+ builder.tag!(property, "rdf:resource": object.relativize(base_uri))
359
+ end
295
360
  else
296
- log_fatal("Missing property template", exception: RDF::WriterError) if template.nil?
297
-
298
- options = {
299
- object: objects.first,
300
- predicate: predicate,
301
- property: get_qname(predicate),
302
- recurse: log_depth <= @max_depth
303
- }.merge(options)
304
- hamlify(template, **options, &block)
361
+ # Render each property using property_value template
362
+ objects.each do |object|
363
+ log_depth do
364
+ render_property(property, [object], builder, **options)
365
+ end
366
+ end
305
367
  end
306
368
  end
307
369
 
@@ -309,27 +371,27 @@ module RDF::RDFXML
309
371
  # Render a collection, which may be included in a property declaration, or
310
372
  # may be recursive within another collection
311
373
  #
312
- # @param [RDF::URI] predicate
374
+ # @param [String] property in QName form
313
375
  # @param [RDF::List] list
376
+ # @param [Builder::RdfXml] builder
314
377
  # @param [Hash{Symbol => Object}] options
315
- # @yield object
316
- # Yields object, unless it is an included list
317
- # @yieldparam [RDF::Resource] object
318
- # @yieldreturn [String, nil]
319
- # The block should only return a string for recursive object definitions.
320
378
  # @return String
321
379
  # The rendered collection is returned as a string
322
- def render_collection(predicate, list, **options, &block)
323
- template = options[:haml] || haml_template[:collection]
324
-
325
- options = {
326
- list: list,
327
- predicate: predicate,
328
- property: get_qname(predicate),
329
- recurse: log_depth <= @max_depth,
330
- }.merge(options)
331
- hamlify(template, **options) do |object|
332
- yield object
380
+ def render_collection(property, list, builder, **options, &block)
381
+ builder.tag!(property, "rdf:parseType": "Collection") do |b|
382
+ list.each do |object|
383
+ if log_depth <= @max_depth && !is_done?(object)
384
+ render_subject(object, b)
385
+ elsif object.node?
386
+ if ref_count(object) > 1
387
+ b.tag!("rdf:Description", "rdf:nodeID": object.id)
388
+ else
389
+ b.tag!("rdf:Description")
390
+ end
391
+ else
392
+ b.tag!("rdf:Description", "rdf:about": object.relativize(base_uri))
393
+ end
394
+ end
333
395
  end
334
396
  end
335
397
 
@@ -337,7 +399,7 @@ module RDF::RDFXML
337
399
  # @return [Hash{String => String}]
338
400
  def prefix_attrs
339
401
  prefixes.inject({}) do |memo, (k, v)|
340
- memo[k ? "xmlns:#{k}" : "xmlns"] = v.to_s
402
+ memo[(k ? "xmlns:#{k}" : "xmlns").to_sym] = v.to_s
341
403
  memo
342
404
  end
343
405
  end
@@ -363,32 +425,174 @@ module RDF::RDFXML
363
425
  prefix(nil, @options[:default_namespace])
364
426
  end
365
427
 
366
- # Process each statement to establish CURIEs and Terms
428
+ # Process each statement to establish QNames and Terms
367
429
  @graph.each {|statement| preprocess_statement(statement)}
368
430
  end
369
431
 
370
- ##
371
- # Turn CURIE into a QNAME
372
- def get_qname(uri)
373
- curie = get_curie(uri)
374
- curie.start_with?(":") ? curie[1..-1] : curie
375
- end
376
-
377
432
  # Perform any statement preprocessing required. This is used to perform reference counts and determine required prefixes.
378
433
  #
379
- # For RDF/XML, make sure that all predicates have CURIEs
434
+ # For RDF/XML, make sure that all predicates have QNames
380
435
  # @param [Statement] statement
381
436
  def preprocess_statement(statement)
382
- super
437
+ #log_debug {"preprocess: #{statement.inspect}"}
438
+ bump_reference(statement.object)
439
+ @subjects[statement.subject] = true
440
+ get_qname(statement.subject)
441
+ ensure_qname(statement.predicate)
442
+ statement.predicate == RDF.type && statement.object.uri? ? ensure_qname(statement.object) : get_qname(statement.object)
443
+ get_qname(statement.object.datatype) if statement.object.literal? && statement.object.datatype?
444
+ end
445
+
446
+ private
383
447
 
384
- # Invent a prefix for the predicate, if necessary
385
- ensure_curie(statement.predicate)
386
- ensure_curie(statement.object) if statement.predicate == RDF.type
448
+ # Order subjects for output. Override this to output subjects in another order.
449
+ #
450
+ # Uses #top_classes and #base_uri.
451
+ # @return [Array<Resource>] Ordered list of subjects
452
+ def order_subjects
453
+ seen = {}
454
+ subjects = []
455
+
456
+ # Start with base_uri
457
+ if base_uri && @subjects.keys.include?(base_uri)
458
+ subjects << base_uri
459
+ seen[base_uri] = true
460
+ end
461
+
462
+ # Add distinguished classes
463
+ top_classes.
464
+ select {|s| !seen.include?(s)}.
465
+ each do |class_uri|
466
+ graph.query({predicate: "rdf:type", object: class_uri}).map {|st| st.subject}.sort.uniq.each do |subject|
467
+ #log_debug {"order_subjects: #{subject.inspect}"}
468
+ subjects << subject
469
+ seen[subject] = true
470
+ end
471
+ end
472
+
473
+ # Sort subjects by resources over nodes, ref_counts and the subject URI itself
474
+ recursable = @subjects.keys.
475
+ select {|s| !seen.include?(s)}.
476
+ map {|r| [r.is_a?(RDF::Node) ? 1 : 0, ref_count(r), r]}.
477
+ sort
478
+
479
+ log_debug {"order_subjects: #{recursable.inspect}"}
480
+
481
+ subjects += recursable.map{|r| r.last}
482
+ end
483
+
484
+ # @param [RDF::Resource] subject
485
+ # @return [Hash{String => Object}]
486
+ def properties_for_subject(subject)
487
+ properties = {}
488
+ @graph.query({subject: subject}) do |st|
489
+ key = get_qname(st.predicate.to_s)
490
+ properties[key] ||= []
491
+ properties[key] << st.object
492
+ end
493
+ properties
494
+ end
495
+
496
+ # Partition properties into attributed, embedded, and types
497
+ #
498
+ # @param [Hash{String => Array<RDF::Resource}] properties
499
+ # @return [Hash, Hash, Array<RDF::Resource>]
500
+ def prop_partition(properties)
501
+ attr_props, embed_props = {}, {}
502
+
503
+ type_prop = "rdf:type"
504
+ types = properties.delete(type_prop)
505
+
506
+ # extract those properties that can be rendered as attributes
507
+ if [:untyped, :typed].include?(@attributes)
508
+ properties.each do |prop, values|
509
+ object = values.first
510
+ if values.length == 1 &&
511
+ object.literal? &&
512
+ (object.plain? || @attributes == :typed) &&
513
+ get_lang(object).nil?
514
+
515
+ attr_props[prop.to_sym] = values.first.to_s
516
+ else
517
+ embed_props[prop] = values
518
+ end
519
+ end
520
+ else
521
+ embed_props = properties
522
+ end
523
+
524
+ [attr_props, embed_props, Array(types)]
525
+ end
526
+
527
+ # Return language for literal, if there is no language, or it is the same as the document, return nil
528
+ #
529
+ # @param [RDF::Literal] literal
530
+ # @return [Symbol, nil]
531
+ # @raise [RDF::WriterError]
532
+ def get_lang(literal)
533
+ if literal.is_a?(RDF::Literal)
534
+ literal.language if literal.literal? && literal.language && literal.language.to_s != @lang.to_s
535
+ else
536
+ log_error("Getting language for #{literal.inspect}, which must be a literal")
537
+ nil
538
+ end
387
539
  end
388
540
 
389
- # Make sure a CURIE is defined
390
- def ensure_curie(resource)
391
- if get_curie(resource) == resource.to_s || get_curie(resource).split(':', 2).last =~ /[\.#]/
541
+ # Return appropriate, term, QName or URI for the given resource.
542
+ #
543
+ # @param [RDF::Value, String] resource
544
+ # @return [String] value to use to identify URI
545
+ # @raise [RDF::WriterError]
546
+ def get_qname(resource)
547
+ return @uri_to_qname[resource] if resource.is_a?(String) && @uri_to_qname.key?(resource)
548
+
549
+ case resource
550
+ when RDF::URI
551
+ begin
552
+ uri = resource.to_s
553
+
554
+ qname = case
555
+ when @uri_to_qname.key?(uri)
556
+ @uri_to_qname[uri]
557
+ when base_uri && uri.index(base_uri.to_s) == 0
558
+ #log_debug {"get_qname(#{uri}): base_uri (#{uri.sub(base_uri.to_s, "")})"}
559
+ uri.sub(base_uri.to_s, "")
560
+ when u = @uri_to_prefix.keys.detect {|u| uri.index(u.to_s) == 0}
561
+ #log_debug {"get_qname(#{uri}): uri_to_prefix"}
562
+ # Use a defined prefix
563
+ prefix = @uri_to_prefix[u]
564
+ prefix(prefix, u) # Define for output
565
+ uri.sub(u.to_s, "#{prefix}:")
566
+ when @options[:standard_prefixes] && vocab = RDF::Vocabulary.detect {|v| uri.index(v.to_uri.to_s) == 0}
567
+ #log_debug {"get_qname(#{uri}): standard_prefixes"}
568
+ prefix = vocab.__name__.to_s.split('::').last.downcase
569
+ prefix(prefix, vocab.to_uri) # Define for output
570
+ uri.sub(vocab.to_uri.to_s, "#{prefix}:")
571
+ end
572
+
573
+ # Don't define ill-formed qnames
574
+ @uri_to_qname[uri] = if qname.nil? || qname == ':'
575
+ resource
576
+ elsif qname.start_with?(':')
577
+ qname[1..-1]
578
+ else
579
+ qname
580
+ end
581
+ rescue ArgumentError => e
582
+ log_error("Invalid URI #{uri.inspect}: #{e.message}")
583
+ nil
584
+ end
585
+ when RDF::Node then resource.to_s
586
+ when RDF::Literal then nil
587
+ else
588
+ log_error("Getting QName for #{resource.inspect}, which must be a resource")
589
+ nil
590
+ end
591
+ end
592
+
593
+ # Make sure a QName is defined
594
+ def ensure_qname(resource)
595
+ if get_qname(resource) == resource.to_s || get_qname(resource).split(':', 2).last =~ /[\.#]/
392
596
  uri = resource.to_s
393
597
  # No vocabulary found, invent one
394
598
  # Add bindings for predicates not already having bindings
@@ -399,29 +603,40 @@ module RDF::RDFXML
399
603
  base_uri = uri.to_s[0..separation]
400
604
  suffix = uri.to_s[separation+1..-1]
401
605
  @gen_prefix = @gen_prefix ? @gen_prefix.succ : "ns0"
402
- log_debug {"ensure_curie: generated prefix #{@gen_prefix} for #{base_uri}"}
606
+ log_debug {"ensure_qname: generated prefix #{@gen_prefix} for #{base_uri}"}
403
607
  @uri_to_prefix[base_uri] = @gen_prefix
404
- @uri_to_term_or_curie[uri] = "#{@gen_prefix}:#{suffix}"
608
+ @uri_to_qname[uri] = "#{@gen_prefix}:#{suffix}"
405
609
  prefix(@gen_prefix, base_uri)
406
- get_curie(resource)
610
+ get_qname(resource)
407
611
  end
408
612
  end
409
-
410
- # If base_uri is defined, use it to try to make uri relative
411
- # @param [#to_s] uri
412
- # @return [String]
413
- def relativize(uri)
414
- uri = expand_curie(uri.to_s)
415
- base_uri ? uri.sub(base_uri.to_s, "") : uri
613
+
614
+ # Mark a subject as done.
615
+ # @param [RDF::Resource] subject
616
+ # @return [Boolean]
617
+ def subject_done(subject)
618
+ @serialized[subject] = true
416
619
  end
417
620
 
418
- # Undo CURIE
419
- # @return [RDF::URI]
420
- def expand_curie(curie)
421
- pfx, suffix = curie.split(":", 2)
422
- prefix(pfx) ? prefix(pfx) + suffix : curie
621
+ # Determine if the subject has been completed
622
+ # @param [RDF::Resource] subject
623
+ # @return [Boolean]
624
+ def is_done?(subject)
625
+ @serialized.include?(subject) || !@subjects.include?(subject)
626
+ end
627
+
628
+ # Increase the reference count of this resource
629
+ # @param [RDF::Resource] resource
630
+ # @return [Integer] resulting reference count
631
+ def bump_reference(resource)
632
+ @references[resource] = ref_count(resource) + 1
633
+ end
634
+
635
+ # Return the number of times this node has been referenced in the object position
636
+ # @param [RDF::Node] node
637
+ # @return [Boolean]
638
+ def ref_count(node)
639
+ @references.fetch(node, 0)
423
640
  end
424
641
  end
425
642
  end
426
-
427
- require 'rdf/rdfxml/writer/haml_templates'
data/lib/rdf/rdfxml.rb CHANGED
@@ -2,7 +2,8 @@ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..')))
2
2
  require 'rdf'
3
3
 
4
4
  module RDF
5
- autoload :XML, 'rdf/rdfa/vocab'
5
+ XML = Class.new(Vocabulary("http://www.w3.org/XML/1998/namespace"))
6
+
6
7
  ##
7
8
  # **`RDF::RDFXML`** is an RDF/XML extension for RDF.rb.
8
9
  #
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rdf-rdfxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregg
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-12-08 00:00:00.000000000 Z
12
+ date: 2023-07-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rdf
@@ -40,33 +40,33 @@ dependencies:
40
40
  - !ruby/object:Gem::Version
41
41
  version: '3.2'
42
42
  - !ruby/object:Gem::Dependency
43
- name: rdf-rdfa
43
+ name: htmlentities
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: '3.2'
48
+ version: '4.3'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
- version: '3.2'
55
+ version: '4.3'
56
56
  - !ruby/object:Gem::Dependency
57
- name: htmlentities
57
+ name: builder
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - "~>"
61
61
  - !ruby/object:Gem::Version
62
- version: '4.3'
62
+ version: '3.2'
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: '4.3'
69
+ version: '3.2'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: json-ld
72
72
  requirement: !ruby/object:Gem::Requirement
@@ -192,18 +192,22 @@ files:
192
192
  - UNLICENSE
193
193
  - VERSION
194
194
  - lib/rdf/rdfxml.rb
195
+ - lib/rdf/rdfxml/extensions.rb
195
196
  - lib/rdf/rdfxml/format.rb
196
- - lib/rdf/rdfxml/patches/nokogiri_hacks.rb
197
197
  - lib/rdf/rdfxml/reader.rb
198
198
  - lib/rdf/rdfxml/reader/nokogiri.rb
199
199
  - lib/rdf/rdfxml/reader/rexml.rb
200
200
  - lib/rdf/rdfxml/version.rb
201
201
  - lib/rdf/rdfxml/writer.rb
202
- - lib/rdf/rdfxml/writer/haml_templates.rb
203
202
  homepage: https://github.com/ruby-rdf/rdf-rdfxml
204
203
  licenses:
205
204
  - Unlicense
206
- metadata: {}
205
+ metadata:
206
+ documentation_uri: https://ruby-rdf.github.io/rdf-rdfxml
207
+ bug_tracker_uri: https://github.com/ruby-rdf/rdf-rdfxml/issues
208
+ homepage_uri: https://github.com/ruby-rdf/rdf-rdfxml
209
+ mailing_list_uri: https://lists.w3.org/Archives/Public/public-rdf-ruby/
210
+ source_code_uri: https://github.com/ruby-rdf/rdf-rdfxml
207
211
  post_install_message:
208
212
  rdoc_options: []
209
213
  require_paths:
@@ -219,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
223
  - !ruby/object:Gem::Version
220
224
  version: '0'
221
225
  requirements: []
222
- rubygems_version: 3.3.3
226
+ rubygems_version: 3.3.26
223
227
  signing_key:
224
228
  specification_version: 4
225
229
  summary: RDF/XML reader/writer for RDF.rb.
@@ -1,23 +0,0 @@
1
- require 'nokogiri'
2
- class Nokogiri::XML::Node
3
- # URI of namespace + node_name
4
- def uri
5
- ns = self.namespace ? self.namespace.href : RDF::XML.to_s
6
- RDF::URI.intern(ns + self.node_name)
7
- end
8
-
9
- alias_method :attribute_with_ns_without_ffi_null, :attribute_with_ns
10
- ##
11
- # Monkey patch attribute_with_ns, to insure nil is returned for #null?
12
- #
13
- # Get the attribute node with name and namespace
14
- #
15
- # @param [String] name
16
- # @param [String] namespace
17
- # @return [Nokogiri::XML::Attr]
18
- def attribute_with_ns(name, namespace)
19
- a = attribute_with_ns_without_ffi_null(name, namespace)
20
-
21
- (a.respond_to?(:null?) && a.null?) ? nil : a # to ensure FFI Pointer compatibility
22
- end
23
- end
@@ -1,86 +0,0 @@
1
- # Default HAML templates used for generating RDF/XML output from the writer
2
- module RDF::RDFXML
3
- class Writer
4
- # The default set of HAML templates used for RDFa code generation
5
- BASE_HAML = {
6
- identifier: "base",
7
- # Document
8
- # Locals: lang, title, prefix, base, subjects
9
- # Yield: subjects.each
10
- doc: %q(
11
- = %(<?xml version='1.0' encoding='utf-8' ?>)
12
- - if stylesheet
13
- = %(<?xml-stylesheet type="text/xsl" href="#{stylesheet}"?>)
14
- %rdf:RDF{prefix_attrs.merge("xml:lang" => lang, "xml:base" => base)}
15
- - subjects.each do |subject|
16
- != yield(subject)
17
- ),
18
-
19
- # Output for non-leaf resources
20
- # Note that @about may be omitted for Nodes that are not referenced
21
- #
22
- # If _rel_ and _resource_ are not nil, the tag will be written relative
23
- # to a previous subject. If _element_ is :li, the tag will be written
24
- # with <li> instead of <div>.
25
- #
26
- # Locals: subject, typeof, predicates, rel, element, inlist, attr_props
27
- # Yield: predicates.each
28
- subject: %q(
29
- - first_type, *types = typeof.to_s.split(' ')
30
- - (types.unshift(first_type); first_type = nil) if first_type && (first_type.include?('/') || first_type.start_with?('_:'))
31
- - first_type ||= get_qname(RDF.Description)
32
- - first_type = first_type[1..-1] if first_type.to_s.start_with?(":")
33
- - attr_props = attr_props.merge(get_qname(RDF.nodeID) => subject.id) if subject.node? && ref_count(subject) >= 1
34
- - attr_props = attr_props.merge(get_qname(RDF.about) => relativize(subject)) if subject.uri?
35
- - haml_tag(first_type, attr_props) do
36
- - types.each do |type|
37
- - expanded_type = expand_curie(type)
38
- - if expanded_type.start_with?('_:')
39
- - haml_tag(get_qname(RDF.type), "rdf:nodeID" => expanded_type[2..-1])
40
- -else
41
- - haml_tag(get_qname(RDF.type), "rdf:resource" => expanded_type)
42
- - predicates.each do |p|
43
- = yield(p)
44
- ),
45
-
46
- # Output for single-valued properties
47
- # Locals: predicate, object, inlist
48
- # Yields: object
49
- # If nil is returned, render as a leaf
50
- # Otherwise, render result
51
- property_value: %q(
52
- - if recurse && res = yield(object)
53
- - haml_tag(property) do
54
- = res
55
- - elsif object.literal? && object.datatype == RDF.XMLLiteral
56
- - haml_tag(property, :"<", "rdf:parseType" => "Literal") do
57
- = object.value
58
- - elsif object.literal?
59
- - haml_tag(property, :"<", "xml:lang" => object.language, "rdf:datatype" => (object.datatype unless object.plain?)) do
60
- = object.value.to_s.encode(xml: :text)
61
- - elsif object.node?
62
- - haml_tag(property, :"/", "rdf:nodeID" => object.id)
63
- - else
64
- - haml_tag(property, :"/", "rdf:resource" => relativize(object))
65
- ),
66
-
67
- # Outpust for a list
68
- # Locals: predicate, list
69
- # Yields: object
70
- # If nil is returned, render as a leaf
71
- # Otherwise, render result
72
- collection: %q(
73
- - haml_tag(property, get_qname(RDF.parseType) => "Collection") do
74
- - list.each do |object|
75
- - if recurse && res = yield(object)
76
- = res
77
- - elsif object.node?
78
- - haml_tag(get_qname(RDF.Description), :"/", "rdf:nodeID" => (object.id if ref_count(object) > 1))
79
- - else
80
- - haml_tag(get_qname(RDF.Description), :"/", "rdf:about" => relativize(object))
81
- ),
82
- }
83
- HAML_TEMPLATES = {base: BASE_HAML}
84
- DEFAULT_HAML = BASE_HAML
85
- end
86
- end