Skip to content

Commit b3f9a12

Browse files
committed
Add version option and accessor to RDF::Reader.
Add VERSION support in N-Triples parser, with warning when unexpected combination is used.
1 parent e6b638b commit b3f9a12

3 files changed

Lines changed: 115 additions & 4 deletions

File tree

lib/rdf/ntriples/reader.rb

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class Reader < RDF::Reader
9292

9393
# LANGTAG is deprecated
9494
LANGTAG = LANG_DIR
95+
RDF_VERSION = /VERSION/.freeze
9596

9697
##
9798
# Reconstructs an RDF value from its serialized N-Triples
@@ -154,7 +155,8 @@ def self.parse_uri(input, intern: false, **options)
154155
def self.parse_literal(input, **options)
155156
case input
156157
when LITERAL_WITH_LANGUAGE
157-
RDF::Literal.new(unescape($1), language: $4)
158+
language, direction = $4.split('--')
159+
RDF::Literal.new(unescape($1), language: language, direction: direction)
158160
when LITERAL_WITH_DATATYPE
159161
RDF::Literal.new(unescape($1), datatype: $4)
160162
when LITERAL_PLAIN
@@ -216,7 +218,11 @@ def read_triple
216218
line = @line # for backtracking input in case of parse error
217219

218220
begin
219-
unless blank? || read_comment
221+
if blank? || read_comment
222+
# No-op
223+
elsif version = read_version
224+
@options[:version] = version
225+
else
220226
subject = read_uriref || read_node || read_quotedTriple || fail_subject
221227
predicate = read_uriref(intern: true) || fail_predicate
222228
object = read_uriref || read_node || read_literal || read_tripleTerm || read_quotedTriple || fail_object
@@ -237,6 +243,9 @@ def read_triple
237243
# @return [RDF::Statement]
238244
def read_tripleTerm
239245
if @options[:rdfstar] && match(TT_START)
246+
if version && version != "1.2"
247+
log_warn("Triple term used with version #{version}")
248+
end
240249
subject = read_uriref || read_node || fail_subject
241250
predicate = read_uriref(intern: true) || fail_predicate
242251
object = read_uriref || read_node || read_literal || read_tripleTerm || fail_object
@@ -307,6 +316,7 @@ def read_literal
307316
when lang_dir = match(LANG_DIR)
308317
language, direction = lang_dir.split('--')
309318
raise ArgumentError if direction && !@options[:rdfstar]
319+
log_warn("Literal base direction used with version #{version}") if version && version == "1.1"
310320
RDF::Literal.new(literal_str, language: language, direction: direction)
311321
when datatype = match(/^(\^\^)/) # FIXME
312322
RDF::Literal.new(literal_str, datatype: read_uriref || fail_object)
@@ -323,6 +333,18 @@ def read_literal
323333
log_error("Invalid Literal (found: \"#{v}\")", lineno: lineno, token: "#v", exception: RDF::ReaderError)
324334
end
325335

336+
##
337+
# @return [String]
338+
def read_version
339+
if match(RDF_VERSION)
340+
ver_tok = match(LITERAL_PLAIN)
341+
unless RDF::Format::VERSIONS.include?(ver_tok)
342+
log_warn("Expected version to be one of #{RDF::Format::VERSIONS.join(', ')}, was #{ver_tok}")
343+
end
344+
ver_tok
345+
end
346+
end
347+
326348
##
327349
# @return [Boolean]
328350
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (triple)

lib/rdf/reader.rb

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,13 @@ def self.options
176176
default: true,
177177
control: :checkbox,
178178
on: ["--[no-]verifySSL"],
179-
description: "Verify SSL results on HTTP GET")
179+
description: "Verify SSL results on HTTP GET"),
180+
RDF::CLI::Option.new(
181+
symbol: :version,
182+
control: :select,
183+
datatype: RDF::Format::VERSIONS, # 1.1, 1.2, or 1.2-basic
184+
on: ["--version"],
185+
description: "RDF Version."),
180186
]
181187
end
182188

@@ -284,6 +290,8 @@ def to_sym
284290
# any additional options
285291
# @param [Boolean] validate (false)
286292
# whether to validate the parsed statements and values
293+
# @option options [String] :version ("1.2")
294+
# Parse a specific version of RDF ("1.1', "1.2", or "1.2-basic"")
287295
# @yield [reader] `self`
288296
# @yieldparam [RDF::Reader] reader
289297
# @yieldreturn [void] ignored
@@ -309,6 +317,9 @@ def initialize(input = $stdin,
309317
validate: validate
310318
})
311319

