metade-rena 0.0.2

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,97 @@
1
+ require 'rexml/document'
2
+
3
+ # @ignore
4
+ # def subdocument_writer(el)
5
+ # el.prefixes.each { |ns|
6
+ # el.add_attribute('xmlns:' + ns, el.namespaces[ns].to_s)
7
+ # }
8
+ # return el.to_s
9
+ # end
10
+
11
+ class REXML::Element
12
+ public
13
+
14
+ ##
15
+ # Tells you whether or not an element has a set xml:lang.
16
+ #
17
+ # @author Tom Morris
18
+ def lang?
19
+ self.lang.nil? ? false : true
20
+ end
21
+
22
+ ##
23
+ # Tells you what the set xml:lang is for an element.
24
+ #
25
+ # ==== Returns
26
+ # @return [String] The URI of the xml:lang.
27
+ #
28
+ # @author Tom Morris
29
+ def lang
30
+ if self.attributes['xml:lang']
31
+ return self.attributes['xml:lang'].to_s
32
+ elsif self.parent != nil
33
+ return self.parent.lang
34
+ else
35
+ return nil
36
+ end
37
+ end
38
+
39
+ ##
40
+ # Tells you whether or not an element has a set xml:base.
41
+ #
42
+ # @author Tom Morris
43
+ def base?
44
+ if self.base != nil
45
+ true
46
+ else
47
+ false
48
+ end
49
+ end
50
+
51
+ ##
52
+ # Tells you what the set xml:lang is for an element.
53
+ #
54
+ # ==== Returns
55
+ # @return [String] The URI of the xml:base.
56
+ #
57
+ # @author Tom Morris
58
+ def base
59
+ if self.attributes['xml:base']
60
+ return self.attributes['xml:base'].to_s
61
+ elsif self.parent != nil
62
+ return self.parent.base
63
+ else
64
+ return nil
65
+ end
66
+ end
67
+
68
+ ##
69
+ # Allows you to write out an XML representation of a particular element and it's children, fixing namespace issues.
70
+ #
71
+ # ==== Returns
72
+ # @return [String] The XML of the element and it's children.
73
+ #
74
+ # @author Tom Morris
75
+ def write_rena(excl=[])
76
+ # TODO: add optional list argument of excluded namespaces
77
+ self.prefixes.each { |ns|
78
+ self.add_attribute('xmlns:' + ns, self.namespaces[ns].to_s) unless excl.include? self.namespaces[ns]
79
+ }
80
+ self.support_write_recursive(self.namespaces, self)
81
+ return self.to_s
82
+ end
83
+
84
+ protected
85
+ def support_write_recursive(array, el)
86
+ el.each_element { |e|
87
+ unless array.has_key?(e.prefix) && array.has_value?(e.namespace)
88
+ if e.prefix != ""
89
+ e.add_attribute('xmlns:' + e.prefix, e.namespace)
90
+ else
91
+ e.add_attribute('xmlns', e.namespace)
92
+ end
93
+ end
94
+ self.support_write_recursive(array, e)
95
+ }
96
+ end
97
+ end
@@ -0,0 +1,89 @@
1
+ module Rena
2
+ class Triple
3
+ class InvalidPredicate < StandardError
4
+ end
5
+
6
+ class InvalidSubject < StandardError
7
+ end
8
+
9
+ class InvalidObject < StandardError
10
+ end
11
+
12
+ attr_accessor :subject, :object, :predicate
13
+
14
+ ##
15
+ # Creates a new triple directly from the intended subject, predicate, and object.
16
+ #
17
+ # ==== Example
18
+ # 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
19
+ #
20
+ # @param [URIRef, BNode] s the subject of the triple
21
+ # @param [URIRef] p the predicate of the triple
22
+ # @param [URIRef, BNode, Literal, TypedLiteral] o the object of the triple
23
+ #
24
+ # ==== Returns
25
+ #
26
+ # @return [Triple] An array of the triples (leaky abstraction? consider returning the graph instead)
27
+ #
28
+ # @raise [Error] Checks parameter types and raises if they are incorrect.
29
+ # @author Tom Morris
30
+ def initialize (subject, predicate, object)
31
+ @subject = self.class.coerce_subject(subject)
32
+ @predicate = self.class.coerce_predicate(predicate)
33
+ @object = self.class.coerce_object(object)
34
+ end
35
+
36
+ def to_ntriples
37
+ @subject.to_ntriples + " " + @predicate.to_ntriples + " " + @object.to_ntriples + " ."
38
+ end
39
+
40
+ def inspect
41
+ [@subject, @predicate, @object].inspect
42
+ end
43
+
44
+ def is_type?
45
+ @predicate.to_s == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
46
+ end
47
+
48
+ protected
49
+
50
+ def self.coerce_subject(subject)
51
+ case subject
52
+ when URIRef, BNode
53
+ subject
54
+ when String
55
+ if subject =~ /\S+\/\/\S+/ # does it smell like a URI?
56
+ URIRef.new(subject)
57
+ else
58
+ BNode.new(subject)
59
+ end
60
+ else
61
+ raise InvalidSubject, "Subject is not of a known class"
62
+ end
63
+ end
64
+
65
+ def self.coerce_predicate(uri_or_string)
66
+ case uri_or_string
67
+ when URIRef
68
+ uri_or_string
69
+ when String
70
+ URIRef.new uri_or_string
71
+ else
72
+ raise InvalidPredicate, "Predicate should be a URI"
73
+ end
74
+ rescue UriRelativeException => e
75
+ raise InvalidPredicate, "Couldn't make a URIRef: #{e.message}"
76
+ end
77
+
78
+ def self.coerce_object(object)
79
+ case object
80
+ when String, Integer, Float
81
+ Literal.build_from(object)
82
+ when URIRef, BNode, Literal
83
+ object
84
+ else
85
+ raise InvalidObject, "#{object.inspect} is not a valid object"
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'addressable/uri'
3
+ require 'rena/exceptions/uri_relative_exception'
4
+ require 'net/http'
5
+
6
+ module Rena
7
+ class URIRef
8
+ attr_accessor :uri
9
+ def initialize (string)
10
+ self.test_string(string)
11
+ @uri = Addressable::URI.parse(string)
12
+ if @uri.relative?
13
+ raise UriRelativeException, "<" + @uri.to_s + ">"
14
+ end
15
+ if !@uri.to_s.match(/^javascript/).nil?
16
+ raise "Javascript pseudo-URIs are not acceptable"
17
+ end
18
+ end
19
+
20
+ def short_name
21
+ if @uri.fragment()
22
+ return @uri.fragment()
23
+ elsif @uri.path.split("/").last.class == String and @uri.path.split("/").last.length > 0
24
+ return @uri.path.split("/").last
25
+ else
26
+ return false
27
+ end
28
+ end
29
+
30
+ def == (other)
31
+ return true if @uri == other.uri
32
+ end
33
+
34
+ def to_s
35
+ @uri.to_s
36
+ end
37
+
38
+ def to_ntriples
39
+ "<" + @uri.to_s + ">"
40
+ end
41
+
42
+ def test_string (string)
43
+ string.to_s.each_byte do |b|
44
+ if b >= 0 and b <= 31
45
+ raise "URI must not contain control characters"
46
+ end
47
+ end
48
+ end
49
+
50
+ def load_graph
51
+ get = Net::HTTP.start(@uri.host, @uri.port) {|http| [:xml, http.get(@uri.path)] }
52
+ return Rena::RdfXmlParser.new(get[1].body, @uri.to_s).graph if get[0] == :xml
53
+ end
54
+ end
55
+ 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,17 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "rena"
3
+ s.version = "0.0.2"
4
+ s.date = "2008-10-5"
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', 'Patrick Sinclair']
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/n3parser.rb", "lib/rena/n3_grammar.treetop", "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", "spec/bnode_spec.rb", "spec/graph_spec.rb", "spec/literal_spec.rb", "spec/namespaces_spec.rb", "spec/parser_spec.rb", "spec/rexml_hacks_spec.rb", "spec/triple_spec.rb", "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", [">= 1.0.4"])
16
+ s.add_dependency("treetop", [">= 1.2.4"])
17
+ end
@@ -0,0 +1,29 @@
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 be_true
24
+
25
+ a3 = URIRef.new('http://somehost.com/wherever.xml')
26
+ a.eql?(a3).should be_false
27
+ end
28
+
29
+ end
@@ -0,0 +1,127 @@
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> ."
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 follow the specification as to output identical triples" do
100
+ pending
101
+ end
102
+
103
+ it "should be able to integrate another graph" do
104
+ f = Graph.new
105
+ f.add_triple(BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new)
106
+ g = Graph.new
107
+ g.join(f)
108
+ g.size.should == 1
109
+
110
+ h = Graph.new
111
+ lambda do
112
+ h.join("")
113
+ end.should raise_error
114
+ end
115
+
116
+ it "should give you a list of resources of a particular type" do
117
+ f = Graph.new
118
+ person = URIRef.new("http://xmlns.com/foaf/0.1/Person")
119
+ f.add_triple(URIRef.new("http://example.org/joe"), URIRef.new("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), URIRef.new("http://xmlns.com/foaf/0.1/Person"))
120
+ f.add_triple(URIRef.new("http://example.org/jane"), URIRef.new("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), URIRef.new("http://xmlns.com/foaf/0.1/Person"))
121
+ f.size.should == 2
122
+
123
+ f.get_by_type("http://xmlns.com/foaf/0.1/Person").size.should == 2
124
+ f.get_by_type("http://xmlns.com/foaf/0.1/Person")[0].to_s.should == "http://example.org/joe"
125
+ f.get_by_type("http://xmlns.com/foaf/0.1/Person")[1].to_s.should == "http://example.org/jane"
126
+ end
127
+ end
@@ -0,0 +1,136 @@
1
+ require 'lib/rena'
2
+
3
+ describe "Literals" do
4
+ it "accept a language tag" do
5
+ f = Literal.untyped("tom", "en")
6
+ f.lang.should == "en"
7
+ end
8
+
9
+ it "accepts an encoding" do
10
+ f = Literal.typed("tom", "http://www.w3.org/2001/XMLSchema#string")
11
+ f.encoding.to_s.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.untyped("tom")
16
+ g = Literal.untyped("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.untyped("tom")
22
+ g = Literal.untyped("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.untyped("tom", "en")
28
+ g = Literal.untyped("tom", "en")
29
+ f.should == g
30
+ end
31
+
32
+ it "should return a string using to_s" do
33
+ f = Literal.untyped("tom")
34
+ f.to_s.should == "tom"
35
+ end
36
+
37
+ it "should not be equal if they do not have the same contents and language" do
38
+ f = Literal.untyped("tom", "en")
39
+ g = Literal.untyped("tim", "en")
40
+ f.should_not == g
41
+
42
+ lf = Literal.untyped("tom", "en")
43
+ lg = Literal.untyped("tom", "fr")
44
+ lf.should_not == lg
45
+ end
46
+
47
+ it "should be equal if they have the same contents and datatype" do
48
+ f = Literal.typed("tom", "http://www.w3.org/2001/XMLSchema#string")
49
+ g = Literal.typed("tom", "http://www.w3.org/2001/XMLSchema#string")
50
+ f.should == g
51
+ end
52
+
53
+ it "should not be equal if they do not have the same contents and datatype" do
54
+ f = Literal.typed("tom", "http://www.w3.org/2001/XMLSchema#string")
55
+ g = Literal.typed("tim", "http://www.w3.org/2001/XMLSchema#string")
56
+ f.should_not == g
57
+
58
+ dtf = Literal.typed("tom", "http://www.w3.org/2001/XMLSchema#string")
59
+ dtg = Literal.typed("tom", "http://www.w3.org/2001/XMLSchema#token")
60
+ dtf.should_not == dtg
61
+ end
62
+
63
+ it "should return valid N3/NTriples format strings" do
64
+ f = Literal.untyped("tom")
65
+ f.to_n3.should == "\"tom\""
66
+ f.to_ntriples.should == f.to_n3
67
+
68
+ g = Literal.untyped("tom", "en")
69
+ g.to_n3.should == "\"tom\"@en"
70
+ f.to_ntriples.should == f.to_n3
71
+
72
+ typed_int = Literal.typed(5, "http://www.w3.org/2001/XMLSchema#int")
73
+ typed_int.to_n3.should == "5^^<http://www.w3.org/2001/XMLSchema#int>"
74
+ typed_int.to_ntriples.should == typed_int.to_n3
75
+
76
+ typed_string = Literal.typed("foo", "http://www.w3.org/2001/XMLSchema#string")
77
+ typed_string.to_n3.should == "\"foo\"^^<http://www.w3.org/2001/XMLSchema#string>"
78
+ typed_string.to_ntriples.should == typed_string.to_n3
79
+ end
80
+
81
+ it "should normalize language tags to lower case" do
82
+ f = Literal.untyped("tom", "EN")
83
+ f.lang.should == "en"
84
+ end
85
+
86
+ it "should support TriX encoding" do
87
+ e = Literal.untyped("tom")
88
+ e.to_trix.should == "<plainLiteral>tom</plainLiteral>"
89
+
90
+ f = Literal.untyped("tom", "en")
91
+ f.to_trix.should == "<plainLiteral xml:lang=\"en\">tom</plainLiteral>"
92
+
93
+ g = Literal.typed("tom", "http://www.w3.org/2001/XMLSchema#string")
94
+ g.to_trix.should == "<typedLiteral datatype=\"http://www.w3.org/2001/XMLSchema#string\">tom</typedLiteral>"
95
+ end
96
+
97
+ it "should handle XML litearls" do
98
+ # first we should detect XML literals and mark them as such in the class
99
+ f = Literal.typed("foo <sup>bar</sup> baz!", "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral")
100
+ f.xmlliteral?.should == true
101
+ # pending "TODO: the thought of XML literals makes me want to wretch"
102
+ end
103
+
104
+ it "build_from should infer the type" do
105
+ int = Literal.build_from(15)
106
+ int.encoding.should == "http://www.w3.org/2001/XMLSchema#int"
107
+
108
+ float = Literal.build_from(15.4)
109
+ float.encoding.should == "http://www.w3.org/2001/XMLSchema#float"
110
+
111
+ other = Literal.build_from("foo")
112
+ other.encoding.should == "http://www.w3.org/2001/XMLSchema#string"
113
+ end
114
+
115
+ it "build_from_language" do
116
+ english = Literal.build_from_language("Have a nice day")
117
+ english.encoding.should == "en"
118
+
119
+ french = Literal.build_from_language("Bonjour, madame. Parlez vous francais?")
120
+ french.encoding.should == "fr"
121
+
122
+ german = Literal.build_from_language("Achtung")
123
+ german.encoding.should == "de"
124
+ end
125
+
126
+ # TODO: refactor based on new interface
127
+ # describe "Languages" do
128
+ # it "should be inspectable" do
129
+ # literal = Rena::Literal.new("foo", "en")
130
+ # lang = literal.lang
131
+ # lang.to_s == "en"
132
+ # lang.hash.class.should == Fixnum
133
+ # end
134
+ # end
135
+
136
+ end