tommorris-rena 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,70 @@
1
+ require 'rexml/document'
2
+
3
+ # def subdocument_writer(el)
4
+ # el.prefixes.each { |ns|
5
+ # el.add_attribute('xmlns:' + ns, el.namespaces[ns].to_s)
6
+ # }
7
+ # return el.to_s
8
+ # end
9
+
10
+ class REXML::Element
11
+ public
12
+ def lang?
13
+ if self.lang != nil
14
+ true
15
+ else
16
+ false
17
+ end
18
+ end
19
+ def lang
20
+ if self.attributes['xml:lang']
21
+ return self.attributes['xml:lang'].to_s
22
+ elsif self.parent != nil
23
+ return self.parent.lang
24
+ else
25
+ return nil
26
+ end
27
+ end
28
+
29
+ def base?
30
+ if self.base != nil
31
+ true
32
+ else
33
+ false
34
+ end
35
+ end
36
+
37
+ def base
38
+ if self.attributes['xml:base']
39
+ return self.attributes['xml:base'].to_s
40
+ elsif self.parent != nil
41
+ return self.parent.base
42
+ else
43
+ return nil
44
+ end
45
+ end
46
+
47
+ def write(excl=[])
48
+ # TODO: add optional list argument of excluded namespaces
49
+ self.prefixes.each { |ns|
50
+ self.add_attribute('xmlns:' + ns, self.namespaces[ns].to_s) unless excl.include? self.namespaces[ns]
51
+ }
52
+ self.support_write_recursive(self.namespaces, self)
53
+ return self.to_s
54
+ end
55
+
56
+ protected
57
+ def support_write_recursive(array, el)
58
+ el.each_element { |e|
59
+ unless array.has_key?(e.prefix) && array.has_value?(e.namespace)
60
+ if e.prefix != ""
61
+ e.add_attribute('xmlns:' + e.prefix, e.namespace)
62
+ else
63
+ e.add_attribute('xmlns', e.namespace)
64
+ end
65
+ end
66
+ self.support_write_recursive(array, e)
67
+ }
68
+ end
69
+
70
+ end
@@ -0,0 +1,72 @@
1
+ class Triple
2
+ attr_accessor :subject, :object, :predicate
3
+
4
+ ##
5
+ # Creates a new triple directly from the intended subject, predicate, and object.
6
+ #
7
+ # ==== Example
8
+ # Triple.new(BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new) # => results in the creation of a new triple and returns it
9
+ #
10
+ # @param [URIRef, BNode] s the subject of the triple
11
+ # @param [URIRef] p the predicate of the triple
12
+ # @param [URIRef, BNode, Literal, TypedLiteral] o the object of the triple
13
+ #
14
+ # ==== Returns
15
+ #
16
+ # @return [Triple] An array of the triples (leaky abstraction? consider returning the graph instead)
17
+ #
18
+ # @raise [Error] Checks parameter types and raises if they are incorrect.
19
+ # @author Tom Morris
20
+ def initialize (subject, predicate, object)
21
+ self.check_subject(subject)
22
+ self.check_predicate(predicate)
23
+ self.check_object(object)
24
+ end
25
+
26
+ def to_ntriples
27
+ @subject.to_ntriples + " " + @predicate.to_ntriples + " " + @object.to_ntriples + " ."
28
+ end
29
+
30
+ protected
31
+ def check_subject(subject)
32
+ if subject.class == BNode || subject.class == URIRef
33
+ @subject = subject
34
+ elsif subject.class == String
35
+ if subject =~ /\S+\/\/\S+/ # does it smell like a URI?
36
+ @subject = URIRef.new(subject)
37
+ else
38
+ @subject = BNode.new(subject)
39
+ end
40
+ else
41
+ raise "Subject is not of a known class"
42
+ end
43
+ end
44
+
45
+ protected
46
+ def check_predicate(predicate)
47
+ if predicate.class == URIRef
48
+ @predicate = predicate
49
+ elsif predicate.class == BNode
50
+ raise "BNode is not allowed as a predicate"
51
+ elsif predicate.class == String
52
+ if predicate =~ /\S+\/\/\S+/ # URI smell check again
53
+ @predicate = URIRef.new(predicate)
54
+ else
55
+ raise "String literals are not acceptable as predicates"
56
+ end
57
+ else
58
+ raise "Predicate should be a uriref"
59
+ end
60
+ end
61
+
62
+ protected
63
+ def check_object(object)
64
+ if [String, Integer, Fixnum, Float].include? object.class
65
+ @object = Literal.new(object.to_s)
66
+ elsif [URIRef, BNode, Literal, TypedLiteral].include? object.class
67
+ @object = object
68
+ else
69
+ raise "Object expects valid class"
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ require 'addressable/uri'
3
+ require 'rena/exceptions/uri_relative_exception'
4
+
5
+ class URIRef
6
+ attr_accessor :uri
7
+ def initialize (string)
8
+ self.test_string(string)
9
+ @uri = Addressable::URI.parse(string)
10
+ if @uri.relative?
11
+ raise UriRelativeException, "<" + @uri.to_s + ">"
12
+ end
13
+ if !@uri.to_s.match(/^javascript/).nil?
14
+ raise "Javascript pseudo-URIs are not acceptable"
15
+ end
16
+ end
17
+
18
+ def == (other)
19
+ return true if @uri == other.uri
20
+ end
21
+
22
+ def to_s
23
+ @uri.to_s
24
+ end
25
+
26
+ def to_ntriples
27
+ "<" + @uri.to_s + ">"
28
+ end
29
+
30
+ def test_string (string)
31
+ if string.class != String
32
+ string = string.to_s
33
+ end
34
+
35
+ string.each_byte do |b|
36
+ if b >= 0 and b <= 31
37
+ raise "URI must not contain control characters"
38
+ end
39
+ end
40
+ end
41
+ end
data/lib/rena.rb ADDED
@@ -0,0 +1,5 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ Dir.glob(File.join(File.dirname(__FILE__), 'rena/**.rb')).each { |f| require f }
3
+
4
+ module Rena
5
+ end
data/rena.gemspec ADDED
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "rena"
3
+ s.version = "0.0.1"
4
+ s.date = "2008-07-13"
5
+ s.summary = "Ruby RDF library."
6
+ s.email = "tom@tommorris.org"
7
+ s.homepage = "http://github.com/tommorris/rena"
8
+ s.description = "Rena is a Ruby library for manipulating RDF files."
9
+ s.has_rdoc = true
10
+ s.authors = ['Tom Morris', 'Pius Uzamere']
11
+ s.files = ["README.txt", "Rakefile", "rena.gemspec", "lib/rena.rb", "lib/rena/bnode.rb", "lib/rena/graph.rb", "lib/rena/literal.rb", "lib/rena/namespace.rb", "lib/rena/rdfxmlparser.rb", "lib/rena/rexml_hacks.rb", "lib/rena/triple.rb", "lib/rena/uriref.rb", "lib/rena/exceptions/about_each_exception.rb", "lib/rena/exceptions/uri_relative_exception.rb"]
12
+ s.test_files = ["test/test_uris.rb", "test/xml.rdf", "test/spec/bnode.spec.rb", "test/spec/graph.spec.rb", "test/spec/literal.spec.rb", "test/spec/namespaces.spec.rb", "test/spec/parser.spec.rb", "test/spec/rexml_hacks.spec.rb", "test/spec/triple.spec.rb", "test/spec/uriref.spec.rb"]
13
+ #s.rdoc_options = ["--main", "README.txt"]
14
+ #s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
15
+ s.add_dependency("addressable", ["> 0.0.1"])
16
+ end
@@ -0,0 +1,25 @@
1
+ require 'lib/rena'
2
+ describe "Blank nodes" do
3
+ it "should accept a custom identifier" do
4
+ b = BNode.new('foo')
5
+ b.identifier.should == "foo"
6
+ b.to_s.should == "foo"
7
+ end
8
+
9
+ it "should reject custom identifiers if they are not acceptable" do
10
+ b = BNode.new("4cake")
11
+ b.identifier.should_not == "4cake"
12
+ end
13
+
14
+ it "should be expressible in N3 and NT syntax" do
15
+ b = BNode.new('test')
16
+ b.to_n3.should == "_:test"
17
+ b.to_ntriples.should == b.to_n3
18
+ end
19
+
20
+ it "should be able to determine equality" do
21
+ a = BNode.new('a')
22
+ a2 = BNode.new('a')
23
+ a.eql?(a2).should == true
24
+ end
25
+ end
@@ -0,0 +1,108 @@
1
+ require 'lib/rena'
2
+
3
+ describe "Graphs" do
4
+ it "should allow you to add one or more triples" do
5
+ lambda do
6
+ f = Graph.new
7
+ f.add_triple(BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new)
8
+ end.should_not raise_error
9
+ end
10
+
11
+ it "should tell you how large the graph is" do
12
+ f = Graph.new
13
+ 5.times do
14
+ f.add_triple BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new
15
+ end
16
+ f.size.should == 5
17
+ end
18
+
19
+ it "should support << as an alias for add_triple" do
20
+ lambda do
21
+ f = Graph.new
22
+ f << Triple.new(BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new)
23
+ end.should_not raise_error
24
+ end
25
+
26
+ it "should output NTriple" do
27
+ f = Graph.new
28
+ ex = Namespace.new("http://example.org/", "ex")
29
+ foaf = Namespace.new("http://xmlns.com/foaf/0.1/", "foaf")
30
+ f << Triple.new(ex.john, foaf.knows, ex.jane)
31
+ f << Triple.new(ex.jane, foaf.knows, ex.rick)
32
+ f << Triple.new(ex.rick, foaf.knows, ex.john)
33
+ nt = "<http://example.org/john> <http://xmlns.com/foaf/0.1/knows> <http://example.org/jane> .\n<http://example.org/jane> <http://xmlns.com/foaf/0.1/knows> <http://example.org/rick> .\n<http://example.org/rick> <http://xmlns.com/foaf/0.1/knows> <http://example.org/john> .\n"
34
+ f.to_ntriples.should == nt
35
+ end
36
+
37
+ it "should allow iteration" do
38
+ f = Graph.new
39
+ ex = Namespace.new("http://example.org/", "ex")
40
+ foaf = Namespace.new("http://xmlns.com/foaf/0.1/", "foaf")
41
+ f << Triple.new(ex.john, foaf.knows, ex.jane)
42
+ f << Triple.new(ex.jane, foaf.knows, ex.rick)
43
+ f << Triple.new(ex.rick, foaf.knows, ex.john)
44
+ count = 0
45
+ f.each do |t|
46
+ count = count + 1
47
+ t.class.should == Triple
48
+ end
49
+ count.should == 3
50
+ end
51
+
52
+ it "should allow iteration over a particular subject" do
53
+ f = Graph.new
54
+ ex = Namespace.new("http://example.org/", "ex")
55
+ foaf = Namespace.new("http://xmlns.com/foaf/0.1/", "foaf")
56
+ f << Triple.new(ex.john, foaf.knows, ex.jane)
57
+ f << Triple.new(ex.jane, foaf.knows, ex.rick)
58
+ f << Triple.new(ex.rick, foaf.knows, ex.john)
59
+ count = 0
60
+ f.each_with_subject(ex.john) do |t|
61
+ count = count + 1
62
+ t.class.should == Triple
63
+ end
64
+ count.should == 1
65
+ end
66
+
67
+ it "should be able to determine whether or not it has existing BNodes" do
68
+ f = Graph.new
69
+ foaf = Namespace.new("http://xmlns.com/foaf/0.1/", "foaf")
70
+ f << Triple.new(BNode.new('john'), foaf.knows, BNode.new('jane'))
71
+ f.has_bnode_identifier?('john').should == true
72
+ f.has_bnode_identifier?('jane').should == true
73
+ f.has_bnode_identifier?('jack').should == false
74
+ end
75
+
76
+ it "should be able to return BNodes on demand" do
77
+ f = Graph.new
78
+ john = BNode.new('john')
79
+ jane = BNode.new('jane')
80
+ foaf = Namespace.new("http://xmlns.com/foaf/0.1/", "foaf")
81
+ f << Triple.new(john, foaf.knows, jane)
82
+ f.get_bnode_by_identifier('john').should == john
83
+ f.get_bnode_by_identifier('jane').should == jane
84
+ end
85
+
86
+ it "should allow you to create and bind Namespace objects on-the-fly" do
87
+ f = Graph.new
88
+ f.namespace("http://xmlns.com/foaf/0.1/", "foaf")
89
+ f.nsbinding["foaf"].uri.should == "http://xmlns.com/foaf/0.1/"
90
+ end
91
+
92
+ it "should not allow you to bind things other than namespaces" do
93
+ lambda do
94
+ f = Graph.new
95
+ f.bind(false)
96
+ end.should raise_error
97
+ end
98
+
99
+ it "should have an error log for parsing errors" do
100
+ pending "TODO: implement an error log at the graph level"
101
+ end
102
+
103
+ it "should follow the specification as to output identical triples" do
104
+ pending
105
+ end
106
+
107
+ it "should be able to integrate another graph"
108
+ end
@@ -0,0 +1,112 @@
1
+ require 'lib/rena'
2
+
3
+ describe "Literals" do
4
+ it "accept a language tag" do
5
+ f = Literal.new("tom", "en")
6
+ f.lang.should == "en"
7
+ end
8
+
9
+ it "accepts an encoding" do
10
+ f = TypedLiteral.new("tom", "http://www.w3.org/2001/XMLSchema#string")
11
+ f.encoding.should == "http://www.w3.org/2001/XMLSchema#string"
12
+ end
13
+
14
+ it "should be equal if they have the same contents" do
15
+ f = Literal.new("tom")
16
+ g = Literal.new("tom")
17
+ f.should == g
18
+ end
19
+
20
+ it "should not be equal if they do not have the same contents" do
21
+ f = Literal.new("tom")
22
+ g = Literal.new("tim")
23
+ f.should_not == g
24
+ end
25
+
26
+ it "should be equal if they have the same contents and language" do
27
+ f = Literal.new("tom", "en")
28
+ g = Literal.new("tom", "en")
29
+ f.should == g
30
+ end
31
+
32
+ it "should not be equal if they do not have the same contents and language" do
33
+ f = Literal.new("tom", "en")
34
+ g = Literal.new("tim", "en")
35
+ f.should_not == g
36
+
37
+ lf = Literal.new("tom", "en")
38
+ lg = Literal.new("tom", "fr")
39
+ lf.should_not == lg
40
+ end
41
+
42
+ it "should be equal if they have the same contents and datatype" do
43
+ f = TypedLiteral.new("tom", "http://www.w3.org/2001/XMLSchema#string")
44
+ g = TypedLiteral.new("tom", "http://www.w3.org/2001/XMLSchema#string")
45
+ f.should == g
46
+ end
47
+
48
+ it "should not be equal if they do not have the same contents and datatype" do
49
+ f = TypedLiteral.new("tom", "http://www.w3.org/2001/XMLSchema#string")
50
+ g = TypedLiteral.new("tim", "http://www.w3.org/2001/XMLSchema#string")
51
+ f.should_not == g
52
+
53
+ dtf = TypedLiteral.new("tom", "http://www.w3.org/2001/XMLSchema#string")
54
+ dtg = TypedLiteral.new("tom", "http://www.w3.org/2001/XMLSchema#token")
55
+ dtf.should_not == dtg
56
+ end
57
+
58
+ it "should return valid N3/NTriples format strings" do
59
+ f = Literal.new("tom")
60
+ f.to_n3.should == "\"tom\""
61
+ f.to_ntriples.should == f.to_n3
62
+
63
+ g = Literal.new("tom", "en")
64
+ g.to_n3.should == "\"tom\"@en"
65
+ f.to_ntriples.should == f.to_n3
66
+
67
+ typed_int = TypedLiteral.new(5, "http://www.w3.org/2001/XMLSchema#int")
68
+ typed_int.to_n3.should == "5^^<http://www.w3.org/2001/XMLSchema#int>"
69
+ typed_int.to_ntriples.should == typed_int.to_n3
70
+
71
+ typed_string = TypedLiteral.new("foo", "http://www.w3.org/2001/XMLSchema#string")
72
+ typed_string.to_n3.should == "\"foo\"^^<http://www.w3.org/2001/XMLSchema#string>"
73
+ typed_string.to_ntriples.should == typed_string.to_n3
74
+ end
75
+
76
+ it "should normalize language tags to lower case" do
77
+ f = Literal.new("tom", "EN")
78
+ f.lang.should == "en"
79
+ end
80
+
81
+ it "should support TriX encoding" do
82
+ e = Literal.new("tom")
83
+ e.to_trix.should == "<plainLiteral>tom</plainLiteral>"
84
+
85
+ f = Literal.new("tom", "en")
86
+ f.to_trix.should == "<plainLiteral xml:lang=\"en\">tom</plainLiteral>"
87
+
88
+ g = TypedLiteral.new("tom", "http://www.w3.org/2001/XMLSchema#string")
89
+ g.to_trix.should == "<typedLiteral datatype=\"http://www.w3.org/2001/XMLSchema#string\">tom</typedLiteral>"
90
+ end
91
+
92
+ it "should handle XML litearls" do
93
+ # first we should detect XML literals and mark them as such in the class
94
+ f = TypedLiteral.new("foo <sup>bar</sup> baz!", "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral")
95
+ f.xmlliteral?.should == true
96
+ # pending "TODO: the thought of XML literals makes me want to wretch"
97
+ end
98
+
99
+ it "should be able to infer!" do
100
+ int = TypedLiteral.new(15, nil)
101
+ int.infer!
102
+ int.encoding.should == "http://www.w3.org/2001/XMLSchema#int"
103
+
104
+ float = TypedLiteral.new(15.4, nil)
105
+ float.infer!
106
+ float.encoding.should == "http://www.w3.org/2001/XMLSchema#float"
107
+
108
+ other = TypedLiteral.new("foo", nil)
109
+ other.infer!
110
+ other.encoding.should == "http://www.w3.org/2001/XMLSchema#string"
111
+ end
112
+ end
@@ -0,0 +1,44 @@
1
+ require 'lib/rena'
2
+
3
+ describe "Namespaces" do
4
+ it "should use method_missing to create URIRefs on the fly" do
5
+ foaf = Namespace.new("http://xmlns.com/foaf/0.1/", "foaf")
6
+ foaf.knows.to_s.should == "http://xmlns.com/foaf/0.1/knows"
7
+
8
+ foaf_frag = Namespace.new("http://xmlns.com/foaf/0.1/", "foaf", true)
9
+ foaf_frag.knows.to_s.should == "http://xmlns.com/foaf/0.1/#knows"
10
+ end
11
+
12
+ it "should have a URI" do
13
+ lambda do
14
+ test = Namespace.new(short='foaf')
15
+ end.should raise_error
16
+ end
17
+
18
+ it "should have equality with URIRefs" do
19
+ foaf = Namespace.new("http://xmlns.com/foaf/0.1/", "foaf")
20
+ foaf_name = URIRef.new("http://xmlns.com/foaf/0.1/name")
21
+ foaf.name.should == foaf_name
22
+ end
23
+
24
+ it "should have an XML and N3-friendly prefix" do
25
+ lambda do
26
+ test = Namespace.new('http://xmlns.com/foaf/0.1/', '*~{')
27
+ end.should raise_error
28
+ end
29
+
30
+ it "should be able to attach to the graph for substitution" do
31
+ # rdflib does this using graph.bind('prefix', namespace)
32
+ g = Graph.new
33
+ foaf = Namespace.new("http://xmlns.com/foaf/0.1/", "foaf")
34
+ foaf.bind(g)
35
+ g.nsbinding["foaf"].should == foaf
36
+ end
37
+
38
+ it "should not allow you to attach to non-graphs" do
39
+ lambda do
40
+ foaf = Namespace.new("http://xmlns.com/foaf/0.1/", "foaf")
41
+ foaf.bind("cheese")
42
+ end.should raise_error
43
+ end
44
+ end