ldpath 0.1.0 → 0.2.0
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/.rubocop.yml +7 -0
- data/.rubocop_hound.yml +1063 -0
- data/.rubocop_todo.yml +36 -0
- data/Gemfile +0 -1
- data/Rakefile +1 -1
- data/bin/ldpath +3 -3
- data/lib/ldpath.rb +3 -3
- data/lib/ldpath/field_mapping.rb +1 -2
- data/lib/ldpath/functions.rb +80 -73
- data/lib/ldpath/parser.rb +323 -258
- data/lib/ldpath/program.rb +23 -24
- data/lib/ldpath/selectors.rb +42 -43
- data/lib/ldpath/tests.rb +28 -31
- data/lib/ldpath/transform.rb +45 -47
- data/lib/ldpath/version.rb +1 -1
- data/spec/ldpath_parser_spec.rb +132 -72
- data/spec/ldpath_program_spec.rb +19 -25
- data/spec/ldpath_transform_spec.rb +114 -36
- data/spec/lib/functions/list_spec.rb +61 -0
- data/spec/spec_helper.rb +1 -1
- metadata +8 -3
data/lib/ldpath/version.rb
CHANGED
data/spec/ldpath_parser_spec.rb
CHANGED
@@ -4,172 +4,232 @@ require 'parslet/convenience'
|
|
4
4
|
describe Ldpath::Parser do
|
5
5
|
subject { Ldpath::Parser.new }
|
6
6
|
context ".parse" do
|
7
|
-
|
8
|
-
describe "lines" do
|
7
|
+
describe "doc" do
|
9
8
|
it "should parse line-oriented data" do
|
10
|
-
subject.
|
9
|
+
subject.doc.parse " \n \n"
|
11
10
|
end
|
12
11
|
end
|
13
|
-
|
14
|
-
describe "
|
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
|
12
|
+
|
13
|
+
describe "eol" do
|
25
14
|
it 'may be a \n character' do
|
26
|
-
subject.
|
15
|
+
subject.eol.parse("\n")
|
27
16
|
end
|
28
|
-
|
17
|
+
|
29
18
|
it 'may be a \n\r' do
|
30
|
-
subject.
|
19
|
+
subject.eol.parse("\n\r")
|
31
20
|
end
|
32
21
|
end
|
33
|
-
|
22
|
+
|
34
23
|
describe "eof" do
|
35
24
|
it "is the eof" do
|
36
25
|
subject.eof.parse ""
|
37
26
|
end
|
38
27
|
end
|
39
|
-
|
28
|
+
|
40
29
|
describe "wsp" do
|
41
30
|
it "may be a space" do
|
42
31
|
subject.wsp.parse " "
|
43
32
|
end
|
44
|
-
|
33
|
+
|
45
34
|
it "may be a tab" do
|
46
35
|
subject.wsp.parse "\t"
|
47
36
|
end
|
48
|
-
|
37
|
+
|
49
38
|
it "may be a multiline comment" do
|
50
39
|
subject.wsp.parse "/* xyz */"
|
51
40
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
it "may be whitespace" do
|
56
|
-
subject.expression.parse " "
|
41
|
+
|
42
|
+
it "may be a single line comment" do
|
43
|
+
subject.wsp.parse "# xyz"
|
57
44
|
end
|
58
|
-
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "directive" do
|
59
48
|
it "may be a namespace declaration" do
|
60
|
-
subject.
|
49
|
+
subject.directive.parse "@prefix x : <info:x> ;"
|
61
50
|
end
|
62
|
-
|
51
|
+
|
63
52
|
it "may be a graph" do
|
64
|
-
subject.
|
53
|
+
subject.directive.parse "@graph test:context, foo:ctx, test:bar ;"
|
65
54
|
end
|
66
|
-
|
67
55
|
|
68
56
|
it "may be a filter" do
|
69
|
-
subject.
|
57
|
+
subject.directive.parse "@filter is-a test:Context ;"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "prefixID" do
|
62
|
+
it "should parse prefix mappings" do
|
63
|
+
subject.prefixID.parse "@prefix x : <info:x> ;"
|
70
64
|
end
|
71
|
-
|
65
|
+
|
66
|
+
it "should parse the null prefix" do
|
67
|
+
subject.prefixID.parse "@prefix : <info:x> ;"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "statement" do
|
72
72
|
it "may be a mapping" do
|
73
|
-
subject.
|
73
|
+
subject.statement.parse "id = . ;"
|
74
74
|
end
|
75
75
|
end
|
76
|
-
|
77
|
-
describe "
|
78
|
-
it "may be
|
79
|
-
result = subject.
|
80
|
-
expect(result[:
|
81
|
-
end
|
82
|
-
|
83
|
-
it "may be a
|
84
|
-
result = subject.
|
85
|
-
expect(result[:
|
86
|
-
expect(result[:
|
76
|
+
|
77
|
+
describe "iri" do
|
78
|
+
it "may be an iriref" do
|
79
|
+
result = subject.iri.parse "<info:x>"
|
80
|
+
expect(result[:iri]).to eq "info:x"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "may be a prefixed name" do
|
84
|
+
result = subject.iri.parse "info:x"
|
85
|
+
expect(result[:iri][:prefix]).to eq "info"
|
86
|
+
expect(result[:iri][:localName]).to eq "x"
|
87
87
|
end
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
describe "identifier" do
|
91
91
|
it "must start with an alphanumeric character" do
|
92
92
|
subject.identifier.parse "a"
|
93
93
|
subject.identifier.parse "J"
|
94
|
-
subject.identifier.parse "4"
|
95
|
-
subject.identifier.parse "_"
|
96
94
|
end
|
97
|
-
|
95
|
+
|
98
96
|
it "may have additional alphanumeric characters" do
|
99
97
|
subject.identifier.parse "aJ0_.-"
|
100
98
|
end
|
99
|
+
|
100
|
+
it "may not end in a dot" do
|
101
|
+
expect { subject.identifier.parse "aJ0_.-." }.to raise_error
|
102
|
+
end
|
101
103
|
end
|
102
|
-
|
103
|
-
describe "
|
104
|
+
|
105
|
+
describe "string" do
|
104
106
|
it "is the content between \"" do
|
105
|
-
subject.
|
107
|
+
subject.string.parse '"abc"'
|
106
108
|
end
|
107
|
-
|
109
|
+
|
108
110
|
it "should handle escaped characters" do
|
109
|
-
subject.
|
111
|
+
subject.string.parse '"a\"b"'
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should handle single quoted strings" do
|
115
|
+
subject.string.parse "'abc'"
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should handle long strings" do
|
119
|
+
str = <<-EOF
|
120
|
+
"""
|
121
|
+
xyz
|
122
|
+
"""
|
123
|
+
EOF
|
124
|
+
|
125
|
+
subject.string.parse str.strip
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should handle long single-quoted strings" do
|
129
|
+
str = <<-EOF
|
130
|
+
'''
|
131
|
+
xyz
|
132
|
+
'''
|
133
|
+
EOF
|
134
|
+
|
135
|
+
subject.string.parse str.strip
|
110
136
|
end
|
111
137
|
end
|
112
|
-
|
138
|
+
|
113
139
|
describe "node" do
|
114
140
|
it "may be a uri" do
|
115
141
|
subject.node.parse "info:x"
|
116
142
|
end
|
117
|
-
|
143
|
+
|
118
144
|
it "may be a literal" do
|
119
145
|
subject.node.parse '"a"'
|
120
146
|
end
|
147
|
+
|
148
|
+
it "may be a typed literal" do
|
149
|
+
subject.node.parse '"a"^^info:x'
|
150
|
+
end
|
151
|
+
|
152
|
+
it "may be a language literal" do
|
153
|
+
subject.node.parse '"a"@en'
|
154
|
+
end
|
155
|
+
|
156
|
+
it "may be a numeric literal" do
|
157
|
+
subject.node.parse '0'
|
158
|
+
end
|
159
|
+
|
160
|
+
it "may be a boolean literal" do
|
161
|
+
subject.node.parse 'true'
|
162
|
+
end
|
121
163
|
end
|
122
|
-
|
123
|
-
describe "selectors" do
|
164
|
+
|
165
|
+
describe "selectors" do
|
124
166
|
it "should parse mappings" do
|
125
167
|
subject.parse("xyz = . ;\n")
|
126
168
|
end
|
127
|
-
|
169
|
+
|
128
170
|
it "should parse wildcards" do
|
129
171
|
subject.parse("xyz = * ;\n")
|
130
172
|
end
|
131
|
-
|
173
|
+
|
132
174
|
it "should parse reverse properties" do
|
133
175
|
subject.parse("xyz = ^info:a ;\n")
|
134
176
|
end
|
135
|
-
|
177
|
+
|
136
178
|
it "should parse uri mappings" do
|
137
179
|
subject.parse("xyz = <info:a> ;\n")
|
138
180
|
end
|
139
|
-
|
181
|
+
|
140
182
|
it "should parse path mappings" do
|
141
183
|
subject.mapping.parse("xyz = info:a / info:b :: a:b;")
|
142
184
|
end
|
143
|
-
|
185
|
+
|
186
|
+
it "should parse path selectors" do
|
187
|
+
subject.selector.parse("info:a / info:b")
|
188
|
+
end
|
189
|
+
|
144
190
|
it "recursive_path_selector" do
|
145
191
|
subject.recursive_path_selector.parse("(foo:go)*")
|
146
192
|
end
|
147
|
-
|
193
|
+
|
148
194
|
it "function_selector" do
|
149
195
|
subject.selector.parse('fn:concat(foaf:givename," ",foaf:surname)')
|
150
196
|
end
|
151
|
-
|
197
|
+
|
152
198
|
it "tap_selector" do
|
153
199
|
subject.selector.parse('?<a><info:a>')
|
154
200
|
end
|
155
|
-
|
201
|
+
|
156
202
|
it "loose_selector" do
|
157
203
|
subject.selector.parse('~<info:a>')
|
158
204
|
end
|
159
205
|
end
|
160
|
-
|
206
|
+
|
161
207
|
describe "integration tests" do
|
208
|
+
it "should parse a simple example" do
|
209
|
+
tree = subject.parse <<-EOF
|
210
|
+
@prefix dcterms : <http://purl.org/dc/terms/> ;
|
211
|
+
topic = <http://xmlns.com/foaf/0.1/primaryTopic> :: xsd:string ;
|
212
|
+
EOF
|
213
|
+
expect(tree.length).to eq 2
|
214
|
+
expect(tree.first).to include :prefixID
|
215
|
+
expect(tree.first[:prefixID]).to include id: 'dcterms'
|
216
|
+
expect(tree.first[:prefixID]).to include iri: 'http://purl.org/dc/terms/'
|
217
|
+
expect(tree.last).to include :mapping
|
218
|
+
expect(tree.last[:mapping]).to include name: 'topic'
|
219
|
+
expect(tree.last[:mapping]).to include :selector
|
220
|
+
end
|
221
|
+
|
162
222
|
it "should parse the foaf example" do
|
163
223
|
subject.parse File.read(File.expand_path(File.join(__FILE__, "..", "fixtures", "foaf_example.program")))
|
164
224
|
end
|
165
|
-
|
225
|
+
|
166
226
|
it "should parse the program.ldpath" do
|
167
227
|
subject.parse File.read(File.expand_path(File.join(__FILE__, "..", "fixtures", "program.ldpath")))
|
168
228
|
end
|
169
|
-
|
229
|
+
|
170
230
|
it "should parse the namespaces.ldpath" do
|
171
|
-
subject.
|
231
|
+
subject.parse File.read(File.expand_path(File.join(__FILE__, "..", "fixtures", "namespaces.ldpath")))
|
172
232
|
end
|
173
|
-
end
|
233
|
+
end
|
174
234
|
end
|
175
235
|
end
|
data/spec/ldpath_program_spec.rb
CHANGED
@@ -25,16 +25,16 @@ is_test = .[dcterms:title is "Hello, world!"] ;
|
|
25
25
|
is_not_test = .[!(dcterms:title is "Hello, world!")] ;
|
26
26
|
EOF
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
let(:object) { RDF::URI.new("info:a") }
|
30
30
|
let(:parent) { RDF::URI.new("info:b") }
|
31
31
|
let(:child) { RDF::URI.new("info:c") }
|
32
32
|
let(:grandparent) { RDF::URI.new("info:d") }
|
33
|
-
|
33
|
+
|
34
34
|
let(:graph) do
|
35
35
|
RDF::Graph.new
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
it "should work" do
|
39
39
|
graph << [object, RDF::DC.title, "Hello, world!"]
|
40
40
|
graph << [object, RDF::DC.isPartOf, parent]
|
@@ -70,7 +70,7 @@ EOF
|
|
70
70
|
expect(result["is_not_test"]).to be_empty
|
71
71
|
end
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
describe "functions" do
|
75
75
|
let(:program) do
|
76
76
|
Ldpath::Program.parse <<-EOF
|
@@ -94,7 +94,7 @@ EOF
|
|
94
94
|
end
|
95
95
|
|
96
96
|
let(:object) { RDF::URI.new("info:a") }
|
97
|
-
|
97
|
+
|
98
98
|
let(:graph) do
|
99
99
|
graph = RDF::Graph.new
|
100
100
|
graph << [object, RDF::DC.title, "Hello, world!"]
|
@@ -137,7 +137,7 @@ EOF
|
|
137
137
|
expect(subject).to include "first_b" => ["b"]
|
138
138
|
end
|
139
139
|
end
|
140
|
-
|
140
|
+
|
141
141
|
describe "last" do
|
142
142
|
it "should take the last value" do
|
143
143
|
expect(subject).to include "last_b" => ["b"]
|
@@ -182,17 +182,15 @@ EOF
|
|
182
182
|
end
|
183
183
|
end
|
184
184
|
end
|
185
|
-
|
185
|
+
|
186
186
|
describe "Data loading" do
|
187
187
|
subject do
|
188
|
-
|
189
188
|
Ldpath::Program.parse <<-EOF
|
190
189
|
@prefix dcterms : <http://purl.org/dc/terms/> ;
|
191
190
|
title = foaf:primaryTopic / dc:title :: xsd:string ;
|
192
191
|
EOF
|
193
|
-
|
194
192
|
end
|
195
|
-
|
193
|
+
|
196
194
|
it "should work" do
|
197
195
|
result = subject.evaluate RDF::URI.new("http://www.bbc.co.uk/programmes/b0081dq5.rdf")
|
198
196
|
expect(result["title"]).to match_array "Huw Stephens"
|
@@ -217,17 +215,17 @@ EOF
|
|
217
215
|
let(:object) { RDF::URI.new("info:a") }
|
218
216
|
let(:child) { RDF::URI.new("info:b") }
|
219
217
|
let(:grandchild) { RDF::URI.new("info:c") }
|
220
|
-
|
218
|
+
|
221
219
|
let(:graph) do
|
222
220
|
graph = RDF::Graph.new
|
223
|
-
|
221
|
+
|
224
222
|
graph << [object, RDF::DC.title, "Object"]
|
225
223
|
graph << [child, RDF::DC.title, "Child"]
|
226
224
|
graph << [object, RDF::DC.hasPart, child]
|
227
225
|
|
228
226
|
graph
|
229
227
|
end
|
230
|
-
|
228
|
+
|
231
229
|
subject do
|
232
230
|
Ldpath::Program.parse <<-EOF
|
233
231
|
@prefix dcterms : <http://purl.org/dc/terms/> ;
|
@@ -243,22 +241,22 @@ child_title_with_tap = dcterms:hasPart / ?<tap>fn:predicates() / dcterms:title :
|
|
243
241
|
expect(result["tap"]).to eq ["http://purl.org/dc/terms/title"]
|
244
242
|
end
|
245
243
|
end
|
246
|
-
|
244
|
+
|
247
245
|
describe "loose selector" do
|
248
246
|
let(:object) { RDF::URI.new("info:a") }
|
249
247
|
let(:child) { RDF::URI.new("info:b") }
|
250
248
|
let(:grandchild) { RDF::URI.new("info:c") }
|
251
|
-
|
249
|
+
|
252
250
|
let(:graph) do
|
253
251
|
graph = RDF::Graph.new
|
254
|
-
|
252
|
+
|
255
253
|
graph << [object, RDF::DC.title, "Object"]
|
256
254
|
graph << [child, RDF::DC.title, "Child"]
|
257
255
|
graph << [object, RDF::DC.hasPart, child]
|
258
256
|
|
259
257
|
graph
|
260
258
|
end
|
261
|
-
|
259
|
+
|
262
260
|
subject do
|
263
261
|
Ldpath::Program.parse <<-EOF
|
264
262
|
@prefix dcterms : <http://purl.org/dc/terms/> ;
|
@@ -275,7 +273,6 @@ title_with_loose = ~dc:title :: xsd:string ;
|
|
275
273
|
end
|
276
274
|
|
277
275
|
describe "filter" do
|
278
|
-
|
279
276
|
subject do
|
280
277
|
Ldpath::Program.parse <<-EOF
|
281
278
|
@prefix dcterms : <http://purl.org/dc/terms/> ;
|
@@ -287,28 +284,25 @@ title_with_loose = ~dc:title :: xsd:string ;
|
|
287
284
|
|
288
285
|
let(:object) { RDF::URI.new("info:a") }
|
289
286
|
let(:other_object) { RDF::URI.new("info:b") }
|
290
|
-
|
291
|
-
|
287
|
+
|
292
288
|
let(:graph) do
|
293
289
|
graph = RDF::Graph.new
|
294
|
-
|
290
|
+
|
295
291
|
graph << [object, RDF.type, RDF::DC.Agent]
|
296
292
|
graph << [object, RDF::DC.title, "Title"]
|
297
293
|
graph << [other_object, RDF::DC.title, "Other Title"]
|
298
294
|
|
299
295
|
graph
|
300
296
|
end
|
301
|
-
|
297
|
+
|
302
298
|
it "should work" do
|
303
299
|
result = subject.evaluate object, graph
|
304
300
|
expect(result["title"]).to eq ["Title"]
|
305
301
|
end
|
306
|
-
|
302
|
+
|
307
303
|
it "filters objects that don't match" do
|
308
304
|
result = subject.evaluate other_object, graph
|
309
305
|
expect(result).to be_empty
|
310
306
|
end
|
311
|
-
|
312
|
-
|
313
307
|
end
|
314
308
|
end
|