tommorris-rena 0.0.1 → 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.
@@ -1,5 +1,6 @@
1
1
  require 'rexml/document'
2
2
 
3
+ # @ignore
3
4
  # def subdocument_writer(el)
4
5
  # el.prefixes.each { |ns|
5
6
  # el.add_attribute('xmlns:' + ns, el.namespaces[ns].to_s)
@@ -9,13 +10,22 @@ require 'rexml/document'
9
10
 
10
11
  class REXML::Element
11
12
  public
13
+
14
+ ##
15
+ # Tells you whether or not an element has a set xml:lang.
16
+ #
17
+ # @author Tom Morris
12
18
  def lang?
13
- if self.lang != nil
14
- true
15
- else
16
- false
17
- end
19
+ self.lang.nil? ? false : true
18
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
19
29
  def lang
20
30
  if self.attributes['xml:lang']
21
31
  return self.attributes['xml:lang'].to_s
@@ -25,7 +35,11 @@ class REXML::Element
25
35
  return nil
26
36
  end
27
37
  end
28
-
38
+
39
+ ##
40
+ # Tells you whether or not an element has a set xml:base.
41
+ #
42
+ # @author Tom Morris
29
43
  def base?
30
44
  if self.base != nil
31
45
  true
@@ -34,6 +48,13 @@ class REXML::Element
34
48
  end
35
49
  end
36
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
37
58
  def base
38
59
  if self.attributes['xml:base']
39
60
  return self.attributes['xml:base'].to_s
@@ -44,7 +65,14 @@ class REXML::Element
44
65
  end
45
66
  end
46
67
 
47
- def write(excl=[])
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=[])
48
76
  # TODO: add optional list argument of excluded namespaces
49
77
  self.prefixes.each { |ns|
50
78
  self.add_attribute('xmlns:' + ns, self.namespaces[ns].to_s) unless excl.include? self.namespaces[ns]
@@ -66,5 +94,4 @@ class REXML::Element
66
94
  self.support_write_recursive(array, e)
67
95
  }
68
96
  end
69
-
70
- end
97
+ end
@@ -1,72 +1,89 @@
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
1
+ module Rena
2
+ class Triple
3
+ class InvalidPredicate < StandardError
4
+ end
25
5
 
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)
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
37
60
  else
38
- @subject = BNode.new(subject)
61
+ raise InvalidSubject, "Subject is not of a known class"
39
62
  end
40
- else
41
- raise "Subject is not of a known class"
42
63
  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)
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
54
71
  else
55
- raise "String literals are not acceptable as predicates"
72
+ raise InvalidPredicate, "Predicate should be a URI"
56
73
  end
57
- else
58
- raise "Predicate should be a uriref"
74
+ rescue UriRelativeException => e
75
+ raise InvalidPredicate, "Couldn't make a URIRef: #{e.message}"
59
76
  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"
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
70
87
  end
71
88
  end
72
- end
89
+ end
@@ -1,41 +1,55 @@
1
1
  require 'rubygems'
2
2
  require 'addressable/uri'
3
3
  require 'rena/exceptions/uri_relative_exception'
4
+ require 'net/http'
4
5
 
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"
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
15
18
  end
16
- end
17
19
 
18
- def == (other)
19
- return true if @uri == other.uri
20
- end
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
21
29
 
22
- def to_s
23
- @uri.to_s
24
- end
30
+ def == (other)
31
+ return true if @uri == other.uri
32
+ end
25
33
 
26
- def to_ntriples
27
- "<" + @uri.to_s + ">"
28
- end
34
+ def to_s
35
+ @uri.to_s
36
+ end
29
37
 
30
- def test_string (string)
31
- if string.class != String
32
- string = string.to_s
38
+ def to_ntriples
39
+ "<" + @uri.to_s + ">"
33
40
  end
34
-
35
- string.each_byte do |b|
36
- if b >= 0 and b <= 31
37
- raise "URI must not contain control characters"
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
38
47
  end
39
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
40
54
  end
41
55
  end
@@ -1,16 +1,17 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rena"
3
- s.version = "0.0.1"
4
- s.date = "2008-07-13"
3
+ s.version = "0.0.2"
4
+ s.date = "2008-10-5"
5
5
  s.summary = "Ruby RDF library."
6
6
  s.email = "tom@tommorris.org"
7
7
  s.homepage = "http://github.com/tommorris/rena"
8
8
  s.description = "Rena is a Ruby library for manipulating RDF files."
9
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"]
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
13
  #s.rdoc_options = ["--main", "README.txt"]
14
14
  #s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
15
- s.add_dependency("addressable", ["> 0.0.1"])
16
- end
15
+ s.add_dependency("addressable", [">= 1.0.4"])
16
+ s.add_dependency("treetop", [">= 1.2.4"])
17
+ end
@@ -20,6 +20,10 @@ describe "Blank nodes" do
20
20
  it "should be able to determine equality" do
21
21
  a = BNode.new('a')
22
22
  a2 = BNode.new('a')
23
- a.eql?(a2).should == true
23
+ a.eql?(a2).should be_true
24
+
25
+ a3 = URIRef.new('http://somehost.com/wherever.xml')
26
+ a.eql?(a3).should be_false
24
27
  end
28
+
25
29
  end
@@ -30,7 +30,7 @@ describe "Graphs" do
30
30
  f << Triple.new(ex.john, foaf.knows, ex.jane)
31
31
  f << Triple.new(ex.jane, foaf.knows, ex.rick)
32
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"
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
34
  f.to_ntriples.should == nt
35
35
  end
36
36
 
@@ -95,14 +95,33 @@ describe "Graphs" do
95
95
  f.bind(false)
96
96
  end.should raise_error
97
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
-
98
+
103
99
  it "should follow the specification as to output identical triples" do
104
100
  pending
105
101
  end
106
102
 
107
- it "should be able to integrate another graph"
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
108
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