ldpath 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +17 -2
- data/ldpath.gemspec +1 -0
- data/lib/ldpath.rb +13 -0
- data/lib/ldpath/field_mapping.rb +2 -0
- data/lib/ldpath/functions.rb +144 -0
- data/lib/ldpath/parser.rb +358 -0
- data/lib/ldpath/program.rb +63 -252
- data/lib/ldpath/selectors.rb +124 -0
- data/lib/ldpath/tests.rb +102 -0
- data/lib/ldpath/transform.rb +143 -0
- data/lib/ldpath/version.rb +1 -1
- data/spec/fixtures/foaf_example.program +9 -0
- data/spec/fixtures/namespaces.ldpath +21 -0
- data/spec/fixtures/program.ldpath +41 -0
- data/spec/ldpath_parser_spec.rb +162 -0
- data/spec/ldpath_program_spec.rb +115 -0
- data/spec/ldpath_transform_spec.rb +62 -0
- metadata +34 -4
- data/spec/ldpath_program_parser_spec.rb +0 -34
@@ -0,0 +1,143 @@
|
|
1
|
+
module Ldpath
|
2
|
+
class Transform < Parslet::Transform
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def default_prefixes
|
6
|
+
@default_prefixes ||= {
|
7
|
+
"rdf" => RDF::Vocabulary.new("http://www.w3.org/1999/02/22-rdf-syntax-ns#"),
|
8
|
+
"rdfs" => RDF::Vocabulary.new("http://www.w3.org/2000/01/rdf-schema#"),
|
9
|
+
"owl" => RDF::Vocabulary.new("http://www.w3.org/2002/07/owl#"),
|
10
|
+
"skos" => RDF::Vocabulary.new("http://www.w3.org/2004/02/skos/core#"),
|
11
|
+
"dc" => RDF::Vocabulary.new("http://purl.org/dc/elements/1.1/"),
|
12
|
+
"xsd" => RDF::Vocabulary.new("http://www.w3.org/2001/XMLSchema#"),# (LMF base index datatypes/XML Schema)
|
13
|
+
"lmf" => RDF::Vocabulary.new("http://www.newmedialab.at/lmf/types/1.0/"),# (LMF extended index datatypes)
|
14
|
+
"fn" => RDF::Vocabulary.new("http://www.newmedialab.at/lmf/functions/1.0/"),# (LMF index functions)
|
15
|
+
"foaf" => RDF::Vocabulary.new("http://xmlns.com/foaf/0.1/"),
|
16
|
+
"info" => RDF::Vocabulary.new("info:"),
|
17
|
+
"urn" => RDF::Vocabulary.new("urn:"),
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def apply obj, context = nil
|
23
|
+
context ||= { }
|
24
|
+
context[:prefixes] ||= {}.merge(self.class.default_prefixes)
|
25
|
+
super obj, context
|
26
|
+
end
|
27
|
+
|
28
|
+
# Core types
|
29
|
+
rule(literal: simple(:literal)) { literal.to_s }
|
30
|
+
rule(uri: simple(:uri)) { RDF::URI.new(uri) }
|
31
|
+
|
32
|
+
# Namespaces
|
33
|
+
rule(namespace: subtree(:namespace)) do
|
34
|
+
prefixes[namespace[:id].to_s] = RDF::Vocabulary.new(namespace[:uri])
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
rule(prefix: simple(:prefix), localName: simple(:localName)) do
|
39
|
+
(prefixes[prefix.to_s] || RDF::Vocabulary.new(prefix.to_s))[localName]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Mappings
|
43
|
+
|
44
|
+
rule(mapping: subtree(:mapping)) do
|
45
|
+
FieldMapping.new mapping[:name].to_s, mapping[:selector], mapping[:field_type]
|
46
|
+
end
|
47
|
+
|
48
|
+
## Selectors
|
49
|
+
|
50
|
+
|
51
|
+
### Atomic Selectors
|
52
|
+
rule(self: simple(:self)) do
|
53
|
+
SelfSelector.new
|
54
|
+
end
|
55
|
+
|
56
|
+
rule(fname: simple(:fname), arglist: subtree(:arglist)) do
|
57
|
+
FunctionSelector.new fname.to_s, arglist
|
58
|
+
end
|
59
|
+
|
60
|
+
rule(property: simple(:property)) do
|
61
|
+
PropertySelector.new property
|
62
|
+
end
|
63
|
+
|
64
|
+
rule(wildcard: simple(:wilcard)) do
|
65
|
+
WildcardSelector.new
|
66
|
+
end
|
67
|
+
|
68
|
+
rule(reverse_property: simple(:property)) do
|
69
|
+
ReversePropertySelector.new property
|
70
|
+
end
|
71
|
+
|
72
|
+
rule(range: subtree(:range)) do
|
73
|
+
range.fetch(:min,0).to_i..range.fetch(:max, Infinity).to_f
|
74
|
+
end
|
75
|
+
|
76
|
+
rule(recursive: subtree(:properties)) do
|
77
|
+
repeat = case properties[:repeat]
|
78
|
+
when "*"
|
79
|
+
0..Infinity
|
80
|
+
when "+"
|
81
|
+
1..Infinity
|
82
|
+
when Range
|
83
|
+
properties[:repeat]
|
84
|
+
end
|
85
|
+
|
86
|
+
RecursivePathSelector.new properties[:delegate], repeat
|
87
|
+
end
|
88
|
+
|
89
|
+
### Test Selectors
|
90
|
+
|
91
|
+
rule(delegate: subtree(:delegate), test: subtree(:test)) do
|
92
|
+
TestSelector.new delegate, test
|
93
|
+
end
|
94
|
+
|
95
|
+
rule(lang: simple(:lang)) do
|
96
|
+
LanguageTest.new lang.to_s.to_sym
|
97
|
+
end
|
98
|
+
|
99
|
+
rule(type: simple(:type)) do
|
100
|
+
TypeTest.new type
|
101
|
+
end
|
102
|
+
|
103
|
+
rule(type: simple(:type)) do
|
104
|
+
TypeTest.new type
|
105
|
+
end
|
106
|
+
|
107
|
+
rule(not: subtree(:not_op)) do
|
108
|
+
NotTest.new not_op[:delegate]
|
109
|
+
end
|
110
|
+
|
111
|
+
rule(and: subtree(:op)) do
|
112
|
+
AndTest.new op[:left], op[:right]
|
113
|
+
end
|
114
|
+
|
115
|
+
rule(or: subtree(:op)) do
|
116
|
+
OrTest.new op[:left], op[:right]
|
117
|
+
end
|
118
|
+
|
119
|
+
rule(is: subtree(:is)) do
|
120
|
+
IsTest.new PropertySelector.new(is[:property]), is[:right]
|
121
|
+
end
|
122
|
+
|
123
|
+
rule(is_a: subtree(:is_a)) do
|
124
|
+
IsTest.new PropertySelector.new(RDF.type), is_a[:right]
|
125
|
+
end
|
126
|
+
### Compound Selectors
|
127
|
+
|
128
|
+
rule(path: subtree(:path)) do
|
129
|
+
PathSelector.new path[:left], path[:right]
|
130
|
+
end
|
131
|
+
|
132
|
+
rule(union: subtree(:union)) do
|
133
|
+
UnionSelector.new union[:left], union[:right]
|
134
|
+
end
|
135
|
+
|
136
|
+
rule(intersection: subtree(:intersection)) do
|
137
|
+
IntersectionSelector.new intersection[:left], intersection[:right]
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
Infinity = 1.0 / 0.0
|
142
|
+
end
|
143
|
+
end
|
data/lib/ldpath/version.rb
CHANGED
@@ -0,0 +1,9 @@
|
|
1
|
+
@prefix foaf : <http://xmlns.com/foaf/0.1/> ;
|
2
|
+
@prefix geo : <http://www.w3.org/2003/01/geo/wgs84_pos#> ;
|
3
|
+
title = foaf:name | fn:concat(foaf:givename," ",foaf:surname) :: xsd:string ;
|
4
|
+
summary = dc:description :: lmf:text ;
|
5
|
+
geo = foaf:based_near :: lmf:location;
|
6
|
+
interest = foaf:interest / (rdfs:label[@en] | rdfs:label[@none] | <http://rdf.freebase.com/ns/type.object.name>[@en]) :: xsd:string;
|
7
|
+
friends = foaf:knows / (foaf:name | fn:concat(foaf:givename," ",foaf:surname)) :: xsd:string;
|
8
|
+
contrycode = foaf:based_near / <http://www.geonames.org/ontology#countryCode> :: xsd:string ;
|
9
|
+
type = rdf:type :: xsd:anyURI ;
|
@@ -0,0 +1,21 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2013 The Apache Software Foundation
|
3
|
+
*
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
* you may not use this file except in compliance with the License.
|
6
|
+
* You may obtain a copy of the License at
|
7
|
+
*
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
*
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
* See the License for the specific language governing permissions and
|
14
|
+
* limitations under the License.
|
15
|
+
*/
|
16
|
+
@prefix skos: <http://www.w3.org/2004/02/skos/core#>
|
17
|
+
@prefix foo: <http://foo.com/some/path#> ;
|
18
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/> ;
|
19
|
+
@prefix test: <http://example.com/>
|
20
|
+
@prefix dcterms: <http://purl.org/dc/terms/> ;
|
21
|
+
@prefix foobar: <urn:uuid:1234> ;
|
@@ -0,0 +1,41 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2013 The Apache Software Foundation
|
3
|
+
*
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
* you may not use this file except in compliance with the License.
|
6
|
+
* You may obtain a copy of the License at
|
7
|
+
*
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
*
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
* See the License for the specific language governing permissions and
|
14
|
+
* limitations under the License.
|
15
|
+
*/
|
16
|
+
|
17
|
+
@prefix test: <http://example.com/> ;
|
18
|
+
@prefix foo: <http://foo.com/some/path#> ;
|
19
|
+
|
20
|
+
@graph test:context, foo:ctx, test:bar ;
|
21
|
+
|
22
|
+
/*
|
23
|
+
@filter test:type is foo:bar | test:p1 & is-a test:Case ;
|
24
|
+
|
25
|
+
@boost foo:boost / ^test:boost ;
|
26
|
+
*/
|
27
|
+
|
28
|
+
path = test:p1 / test:p2 :: test:type ;
|
29
|
+
lang_test = test:p1[@en] :: test:type ;
|
30
|
+
type_test = foo:p2[^^test:int] :: test:type ;
|
31
|
+
int_s = (foo:go)* :: test:type ;
|
32
|
+
int_p = (foo:go)+ :: test:type ;
|
33
|
+
group = (test:p1 / test:p2) :: test:type ;
|
34
|
+
|
35
|
+
inverse = ^test:incoming :: test:type ;
|
36
|
+
|
37
|
+
config = test:foo :: test:type ;
|
38
|
+
/*
|
39
|
+
foo:bar = test:foo :: test:type ;
|
40
|
+
<http://test/> = test:test :: test:type ;
|
41
|
+
*/
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pp'
|
3
|
+
require 'parslet/convenience'
|
4
|
+
describe Ldpath::Parser do
|
5
|
+
subject { Ldpath::Parser.new }
|
6
|
+
context ".parse" do
|
7
|
+
|
8
|
+
describe "lines" do
|
9
|
+
it "should parse line-oriented data" do
|
10
|
+
subject.lines.parse " \n \n"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "line" do
|
15
|
+
it "may be a line ending in a newline" do
|
16
|
+
subject.line.parse " \n"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "may be a line ending in EOF" do
|
20
|
+
subject.line.parse("/* abc */")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "newline" do
|
25
|
+
it 'may be a \n character' do
|
26
|
+
subject.newline.parse("\n")
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'may be a \n\r' do
|
30
|
+
subject.newline.parse("\n\r")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "eof" do
|
35
|
+
it "is the eof" do
|
36
|
+
subject.eof.parse ""
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "wsp" do
|
41
|
+
it "may be a space" do
|
42
|
+
subject.wsp.parse " "
|
43
|
+
end
|
44
|
+
|
45
|
+
it "may be a tab" do
|
46
|
+
subject.wsp.parse "\t"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "may be a multiline comment" do
|
50
|
+
subject.wsp.parse "/* xyz */"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "expression" do
|
55
|
+
it "may be whitespace" do
|
56
|
+
subject.expression.parse " "
|
57
|
+
end
|
58
|
+
|
59
|
+
it "may be a namespace declaration" do
|
60
|
+
subject.expression.parse "@prefix x : info:x ;"
|
61
|
+
end
|
62
|
+
|
63
|
+
it "may be a graph" do
|
64
|
+
subject.expression.parse "@graph test:context, foo:ctx, test:bar ;"
|
65
|
+
end
|
66
|
+
|
67
|
+
it "may be a mapping" do
|
68
|
+
subject.expression.parse "id = . ;"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "uri" do
|
73
|
+
it "may be a bracketed uri" do
|
74
|
+
result = subject.uri.parse "<info:x>"
|
75
|
+
expect(result[:uri]).to eq "info:x"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "may be a namespace and local name" do
|
79
|
+
result = subject.uri.parse "info:x"
|
80
|
+
expect(result[:uri][:prefix]).to eq "info"
|
81
|
+
expect(result[:uri][:localName]).to eq "x"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "identifier" do
|
86
|
+
it "must start with an alphanumeric character" do
|
87
|
+
subject.identifier.parse "a"
|
88
|
+
subject.identifier.parse "J"
|
89
|
+
subject.identifier.parse "4"
|
90
|
+
subject.identifier.parse "_"
|
91
|
+
end
|
92
|
+
|
93
|
+
it "may have additional alphanumeric characters" do
|
94
|
+
subject.identifier.parse "aJ0_.-"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "strlit" do
|
99
|
+
it "is the content between \"" do
|
100
|
+
subject.strlit.parse '"abc"'
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should handle escaped characters" do
|
104
|
+
subject.strlit.parse '"a\"b"'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "node" do
|
109
|
+
it "may be a uri" do
|
110
|
+
subject.node.parse "info:x"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "may be a literal" do
|
114
|
+
subject.node.parse '"a"'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "selectors" do
|
119
|
+
it "should parse mappings" do
|
120
|
+
subject.parse("xyz = . ;\n")
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should parse wildcards" do
|
124
|
+
subject.parse("xyz = * ;\n")
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should parse reverse properties" do
|
128
|
+
subject.parse("xyz = ^info:a ;\n")
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should parse uri mappings" do
|
132
|
+
subject.parse("xyz = <info:a> ;\n")
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should parse path mappings" do
|
136
|
+
subject.parse("xyz = info:a / info:b :: a:b;\n")
|
137
|
+
end
|
138
|
+
|
139
|
+
it "recursive_path_selector" do
|
140
|
+
subject.recursive_path_selector.parse("(foo:go)*")
|
141
|
+
end
|
142
|
+
|
143
|
+
it "function_selector" do
|
144
|
+
subject.selector.parse('fn:concat(foaf:givename," ",foaf:surname)')
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "integration tests" do
|
149
|
+
it "should parse the foaf example" do
|
150
|
+
subject.parse File.read(File.expand_path(File.join(__FILE__, "..", "fixtures", "foaf_example.program")))
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should parse the program.ldpath" do
|
154
|
+
subject.parse File.read(File.expand_path(File.join(__FILE__, "..", "fixtures", "program.ldpath")))
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should parse the namespaces.ldpath" do
|
158
|
+
subject.parse File.read(File.expand_path(File.join(__FILE__, "..", "fixtures", "namespaces.ldpath")))
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ldpath::Program do
|
4
|
+
describe "Simple program" do
|
5
|
+
subject do
|
6
|
+
Ldpath::Program.parse <<-EOF
|
7
|
+
@prefix dcterms : <http://purl.org/dc/terms/> ;
|
8
|
+
title = dcterms:title :: xsd:string ;
|
9
|
+
parent_title = dcterms:isPartOf / dcterms:title :: xsd:string ;
|
10
|
+
titles = dcterms:title | (dcterms:isPartOf / dcterms:title) | (^dcterms:isPartOf / dcterms:title) :: xsd:string ;
|
11
|
+
no_titles = dcterms:title & (dcterms:isPartOf / dcterms:title) & (^dcterms:isPartOf / dcterms:title) :: xsd:string ;
|
12
|
+
self = . :: xsd:string ;
|
13
|
+
wildcard = * ::xsd:string ;
|
14
|
+
child_title = ^dcterms:isPartOf / dcterms:title :: xsd:string ;
|
15
|
+
recursive = (dcterms:isPartOf)* ;
|
16
|
+
en_description = dcterms:description[@en] ;
|
17
|
+
conditional = dcterms:isPartOf[dcterms:title] ;
|
18
|
+
conditional_false = dcterms:isPartOf[dcterms:description] ;
|
19
|
+
int_value = <info:intProperty>[^^xsd:integer] :: xsd:integer ;
|
20
|
+
numeric_value = <info:numericProperty> :: xsd:integer ;
|
21
|
+
escaped_string = "\\"" :: xsd:string;
|
22
|
+
and_test = .[dcterms:title & dcterms:gone] ;
|
23
|
+
or_test = .[dcterms:title | dcterms:gone] ;
|
24
|
+
is_test = .[dcterms:title is "Hello, world!"] ;
|
25
|
+
is_not_test = .[!(dcterms:title is "Hello, world!")] ;
|
26
|
+
EOF
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:object) { RDF::URI.new("info:a") }
|
30
|
+
let(:parent) { RDF::URI.new("info:b") }
|
31
|
+
let(:child) { RDF::URI.new("info:c") }
|
32
|
+
let(:grandparent) { RDF::URI.new("info:d") }
|
33
|
+
|
34
|
+
let(:graph) do
|
35
|
+
RDF::Graph.new
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should work" do
|
39
|
+
graph << [object, RDF::DC.title, "Hello, world!"]
|
40
|
+
graph << [object, RDF::DC.isPartOf, parent]
|
41
|
+
graph << [object, RDF::DC.description, RDF::Literal.new("English!", language: "en")]
|
42
|
+
graph << [object, RDF::DC.description, RDF::Literal.new("French!", language: "fr")]
|
43
|
+
graph << [object, RDF::URI.new("info:intProperty"), 1]
|
44
|
+
graph << [object, RDF::URI.new("info:intProperty"), "garbage"]
|
45
|
+
graph << [object, RDF::URI.new("info:numericProperty"), "1"]
|
46
|
+
graph << [parent, RDF::DC.title, "Parent title"]
|
47
|
+
graph << [child, RDF::DC.isPartOf, object]
|
48
|
+
graph << [child, RDF::DC.title, "Child title"]
|
49
|
+
graph << [parent, RDF::DC.isPartOf, grandparent]
|
50
|
+
|
51
|
+
result = subject.evaluate object, graph
|
52
|
+
|
53
|
+
expect(result["title"]).to match_array "Hello, world!"
|
54
|
+
expect(result["parent_title"]).to match_array "Parent title"
|
55
|
+
expect(result["self"]).to match_array(object)
|
56
|
+
expect(result["wildcard"]).to include "Hello, world!", parent
|
57
|
+
expect(result["child_title"]).to match_array "Child title"
|
58
|
+
expect(result["titles"]).to match_array ["Hello, world!", "Parent title", "Child title"]
|
59
|
+
expect(result["no_titles"]).to be_empty
|
60
|
+
expect(result["recursive"]).to match_array [parent, grandparent]
|
61
|
+
expect(result["en_description"].first.to_s).to eq "English!"
|
62
|
+
expect(result["conditional"]).to match_array parent
|
63
|
+
expect(result["conditional_false"]).to be_empty
|
64
|
+
expect(result["int_value"]).to match_array 1
|
65
|
+
expect(result["numeric_value"]).to match_array 1
|
66
|
+
expect(result["escaped_string"]).to match_array '\"'
|
67
|
+
expect(result["and_test"]).to be_empty
|
68
|
+
expect(result["or_test"]).to match_array(object)
|
69
|
+
expect(result["is_test"]).to match_array(object)
|
70
|
+
expect(result["is_not_test"]).to be_empty
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "functions" do
|
75
|
+
|
76
|
+
subject do
|
77
|
+
Ldpath::Program.parse <<-EOF
|
78
|
+
@prefix dcterms : <http://purl.org/dc/terms/> ;
|
79
|
+
title = fn:concat("a", "b") ;
|
80
|
+
first_a = fn:first("a", "b") ;
|
81
|
+
last_b = fn:last("a", "b") ;
|
82
|
+
EOF
|
83
|
+
end
|
84
|
+
|
85
|
+
let(:object) { RDF::URI.new("info:a") }
|
86
|
+
|
87
|
+
let(:graph) do
|
88
|
+
RDF::Graph.new
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should work" do
|
92
|
+
result = subject.evaluate object, graph
|
93
|
+
expect(result["title"]).to match_array "ab"
|
94
|
+
expect(result["first_a"]).to match_array "a"
|
95
|
+
expect(result["last_b"]).to match_array "b"
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "Data loading" do
|
101
|
+
subject do
|
102
|
+
|
103
|
+
Ldpath::Program.parse <<-EOF
|
104
|
+
@prefix dcterms : <http://purl.org/dc/terms/> ;
|
105
|
+
title = foaf:primaryTopic / dc:title :: xsd:string ;
|
106
|
+
EOF
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should work" do
|
111
|
+
result = subject.evaluate RDF::URI.new("http://www.bbc.co.uk/programmes/b0081dq5.rdf")
|
112
|
+
expect(result["title"]).to match_array "Huw Stephens"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|