ldpath 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Ldpath
2
- VERSION = "0.0.0"
2
+ VERSION = "0.0.1"
3
3
  end
@@ -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