tommorris-rena 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.txt ADDED
@@ -0,0 +1,48 @@
1
+ = rena
2
+
3
+ * http://github.com/tommorris/rena
4
+
5
+ == DESCRIPTION:
6
+
7
+ Rena is an RDF library for Ruby.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * Features
12
+
13
+ == SYNOPSIS:
14
+
15
+ Synopsis
16
+
17
+ == REQUIREMENTS:
18
+
19
+ * Addressable gem
20
+
21
+ == INSTALL:
22
+
23
+ * (sudo gem install rena)
24
+
25
+ == LICENSE:
26
+
27
+ (The MIT License)
28
+
29
+ Copyright (c) 2008 Tom Morris
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining
32
+ a copy of this software and associated documentation files (the
33
+ 'Software'), to deal in the Software without restriction, including
34
+ without limitation the rights to use, copy, modify, merge, publish,
35
+ distribute, sublicense, and/or sell copies of the Software, and to
36
+ permit persons to whom the Software is furnished to do so, subject to
37
+ the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be
40
+ included in all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
43
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
45
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
46
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
47
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
48
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'spec/rake/spectask'
4
+
5
+ task :default => [:spec]
6
+
7
+ desc "Install dependencies"
8
+ task :dependencies do
9
+ require ''
10
+ gems = ['addressable/uri']
11
+ gems.each do |g|
12
+ g2 = g.split('/')[0]
13
+ begin
14
+ require g2
15
+ rescue
16
+ sh "sudo gem install " + g2
17
+ end
18
+ end
19
+ end
20
+
21
+ desc "Pushes to git"
22
+ task :push do
23
+ sh "git push --all"
24
+ sh "growlnotify -m \"Updates pushed\" \"Git\""
25
+ end
26
+
27
+ desc "Runs specs"
28
+ task :spec do
29
+ sh "spec --colour --pattern test/spec/*.spec.rb"
30
+ end
31
+
32
+ desc "Turns spec results into HTML and publish to web (Tom only!)"
33
+ task :spec_html do
34
+ sh "spec --pattern test/spec/*.spec.rb --format html:rena_new_spec.html"
35
+ sh "scp rena_new_spec.html bbcityco@bbcity.co.uk:www/tom/files/rena_new_spec.html"
36
+ sh "rm rena_new_spec.html"
37
+ end
38
+
39
+ desc "Turns spec results into local HTML"
40
+ task :spec_local do
41
+ sh "spec --pattern test/spec/*.spec.rb --format html:rena_new_spec.html"
42
+ # sh "open rena_new_spec.html"
43
+ end
44
+
45
+ desc "Run specs through RCov"
46
+ Spec::Rake::SpecTask.new('coverage') do |t|
47
+ t.spec_files = FileList['test/spec/**/*.rb']
48
+ t.rcov = true
49
+ t.rcov_opts = ['--exclude', 'test,\/Library\/Ruby\/Gems\/1.8\/gems']
50
+ end
data/lib/rena/bnode.rb ADDED
@@ -0,0 +1,63 @@
1
+ class BNode
2
+ attr_accessor :identifier
3
+ def initialize(identifier = nil)
4
+ if identifier != nil && self.valid_id?(identifier) != false
5
+ @identifier = identifier
6
+ else
7
+ @identifier = "bn" + self.hash.to_s
8
+ end
9
+ end
10
+
11
+ def eql? (eql)
12
+ if self.identifier == eql.identifier
13
+ true
14
+ else
15
+ false
16
+ end
17
+ end
18
+
19
+ ##
20
+ # Exports the BNode in N-Triples form.
21
+ #
22
+ # ==== Example
23
+ # b = BNode.new; b.to_n3 # => returns a string of the BNode in n3 form
24
+ #
25
+ # ==== Returns
26
+ # @return [String] The BNode in n3.
27
+ #
28
+ # @author Tom Morris
29
+
30
+ def to_n3
31
+ "_:" + @identifier
32
+ end
33
+
34
+
35
+ ##
36
+ # Exports the BNode in N-Triples form.
37
+ #
38
+ # ==== Example
39
+ # b = BNode.new; b.to_ntriples # => returns a string of the BNode in N-Triples form
40
+ #
41
+ # ==== Returns
42
+ # @return [String] The BNode in N-Triples.
43
+ #
44
+ # @author Tom Morris
45
+
46
+ def to_ntriples
47
+ self.to_n3
48
+ end
49
+
50
+ def to_s
51
+ @identifier
52
+ end
53
+
54
+ # TODO: add valid bnode name exceptions?
55
+ protected
56
+ def valid_id? name
57
+ if name =~ /^[a-zA-Z_][a-zA-Z0-9]*$/
58
+ true
59
+ else
60
+ false
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,2 @@
1
+ class AboutEachException
2
+ end
@@ -0,0 +1,2 @@
1
+ class UriRelativeException < RuntimeError
2
+ end
data/lib/rena/graph.rb ADDED
@@ -0,0 +1,150 @@
1
+ require 'rena/namespace'
2
+ require 'rena/bnode'
3
+ require 'rena/uriref'
4
+ require 'rena/literal'
5
+ require 'rena/triple'
6
+
7
+ class Graph
8
+ attr_accessor :triples, :nsbinding
9
+
10
+ def initialize
11
+ @triples = []
12
+ @nsbinding = {}
13
+ end
14
+
15
+ def size
16
+ @triples.size
17
+ end
18
+
19
+ def each
20
+ @triples.each { |value| yield value }
21
+ end
22
+
23
+ def each_with_subject(subject)
24
+ @triples.each {|value|
25
+ if value.subject == subject
26
+ yield value
27
+ end
28
+ }
29
+ end
30
+
31
+ ##
32
+ # Adds a triple to a graph directly from the intended subject, predicate, and object.
33
+ #
34
+ # ==== Example
35
+ # g = Graph.new; g.add_triple(BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new) # => results in the triple being added to g; returns an array of g's triples
36
+ #
37
+ # @param [URIRef, BNode] s the subject of the triple
38
+ # @param [URIRef] p the predicate of the triple
39
+ # @param [URIRef, BNode, Literal, TypedLiteral] o the object of the triple
40
+ #
41
+ # ==== Returns
42
+ # @return [Array] An array of the triples (leaky abstraction? consider returning the graph instead)
43
+ #
44
+ # @raise [Error] Checks parameter types and raises if they are incorrect.
45
+ # @author Tom Morris
46
+
47
+ def add_triple(s, p, o)
48
+ @triples += [ Triple.new(s, p, o) ]
49
+ end
50
+
51
+ ##
52
+ # Adds an extant triple to a graph
53
+ #
54
+ # ==== Example
55
+ # g = Graph.new; t = Triple.new(BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new); g << t) # => results in the triple being added to g; returns an array of g's triples
56
+ #
57
+ # @param [Triple] t the triple to be added to the graph
58
+ #
59
+ # ==== Returns
60
+ # @return [Array] An array of the triples (leaky abstraction? consider returning the graph instead)
61
+ #
62
+ # @author Tom Morris
63
+
64
+
65
+ def << (triple)
66
+ # self.add_triple(s, p, o)
67
+ @triples += [ triple ]
68
+ end
69
+
70
+ ##
71
+ # Exports the graph to RDF in N-Triples form.
72
+ #
73
+ # ==== Example
74
+ # g = Graph.new; g.add_triple(BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new); g.to_ntriples # => returns a string of the graph in N-Triples form
75
+ #
76
+ # ==== Returns
77
+ # @return [String] The graph in N-Triples.
78
+ #
79
+ # @author Tom Morris
80
+
81
+ def to_ntriples
82
+ str = ""
83
+ @triples.each do |t|
84
+ str << t.to_ntriples + "\n"
85
+ end
86
+ return str
87
+ end
88
+
89
+ ##
90
+ # Creates a new namespace given a URI and the short name and binds it to the graph.
91
+ #
92
+ # ==== Example
93
+ # g = Graph.new; g.namespace("http://xmlns.com/foaf/0.1/", "foaf") # => binds the Foaf namespace to g
94
+ #
95
+ # @param [String] uri the URI of the namespace
96
+ # @param [String] short the short name of the namespace
97
+ #
98
+ # ==== Returns
99
+ # @return [Namespace] The newly created namespace.
100
+ #
101
+ # @raise [Error] Checks validity of the desired shortname and raises if it is incorrect.
102
+ # @raise [Error] Checks that the newly created Namespace is of type Namespace and raises if it is incorrect.
103
+ # @author Tom Morris
104
+
105
+ def namespace(uri, short)
106
+ self.bind Namespace.new(uri, short)
107
+ end
108
+
109
+ def bind(namespace)
110
+ if namespace.class == Namespace
111
+ @nsbinding["#{namespace.short}"] = namespace
112
+ else
113
+ raise
114
+ end
115
+ end
116
+
117
+ def has_bnode_identifier?(bnodeid)
118
+ temp_bnode = BNode.new(bnodeid)
119
+ returnval = false
120
+ @triples.each { |triple|
121
+ if triple.subject.eql?(temp_bnode)
122
+ returnval = true
123
+ break
124
+ end
125
+ if triple.object.eql?(temp_bnode)
126
+ returnval = true
127
+ break
128
+ end
129
+ }
130
+ return returnval
131
+ end
132
+
133
+ def get_bnode_by_identifier(bnodeid)
134
+ temp_bnode = BNode.new(bnodeid)
135
+ returnval = false
136
+ @triples.each { |triple|
137
+ if triple.subject.eql?(temp_bnode)
138
+ returnval = triple.subject
139
+ break
140
+ end
141
+ if triple.object.eql?(temp_bnode)
142
+ returnval = triple.object
143
+ break
144
+ end
145
+ }
146
+ return returnval
147
+ end
148
+ # alias :add, :add_triple
149
+ # alias (=+, add_triple)
150
+ end
@@ -0,0 +1,89 @@
1
+ class Literal
2
+ attr_accessor :contents, :lang
3
+ def initialize(contents, lang = nil)
4
+ @contents = contents
5
+ if lang != nil && lang != false
6
+ @lang = lang.downcase
7
+ end
8
+ end
9
+
10
+ def == (obj)
11
+ if obj.class == Literal && obj.contents == @contents && (obj.lang == @lang || (obj.lang == nil && @lang == nil))
12
+ true
13
+ else
14
+ false
15
+ end
16
+ end
17
+
18
+ def to_n3
19
+ out = "\"" + @contents + "\""
20
+ out += "@" + @lang if @lang != nil
21
+ out += "^^" + @encoding if @encoding != nil
22
+ return out
23
+ end
24
+
25
+ def to_ntriples
26
+ return self.to_n3
27
+ end
28
+
29
+ def to_trix
30
+ if @lang != nil && @lang != false
31
+ out = "<plainLiteral xml:lang=\"" + @lang + "\">"
32
+ else
33
+ out = "<plainLiteral>"
34
+ end
35
+ out += @contents
36
+ out += "</plainLiteral>"
37
+ return out
38
+ end
39
+
40
+ end
41
+
42
+ class TypedLiteral < Literal
43
+ attr_accessor :contents, :encoding
44
+ def initialize(contents, encoding)
45
+ @contents = contents
46
+ @encoding = encoding
47
+ if @encoding == "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral"
48
+ @xmlliteral = true
49
+ else
50
+ @xmlliteral = false
51
+ end
52
+ end
53
+
54
+ def == (obj)
55
+ if obj.class == TypedLiteral && obj.contents == @contents && obj.encoding == @encoding
56
+ return true
57
+ else
58
+ return false
59
+ end
60
+ end
61
+
62
+ def to_n3
63
+ if @encoding == "http://www.w3.org/2001/XMLSchema#int"
64
+ out = @contents.to_s
65
+ else
66
+ out = "\"" + @contents.to_s + "\""
67
+ end
68
+ out += "^^<" + @encoding + ">" if @encoding != nil
69
+ return out
70
+ end
71
+
72
+ def to_trix
73
+ "<typedLiteral datatype=\"" + @encoding + "\">" + @contents + "</typedLiteral>"
74
+ end
75
+
76
+ def xmlliteral?
77
+ @xmlliteral
78
+ end
79
+
80
+ def infer!
81
+ if @contents.class == Fixnum
82
+ @encoding = "http://www.w3.org/2001/XMLSchema#int"
83
+ elsif @contents.class == Float
84
+ @encoding = "http://www.w3.org/2001/XMLSchema#float"
85
+ else
86
+ @encoding = "http://www.w3.org/2001/XMLSchema#string"
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,74 @@
1
+ require 'rena/uriref'
2
+ require 'rena/graph'
3
+
4
+ class Namespace
5
+ attr_accessor :short, :uri, :fragment
6
+
7
+ ##
8
+ # Creates a new namespace given a URI and the short name.
9
+ #
10
+ # ==== Example
11
+ # Namespace.new("http://xmlns.com/foaf/0.1/", "foaf") # => returns a new Foaf namespace
12
+ #
13
+ # @param [String] uri the URI of the namespace
14
+ # @param [String] short the short name of the namespace
15
+ # @param [Boolean] fragment are the identifiers on this resource fragment identifiers? (e.g. '#') Defaults to false.
16
+ #
17
+ # ==== Returns
18
+ # @return [Namespace] The newly created namespace.
19
+ #
20
+ # @raise [Error] Checks validity of the desired shortname and raises if it is incorrect.
21
+ # @author Tom Morris, Pius Uzamere
22
+
23
+ def initialize(uri, short, fragment = false)
24
+ @uri = uri
25
+ @fragment = fragment
26
+ if shortname_valid?(short)
27
+ @short = short
28
+ else
29
+ raise
30
+ end
31
+ end
32
+
33
+ ##
34
+ # Allows the construction of arbitrary URIs on the namespace.
35
+ #
36
+ # ==== Example
37
+ # foaf = Namespace.new("http://xmlns.com/foaf/0.1/", "foaf"); foaf.knows # => returns a new URIRef with URI "http://xmlns.com/foaf/0.1/knows"
38
+ # foaf = Namespace.new("http://xmlns.com/foaf/0.1/", "foaf", true); foaf.knows # => returns a new URIRef with URI "http://xmlns.com/foaf/0.1/#knows"
39
+ #
40
+ # @param [String] uri the URI of the namespace
41
+ # @param [String] short the short name of the namespace
42
+ # @param [Boolean] fragment are the identifiers on this resource fragment identifiers? (e.g. '#') Defaults to false.
43
+ #
44
+ # ==== Returns
45
+ # @return [URIRef] The newly created URIRegerence.
46
+ #
47
+ # @raise [Error] Checks validity of the desired shortname and raises if it is incorrect.
48
+ # @author Tom Morris, Pius Uzamere
49
+
50
+ def method_missing(methodname, *args)
51
+ unless fragment
52
+ URIRef.new(@uri + methodname.to_s)
53
+ else
54
+ URIRef.new(@uri + '#' + methodname.to_s)
55
+ end
56
+ end
57
+
58
+ def bind(graph)
59
+ if graph.class == Graph
60
+ graph.bind(self)
61
+ else
62
+ raise
63
+ end
64
+ end
65
+
66
+ private
67
+ def shortname_valid?(shortname)
68
+ if shortname =~ /[a-zA-Z_][a-zA-Z0-9_]+/
69
+ return true
70
+ else
71
+ return false
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,182 @@
1
+ require 'rena/uriref'
2
+ require 'rena/graph'
3
+ require 'rena/literal'
4
+ require 'rena/exceptions/uri_relative_exception'
5
+ require 'rena/exceptions/about_each_exception'
6
+ require 'rena/rexml_hacks'
7
+
8
+ class RdfXmlParser
9
+ attr_accessor :xml, :graph
10
+ def initialize (xml_str, uri = nil)
11
+ @excl = ["http://www.w3.org/1999/02/22-rdf-syntax-ns#resource", "http://www.w3.org/1999/02/22-rdf-syntax-ns#nodeID", "http://www.w3.org/1999/02/22-rdf-syntax-ns#about", "http://www.w3.org/1999/02/22-rdf-syntax-ns#ID"]
12
+ if uri != nil
13
+ @uri = Addressable::URI.parse(uri)
14
+ end
15
+ @xml = REXML::Document.new(xml_str)
16
+ # self.iterator @xml.root.children
17
+ if self.is_rdf?
18
+ @graph = Graph.new
19
+ @xml.root.each_element { |e|
20
+ self.parse_element e
21
+ }
22
+ # puts @graph.size
23
+ end
24
+ end
25
+
26
+ def is_rdf?
27
+ trigger = false
28
+ @xml.each_element do |e|
29
+ if e.namespaces.has_value? "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
30
+ trigger = true
31
+ end
32
+ end
33
+ return trigger
34
+ end
35
+
36
+ protected
37
+ def get_uri_from_atts (element, aboutmode = false)
38
+ if aboutmode == false
39
+ resourceuri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#resource"
40
+ else
41
+ resourceuri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#about"
42
+ end
43
+
44
+ subject = nil
45
+ element.attributes.each_attribute { |att|
46
+ uri = att.namespace + att.name
47
+ value = att.to_s
48
+ if uri == "http://www.w3.org/1999/02/22-rdf-syntax-ns#aboutEach"
49
+ raise AboutEachException, "Failed as per RDFMS-AboutEach-Error001.rdf test from 2004 test suite"
50
+ end
51
+ if uri == "http://www.w3.org/1999/02/22-rdf-syntax-ns#aboutEachPrefix"
52
+ raise AboutEachException, "Failed as per RDFMS-AboutEach-Error002.rdf test from 2004 test suite"
53
+ end
54
+ if uri == "http://www.w3.org/1999/02/22-rdf-syntax-ns#bagID"
55
+ raise
56
+ if name =~ /^[a-zA-Z_][a-zA-Z0-9]*$/
57
+ # TODO: do something intelligent with the bagID
58
+ else
59
+ raise
60
+ end
61
+ end
62
+
63
+ if uri == resourceuri #specified resource
64
+ begin
65
+ possible_subject = URIRef.new(value)
66
+ rescue UriRelativeException
67
+ if value[0..0].to_s != "#"
68
+ value = "#" + value
69
+ end
70
+ begin
71
+ value = URIRef.new(element.base + value)
72
+ rescue UriRelativeException
73
+ # still not a URI
74
+ raise
75
+ else
76
+ subject = value
77
+ end
78
+ else
79
+ subject = possible_subject
80
+ break
81
+ end
82
+ end
83
+
84
+ if uri == "http://www.w3.org/1999/02/22-rdf-syntax-ns#nodeID" #BNode with ID
85
+ # we have a BNode with an identifier. First, we need to do syntax checking.
86
+ if value =~ /^[a-zA-Z_][a-zA-Z0-9]*$/
87
+ # now we check to see if the graph has the value
88
+ if @graph.has_bnode_identifier?(value)
89
+ # if so, pull it in - no need to recreate objects.
90
+ subject = @graph.get_bnode_by_identifier(value)
91
+ else
92
+ # if not, create a new one.
93
+ subject = BNode.new(value)
94
+ end
95
+ end
96
+ end
97
+
98
+ if uri == "http://www.w3.org/1999/02/22-rdf-syntax-ns#ID"
99
+ begin
100
+ # check for base
101
+ if att.element.base?
102
+ subject = att.element.base.to_s + value
103
+ elsif @uri != nil
104
+ compound = @uri.to_s + "#" + value
105
+ subject = compound.to_s
106
+ else
107
+ raise "Needs to have an ID"
108
+ end
109
+ # rescue UriRelativeException
110
+ end
111
+ end
112
+
113
+ # add other subject detection subroutines here
114
+ }
115
+ if subject.class == NilClass
116
+ subject = BNode.new
117
+ end
118
+ return subject
119
+ end
120
+
121
+ protected
122
+ def parse_element (element, subject = nil, resource = false)
123
+ if subject == nil
124
+ # figure out subject
125
+ subject = self.get_uri_from_atts(element, true)
126
+ end
127
+
128
+ # type parsing
129
+ if resource == true
130
+ type = URIRef.new(element.namespace + element.name)
131
+ unless type.to_s == "http://www.w3.org/1999/02/22-rdf-syntax-ns#Description"
132
+ @graph.add_triple(subject, URIRef.new("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), type)
133
+ end
134
+ end
135
+
136
+ # attribute parsing
137
+ element.attributes.each_attribute { |att|
138
+ uri = att.namespace + att.name
139
+ value = att.to_s
140
+
141
+ unless @excl.member? uri
142
+ @graph.add_triple(subject, URIRef.new(uri), Literal.new(value))
143
+ end
144
+ }
145
+
146
+ # element parsing
147
+ element.each_element { |e|
148
+ self.parse_resource_element e, subject
149
+ }
150
+ end
151
+
152
+ def parse_resource_element e, subject
153
+ uri = e.namespace + e.name
154
+ if e.attributes.get_attribute_ns("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "parseType").to_s == "Literal"
155
+ @graph.add_triple(subject, URIRef.new(uri), TypedLiteral.new(e.children.to_s.strip, "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral"))
156
+ elsif e.has_elements?
157
+ # subparsing
158
+ e.each_element { |se| #se = 'striped element'
159
+ if e.attributes.get_attribute_ns("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "parseType").to_s == "Resource"
160
+ object = BNode.new
161
+ else
162
+ object = self.get_uri_from_atts(se, true)
163
+ end
164
+ @graph.add_triple(subject, URIRef.new(uri), object)
165
+ self.parse_element(se, object, true)
166
+ }
167
+ elsif e.attributes.get_attribute_ns("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "datatype")
168
+ @graph.add_triple(subject, URIRef.new(uri), TypedLiteral.new(e.text, e.attributes.get_attribute_ns("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "datatype").to_s.strip))
169
+ elsif e.has_attributes?
170
+ # get object out
171
+ object = self.get_uri_from_atts(e)
172
+ @graph.add_triple(subject, URIRef.new(uri), object)
173
+ elsif e.has_text?
174
+ if e.lang?
175
+ @graph.add_triple(subject, URIRef.new(uri), Literal.new(e.text, e.lang))
176
+ else
177
+ @graph.add_triple(subject, URIRef.new(uri), Literal.new(e.text))
178
+ end
179
+ end
180
+ end
181
+
182
+ end