tommorris-rena 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.
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