320+
# The rdfstar option implies version 1.2, but can be overridden
321+
@options[:version] ||= "1.2" if @options[:rdfstar]
322+
312323
@input = case input
313324
when String then StringIO.new(input)
314325
else input
@@ -391,6 +402,18 @@ def prefix(name, uri = nil)
391402
end
392403
alias_method :prefix!, :prefix
393404

405+
##
406+
# Returns the RDF version determined by this reader.
407+
#
408+
# @example
409+
# reader.version #=> RDF::URI('http://purl.org/dc/terms/')
410+
#
411+
# @return [String]
412+
# @since 3.3.2
413+
def version
414+
@options[:version]
415+
end
416+
394417
##
395418
# Iterates the given block for each RDF statement.
396419
#

spec/ntriples_spec.rb

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@
152152
it "should accept strings" do
153153
expect { reader.new('') }.not_to raise_error
154154
end
155+
156+
it "sets version from reader option" do
157+
expect(reader.new('', version: '1.2').version).to eql '1.2'
158+
end
155159
end
156160

157161
describe ".open" do
@@ -566,6 +570,16 @@
566570
end
567571
end
568572
end
573+
574+
it "warns if version is not 1.2 or 1.2-basic" do
575+
expect {parse('<http://subj> <http://pred> "Hello"@en--ltr .', rdfstar: true, version: '1.1', logger: logger)}.not_to raise_error
576+
expect(logger.to_s).to include("WARN")
577+
end
578+
579+
it "warns if inline version is not 1.2 or 1.2-basic" do
580+
expect {parse(%{VERSION "1.1"\n<http://subj> <http://pred> "Hello"@en--ltr .}, rdfstar: true, logger: logger)}.not_to raise_error
581+
expect(logger.to_s).to include("WARN")
582+
end
569583
end
570584

571585
context 'should parse a value that was written without passing through the writer encoding' do
@@ -664,6 +678,43 @@
664678
end
665679
end
666680

681+
context "version" do
682+
{
683+
'VERSION "1.2"': %(
684+
VERSION "1.2"
685+
<http://example/s> <http://example/p> <http://example/o>
686+
),
687+
'VERSION "1.2-basic"': %(
688+
VERSION "1.2-basic"
689+
<http://example/s> <http://example/p> <http://example/o>
690+
),
691+
'VERSION "1.1"': %(
692+
VERSION "1.1"
693+
<http://example/s> <http://example/p> <http://example/o>
694+
),
695+
}.each do |name, input|
696+
it name do
697+
expect do
698+
parse(input, logger: logger)
699+
expect(logger.to_s).not_to include("WARN")
700+
end.not_to write.to(:error)
701+
end
702+
end
703+
704+
it 'version "1.2" is an error' do
705+
expect do
706+
parse('version "1.2"', logger: logger)
707+
end.to raise_error(RDF::ReaderError)
708+
end
709+
710+
it 'VERSION "1.0" is a warning' do
711+
expect do
712+
parse('VERSION "1.0"', logger: logger)
713+
end.not_to write.to(:error)
714+
expect(logger.to_s).to include('WARN')
715+
end
716+
end
717+
667718
context "triple terms" do
668719
ill_statements = {
669720
"subject-iii": '<<(<http://example/s1> <http://example/p1> <http://example/o1>)>> <http://example/p> <http://example/o> .',
@@ -709,20 +760,35 @@
709760

710761
statements.each do |name, st|
711762
context name do
712-
let(:graph) {parse(st, rdfstar: true)}
763+
let(:graph) {parse(st, rdfstar: true, logger: logger)}
713764

714765
it "creates an unquoted statement" do
715766
expect(graph.count).to eql(1)
716767
graph.statements.each do |stmt|
717768
expect(stmt).not_to be_quoted
718769
end
770+
expect(logger.to_s).not_to include("WARN")
719771
end
720772

721773
it "statements which are object of another statement are triple terms" do
722774
referencing = graph.statements.first
723775
expect(referencing).to be_a_statement
724776
expect(referencing.object).to be_a_statement
725777
expect(referencing.object).to be_tripleTerm
778+
expect(logger.to_s).not_to include("WARN")
779+
end
780+
781+
it "warns if version is not 1.2" do
782+
expect {parse(st, rdfstar: true, version: '1.1', logger: logger)}.not_to raise_error
783+
expect(logger.to_s).to include("WARN")
784+
end
785+
786+
it "warns if inline version is not 1.2" do
787+
expect {parse(%{VERSION "1.1"\n#{st}}, rdfstar: true, logger: logger)}.not_to raise_error
788+
expect(logger.to_s).to include("WARN")
789+
790+
expect {parse(%{VERSION "1.2-basic"\n#{st}}, rdfstar: true, logger: logger)}.not_to raise_error
791+
expect(logger.to_s).to include("WARN")
726792
end
727793
end
728794
end

0 commit comments

Comments
 (0)