ldpath 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/bin/ldpath +30 -0
- data/ldpath.gemspec +1 -1
- data/lib/ldpath/functions.rb +42 -8
- data/lib/ldpath/parser.rb +67 -71
- data/lib/ldpath/program.rb +13 -6
- data/lib/ldpath/selectors.rb +1 -1
- data/lib/ldpath/transform.rb +12 -0
- data/lib/ldpath/version.rb +1 -1
- data/spec/fixtures/program.ldpath +3 -6
- data/spec/ldpath_parser_spec.rb +7 -2
- data/spec/ldpath_program_spec.rb +136 -17
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76772761e5cb85b3f0d16df78e8673524dd4849b
|
4
|
+
data.tar.gz: 292da39f546dd6151e6754a060ffadf9464df44d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 536f06a5bb004c85e76ff303cc39cb27204bb853126eb18049e98d9b5bd64e16aa477cc1b1be8314f0ea199fdbd0c186f2b89fb150c3aa45f3e293c339c1c949
|
7
|
+
data.tar.gz: a3914cc93b8b07c9086ecaac7d8a12502cbcb5dfd4a02dd916163ccd92bc8e740176a5379ecc3ddb5904eeb01318f80359dc178673f18a4f838d5a7a6eef6447
|
data/README.md
CHANGED
@@ -37,7 +37,7 @@ output = program.evaluate uri, context
|
|
37
37
|
|
38
38
|
## Contributing
|
39
39
|
|
40
|
-
1. Fork it ( http://github.com
|
40
|
+
1. Fork it ( http://github.com/cbeer/ldpath.rb/fork )
|
41
41
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
42
42
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
43
43
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/bin/ldpath
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'ldpath'
|
6
|
+
require 'byebug'
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'rest-client'
|
10
|
+
rescue LoadError
|
11
|
+
end
|
12
|
+
|
13
|
+
options = {}
|
14
|
+
OptionParser.new do |opts|
|
15
|
+
opts.banner = "Usage: ldpath [options] URI"
|
16
|
+
|
17
|
+
opts.on("--program=STRING_URI_OR_FILE", "LDPath program to run (- for STDIN)") do |program|
|
18
|
+
if File.exists? program or program =~ /^http/
|
19
|
+
options[:program] = open(program).read
|
20
|
+
elsif program.strip == "-"
|
21
|
+
options[:program] = $stdin.read
|
22
|
+
else
|
23
|
+
options[:program] = program
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end.parse!
|
27
|
+
|
28
|
+
uri = ARGV.shift
|
29
|
+
|
30
|
+
puts Ldpath::Program.parse(options[:program]).evaluate(RDF::URI.new(uri)).to_json
|
data/ldpath.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Chris Beer"]
|
10
10
|
spec.email = ["cabeer@stanford.edu"]
|
11
11
|
spec.summary = %q{Ruby implementation of LDPath}
|
12
|
-
spec.homepage = "
|
12
|
+
spec.homepage = "https://github.com/cbeer/ldpath.rb"
|
13
13
|
spec.license = "Apache 2"
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
data/lib/ldpath/functions.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Ldpath
|
2
2
|
module Functions
|
3
3
|
def concat uri, context, *args
|
4
|
-
args.join
|
4
|
+
args.flatten.compact.join
|
5
5
|
end
|
6
6
|
|
7
7
|
def first uri, context, *args
|
@@ -11,29 +11,57 @@ module Ldpath
|
|
11
11
|
def last uri, context, *args
|
12
12
|
args.flatten.compact.last
|
13
13
|
end
|
14
|
+
|
15
|
+
def count uri, context, *args
|
16
|
+
args.flatten.compact.length
|
17
|
+
end
|
14
18
|
|
15
19
|
def eq uri, context, *args
|
16
|
-
|
20
|
+
a, b, *rem = args.flatten
|
21
|
+
unless rem.empty?
|
22
|
+
raise "Too many arguments to fn:eq"
|
23
|
+
end
|
24
|
+
a == b
|
17
25
|
end
|
18
26
|
|
19
27
|
def ne uri, context, *args
|
20
|
-
|
28
|
+
a, b, *rem = args.flatten
|
29
|
+
unless rem.empty?
|
30
|
+
raise "Too many arguments to fn:ne"
|
31
|
+
end
|
32
|
+
a != b
|
21
33
|
end
|
22
34
|
|
23
35
|
def lt uri, context, *args
|
24
|
-
|
36
|
+
a, b, *rem = args.flatten
|
37
|
+
unless rem.empty?
|
38
|
+
raise "Too many arguments to fn:lt"
|
39
|
+
end
|
40
|
+
a < b
|
25
41
|
end
|
26
42
|
|
27
43
|
def le uri, context, *args
|
28
|
-
|
44
|
+
a, b, *rem = args.flatten
|
45
|
+
unless rem.empty?
|
46
|
+
raise "Too many arguments to fn:le"
|
47
|
+
end
|
48
|
+
a <= b
|
29
49
|
end
|
30
50
|
|
31
51
|
def gt uri, context, *args
|
32
|
-
|
52
|
+
a, b, *rem = args.flatten
|
53
|
+
unless rem.empty?
|
54
|
+
raise "Too many arguments to fn:gt"
|
55
|
+
end
|
56
|
+
a > b
|
33
57
|
end
|
34
58
|
|
35
59
|
def ge uri, context, *args
|
36
|
-
|
60
|
+
a, b, *rem = args.flatten
|
61
|
+
unless rem.empty?
|
62
|
+
raise "Too many arguments to fn:ge"
|
63
|
+
end
|
64
|
+
a >= b
|
37
65
|
end
|
38
66
|
|
39
67
|
# collections
|
@@ -130,7 +158,6 @@ module Ldpath
|
|
130
158
|
end
|
131
159
|
|
132
160
|
def endsWith uri, context, str, suffix
|
133
|
-
|
134
161
|
Array(str).map { |x| x.end_with? suffix }
|
135
162
|
end
|
136
163
|
|
@@ -141,6 +168,13 @@ module Ldpath
|
|
141
168
|
def predicates uri, context, *args
|
142
169
|
context.query([uri, nil, nil]).map(&:predicate).uniq
|
143
170
|
end
|
171
|
+
|
172
|
+
def xpath uri, context, xpath, node
|
173
|
+
x = Array(xpath).flatten.first
|
174
|
+
Array(node).flatten.compact.map do |n|
|
175
|
+
Nokogiri::XML(n).xpath(x, prefixes.map { |k,v| [k, v.to_s] }).map(&:text)
|
176
|
+
end
|
177
|
+
end
|
144
178
|
|
145
179
|
end
|
146
180
|
end
|
data/lib/ldpath/parser.rb
CHANGED
@@ -4,15 +4,21 @@ module Ldpath
|
|
4
4
|
class Parser < Parslet::Parser
|
5
5
|
root :lines
|
6
6
|
rule(:lines) { line.repeat }
|
7
|
-
rule(:line) { expression >>
|
7
|
+
rule(:line) { ((wsp >> expression) | expression) >> space_not_newline? >> (newline | eof) }
|
8
8
|
|
9
9
|
rule(:newline) { (str("\n") >> str("\r").maybe).repeat(1) }
|
10
10
|
rule(:eof) { any.absent? }
|
11
|
-
|
11
|
+
|
12
|
+
rule(:space) { match('\s').repeat(1) }
|
13
|
+
rule(:spaces?) { space.maybe }
|
14
|
+
rule(:space_not_newline) { str("\n").absent? >> space }
|
15
|
+
rule(:space_not_newline?) { space_not_newline.maybe }
|
16
|
+
|
17
|
+
rule(:wsp) { (space | multiline_comment).repeat(1) }
|
12
18
|
rule(:wsp?) { wsp.maybe }
|
13
19
|
rule(:multiline_comment) { (str('/*') >> (str('*/').absent? >> any).repeat >> str('*/') ) }
|
14
20
|
|
15
|
-
rule(:expression) { wsp | namespace | mapping | graph }
|
21
|
+
rule(:expression) { wsp | namespace | mapping | graph | filter | boost }
|
16
22
|
|
17
23
|
rule(:int) { match("\\d+") }
|
18
24
|
|
@@ -23,6 +29,8 @@ module Ldpath
|
|
23
29
|
rule(:assign) { str("=") }
|
24
30
|
rule(:k_prefix) { str("@prefix")}
|
25
31
|
rule(:k_graph) { str("@graph")}
|
32
|
+
rule(:k_filter) { str("@filter")}
|
33
|
+
rule(:k_boost) { str("@boost")}
|
26
34
|
|
27
35
|
rule(:self_op) { str(".") }
|
28
36
|
rule(:and_op) { str("&") }
|
@@ -57,9 +65,7 @@ module Ldpath
|
|
57
65
|
rule(:identifier) { match["a-zA-Z0-9_"] >> (match["a-zA-Z0-9_'\\.-"]).repeat }
|
58
66
|
|
59
67
|
rule(:strlit) {
|
60
|
-
|
61
|
-
str('"') >> (str("\\") >> str("\"") | (str('"').absent? >> any)).repeat.as(:literal) >> str('"') >>
|
62
|
-
wsp?
|
68
|
+
str('"') >> (str("\\") >> str("\"") | (str('"').absent? >> any)).repeat.as(:literal) >> str('"')
|
63
69
|
}
|
64
70
|
|
65
71
|
rule(:node) {
|
@@ -69,24 +75,20 @@ module Ldpath
|
|
69
75
|
# @prefix id = uri ;
|
70
76
|
rule(:namespace) {
|
71
77
|
(
|
72
|
-
wsp? >>
|
73
78
|
k_prefix >> wsp? >>
|
74
79
|
identifier.as(:id) >> wsp? >>
|
75
80
|
colon >> wsp? >>
|
76
|
-
uri.as(:uri) >>
|
77
|
-
scolon.maybe >> wsp?
|
81
|
+
uri.as(:uri) >> space_not_newline? >> scolon.maybe
|
78
82
|
).as(:namespace)
|
79
83
|
}
|
80
84
|
|
81
85
|
# @graph uri, uri, uri ;
|
82
86
|
rule(:graph) {
|
83
87
|
k_graph >> wsp? >>
|
84
|
-
uri_list.as(:graphs) >> wsp? >>
|
85
|
-
scolon
|
88
|
+
uri_list.as(:graphs) >> wsp? >> scolon
|
86
89
|
}
|
87
90
|
|
88
91
|
rule(:uri_list) {
|
89
|
-
wsp? >>
|
90
92
|
uri.as(:uri) >>
|
91
93
|
(
|
92
94
|
wsp? >>
|
@@ -94,22 +96,41 @@ module Ldpath
|
|
94
96
|
uri_list.as(:rest)
|
95
97
|
).repeat
|
96
98
|
}
|
99
|
+
|
100
|
+
# @filter test ;
|
101
|
+
rule(:filter) {
|
102
|
+
(k_filter >> wsp? >> node_test.as(:test) >> wsp? >> scolon).as(:filter)
|
103
|
+
}
|
104
|
+
|
105
|
+
# @boost selector ;
|
106
|
+
rule(:boost) {
|
107
|
+
(k_boost >> wsp? >> selector.as(:selector) >> wsp? >> scolon).as(:boost)
|
108
|
+
}
|
97
109
|
|
98
110
|
# id = . ;
|
99
111
|
rule(:mapping) {
|
100
112
|
(
|
101
|
-
identifier.as(:name) >> wsp? >>
|
113
|
+
(uri_in_brackets | prefix_and_localname | identifier).as(:name) >> wsp? >>
|
102
114
|
assign >> wsp? >>
|
103
|
-
selector.as(:selector) >>
|
104
|
-
(
|
105
|
-
dcolon >> wsp? >>
|
106
|
-
|
107
|
-
).maybe >> wsp? >>
|
108
|
-
scolon
|
115
|
+
selector.as(:selector) >>
|
116
|
+
( wsp? >>
|
117
|
+
dcolon >> wsp? >> field_type
|
118
|
+
).maybe >> wsp? >> scolon
|
109
119
|
).as(:mapping)
|
110
120
|
}
|
111
121
|
|
122
|
+
rule(:field_type) {
|
123
|
+
uri.as(:field_type) >> field_type_options.maybe
|
124
|
+
}
|
125
|
+
|
126
|
+
rule(:field_type_options) {
|
127
|
+
str("(") >> wsp? >> (field_type_option >> (wsp? >> comma >> wsp? >> field_type_option).repeat).as(:options) >> wsp? >> str(")")
|
128
|
+
}
|
112
129
|
|
130
|
+
rule(:field_type_option) {
|
131
|
+
identifier.as(:key) >> wsp? >> assign >> wsp? >> strlit.as(:value)
|
132
|
+
}
|
133
|
+
|
113
134
|
# selector groups
|
114
135
|
rule(:selector) {
|
115
136
|
(
|
@@ -128,11 +149,10 @@ module Ldpath
|
|
128
149
|
}
|
129
150
|
|
130
151
|
rule(:testing_selector) {
|
131
|
-
wsp? >>
|
132
152
|
atomic_selector.as(:delegate) >>
|
133
153
|
str("[") >> wsp? >>
|
134
154
|
node_test.as(:test) >> wsp? >>
|
135
|
-
str("]")
|
155
|
+
str("]")
|
136
156
|
}
|
137
157
|
|
138
158
|
rule(:atomic_selector) {
|
@@ -163,80 +183,70 @@ module Ldpath
|
|
163
183
|
## x / y
|
164
184
|
rule(:path_selector) {
|
165
185
|
(
|
166
|
-
wsp? >>
|
167
186
|
atomic_or_testing_selector.as(:left) >> wsp? >>
|
168
187
|
p_sep >> wsp? >>
|
169
|
-
atomic_or_testing_or_path_selector.as(:right)
|
188
|
+
atomic_or_testing_or_path_selector.as(:right)
|
170
189
|
).as(:path)
|
171
190
|
}
|
172
191
|
|
173
192
|
## x & y
|
174
193
|
rule(:intersection_selector) {
|
175
194
|
(
|
176
|
-
wsp? >>
|
177
195
|
atomic_or_testing_or_path_selector.as(:left) >> wsp? >>
|
178
196
|
and_op >> wsp? >>
|
179
|
-
selector.as(:right)
|
197
|
+
selector.as(:right)
|
180
198
|
).as(:intersection)
|
181
199
|
}
|
182
200
|
|
183
201
|
## x | y
|
184
202
|
rule(:union_selector) {
|
185
203
|
(
|
186
|
-
wsp? >>
|
187
204
|
atomic_or_testing_or_path_selector.as(:left) >> wsp? >>
|
188
205
|
or_op >> wsp? >>
|
189
|
-
selector.as(:right)
|
206
|
+
selector.as(:right)
|
190
207
|
).as(:union)
|
191
208
|
}
|
192
209
|
|
193
210
|
# Atomic Selectors
|
194
211
|
rule(:self_selector) {
|
195
|
-
|
196
|
-
self_op.as(:self) >> wsp?
|
212
|
+
self_op.as(:self)
|
197
213
|
}
|
198
214
|
|
199
215
|
# fn:x() or fn:x(1,2,3)
|
200
216
|
rule(:function_selector) {
|
201
217
|
func >> identifier.as(:fname) >> str("()") |
|
202
|
-
func >> identifier.as(:fname) >> str("(") >> arglist.as(:arglist) >> str(")")
|
218
|
+
func >> identifier.as(:fname) >> str("(") >> wsp? >> arglist.as(:arglist) >> wsp? >> str(")")
|
203
219
|
}
|
204
220
|
|
205
221
|
rule(:arglist) {
|
206
|
-
wsp? >>
|
207
222
|
selector >>
|
208
223
|
(
|
209
224
|
wsp? >>
|
210
225
|
comma >> wsp? >>
|
211
226
|
selector
|
212
|
-
).repeat
|
213
|
-
wsp?
|
227
|
+
).repeat
|
214
228
|
}
|
215
229
|
|
216
230
|
# xyz
|
217
231
|
rule(:loose_property_selector) {
|
218
|
-
wsp? >>
|
219
232
|
loose >>
|
220
233
|
wsp? >>
|
221
|
-
uri.as(:loose_property)
|
234
|
+
uri.as(:loose_property)
|
222
235
|
}
|
223
236
|
|
224
237
|
# xyz
|
225
238
|
rule(:property_selector) {
|
226
|
-
|
227
|
-
uri.as(:property) >> wsp?
|
239
|
+
uri.as(:property)
|
228
240
|
}
|
229
241
|
|
230
242
|
# *
|
231
243
|
rule(:wildcard_selector) {
|
232
|
-
|
233
|
-
star.as(:wildcard) >> wsp?
|
244
|
+
star.as(:wildcard)
|
234
245
|
}
|
235
246
|
|
236
247
|
# ^xyz
|
237
248
|
rule(:reverse_property_selector) {
|
238
|
-
|
239
|
-
inverse >> uri.as(:reverse_property) >> wsp?
|
249
|
+
inverse >> uri.as(:reverse_property)
|
240
250
|
}
|
241
251
|
|
242
252
|
rule(:string_constant_selector) {
|
@@ -246,7 +256,6 @@ module Ldpath
|
|
246
256
|
# (x)*
|
247
257
|
rule(:recursive_path_selector) {
|
248
258
|
(
|
249
|
-
wsp? >>
|
250
259
|
str("(") >> wsp? >>
|
251
260
|
selector.as(:delegate) >> wsp? >>
|
252
261
|
str(")") >>
|
@@ -254,23 +263,21 @@ module Ldpath
|
|
254
263
|
star |
|
255
264
|
plus |
|
256
265
|
(str("{") >> wsp? >> int.as(:min).maybe >> wsp? >>str(",") >> wsp? >> int.as(:max).maybe >> wsp? >>str("}") ).as(:range)
|
257
|
-
).as(:repeat)
|
266
|
+
).as(:repeat)
|
258
267
|
).as(:recursive)
|
259
268
|
}
|
260
269
|
|
261
270
|
rule(:grouped_selector) {
|
262
|
-
wsp? >>
|
263
271
|
str("(") >> wsp? >>
|
264
272
|
selector >> wsp? >>
|
265
|
-
str(")")
|
273
|
+
str(")")
|
266
274
|
}
|
267
275
|
|
268
276
|
rule(:tap_selector) {
|
269
|
-
wsp? >>
|
270
277
|
tap >>
|
271
|
-
str("<") >>
|
272
|
-
identifier.as(:identifier) >>
|
273
|
-
str(">") >>
|
278
|
+
str("<") >> wsp? >>
|
279
|
+
identifier.as(:identifier) >> wsp? >>
|
280
|
+
str(">") >> wsp? >>
|
274
281
|
(atomic_selector).as(:tap)
|
275
282
|
}
|
276
283
|
|
@@ -294,78 +301,67 @@ module Ldpath
|
|
294
301
|
}
|
295
302
|
|
296
303
|
rule(:grouped_test) {
|
297
|
-
wsp? >>
|
298
304
|
str("(") >> wsp? >>
|
299
305
|
node_test >> wsp? >>
|
300
|
-
str(")")
|
306
|
+
str(")")
|
301
307
|
}
|
302
308
|
|
303
309
|
rule(:not_test) {
|
304
310
|
(
|
305
|
-
|
306
|
-
not_op >> node_test.as(:delegate) >>
|
307
|
-
wsp?
|
311
|
+
not_op >> node_test.as(:delegate)
|
308
312
|
).as(:not)
|
309
313
|
}
|
310
314
|
|
311
315
|
rule(:and_test) {
|
312
316
|
(
|
313
|
-
wsp? >>
|
314
317
|
atomic_node_test.as(:left) >> wsp? >>
|
315
318
|
and_op >> wsp? >>
|
316
|
-
node_test.as(:right)
|
319
|
+
node_test.as(:right)
|
317
320
|
).as(:and)
|
318
321
|
}
|
319
322
|
|
320
323
|
rule(:or_test) {
|
321
324
|
(
|
322
|
-
wsp? >>
|
323
325
|
atomic_node_test.as(:left) >> wsp? >>
|
324
326
|
or_op >> wsp? >>
|
325
|
-
node_test.as(:right)
|
327
|
+
node_test.as(:right)
|
326
328
|
).as(:or)
|
327
329
|
}
|
328
330
|
|
329
331
|
# @en
|
330
332
|
rule(:literal_language_test) {
|
331
|
-
|
332
|
-
lang >> identifier.as(:lang) >>
|
333
|
-
wsp?
|
333
|
+
lang >> identifier.as(:lang)
|
334
334
|
}
|
335
335
|
|
336
336
|
# ^^xyz
|
337
337
|
rule(:literal_type_test) {
|
338
|
-
|
339
|
-
type >> uri.as(:type) >>
|
340
|
-
wsp?
|
338
|
+
type >> uri.as(:type)
|
341
339
|
}
|
342
340
|
|
343
341
|
rule(:is_a_test) {
|
344
342
|
(
|
345
|
-
wsp? >>
|
346
343
|
is_a >> wsp? >>
|
347
|
-
node.as(:right)
|
348
|
-
wsp?
|
344
|
+
node.as(:right)
|
349
345
|
).as(:is_a)
|
350
346
|
}
|
351
347
|
|
352
348
|
rule(:path_equality_test) {
|
353
349
|
(
|
354
|
-
wsp? >>
|
355
350
|
selector >> wsp? >>
|
356
351
|
is >> wsp? >>
|
357
|
-
node.as(:right)
|
352
|
+
node.as(:right)
|
358
353
|
).as(:is)
|
359
354
|
}
|
360
355
|
|
361
356
|
rule(:function_test) {
|
362
|
-
wsp? >>
|
363
357
|
(
|
364
358
|
func >> identifier.as(:fname) >> str("()") |
|
365
359
|
func >> identifier.as(:fname) >> str("(") >>
|
360
|
+
wsp? >>
|
366
361
|
arglist.as(:arglist) >>
|
362
|
+
wsp? >>
|
367
363
|
str(")")
|
368
|
-
)
|
364
|
+
)
|
369
365
|
}
|
370
366
|
|
371
367
|
rule(:path_test) {
|
data/lib/ldpath/program.rb
CHANGED
@@ -8,7 +8,7 @@ module Ldpath
|
|
8
8
|
parsed = parser.parse(program)
|
9
9
|
ast = transform.apply parsed, transform_context
|
10
10
|
|
11
|
-
Ldpath::Program.new ast.compact
|
11
|
+
Ldpath::Program.new ast.compact, transform_context
|
12
12
|
end
|
13
13
|
|
14
14
|
private
|
@@ -21,16 +21,19 @@ module Ldpath
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
attr_reader :mappings, :cache, :loaded
|
25
|
-
def initialize mappings,
|
24
|
+
attr_reader :mappings, :cache, :loaded, :prefixes, :filters
|
25
|
+
def initialize mappings, options = {}
|
26
|
+
|
26
27
|
@mappings ||= mappings
|
27
|
-
@cache = cache
|
28
|
+
@cache = options[:cache] || RDF::Util::Cache.new
|
29
|
+
@prefixes = options[:prefixes] || {}
|
30
|
+
@filters = options[:filters] || []
|
28
31
|
@loaded = {}
|
29
32
|
end
|
30
33
|
|
31
34
|
def loading uri, context
|
32
|
-
if uri.to_s =~ /^http/ and !loaded[uri]
|
33
|
-
context << load_graph(uri)
|
35
|
+
if uri.to_s =~ /^http/ and !loaded[uri.to_s]
|
36
|
+
context << load_graph(uri.to_s)
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
@@ -50,6 +53,10 @@ module Ldpath
|
|
50
53
|
h = {}
|
51
54
|
context ||= load_graph(uri.to_s)
|
52
55
|
|
56
|
+
unless filters.empty?
|
57
|
+
return h unless filters.all? { |f| f.evaluate(self, uri, context) }
|
58
|
+
end
|
59
|
+
|
53
60
|
mappings.each do |m|
|
54
61
|
h[m.name] ||= []
|
55
62
|
h[m.name] += case m.selector
|
data/lib/ldpath/selectors.rb
CHANGED
data/lib/ldpath/transform.rb
CHANGED
@@ -21,6 +21,7 @@ module Ldpath
|
|
21
21
|
|
22
22
|
def apply obj, context = nil
|
23
23
|
context ||= { }
|
24
|
+
context[:filters] ||= []
|
24
25
|
context[:prefixes] ||= {}.merge(self.class.default_prefixes)
|
25
26
|
super obj, context
|
26
27
|
end
|
@@ -38,6 +39,17 @@ module Ldpath
|
|
38
39
|
rule(prefix: simple(:prefix), localName: simple(:localName)) do
|
39
40
|
(prefixes[prefix.to_s] || RDF::Vocabulary.new(prefix.to_s))[localName]
|
40
41
|
end
|
42
|
+
|
43
|
+
rule(filter: subtree(:filter)) do
|
44
|
+
filters << filter[:test]
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
rule(boost: subtree(:boost)) do
|
49
|
+
# no-op
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
|
41
53
|
|
42
54
|
# Mappings
|
43
55
|
|
data/lib/ldpath/version.rb
CHANGED
@@ -19,11 +19,9 @@
|
|
19
19
|
|
20
20
|
@graph test:context, foo:ctx, test:bar ;
|
21
21
|
|
22
|
-
/*
|
23
22
|
@filter test:type is foo:bar | test:p1 & is-a test:Case ;
|
24
23
|
|
25
24
|
@boost foo:boost / ^test:boost ;
|
26
|
-
*/
|
27
25
|
|
28
26
|
path = test:p1 / test:p2 :: test:type ;
|
29
27
|
lang_test = test:p1[@en] :: test:type ;
|
@@ -34,8 +32,7 @@ group = (test:p1 / test:p2) :: test:type ;
|
|
34
32
|
|
35
33
|
inverse = ^test:incoming :: test:type ;
|
36
34
|
|
37
|
-
config = test:foo :: test:type ;
|
38
|
-
|
35
|
+
config = test:foo :: test:type(c1="true", c2="false", c3="1.234") ;
|
36
|
+
|
39
37
|
foo:bar = test:foo :: test:type ;
|
40
|
-
<http://test/> = test:test :: test:type ;
|
41
|
-
*/
|
38
|
+
<http://test/> = test:test :: test:type ;
|
data/spec/ldpath_parser_spec.rb
CHANGED
@@ -64,6 +64,11 @@ describe Ldpath::Parser do
|
|
64
64
|
subject.expression.parse "@graph test:context, foo:ctx, test:bar ;"
|
65
65
|
end
|
66
66
|
|
67
|
+
|
68
|
+
it "may be a filter" do
|
69
|
+
subject.expression.parse "@filter is-a test:Context ;"
|
70
|
+
end
|
71
|
+
|
67
72
|
it "may be a mapping" do
|
68
73
|
subject.expression.parse "id = . ;"
|
69
74
|
end
|
@@ -133,7 +138,7 @@ describe Ldpath::Parser do
|
|
133
138
|
end
|
134
139
|
|
135
140
|
it "should parse path mappings" do
|
136
|
-
subject.parse("xyz = info:a / info:b :: a:b
|
141
|
+
subject.mapping.parse("xyz = info:a / info:b :: a:b;")
|
137
142
|
end
|
138
143
|
|
139
144
|
it "recursive_path_selector" do
|
@@ -163,7 +168,7 @@ describe Ldpath::Parser do
|
|
163
168
|
end
|
164
169
|
|
165
170
|
it "should parse the namespaces.ldpath" do
|
166
|
-
subject.
|
171
|
+
subject.parse_with_debug File.read(File.expand_path(File.join(__FILE__, "..", "fixtures", "namespaces.ldpath")))
|
167
172
|
end
|
168
173
|
end
|
169
174
|
end
|
data/spec/ldpath_program_spec.rb
CHANGED
@@ -72,33 +72,114 @@ EOF
|
|
72
72
|
end
|
73
73
|
|
74
74
|
describe "functions" do
|
75
|
-
|
76
|
-
|
77
|
-
Ldpath::Program.parse <<-EOF
|
75
|
+
let(:program) do
|
76
|
+
Ldpath::Program.parse <<-EOF
|
78
77
|
@prefix dcterms : <http://purl.org/dc/terms/> ;
|
79
78
|
ab = fn:concat("a", "b") ;
|
80
79
|
title = fn:concat(dcterms:title, dcterms:description) ;
|
80
|
+
title_mix = fn:concat("!", dcterms:title) ;
|
81
|
+
title_missing = fn:concat("z", dcterms:genre) ;
|
81
82
|
first_a = fn:first("a", "b") ;
|
83
|
+
first_b = fn:first(dcterms:genre, "b") ;
|
84
|
+
last_a = fn:last("a", dcterms:genre) ;
|
82
85
|
last_b = fn:last("a", "b") ;
|
86
|
+
count_5 = fn:count("a", "b", "c", "d", "e");
|
87
|
+
count_3 = fn:count(dcterms:hasPart);
|
88
|
+
count_still_3 = fn:count(dcterms:hasPart, dcterms:genre);
|
89
|
+
eq_true = fn:eq("a", "a");
|
90
|
+
eq_false = fn:eq("a", "b");
|
91
|
+
eq_node_true = fn:eq(dcterms:description, "Description");
|
92
|
+
xpath_test = fn:xpath("//title", "<root><title>xyz</title></root>");
|
83
93
|
EOF
|
94
|
+
end
|
95
|
+
|
96
|
+
let(:object) { RDF::URI.new("info:a") }
|
97
|
+
|
98
|
+
let(:graph) do
|
99
|
+
graph = RDF::Graph.new
|
100
|
+
graph << [object, RDF::DC.title, "Hello, world!"]
|
101
|
+
graph << [object, RDF::DC.description, "Description"]
|
102
|
+
graph << [object, RDF::DC.hasPart, "a"]
|
103
|
+
graph << [object, RDF::DC.hasPart, "b"]
|
104
|
+
graph << [object, RDF::DC.hasPart, "c"]
|
105
|
+
|
106
|
+
graph
|
107
|
+
end
|
108
|
+
|
109
|
+
subject do
|
110
|
+
program.evaluate object, graph
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "concat" do
|
114
|
+
it "should concatenate simple string arguments" do
|
115
|
+
expect(subject).to include "ab" => ["ab"]
|
84
116
|
end
|
85
117
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
graph = RDF::Graph.new
|
90
|
-
graph << [object, RDF::DC.title, "Hello, world!"]
|
91
|
-
graph << [object, RDF::DC.description, "Description"]
|
118
|
+
it "should concatenate node values" do
|
119
|
+
expect(subject).to include "title" => ["Hello, world!Description"]
|
120
|
+
end
|
92
121
|
|
93
|
-
|
122
|
+
it "should allow a mixture of string and node values" do
|
123
|
+
expect(subject).to include "title_mix" => ["!Hello, world!"]
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should ignore missing node values" do
|
127
|
+
expect(subject).to include "title_missing" => ["z"]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "first" do
|
132
|
+
it "should take the first value" do
|
133
|
+
expect(subject).to include "first_a" => ["a"]
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should skip missing values" do
|
137
|
+
expect(subject).to include "first_b" => ["b"]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "last" do
|
142
|
+
it "should take the last value" do
|
143
|
+
expect(subject).to include "last_b" => ["b"]
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should skip missing values" do
|
147
|
+
expect(subject).to include "last_a" => ["a"]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "count" do
|
152
|
+
it "should return the number of arguments" do
|
153
|
+
expect(subject).to include "count_5" => [5]
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should count the number of values for nodes" do
|
157
|
+
expect(subject).to include "count_3" => [3]
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should skip missing nodes" do
|
161
|
+
expect(subject).to include "count_still_3" => [3]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "eq" do
|
166
|
+
it "checks if the arguments match" do
|
167
|
+
expect(subject).to include "eq_true" => [true]
|
168
|
+
end
|
169
|
+
|
170
|
+
it "checks if the arguments fail to match" do
|
171
|
+
expect(subject).to include "eq_false" => [false]
|
172
|
+
end
|
173
|
+
|
174
|
+
it "checks node values" do
|
175
|
+
expect(subject).to include "eq_node_true" => [true]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "xpath" do
|
180
|
+
it "evaluates xpath queries against the string contents" do
|
181
|
+
expect(subject).to include "xpath_test" => ["xyz"]
|
94
182
|
end
|
95
|
-
|
96
|
-
it "should work" do
|
97
|
-
result = subject.evaluate object, graph
|
98
|
-
expect(result["ab"]).to match_array "ab"
|
99
|
-
expect(result["title"]).to match_array "Hello, world!Description"
|
100
|
-
expect(result["first_a"]).to match_array "a"
|
101
|
-
expect(result["last_b"]).to match_array "b"
|
102
183
|
end
|
103
184
|
end
|
104
185
|
|
@@ -192,4 +273,42 @@ title_with_loose = ~dc:title :: xsd:string ;
|
|
192
273
|
expect(result["title_with_loose"]).to eq result["title"]
|
193
274
|
end
|
194
275
|
end
|
276
|
+
|
277
|
+
describe "filter" do
|
278
|
+
|
279
|
+
subject do
|
280
|
+
Ldpath::Program.parse <<-EOF
|
281
|
+
@prefix dcterms : <http://purl.org/dc/terms/> ;
|
282
|
+
@prefix dc: <http://purl.org/dc/elements/1.1/> ;
|
283
|
+
@filter is-a dcterms:Agent ;
|
284
|
+
title = dcterms:title :: xsd:string ;
|
285
|
+
EOF
|
286
|
+
end
|
287
|
+
|
288
|
+
let(:object) { RDF::URI.new("info:a") }
|
289
|
+
let(:other_object) { RDF::URI.new("info:b") }
|
290
|
+
|
291
|
+
|
292
|
+
let(:graph) do
|
293
|
+
graph = RDF::Graph.new
|
294
|
+
|
295
|
+
graph << [object, RDF.type, RDF::DC.Agent]
|
296
|
+
graph << [object, RDF::DC.title, "Title"]
|
297
|
+
graph << [other_object, RDF::DC.title, "Other Title"]
|
298
|
+
|
299
|
+
graph
|
300
|
+
end
|
301
|
+
|
302
|
+
it "should work" do
|
303
|
+
result = subject.evaluate object, graph
|
304
|
+
expect(result["title"]).to eq ["Title"]
|
305
|
+
end
|
306
|
+
|
307
|
+
it "filters objects that don't match" do
|
308
|
+
result = subject.evaluate other_object, graph
|
309
|
+
expect(result).to be_empty
|
310
|
+
end
|
311
|
+
|
312
|
+
|
313
|
+
end
|
195
314
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ldpath
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Beer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parslet
|
@@ -97,7 +97,8 @@ dependencies:
|
|
97
97
|
description:
|
98
98
|
email:
|
99
99
|
- cabeer@stanford.edu
|
100
|
-
executables:
|
100
|
+
executables:
|
101
|
+
- ldpath
|
101
102
|
extensions: []
|
102
103
|
extra_rdoc_files: []
|
103
104
|
files:
|
@@ -108,6 +109,7 @@ files:
|
|
108
109
|
- LICENSE.txt
|
109
110
|
- README.md
|
110
111
|
- Rakefile
|
112
|
+
- bin/ldpath
|
111
113
|
- ldpath.gemspec
|
112
114
|
- lib/ldpath.rb
|
113
115
|
- lib/ldpath/field_mapping.rb
|
@@ -126,7 +128,7 @@ files:
|
|
126
128
|
- spec/ldpath_spec.rb
|
127
129
|
- spec/ldpath_transform_spec.rb
|
128
130
|
- spec/spec_helper.rb
|
129
|
-
homepage:
|
131
|
+
homepage: https://github.com/cbeer/ldpath.rb
|
130
132
|
licenses:
|
131
133
|
- Apache 2
|
132
134
|
metadata: {}
|
@@ -146,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
146
148
|
version: '0'
|
147
149
|
requirements: []
|
148
150
|
rubyforge_project:
|
149
|
-
rubygems_version: 2.4.
|
151
|
+
rubygems_version: 2.4.6
|
150
152
|
signing_key:
|
151
153
|
specification_version: 4
|
152
154
|
summary: Ruby implementation of LDPath
|