rdf 1.99.1 → 2.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/{README → README.md} +9 -44
- data/VERSION +1 -1
- data/bin/rdf +1 -1
- data/lib/rdf.rb +40 -49
- data/lib/rdf/changeset.rb +161 -0
- data/lib/rdf/cli.rb +195 -33
- data/lib/rdf/cli/vocab-loader.rb +13 -3
- data/lib/rdf/format.rb +44 -26
- data/lib/rdf/mixin/enumerable.rb +133 -97
- data/lib/rdf/mixin/enumerator.rb +8 -0
- data/lib/rdf/mixin/indexable.rb +1 -1
- data/lib/rdf/mixin/mutable.rb +101 -22
- data/lib/rdf/mixin/queryable.rb +21 -32
- data/lib/rdf/mixin/transactable.rb +94 -0
- data/lib/rdf/mixin/writable.rb +12 -3
- data/lib/rdf/model/dataset.rb +48 -0
- data/lib/rdf/model/graph.rb +73 -43
- data/lib/rdf/model/list.rb +61 -33
- data/lib/rdf/model/literal.rb +20 -19
- data/lib/rdf/model/literal/double.rb +20 -4
- data/lib/rdf/model/literal/numeric.rb +15 -13
- data/lib/rdf/model/node.rb +15 -16
- data/lib/rdf/model/statement.rb +1 -43
- data/lib/rdf/model/term.rb +10 -8
- data/lib/rdf/model/uri.rb +35 -34
- data/lib/rdf/model/value.rb +1 -1
- data/lib/rdf/nquads.rb +2 -11
- data/lib/rdf/ntriples.rb +1 -1
- data/lib/rdf/ntriples/reader.rb +33 -46
- data/lib/rdf/ntriples/writer.rb +42 -5
- data/lib/rdf/query.rb +6 -40
- data/lib/rdf/query/pattern.rb +4 -17
- data/lib/rdf/query/solutions.rb +6 -6
- data/lib/rdf/reader.rb +65 -14
- data/lib/rdf/repository.rb +365 -229
- data/lib/rdf/transaction.rb +211 -84
- data/lib/rdf/util.rb +1 -0
- data/lib/rdf/util/cache.rb +5 -5
- data/lib/rdf/util/file.rb +12 -9
- data/lib/rdf/util/logger.rb +272 -0
- data/lib/rdf/version.rb +2 -2
- data/lib/rdf/vocab/owl.rb +82 -77
- data/lib/rdf/vocab/rdfs.rb +22 -17
- data/lib/rdf/vocab/xsd.rb +5 -0
- data/lib/rdf/vocabulary.rb +50 -56
- data/lib/rdf/writer.rb +104 -52
- metadata +45 -90
- data/lib/rdf/mixin/inferable.rb +0 -5
- data/lib/rdf/vocab/cc.rb +0 -128
- data/lib/rdf/vocab/cert.rb +0 -245
- data/lib/rdf/vocab/dc.rb +0 -948
- data/lib/rdf/vocab/dc11.rb +0 -167
- data/lib/rdf/vocab/dcat.rb +0 -214
- data/lib/rdf/vocab/doap.rb +0 -337
- data/lib/rdf/vocab/exif.rb +0 -941
- data/lib/rdf/vocab/foaf.rb +0 -614
- data/lib/rdf/vocab/geo.rb +0 -157
- data/lib/rdf/vocab/gr.rb +0 -1501
- data/lib/rdf/vocab/ht.rb +0 -236
- data/lib/rdf/vocab/ical.rb +0 -528
- data/lib/rdf/vocab/ma.rb +0 -513
- data/lib/rdf/vocab/mo.rb +0 -2412
- data/lib/rdf/vocab/og.rb +0 -222
- data/lib/rdf/vocab/ogc.rb +0 -58
- data/lib/rdf/vocab/prov.rb +0 -1550
- data/lib/rdf/vocab/rsa.rb +0 -72
- data/lib/rdf/vocab/rss.rb +0 -66
- data/lib/rdf/vocab/schema.rb +0 -10569
- data/lib/rdf/vocab/sioc.rb +0 -669
- data/lib/rdf/vocab/skos.rb +0 -238
- data/lib/rdf/vocab/skosxl.rb +0 -57
- data/lib/rdf/vocab/v.rb +0 -383
- data/lib/rdf/vocab/vcard.rb +0 -841
- data/lib/rdf/vocab/vmd.rb +0 -383
- data/lib/rdf/vocab/void.rb +0 -186
- data/lib/rdf/vocab/vs.rb +0 -28
- data/lib/rdf/vocab/wdrs.rb +0 -134
- data/lib/rdf/vocab/wot.rb +0 -167
- data/lib/rdf/vocab/xhtml.rb +0 -8
- data/lib/rdf/vocab/xhv.rb +0 -505
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6b5ce1d8969fb6463efd82a7fe9826589bac799
|
4
|
+
data.tar.gz: e0c0066693ddc81d5089bf63f7502527750d1380
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f866ac2a48e98e154bd786f6e0c30f5298e70c01de47847c660fded022f14ef58f19b0a727e20a53704d0d46f610cae4f53a08b5b2b94dc5f82d85da006e9d0b
|
7
|
+
data.tar.gz: ccede9fff108419d7d0df702df4dd8e8a24bd53be1e0aa0bd1a1986158ff5dcdbfd7bb824eb36cbe1dc0ea41243732bb49f0aec5dbbf869721595911e7f39709
|
data/{README → README.md}
RENAMED
@@ -11,6 +11,8 @@ This is a pure-Ruby library for working with [Resource Description Framework
|
|
11
11
|
|
12
12
|
[](http://badge.fury.io/rb/rdf)
|
13
13
|
[](http://travis-ci.org/ruby-rdf/rdf)
|
14
|
+
[](https://codeclimate.com/github/ruby-rdf/rdf)
|
15
|
+
[](https://coveralls.io/r/ruby-rdf/rdf)
|
14
16
|
[](https://gitter.im/ruby-rdf/rdf?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
15
17
|
|
16
18
|
## Features
|
@@ -28,8 +30,7 @@ This is a pure-Ruby library for working with [Resource Description Framework
|
|
28
30
|
not modify any of Ruby's core classes or standard library.
|
29
31
|
* Based entirely on Ruby's autoloading, meaning that you can generally make
|
30
32
|
use of any one part of the library without needing to load up the rest.
|
31
|
-
* Compatible with Ruby Ruby
|
32
|
-
* Compatible with older Ruby versions with the help of the [Backports][] gem.
|
33
|
+
* Compatible with Ruby Ruby 2.x, Rubinius and JRuby 1.7+ (in Ruby 2.0 mode).
|
33
34
|
* Performs auto-detection of input to select appropriate Reader class if one
|
34
35
|
cannot be determined from file characteristics.
|
35
36
|
|
@@ -50,12 +51,12 @@ the 1.1 release of RDF.rb:
|
|
50
51
|
* Introduces {RDF::IRI}, as a synonym for {RDF::URI} either {RDF::IRI} or {RDF::URI} can be used interchangeably. Versions of RDF.rb prior to the 1.1 release were already compatible with IRIs. Internationalized Resource Identifiers (see [RFC3987][]) are a super-set of URIs (see [RFC3986][]) which allow for characters other than standard US-ASCII.
|
51
52
|
* {RDF::URI} no longer uses the `Addressable` gem. As URIs typically don't need to be parsed, this provides a substantial performance improvement when enumerating or querying graphs and repositories.
|
52
53
|
* {RDF::List} no longer emits a `rdf:List` type. However, it will now recognize any subjects that are {RDF::Node} instances as being list elements, as long as they have both `rdf:first` and `rdf:rest` predicates.
|
53
|
-
* {RDF::Graph} adding a `
|
54
|
+
* {RDF::Graph} adding a `graph_name` to a graph may only be done when the underlying storage model supports graph_names (the default {RDF::Repository} does). The notion of `graph_name` in RDF.rb is treated equivalently to [Named Graphs](http://www.w3.org/TR/rdf11-concepts/#dfn-named-graph) within an RDF Dataset, and graphs on their own are not named.
|
54
55
|
* {RDF::Graph}, {RDF::Statement} and {RDF::List} now include {RDF::Value}, and not {RDF::Resource}. Made it clear that using {RDF::Graph} does not mean that it may be used within an {RDF::Statement}, for this see {RDF::Term}.
|
55
56
|
* {RDF::Statement} now is stricter about checking that all elements are valid when validating.
|
56
57
|
* {RDF::NTriples::Writer} and {RDF::NQuads::Writer} now default to validate output, only allowing valid statements to be emitted. This may disabled by setting the `:validate` option to `false`.
|
57
58
|
* {RDF::Dataset} is introduced as a class alias of {RDF::Repository}. This allows closer alignment to the RDF concept of [Dataset](http://www.w3.org/TR/rdf11-concepts/#dfn-dataset).
|
58
|
-
* The `
|
59
|
+
* The `graph_name` of a graph within a Dataset or Repository may be either an {RDF::IRI} or {RDF::Node}. Implementations of repositories may restrict this to being only {RDF::IRI}.
|
59
60
|
* There are substantial and somewhat incompatible changes to {RDF::Literal}. In [RDF 1.1][], all literals are typed, including plain literals and language tagged literals. Internally, plain literals are given the `xsd:string` datatype and language tagged literals are given the `rdf:langString` datatype. Creating a plain literal, without a datatype or language, will automatically provide the `xsd:string` datatype; similar for language tagged literals. Note that most serialization formats will remove this datatype. Code which depends on a literal having the `xsd:string` datatype being different from a plain literal (formally, without a datatype) may break. However note that the `#has\_datatype?` will continue to return `false` for plain or language-tagged literals.
|
60
61
|
* {RDF::Query#execute} now accepts a block and returns {RDF::Query::Solutions}. This allows `enumerable.query(query)` to behave like `query.execute(enumerable)` and either return an enumerable or yield each solution.
|
61
62
|
* {RDF::Queryable#query} now returns {RDF::Query::Solutions} instead of an Enumerator if it's argument is an {RDF::Query}.
|
@@ -146,7 +147,7 @@ appropriate writer to use.
|
|
146
147
|
|
147
148
|
RDF::Writer.open("hello.nq", format: :nquads) do |writer|
|
148
149
|
writer << RDF::Repository.new do |repo|
|
149
|
-
repo << RDF::Statement.new(:hello, RDF::RDFS.label, "Hello, world!",
|
150
|
+
repo << RDF::Statement.new(:hello, RDF::RDFS.label, "Hello, world!", graph_name: RDF::URI("http://example/graph_name"))
|
150
151
|
end
|
151
152
|
end
|
152
153
|
|
@@ -154,7 +155,7 @@ A specific sub-type of Writer can also be invoked directly:
|
|
154
155
|
|
155
156
|
require 'rdf/nquads'
|
156
157
|
|
157
|
-
repo = RDF::Repository.new << RDF::Statement.new(:hello, RDF::RDFS.label, "Hello, world!",
|
158
|
+
repo = RDF::Repository.new << RDF::Statement.new(:hello, RDF::RDFS.label, "Hello, world!", graph_name: RDF::URI("http://example/graph_name"))
|
158
159
|
File.open("hello.nq", "w") {|f| f << repo.dump(:nquads)}
|
159
160
|
|
160
161
|
## Reader/Writer convenience methods
|
@@ -298,7 +299,6 @@ from BNode identity (i.e., they each entail the other)
|
|
298
299
|
* {RDF::Countable}
|
299
300
|
* {RDF::Enumerable}
|
300
301
|
* {RDF::Indexable}
|
301
|
-
* {RDF::Inferable}
|
302
302
|
* {RDF::Queryable}
|
303
303
|
* {RDF::Mutable}
|
304
304
|
* {RDF::Durable}
|
@@ -327,44 +327,10 @@ from BNode identity (i.e., they each entail the other)
|
|
327
327
|
* {RDF::RDFV} - RDF Vocabulary (RDFV)
|
328
328
|
* {RDF::XSD} - XML Schema (XSD)
|
329
329
|
|
330
|
-
#### Deprecated Vocabularies
|
331
|
-
|
332
|
-
The following vocabularies will be deprecated in RDF.rb 2.0 and moved to the rdf-vocab gem.
|
333
|
-
|
334
|
-
* {RDF::CC} - Creative Commons (CC)
|
335
|
-
* {RDF::CERT} - W3 Authentication Certificate (CERT)
|
336
|
-
* {RDF::DC} - Dublin Core (DC)
|
337
|
-
* {RDF::DC11} - Dublin Core 1.1 (DC11) _deprecated_
|
338
|
-
* {RDF::DOAP} - Description of a Project (DOAP)
|
339
|
-
* {RDF::EXIF} - Exchangeable Image File Format (EXIF)
|
340
|
-
* {RDF::FOAF} - Friend of a Friend (FOAF)
|
341
|
-
* {RDF::GEO} - WGS84 Geo Positioning (GEO)
|
342
|
-
* {RDF::GR} - GoodRelations (GR)
|
343
|
-
* {RDF::HT} - Hypertext Transfer Protocol (HT)
|
344
|
-
* {RDF::ICAL} - RDF Calendar Workspace (ICAL)
|
345
|
-
* {RDF::MA} - Media Resources (MA)
|
346
|
-
* {RDF::MO} - Music Ontology (MO)
|
347
|
-
* {RDF::OG} - Open Graph protocol (OG)
|
348
|
-
* {RDF::PROV} - Provenance on the web (PROV)
|
349
|
-
* {RDF::RSA} - W3 RSA Keys (RSA)
|
350
|
-
* {RDF::RSS} - RDF Site Summary (RSS)
|
351
|
-
* {RDF::SCHEMA} - Schema.org (SCHEMA)
|
352
|
-
* {RDF::SIOC} - Semantically-Interlinked Online Communities (SIOC)
|
353
|
-
* {RDF::SKOS} - Simple Knowledge Organization System (SKOS)
|
354
|
-
* {RDF::SKOSXL} - SKOS eXtension for Labels (SKOSXL)
|
355
|
-
* {RDF::V} - RDF data vocabulary (V)
|
356
|
-
* {RDF::VCARD} - Ontology for vCards (VCARD)
|
357
|
-
* {RDF::VMD} - Data-Vocabulary.org (VMD)
|
358
|
-
* {RDF::VOID} - Vocabulary of Interlinked Datasets (VOID)
|
359
|
-
* {RDF::VS} - SemWeb Vocab Status ontology (VS)
|
360
|
-
* {RDF::WDRS} - Protocol for Web Description Resources (WDRS)
|
361
|
-
* {RDF::WOT} - Web of Trust (WOT)
|
362
|
-
* {RDF::XHTML} - Extensible HyperText Markup Language (XHTML)
|
363
|
-
* {RDF::XHV} - XHTML Vocabulary (XHV)
|
364
330
|
|
365
331
|
## Dependencies
|
366
332
|
|
367
|
-
* [Ruby](http://ruby-lang.org/) (>=
|
333
|
+
* [Ruby](http://ruby-lang.org/) (>= 2.0)
|
368
334
|
* [LinkHeader][] (>= 0.0.8)
|
369
335
|
* Soft dependency on [RestClient][] (>= 1.7)
|
370
336
|
|
@@ -373,7 +339,7 @@ The following vocabularies will be deprecated in RDF.rb 2.0 and moved to the rdf
|
|
373
339
|
The recommended installation method is via [RubyGems](http://rubygems.org/).
|
374
340
|
To install the latest official release of RDF.rb, do:
|
375
341
|
|
376
|
-
% [sudo] gem install rdf # Ruby
|
342
|
+
% [sudo] gem install rdf # Ruby 2+
|
377
343
|
|
378
344
|
## Download
|
379
345
|
|
@@ -452,7 +418,6 @@ see <http://unlicense.org/> or the accompanying {file:UNLICENSE} file.
|
|
452
418
|
[YARD]: http://yardoc.org/
|
453
419
|
[YARD-GS]: http://rubydoc.info/docs/yard/file/docs/GettingStarted.md
|
454
420
|
[PDD]: http://lists.w3.org/Archives/Public/public-rdf-ruby/2010May/0013.html
|
455
|
-
[Backports]: http://rubygems.org/gems/backports
|
456
421
|
[JSONLD doc]: http://rubydoc.info/github/ruby-rdf/json-ld/frames
|
457
422
|
[LinkedData doc]: http://rubydoc.info/github/datagraph/linkeddata/master/frames
|
458
423
|
[Microdata doc]: http://rubydoc.info/github/ruby-rdf/rdf-microdata/frames
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0.0.beta1
|
data/bin/rdf
CHANGED
@@ -5,7 +5,7 @@ require 'rdf/cli'
|
|
5
5
|
|
6
6
|
options = RDF::CLI.options do
|
7
7
|
self.on('-v', '--verbose', 'Enable verbose output. May be given more than once.') do
|
8
|
-
|
8
|
+
self.options[:logger].level = Logger::INFO
|
9
9
|
end
|
10
10
|
|
11
11
|
self.on('-V', '--version', 'Display the RDF.rb version and exit.') do
|
data/lib/rdf.rb
CHANGED
@@ -7,72 +7,61 @@ require 'rdf/version'
|
|
7
7
|
|
8
8
|
module RDF
|
9
9
|
# RDF mixins
|
10
|
-
autoload :Countable,
|
11
|
-
autoload :Durable,
|
12
|
-
autoload :Enumerable,
|
13
|
-
autoload :Indexable,
|
14
|
-
autoload :
|
15
|
-
autoload :
|
16
|
-
autoload :
|
17
|
-
autoload :
|
18
|
-
autoload :
|
19
|
-
autoload :Writable,
|
10
|
+
autoload :Countable, 'rdf/mixin/countable'
|
11
|
+
autoload :Durable, 'rdf/mixin/durable'
|
12
|
+
autoload :Enumerable, 'rdf/mixin/enumerable'
|
13
|
+
autoload :Indexable, 'rdf/mixin/indexable'
|
14
|
+
autoload :Mutable, 'rdf/mixin/mutable'
|
15
|
+
autoload :Queryable, 'rdf/mixin/queryable'
|
16
|
+
autoload :Readable, 'rdf/mixin/readable'
|
17
|
+
autoload :TypeCheck, 'rdf/mixin/type_check'
|
18
|
+
autoload :Transactable, 'rdf/mixin/transactable'
|
19
|
+
autoload :Writable, 'rdf/mixin/writable'
|
20
20
|
|
21
21
|
# RDF objects
|
22
|
-
autoload :Graph,
|
23
|
-
autoload :IRI,
|
24
|
-
autoload :Literal,
|
25
|
-
autoload :Node,
|
26
|
-
autoload :Resource,
|
27
|
-
autoload :Statement,
|
28
|
-
autoload :URI,
|
29
|
-
autoload :Value,
|
30
|
-
autoload :Term,
|
22
|
+
autoload :Graph, 'rdf/model/graph'
|
23
|
+
autoload :IRI, 'rdf/model/uri'
|
24
|
+
autoload :Literal, 'rdf/model/literal'
|
25
|
+
autoload :Node, 'rdf/model/node'
|
26
|
+
autoload :Resource, 'rdf/model/resource'
|
27
|
+
autoload :Statement, 'rdf/model/statement'
|
28
|
+
autoload :URI, 'rdf/model/uri'
|
29
|
+
autoload :Value, 'rdf/model/value'
|
30
|
+
autoload :Term, 'rdf/model/term'
|
31
31
|
|
32
32
|
# RDF collections
|
33
|
-
autoload :List,
|
33
|
+
autoload :List, 'rdf/model/list'
|
34
34
|
|
35
35
|
# RDF serialization
|
36
|
-
autoload :Format,
|
37
|
-
autoload :Reader,
|
38
|
-
autoload :ReaderError,
|
39
|
-
autoload :Writer,
|
40
|
-
autoload :WriterError,
|
36
|
+
autoload :Format, 'rdf/format'
|
37
|
+
autoload :Reader, 'rdf/reader'
|
38
|
+
autoload :ReaderError, 'rdf/reader'
|
39
|
+
autoload :Writer, 'rdf/writer'
|
40
|
+
autoload :WriterError, 'rdf/writer'
|
41
41
|
|
42
42
|
# RDF serialization formats
|
43
|
-
autoload :NTriples,
|
44
|
-
autoload :NQuads,
|
43
|
+
autoload :NTriples, 'rdf/ntriples'
|
44
|
+
autoload :NQuads, 'rdf/nquads'
|
45
45
|
|
46
46
|
# RDF storage
|
47
|
-
autoload :
|
48
|
-
autoload :
|
49
|
-
autoload :
|
47
|
+
autoload :Changeset, 'rdf/changeset'
|
48
|
+
autoload :Dataset, 'rdf/model/dataset'
|
49
|
+
autoload :Repository, 'rdf/repository'
|
50
|
+
autoload :Transaction, 'rdf/transaction'
|
50
51
|
|
51
52
|
# RDF querying
|
52
|
-
autoload :Query,
|
53
|
+
autoload :Query, 'rdf/query'
|
53
54
|
|
54
55
|
# RDF vocabularies
|
55
|
-
autoload :Vocabulary,
|
56
|
+
autoload :Vocabulary, 'rdf/vocabulary'
|
56
57
|
autoload :StrictVocabulary, 'rdf/vocabulary'
|
57
58
|
VOCABS = Dir.glob(File.join(File.dirname(__FILE__), 'rdf', 'vocab', '*.rb')).map { |f| File.basename(f)[0...-(File.extname(f).size)].to_sym } rescue []
|
58
59
|
|
59
|
-
# Ruby versions < 2.0 are deprecated in RDF.rb 2.0
|
60
|
-
warn %([DEPRECATION] Ruby versions before 2.0 are no longer supported starting with RDF.rb 2.0.) if RUBY_VERSION < "2.0"
|
61
|
-
|
62
60
|
# Use const_missing instead of autoload to load most vocabularies so we can provide deprecation messages
|
63
61
|
def self.const_missing(constant)
|
64
62
|
if VOCABS.include?(constant.to_s.downcase.to_sym)
|
65
|
-
@vocab_fault ||= {}
|
66
|
-
raise "Class not found: RDF::#{constant}" if @vocab_fault[constant.to_s]
|
67
|
-
@vocab_fault[constant.to_s] = true
|
68
|
-
warn %([DEPRECATION] the #{constant} vocabulary will be moved to the rdf-vocab gem
|
69
|
-
for the RDF.rb 2.0 release. Use as RDF::Vocab::#{constant}, or include RDF::Vocab in the RDF module.
|
70
|
-
Called from #{Gem.location_of_caller.join(':')}
|
71
|
-
).gsub(/^\s+/, '') unless [:OWL, :RDFS, :RDFV, :XSD].include?(constant)
|
72
63
|
require "rdf/vocab/#{constant.to_s.downcase}"
|
73
|
-
|
74
|
-
return klass if klass
|
75
|
-
raise "Class not found: RDF::#{constant}"
|
64
|
+
const_get(constant)
|
76
65
|
else
|
77
66
|
super
|
78
67
|
end
|
@@ -131,8 +120,12 @@ module RDF
|
|
131
120
|
#
|
132
121
|
# @param (see RDF::Graph#initialize)
|
133
122
|
# @return [RDF::Graph]
|
134
|
-
def self.Graph(*args, &block)
|
135
|
-
|
123
|
+
def self.Graph(*args, **options, &block)
|
124
|
+
unless args.empty?
|
125
|
+
warn "[DEPRECATION] Graph#initialize now uses keyword arguments. Called from #{Gem.location_of_caller.join(':')}"
|
126
|
+
options[:graph_name] ||= args.first
|
127
|
+
end
|
128
|
+
Graph.new(options, &block)
|
136
129
|
end
|
137
130
|
|
138
131
|
##
|
@@ -172,8 +165,6 @@ module RDF
|
|
172
165
|
# @option options [RDF::Resource] :subject (nil)
|
173
166
|
# @option options [RDF::URI] :predicate (nil)
|
174
167
|
# @option options [RDF::Term] :object (nil)
|
175
|
-
# @option options [RDF::Resource] :context (nil)
|
176
|
-
# Alias for :graph_name, :context is deprecated in RDF.rb.
|
177
168
|
# @option options [RDF::Resource] :graph_name (nil)
|
178
169
|
# Note, a graph_name MUST be an IRI or BNode.
|
179
170
|
# @return [RDF::Statement]
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module RDF
|
2
|
+
##
|
3
|
+
# An RDF changeset that can be applied to an {RDF::Mutable}.
|
4
|
+
#
|
5
|
+
# Changesets consist of a sequence of RDF statements to delete from and a
|
6
|
+
# sequence of RDF statements to insert into a target dataset.
|
7
|
+
#
|
8
|
+
# @example Applying a Changeset with block syntax
|
9
|
+
# graph = RDF::Graph.new
|
10
|
+
# graph << [RDF::URI('s_del'), RDF::URI('p_del'), RDF::URI('o_del')]
|
11
|
+
#
|
12
|
+
# RDF::Changeset.apply(graph) do |c|
|
13
|
+
# c.insert [RDF::URI('s1'), RDF::URI('p1'), RDF::URI('o1')]
|
14
|
+
# c.insert [RDF::URI('s2'), RDF::URI('p2'), RDF::URI('o2')]
|
15
|
+
# c.delete [RDF::URI('s_del'), RDF::URI('p_del'), RDF::URI('o_del')]
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# @example Defining a changeset for later application to a Mutable
|
19
|
+
# changes = RDF::Changeset.new do |c|
|
20
|
+
# c.insert [RDF::URI('s1'), RDF::URI('p1'), RDF::URI('o1')]
|
21
|
+
# c.insert [RDF::URI('s2'), RDF::URI('p2'), RDF::URI('o2')]
|
22
|
+
# c.delete [RDF::URI('s_del'), RDF::URI('p_del'), RDF::URI('o_del')]
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# graph = RDF::Graph.new
|
26
|
+
# graph << [RDF::URI('s_del'), RDF::URI('p_del'), RDF::URI('o_del')]
|
27
|
+
#
|
28
|
+
# changes.apply(graph) # or graph.apply_changeset(changes)
|
29
|
+
#
|
30
|
+
# @note When applying a Changeset, deletes are resolved before inserts.
|
31
|
+
#
|
32
|
+
# @since 2.0.0
|
33
|
+
class Changeset
|
34
|
+
include RDF::Mutable
|
35
|
+
|
36
|
+
##
|
37
|
+
# Applies a changeset to the given mutable RDF::Enumerable .
|
38
|
+
#
|
39
|
+
# @param [RDF::Mutable] mutable
|
40
|
+
# @param [Hash{Symbol => Object}] options
|
41
|
+
# @yield [changes]
|
42
|
+
# @yieldparam [RDF::Changeset] changes
|
43
|
+
# @return [void]
|
44
|
+
def self.apply(mutable, options = {}, &block)
|
45
|
+
self.new(&block).apply(mutable, options)
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# RDF statements to delete when applied.
|
50
|
+
#
|
51
|
+
# @return [RDF::Enumerable]
|
52
|
+
attr_reader :deletes
|
53
|
+
|
54
|
+
##
|
55
|
+
# RDF statements to insert when applied.
|
56
|
+
#
|
57
|
+
# @return [RDF::Enumerable]
|
58
|
+
attr_reader :inserts
|
59
|
+
|
60
|
+
##
|
61
|
+
# Any additional options for this changeset.
|
62
|
+
#
|
63
|
+
# @return [Hash{Symbol => Object}]
|
64
|
+
attr_reader :options
|
65
|
+
|
66
|
+
##
|
67
|
+
# Initializes this changeset.
|
68
|
+
#
|
69
|
+
# @param [RDF::Enumerable] insert (RDF::Graph.new)
|
70
|
+
# @param [RDF::Enumerable] delete (RDF::Graph.new)
|
71
|
+
# @yield [changes]
|
72
|
+
# @yieldparam [RDF::Changeset] changes
|
73
|
+
def initialize(insert: [], delete: [], &block)
|
74
|
+
@inserts = insert
|
75
|
+
@deletes = delete
|
76
|
+
|
77
|
+
@inserts.extend(RDF::Enumerable) unless @inserts.kind_of?(RDF::Enumerable)
|
78
|
+
@deletes.extend(RDF::Enumerable) unless @deletes.kind_of?(RDF::Enumerable)
|
79
|
+
|
80
|
+
if block_given?
|
81
|
+
case block.arity
|
82
|
+
when 1 then block.call(self)
|
83
|
+
else self.instance_eval(&block)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Returns `false` to indicate that this changeset is append-only.
|
90
|
+
#
|
91
|
+
# Changesets do not support the `RDF::Enumerable` protocol directly.
|
92
|
+
# To enumerate the RDF statements to be inserted or deleted, use the
|
93
|
+
# {RDF::Changeset#inserts} and {RDF::Changeset#deletes} accessors.
|
94
|
+
#
|
95
|
+
# @return [Boolean]
|
96
|
+
# @see RDF::Readable#readable?
|
97
|
+
def readable?
|
98
|
+
false
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Applies this changeset to the given mutable RDF::Enumerable.
|
103
|
+
#
|
104
|
+
# This operation executes as a single write transaction.
|
105
|
+
#
|
106
|
+
# @param [RDF::Mutable] mutable
|
107
|
+
# @param [Hash{Symbol => Object}] options
|
108
|
+
# @return [void]
|
109
|
+
def apply(mutable, options = {})
|
110
|
+
mutable.apply_changeset(self)
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# @return [Boolean] `true` iff inserts and deletes are both empty
|
115
|
+
def empty?
|
116
|
+
deletes.empty? && inserts.empty?
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Returns a developer-friendly representation of this changeset.
|
121
|
+
#
|
122
|
+
# @return [String]
|
123
|
+
def inspect
|
124
|
+
sprintf("#<%s:%#0x(deletes: %d, inserts: %d)>", self.class.name,
|
125
|
+
self.__id__, self.deletes.count, self.inserts.count)
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
# Outputs a developer-friendly representation of this changeset to
|
130
|
+
# `stderr`.
|
131
|
+
#
|
132
|
+
# @return [void]
|
133
|
+
def inspect!
|
134
|
+
$stderr.puts(self.inspect)
|
135
|
+
end
|
136
|
+
|
137
|
+
protected
|
138
|
+
|
139
|
+
##
|
140
|
+
# Appends an RDF statement to the sequence to insert when applied.
|
141
|
+
#
|
142
|
+
# @param [RDF::Statement] statement
|
143
|
+
# @return [void]
|
144
|
+
# @see RDF::Writable#insert_statement
|
145
|
+
def insert_statement(statement)
|
146
|
+
self.inserts << statement
|
147
|
+
end
|
148
|
+
|
149
|
+
##
|
150
|
+
# Appends an RDF statement to the sequence to delete when applied.
|
151
|
+
#
|
152
|
+
# @param [RDF::Statement] statement
|
153
|
+
# @return [void]
|
154
|
+
# @see RDF::Mutable#delete_statement
|
155
|
+
def delete_statement(statement)
|
156
|
+
self.deletes << statement
|
157
|
+
end
|
158
|
+
|
159
|
+
undef_method :load, :update, :clear
|
160
|
+
end # Changeset
|
161
|
+
end # RDF
|
data/lib/rdf/cli.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rdf'
|
2
2
|
require 'rdf/ntriples'
|
3
3
|
require 'rdf/nquads'
|
4
|
+
require 'logger'
|
4
5
|
require 'optparse'
|
5
6
|
begin
|
6
7
|
gem 'linkeddata'
|
@@ -13,6 +14,11 @@ rescue LoadError
|
|
13
14
|
rescue LoadError
|
14
15
|
end
|
15
16
|
end
|
17
|
+
|
18
|
+
begin
|
19
|
+
require 'json/ld'
|
20
|
+
rescue LoadError
|
21
|
+
end
|
16
22
|
end
|
17
23
|
|
18
24
|
class OptionParser
|
@@ -21,10 +27,87 @@ class OptionParser
|
|
21
27
|
end
|
22
28
|
|
23
29
|
module RDF
|
30
|
+
# Individual formats can modify options by updating {Reader.options} or {Writer.options}. Format-specific commands are taken from {Format.cli_commands} for each loaded format, which returns an array of lambdas taking arguments and options.
|
31
|
+
#
|
32
|
+
# @example Creating Reader-specific options:
|
33
|
+
# class Reader
|
34
|
+
# def self.options
|
35
|
+
# [
|
36
|
+
# RDF::CLI::Option.new(
|
37
|
+
# symbol: :canonicalize,
|
38
|
+
# datatype: TrueClass,
|
39
|
+
# on: ["--canonicalize"],
|
40
|
+
# description: "Canonicalize input/output.") {true},
|
41
|
+
# ...
|
42
|
+
# ]
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# @example Creating Format-specific commands:
|
46
|
+
# class Format
|
47
|
+
# def self.cli_commands
|
48
|
+
# {
|
49
|
+
# count: ->(argv, opts) do
|
50
|
+
# count = 0
|
51
|
+
# RDF::CLI.parse(argv, opts) do |reader|
|
52
|
+
# reader.each_statement do |statement|
|
53
|
+
# count += 1
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
# $stdout.puts "Parsed #{count} statements"
|
57
|
+
# end,
|
58
|
+
# }
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# Format-specific commands should verify that the reader and/or output format are appropriate for the command.
|
24
62
|
class CLI
|
25
63
|
|
64
|
+
# Option description for use within Readers/Writers. See {RDF::Reader.options} and {RDF::Writer.options} for example usage.
|
65
|
+
class Option
|
66
|
+
# Symbol used for this option when calling `Reader.new`
|
67
|
+
# @return [Symbol]
|
68
|
+
attr_reader :symbol
|
69
|
+
|
70
|
+
# Arguments passed to OptionParser#on
|
71
|
+
# @return [Array<String>]
|
72
|
+
attr_reader :on
|
73
|
+
|
74
|
+
# Description of this option (optional)
|
75
|
+
# @return [String]
|
76
|
+
attr_reader :description
|
77
|
+
|
78
|
+
# Argument datatype, which may be enumerated string values
|
79
|
+
# @return [Class, Array<String>]
|
80
|
+
attr_reader :datatype
|
81
|
+
|
82
|
+
# Allows multiple comma-spearated values.
|
83
|
+
# @return [Boolean]
|
84
|
+
attr_reader :multiple
|
85
|
+
|
86
|
+
##
|
87
|
+
# Create a new option with optional callback.
|
88
|
+
#
|
89
|
+
# @param [Symbol] symbol
|
90
|
+
# @param [Array<String>] on
|
91
|
+
# @param [String] description
|
92
|
+
# @param [Class, Array<String>] datatype of value
|
93
|
+
# @param [Boolean] multiple can have multiple comma-separated values
|
94
|
+
# @yield value which may be used within `OptionParser#on`
|
95
|
+
# @yieldparam [Object] value The option value as parsed using `on` argument
|
96
|
+
# @yieldreturn [Object] a possibly modified input value
|
97
|
+
def initialize(symbol: nil, on: nil, description: nil, datatype: String, multiple: false, &block)
|
98
|
+
raise ArgumentError, "symbol is a required argument" unless symbol
|
99
|
+
raise ArgumentError, "on is a required argument" unless on
|
100
|
+
@symbol, @on, @description, @datatype, @multiple, @callback = symbol.to_sym, Array(on), description, datatype, multiple, block
|
101
|
+
end
|
102
|
+
|
103
|
+
def call(arg)
|
104
|
+
@callback ? @callback.call(arg) : arg
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# @private
|
26
109
|
COMMANDS = {
|
27
|
-
|
110
|
+
count: ->(argv, opts) do
|
28
111
|
start = Time.new
|
29
112
|
count = 0
|
30
113
|
self.parse(argv, opts) do |reader|
|
@@ -35,14 +118,17 @@ module RDF
|
|
35
118
|
secs = Time.new - start
|
36
119
|
$stdout.puts "Parsed #{count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
|
37
120
|
end,
|
38
|
-
|
121
|
+
help: ->(argv, opts) do
|
122
|
+
self.usage(self.options)
|
123
|
+
end,
|
124
|
+
lenghts: ->(argv, opts) do
|
39
125
|
self.parse(argv, opts) do |reader|
|
40
126
|
reader.each_statement do |statement|
|
41
127
|
$stdout.puts statement.to_s.size
|
42
128
|
end
|
43
129
|
end
|
44
130
|
end,
|
45
|
-
|
131
|
+
objects: ->(argv, opts) do
|
46
132
|
$stdout.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
|
47
133
|
self.parse(argv, opts) do |reader|
|
48
134
|
reader.each_statement do |statement|
|
@@ -50,7 +136,7 @@ module RDF
|
|
50
136
|
end
|
51
137
|
end
|
52
138
|
end,
|
53
|
-
|
139
|
+
predicates: ->(argv, opts) do
|
54
140
|
$stdout.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
|
55
141
|
self.parse(argv, opts) do |reader|
|
56
142
|
reader.each_statement do |statement|
|
@@ -58,7 +144,7 @@ module RDF
|
|
58
144
|
end
|
59
145
|
end
|
60
146
|
end,
|
61
|
-
|
147
|
+
serialize: ->(argv, opts) do
|
62
148
|
writer_class = RDF::Writer.for(opts[:output_format]) || RDF::NTriples::Writer
|
63
149
|
out = opts[:output] || $stdout
|
64
150
|
out.set_encoding(Encoding::UTF_8) if out.respond_to?(:set_encoding) && RUBY_PLATFORM == "java"
|
@@ -70,13 +156,26 @@ module RDF
|
|
70
156
|
end
|
71
157
|
end
|
72
158
|
end,
|
73
|
-
|
159
|
+
subjects: ->(argv, opts) do
|
74
160
|
$stdout.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
|
75
161
|
self.parse(argv, opts) do |reader|
|
76
162
|
reader.each_statement do |statement|
|
77
163
|
$stdout.puts statement.subject.to_ntriples
|
78
164
|
end
|
79
165
|
end
|
166
|
+
end,
|
167
|
+
validate: ->(argv, opts) do
|
168
|
+
start = Time.new
|
169
|
+
count = 0
|
170
|
+
valid = true
|
171
|
+
self.parse(argv, opts) do |reader|
|
172
|
+
reader.each_statement do |statement|
|
173
|
+
count += 1
|
174
|
+
valid = false if statement.invalid?
|
175
|
+
end
|
176
|
+
end
|
177
|
+
secs = Time.new - start
|
178
|
+
$stdout.puts "Validated #{count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
|
80
179
|
end
|
81
180
|
}
|
82
181
|
|
@@ -90,17 +189,36 @@ module RDF
|
|
90
189
|
# @return [OptionParser]
|
91
190
|
def self.options(&block)
|
92
191
|
options = OptionParser.new
|
192
|
+
logger = Logger.new($stderr)
|
193
|
+
logger.level = Logger::ERROR
|
194
|
+
logger.formatter = lambda {|severity, datetime, progname, msg| "#{severity} #{msg}\n"}
|
93
195
|
opts = options.options = {
|
94
|
-
base_uri: nil,
|
95
|
-
canonicalize: false,
|
96
196
|
debug: false,
|
97
197
|
evaluate: nil,
|
98
198
|
format: nil,
|
99
199
|
output: $stdout,
|
100
200
|
output_format: :ntriples,
|
101
|
-
|
201
|
+
logger: logger
|
102
202
|
}
|
103
203
|
|
204
|
+
# Add default Reader and Writer options
|
205
|
+
RDF::Reader.options.each do |cli_opt|
|
206
|
+
next if opts.has_key?(cli_opt.symbol)
|
207
|
+
on_args = cli_opt.on || []
|
208
|
+
on_args << cli_opt.description if cli_opt.description
|
209
|
+
options.on(*on_args) do |arg|
|
210
|
+
opts[cli_opt.symbol] = cli_opt.call(arg)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
RDF::Writer.options.each do |cli_opt|
|
214
|
+
next if opts.has_key?(cli_opt.symbol)
|
215
|
+
on_args = cli_opt.on || []
|
216
|
+
on_args << cli_opt.description if cli_opt.description
|
217
|
+
options.on(*on_args) do |arg|
|
218
|
+
opts[cli_opt.symbol] = cli_opt.call(arg)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
104
222
|
# Command-specific options
|
105
223
|
if block_given?
|
106
224
|
case block.arity
|
@@ -110,19 +228,28 @@ module RDF
|
|
110
228
|
end
|
111
229
|
options.banner ||= "Usage: #{self.basename} [options] command [args...]"
|
112
230
|
|
113
|
-
options.on('--canonicalize', 'Canonicalize input.') do
|
114
|
-
opts[:canonicalize] = true
|
115
|
-
end
|
116
|
-
|
117
231
|
options.on('-d', '--debug', 'Enable debug output for troubleshooting.') do
|
118
|
-
opts[:
|
232
|
+
opts[:logger].level = Logger::DEBUG
|
119
233
|
end
|
120
234
|
|
121
235
|
options.on("-e", "--evaluate STRING", "Evaluate argument as RDF input, if no files are specified") do |arg|
|
122
236
|
opts[:evaluate] = arg
|
123
237
|
end
|
124
238
|
|
125
|
-
options.on("--input-format FORMAT", "Format of input file, uses heuristic if not specified") do |arg|
|
239
|
+
options.on("--input-format FORMAT", "--format FORMAT", "Format of input file, uses heuristic if not specified") do |arg|
|
240
|
+
unless reader = RDF::Reader.for(arg.downcase.to_sym)
|
241
|
+
self.abort "No reader found for #{arg.downcase.to_sym}. Available readers:\n #{self.formats(reader: true).join("\n ")}"
|
242
|
+
end
|
243
|
+
|
244
|
+
# Add format-specific reader options
|
245
|
+
reader.options.each do |cli_opt|
|
246
|
+
next if opts.has_key?(cli_opt.symbol)
|
247
|
+
on_args = cli_opt.on || []
|
248
|
+
on_args << cli_opt.description if cli_opt.description
|
249
|
+
options.on(*on_args) do |arg|
|
250
|
+
opts[cli_opt.symbol] = cli_opt.call(arg)
|
251
|
+
end
|
252
|
+
end
|
126
253
|
opts[:format] = arg.downcase.to_sym
|
127
254
|
end
|
128
255
|
|
@@ -131,21 +258,24 @@ module RDF
|
|
131
258
|
end
|
132
259
|
|
133
260
|
options.on("--output-format FORMAT", "Format of output file, defaults to NTriples") do |arg|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
options.on('--uri URI', 'Base URI of input file, defaults to the filename.') do |arg|
|
138
|
-
opts[:base_uri] = arg
|
139
|
-
end
|
261
|
+
unless writer = RDF::Writer.for(arg.downcase.to_sym)
|
262
|
+
self.abort "No writer found for #{arg.downcase.to_sym}. Available writers:\n #{self.formats(writer: true).join("\n ")}"
|
263
|
+
end
|
140
264
|
|
141
|
-
|
142
|
-
|
265
|
+
# Add format-specific writer options
|
266
|
+
writer.options.each do |cli_opt|
|
267
|
+
next if opts.has_key?(cli_opt.symbol)
|
268
|
+
on_args = cli_opt.on || []
|
269
|
+
on_args << cli_opt.description if cli_opt.description
|
270
|
+
options.on(*on_args) do |arg|
|
271
|
+
opts[cli_opt.symbol] = cli_opt.call(arg)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
opts[:output_format] = arg.downcase.to_sym
|
143
275
|
end
|
144
276
|
|
145
277
|
options.on_tail("-h", "--help", "Show this message") do
|
146
|
-
|
147
|
-
$stdout.puts "Available commands:\n\t#{self.commands.join("\n\t")}"
|
148
|
-
exit
|
278
|
+
self.usage(options)
|
149
279
|
end
|
150
280
|
|
151
281
|
begin
|
@@ -158,25 +288,57 @@ module RDF
|
|
158
288
|
end
|
159
289
|
|
160
290
|
##
|
161
|
-
#
|
291
|
+
# Output usage message
|
292
|
+
def self.usage(options)
|
293
|
+
$stdout.puts options
|
294
|
+
$stdout.puts "Note: available commands and options may be different depending on selected --input-format and/or --output-format."
|
295
|
+
$stdout.puts "Available commands:\n\t#{self.commands.join("\n\t")}"
|
296
|
+
$stdout.puts "Available formats:\n\t#{(self.formats).join("\n\t")}"
|
297
|
+
end
|
298
|
+
|
299
|
+
##
|
300
|
+
# @param [#to_sym] command
|
162
301
|
# @param [Array<String>] args
|
163
302
|
# @return [Boolean]
|
164
303
|
def self.exec_command(command, args, options = {})
|
165
|
-
unless
|
166
|
-
abort "
|
304
|
+
unless commands.include?(command.to_s)
|
305
|
+
abort "unknown command `#{command}'"
|
167
306
|
end
|
168
307
|
|
169
|
-
COMMANDS[command].call(args, options)
|
308
|
+
COMMANDS[command.to_sym].call(args, options)
|
309
|
+
rescue ArgumentError => e
|
310
|
+
abort e.message
|
170
311
|
end
|
171
312
|
|
172
313
|
##
|
173
314
|
# @return [Array<String>] list of executable commands
|
174
315
|
def self.commands
|
175
|
-
|
316
|
+
# First, load commands from other formats
|
317
|
+
unless @commands_loaded
|
318
|
+
RDF::Format.each do |format|
|
319
|
+
format.cli_commands.each do |command, lambda|
|
320
|
+
COMMANDS[command] ||= lambda
|
321
|
+
end
|
322
|
+
end
|
323
|
+
@commands_loaded = true
|
324
|
+
end
|
325
|
+
COMMANDS.keys.map(&:to_s).sort
|
326
|
+
end
|
327
|
+
|
328
|
+
##
|
329
|
+
# @return [Array<String>] list of available formats
|
330
|
+
def self.formats(reader: false, writer: false)
|
331
|
+
f = RDF::Format.each.
|
332
|
+
select {|f| (reader ? f.reader : (writer ? f.writer : true))}.
|
333
|
+
inject({}) do |memo, reader|
|
334
|
+
memo.merge(reader.to_sym => reader.name)
|
335
|
+
end
|
336
|
+
sym_len = f.keys.map {|k| k.to_s.length}.max
|
337
|
+
f.map {|s, t| "%*s: %s" % [sym_len, s, t]}
|
176
338
|
end
|
177
339
|
|
178
340
|
##
|
179
|
-
# Parse each file,
|
341
|
+
# Parse each file, $stdin or specified string in `options[:evaluate]`
|
180
342
|
# yielding a reader
|
181
343
|
#
|
182
344
|
# @param [Array<String>] files
|
@@ -186,7 +348,7 @@ module RDF
|
|
186
348
|
def self.parse(files, options = {}, &block)
|
187
349
|
if files.empty?
|
188
350
|
# If files are empty, either use options[:execute]
|
189
|
-
input = options[:evaluate] ? StringIO.new(options[:evaluate]) :
|
351
|
+
input = options[:evaluate] ? StringIO.new(options[:evaluate]) : $stdin
|
190
352
|
input.set_encoding(options.fetch(:encoding, Encoding::UTF_8))
|
191
353
|
RDF::Reader.for(options[:format] || :ntriples).new(input, options) do |reader|
|
192
354
|
yield(reader)
|