ldpath 0.0.2 → 0.1.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/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
|