ldpath 0.0.0 → 0.0.1

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.
@@ -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