lightrdf 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,3 @@
1
+ === 0.1 2010-10-06
2
+
3
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,12 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ bin/yarfp
6
+ lib/lightrdf.rb
7
+ lib/lightrdf/graph.rb
8
+ lib/lightrdf/node.rb
9
+ lib/lightrdf/parser.rb
10
+ lib/lightrdf/quri.rb
11
+ test/test_helper.rb
12
+ test/test_lightrdf.rb
data/README.rdoc ADDED
@@ -0,0 +1,163 @@
1
+ = LightRDF
2
+
3
+ * http://github.com/josei/lightrdf
4
+
5
+ == DESCRIPTION:
6
+
7
+ LightRDF is a gem that makes managing RDF data and graphs easily with a Ruby interface.
8
+
9
+ == INSTALL:
10
+
11
+ gem install lightrdf
12
+
13
+ The gem also requires Raptor library (in Debian systems: sudo aptitude install raptor-utils), which is used
14
+ for outputting different RDF serialization formats.
15
+
16
+ Additionally, PNG output of RDF graphs requires Graphviz (in Debian systems: sudo aptitude install graphviz).
17
+
18
+ == RDF NODES:
19
+
20
+ RDF nodes can be created using the Node method:
21
+
22
+ a = Node('http://www.example.com/ontology#test')
23
+
24
+ Namespaces can be defined in order to simplify URI manipulation:
25
+
26
+ Namespace :ex, 'http://www.example.com/ontology#'
27
+ a = Node('ex:test')
28
+
29
+ BNodes can be created too:
30
+
31
+ bnode = Node('_:bnode1')
32
+
33
+ BNode identifiers can be created automatically if a nil ID is supplied:
34
+
35
+ bnode = Node(nil)
36
+ puts bnode # Outputs: _:1
37
+
38
+ LightRDF's nodes are Ruby hashes that are evaluated as equal whenever their IDs are the same:
39
+
40
+ Node('_:bnode1') == Node('_:bnode1') # => true
41
+ Node('ex:example') == Node('ex:example') # => true
42
+
43
+ == RDF TRIPLES:
44
+
45
+ Triples can be created by creating relations between nodes.
46
+
47
+ user = Node('ex:bob')
48
+ user.foaf::name = "Bob"
49
+
50
+ If you want to get the property, an array will be returned, as many foaf::name's can be associated to it:
51
+
52
+ user.foaf::name # => ["Bob"]
53
+
54
+ Therefore, we can add multiple values to a property:
55
+
56
+ user.foaf::weblog = Node('http://www.awesomeweblogfordummies.com')
57
+ user.foaf::weblog << Node('http://www.anotherawesomeweblogfordummies.com')
58
+ user.foaf::weblog.size # => 2
59
+ user.foaf::weblog?(Node('http://www.awesomeweblogfordummies.com')) # => true
60
+
61
+ == RDF GRAPHS:
62
+
63
+ Every node has an associated graph, where all its relations are stored.
64
+ A node's own graph can be accessed by using node.graph.
65
+
66
+ Graphs can also be created and merged:
67
+
68
+ alice = Node("ex:alice")
69
+ alice.foaf::name = "Alice"
70
+
71
+ bob = Node("ex:bob")
72
+ bob.foaf::name = "Bob"
73
+
74
+ graph = Graph.new # Creates a new empty graph
75
+ graph << alice # Adds alice to the graph
76
+ graph[Node("ex:alice")].foaf::name # => ["Alice"]
77
+ graph[Node("ex:bob")].foaf::name # => []
78
+
79
+ merged_graph = graph.merge(bob.graph)
80
+ graph[Node("ex:alice")].foaf::name # => ["Alice"]
81
+ graph[Node("ex:bob")].foaf::name # => ["Bob"]
82
+ merged_graph.triples.size # => 2
83
+
84
+ Also, queries for triples can be performed on a graph:
85
+
86
+ a = Node('ex:bob')
87
+ a.foaf::name = "Bob"
88
+ a.foaf::age = "24"
89
+
90
+ b = Node('ex:alice')
91
+ b.foaf::name = "Alice"
92
+ b.foaf::age = "22"
93
+
94
+ g = RDF::Graph.new
95
+ g << a
96
+ g << b
97
+
98
+ # Find a person with age == 22
99
+ g.find(nil, Node('foaf:age'), "22") # => [Node('ex:bob')]
100
+
101
+ # Find all predicates bob has
102
+ g.find(Node('ex:bob'), nil, []) # => [Node('foaf:name'), Node('foaf:age')]
103
+
104
+ == PARSING AND SERIALIZING:
105
+
106
+ LightRDF uses Raptor library internally to support many RDF formats. It also introduces
107
+ YARF (Yet Another RDF Format) for serializing and parsing. A YARF document is as follows:
108
+
109
+ # Namespaces
110
+ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
111
+ rdfs: http://www.w3.org/2000/01/rdf-schema#
112
+ ex: http://www.example.com/ontology#
113
+ foaf: http://xmlns.com/foaf/0.1/
114
+ geo: http://www.w3.org/2003/01/geo/wgs84_pos#
115
+
116
+ # RDF data
117
+ ex:bob:
118
+ rdf:type: rdf:Resource
119
+ foaf:name: "Bob"
120
+ foaf:weblog:
121
+ http://www.awesomeblog.com:
122
+ dc:title: "Awesome blog"
123
+ http://www.anotherawesomeblog.com:
124
+ dc:title: "Another awesome blog"
125
+ foaf:based_near:
126
+ _:spain: # Blank node with a specified id
127
+ geo:lat: "37.0625"
128
+ *: # Blank node with auto generated id
129
+ geo:lat: "55.701"
130
+ geo:lng: "12.552"
131
+ _:spain: # Blank node with an id can be referenced in the doc
132
+ geo:lng: "-95.677068"
133
+
134
+ Documents in either yarf, rdf, json, ejson (for easyJSON), dot, png, ntriples format can be parsed into
135
+ graphs and serialized using the parse and serialize methods:
136
+
137
+ graph = RDF::Parser.parse(:rdf, open('http://planetrdf.com/guide/rss.rdf').read) # Builds a graph
138
+ graph.serialize(:ntriples) # Outputs a string with triples
139
+
140
+ == LICENSE:
141
+
142
+ (The MIT License)
143
+
144
+ Copyright (c) 2010 José Ignacio Fernández (joseignacio.fernandez <at> gmail.com)
145
+
146
+ Permission is hereby granted, free of charge, to any person obtaining
147
+ a copy of this software and associated documentation files (the
148
+ 'Software'), to deal in the Software without restriction, including
149
+ without limitation the rights to use, copy, modify, merge, publish,
150
+ distribute, sublicense, and/or sell copies of the Software, and to
151
+ permit persons to whom the Software is furnished to do so, subject to
152
+ the following conditions:
153
+
154
+ The above copyright notice and this permission notice shall be
155
+ included in all copies or substantial portions of the Software.
156
+
157
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
158
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
159
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
160
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
161
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
162
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
163
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/lightrdf'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'lightrdf' do
14
+ self.developer 'José Ignacio', 'joseignacio.fernandez@gmail.com'
15
+ self.summary = "Light and easy library for managing RDF data and graphs"
16
+ self.post_install_message = '**Remember to install raptor RDF tools and (optionally for RDF PNG output) Graphviz**'
17
+ self.rubyforge_name = self.name # TODO this is default value
18
+ self.extra_deps = [['activesupport','>= 2.0.2']]
19
+ end
20
+
21
+ require 'newgem/tasks'
22
+ Dir['tasks/**/*.rake'].each { |t| load t }
data/bin/yarfp ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'rubygems'
4
+ require 'lightrdf'
5
+ require 'open-uri'
6
+
7
+ if $*.size < 1
8
+ puts 'YARF Parser'
9
+ puts 'Usage: yarfp URI_or_file [output_format] [input_format] [namespaces]'
10
+ puts ''
11
+ puts 'Available formats: yarf (default input), ntriples (default output), rdfxml, turtle, rdfa, dot, png'
12
+ puts 'Namespaces: prefix1,uri1,prefix2,uri2... e.g.: sc,http://lab.gsi.dit.upm.es/scrappy/schema.rdf#'
13
+ exit
14
+ else
15
+ uri = $*[0]
16
+ output_format = ($*[1] || :ntriples).to_sym
17
+ input_format = ($*[2] || :yarf).to_sym
18
+ end
19
+
20
+ $*[3].split(',').each_slice(2) { |prefix, url| Namespace prefix, url } if $*[3]
21
+
22
+ data = open(uri).read
23
+ puts RDF::Parser.parse(input_format.to_sym, data).serialize(output_format.to_sym)
@@ -0,0 +1,52 @@
1
+ module RDF
2
+ class Graph < Hash
3
+ include Parser
4
+ # Namespace set stored when parsing a file. Can be used for reference
5
+ attr_accessor :ns
6
+ def initialize triples=[]
7
+ super(nil)
8
+ @ns = {}
9
+ self.triples = triples
10
+ end
11
+
12
+ def << node
13
+ self[node] = node
14
+ end
15
+
16
+ def [] id
17
+ super(ID(id)) || Node.new(id, self)
18
+ end
19
+ def []= id, node
20
+ node.graph = self
21
+ super ID(id), node
22
+ end
23
+ def nodes; values; end
24
+ def inspect
25
+ "{" + (values.map{|v| v.inspect} * ", ") + "}"
26
+ end
27
+ def merge graph
28
+ new_graph = Graph.new
29
+ new_graph.triples = triples + graph.triples
30
+ new_graph
31
+ end
32
+ def triples
33
+ triples = []; values.each { |n| triples += n.triples }
34
+ triples
35
+ end
36
+ def triples= triples
37
+ self.clear
38
+ triples.each { |s, p, o| self[s][p] = self[s][p] + [o] }
39
+ end
40
+
41
+ def find subject, predicate, object
42
+ matches = triples.select { |s,p,o| (subject.nil? or subject==[] or s==subject) and
43
+ (predicate.nil? or predicate==[] or p==predicate) and
44
+ (object.nil? or object==[] or o==object) }
45
+ result = []
46
+ result += matches.map {|t| t[0] } if subject.nil?
47
+ result += matches.map {|t| t[1] } if predicate.nil?
48
+ result += matches.map {|t| t[2] } if object.nil?
49
+ result.uniq
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,84 @@
1
+ module RDF
2
+ class NsProxy
3
+ undef :type
4
+ undef :class
5
+ undef :id
6
+ def initialize ns, object
7
+ @object = object
8
+ @ns = ns
9
+ end
10
+ def method_missing method, *args
11
+ if method.to_s =~ /.*=\Z/
12
+ @object["#{@ns}:#{method.to_s[0..-2]}"] = args.first
13
+ elsif method.to_s =~ /.*\?\Z/
14
+ @object["#{@ns}:#{method.to_s[0..-2]}"].include?(args.first)
15
+ else
16
+ @object["#{@ns}:#{method}"]
17
+ end
18
+ end
19
+ end
20
+
21
+ class Node < Hash
22
+ include Parser
23
+ attr_accessor :graph, :id
24
+
25
+ def initialize id=nil, graph=nil
26
+ @id = ID::parse(id)
27
+ @graph = graph || Graph.new
28
+ @graph << self
29
+ end
30
+
31
+ def method_missing method, *args
32
+ QURI.ns[method] ? NsProxy.new(method, self) : super
33
+ end
34
+ def inspect; "<#{self.class} #{id} #{super}>"; end
35
+ def to_s; id.to_s; end
36
+
37
+ def [] name
38
+ self[Node(name)] = [] if super(Node(name)).nil?
39
+ super(Node(name)).map! {|n| n.is_a?(Node) ? @graph[n] : n}
40
+ end
41
+ def []= name, values
42
+ super(Node(name), [values].flatten.map { |node| node.is_a?(Node) ? @graph[node] : node })
43
+ end
44
+
45
+ def == b
46
+ eql? b
47
+ end
48
+ def eql? b
49
+ b.is_a?(Node) and self.id == b.id
50
+ end
51
+ def hash # Hack for Ruby 1.8.6
52
+ id.hash + self.class.hash
53
+ end
54
+
55
+ def predicates
56
+ keys.map { |p| [ @graph[p], self[p] ] }
57
+ end
58
+
59
+ def triples
60
+ triples = []; each { |k, v| v.each { |o| triples << [self, Node(k), o] } }
61
+ triples
62
+ end
63
+
64
+ def merge node
65
+ new_node = clone
66
+ (self.keys + node.keys).uniq.each do |k|
67
+ new_node[k] = (node[k] + self[k]).uniq
68
+ end
69
+ new_node
70
+ end
71
+
72
+ def clone
73
+ Node.new self
74
+ end
75
+
76
+ def bnode?
77
+ id.is_a?(BNodeID)
78
+ end
79
+ end
80
+ end
81
+
82
+ def Node id, graph=nil
83
+ graph.nil? ? RDF::Node.new(id, graph) : graph[id]
84
+ end
@@ -0,0 +1,225 @@
1
+ module RDF
2
+ module Parser
3
+ def to_ntriples; serialize :ntriples; end
4
+
5
+ def serialize format, header=true
6
+ nodes = if self.is_a?(Graph)
7
+ nodes_as_objects = find([], [], nil).select{|n| n.is_a?(Node)}
8
+ values - nodes_as_objects + nodes_as_objects # Put them at the end
9
+ else [self]
10
+ end
11
+
12
+ case format.to_sym
13
+ when :ntriples
14
+ triples.map { |s,p,o| "#{serialize_chunk_ntriples(s)} #{serialize_chunk_ntriples(p)} #{serialize_chunk_ntriples(o)} ." } * "\n"
15
+ when :yarf
16
+ ns = respond_to?(:ns) ? QURI.ns.merge(self.ns) : QURI.ns
17
+ if header
18
+ (ns.map{|k,v| "#{k}: #{v}\n"} * '') + serialize_yarf(nodes, ns)
19
+ else
20
+ serialize_yarf(nodes, ns)
21
+ end
22
+ when :ejson
23
+ stdin, stdout, stderr = Open3.popen3("python -mjson.tool")
24
+ stdin.puts ActiveSupport::JSON.encode(serialize_ejson(nodes))
25
+ stdin.close
26
+ stdout.read
27
+ when :png
28
+ dot = serialize(:dot)
29
+ ns = respond_to?(:ns) ? QURI.ns.merge(self.ns) : QURI.ns
30
+ ns.each { |k,v| dot.gsub!(v, "#{k}:") }
31
+ dot.gsub!(/label=\"\\n\\nModel:.*\)\";/, '')
32
+ stdin, stdout, stderr = Open3.popen3("dot -o/dev/stdout -Tpng")
33
+ stdin.puts dot
34
+ stdin.close
35
+ stdout.read
36
+ when :rdf
37
+ serialize(:rdfxml)
38
+ else
39
+ stdin, stdout, stderr = Open3.popen3("rapper -q -i ntriples -o #{format} /dev/stdin")
40
+ stdin.puts serialize(:ntriples)
41
+ stdin.close
42
+ stdout.read
43
+ end
44
+ end
45
+
46
+ def self.parse format, text
47
+ case format
48
+ when :ntriples
49
+ graph = RDF::Graph.new
50
+ graph.triples = text.split(".\n").map do |l|
51
+ s,p,o = l.strip.match(/(<.+>|".*"|_:.*)\W(<.+>|".*"|_:.*)\W(<.+>|".*"|_:.*)/).captures
52
+ [parse_chunk_ntriples(s), parse_chunk_ntriples(p), parse_chunk_ntriples(o)]
53
+ end
54
+ graph
55
+ when :yarf
56
+ graph = RDF::Graph.new
57
+ ns = {}
58
+ # Preprocessing - Extract namespaces, remove comments, get indent levels
59
+ lines = []
60
+ text.split("\n").each_with_index do |line, n|
61
+ if line =~ /(\A\s*#|\A\w*\Z)/ # Comment or blank line - do nothing
62
+ elsif line =~ /\A(\w+):\s+(.+)/ # Namespace
63
+ ns[$1.to_sym] = $2
64
+ else # Normal line - store line number, get indent level and strip line
65
+ lines << [n+1, (line.size - line.lstrip.size)/2, line.strip]
66
+ end
67
+ end
68
+ parse_yarf_nodes lines, graph, ns
69
+ graph.ns = ns
70
+ graph
71
+ when :rdf
72
+ parse :rdfxml, text
73
+ when :ejson
74
+ raise Exception, "eJSON format cannot be parsed (yet)"
75
+ else
76
+ begin
77
+ stdin, stdout, stderr = Open3.popen3("rapper -q -i #{format} -o ntriples /dev/stdin")
78
+ stdin.puts text
79
+ stdin.close
80
+ rescue
81
+ end
82
+ parse :ntriples, stdout.read
83
+ end
84
+ end
85
+
86
+ private
87
+ def self.parse_yarf_nodes lines, graph, ns, base_level=0, i=0
88
+ nodes = []
89
+ while i < lines.size
90
+ number, level, line = lines[i]
91
+ if level == base_level
92
+ nodes << if line =~ /\A(".*")\Z/ # Literal
93
+ i += 1
94
+ ActiveSupport::JSON.decode($1)
95
+ elsif line =~ /\A(.+):\Z/ # Node with relations
96
+ node = Node(ID($1, ns), graph)
97
+ i, relations = parse_yarf_relations(lines, graph, ns, level+1, i+1)
98
+ relations.each { |predicate, object| node[predicate] = node[predicate] + [object] }
99
+ node
100
+ elsif line =~ /\A(.+)\Z/ # Node
101
+ i += 1
102
+ Node(ID($1, ns), graph)
103
+ else
104
+ raise Exception, "Syntax error on line #{number}"
105
+ end
106
+ elsif level < base_level
107
+ break
108
+ else
109
+ raise Exception, "Indentation error on line #{number}"
110
+ end
111
+ end
112
+ [i, nodes]
113
+ end
114
+ def self.parse_yarf_relations lines, graph, ns, base_level, i
115
+ relations = []
116
+ while i < lines.size
117
+ number, level, line = lines[i]
118
+ if level == base_level
119
+ relations += if line =~ /\A(.+):\s+(".+")\Z/ # Predicate and literal
120
+ i += 1
121
+ [[Node(ID($1, ns), graph), ActiveSupport::JSON.decode($2)]]
122
+ elsif line =~ /\A(.+):\s+(.+)\Z/ # Predicate and node
123
+ i += 1
124
+ [[Node(ID($1, ns), graph), Node(ID($2, ns), graph)]]
125
+ elsif line =~ /\A(.+):\Z/ # Just the predicate
126
+ predicate = Node(ID($1, ns), graph)
127
+ i, objects = parse_yarf_nodes(lines, graph, ns, level+1, i+1)
128
+ objects.map {|n| [predicate, n]}
129
+ end
130
+ elsif level < base_level
131
+ break
132
+ else
133
+ raise Exception, "Indentation error on line #{number}"
134
+ end
135
+ end
136
+ [i, relations]
137
+ end
138
+
139
+ def serialize_yarf nodes, ns=QURI.ns, level=0, already_serialized=[]
140
+ text = ""
141
+
142
+ for node in nodes
143
+ next if level == 0 and (node.triples.size == 0 or already_serialized.include?(node))
144
+ text += " " *level*2
145
+ text += serialize_chunk_yarf(node, ns)
146
+ if already_serialized.include?(node) or !node.is_a?(Node) or node.triples.size == 0
147
+ text += "\n"
148
+ else
149
+ already_serialized << node
150
+ text += ":\n"
151
+ node.predicates.each do |p, o| # Predicate and object
152
+ text += " " *(level+1)*2
153
+ text += p.id.compressed(ns)
154
+ text += ":"
155
+ if o.size == 1 and (already_serialized.include?(o.first) or !o.first.is_a?(Node) or o.first.triples.size==0)
156
+ text += " " + serialize_chunk_yarf(o.first, ns)
157
+ text += "\n"
158
+ else
159
+ text += "\n" + serialize_yarf(o, ns, level+2, already_serialized)
160
+ end
161
+ end
162
+ end
163
+ end
164
+ text
165
+ end
166
+
167
+ def serialize_chunk_yarf node, ns=QURI.ns
168
+ if node.is_a?(Node)
169
+ if node.bnode?
170
+ if node.graph.find(nil, [], node).size > 1 # Only use a bnode-id if it appears again as object
171
+ node.id.to_s
172
+ else
173
+ "*"
174
+ end
175
+ else
176
+ node.id.compressed ns
177
+ end
178
+ else
179
+ ActiveSupport::JSON.encode(node)
180
+ end
181
+ end
182
+
183
+ def serialize_ejson nodes, already_serialized=[], level=0
184
+ list = []
185
+
186
+ nodes.each do |n|
187
+ if n.is_a?(Node)
188
+ if already_serialized.include?(n)
189
+ list << { 'id' => n.to_s } unless level == 0
190
+ else
191
+ already_serialized << n
192
+ hash = { 'id' => n.to_s }
193
+ n.predicates.each do |k,v|
194
+ hash[k.to_s] = serialize_ejson(v, already_serialized, level+1)
195
+ end
196
+ list << hash
197
+ end
198
+ else
199
+ list << n.to_s
200
+ end
201
+ end
202
+
203
+ list
204
+ end
205
+
206
+ def serialize_chunk_ntriples n
207
+ if n.is_a? Node
208
+ n.bnode? ? n.to_s : "<#{n}>"
209
+ else
210
+ ActiveSupport::JSON.encode(n)
211
+ end
212
+ end
213
+
214
+ def self.parse_chunk_ntriples c
215
+ case c[0..0]
216
+ when '<' then Node c[1..-2]
217
+ when '_' then Node c
218
+ when '"' then
219
+ ActiveSupport::JSON.decode(c)
220
+ else
221
+ raise Exception, "Parsing error: #{c}"
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,81 @@
1
+ module RDF
2
+ module QURI
3
+ @ns = { :rdf => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
4
+ :rdfs => 'http://www.w3.org/2000/01/rdf-schema#',
5
+ :dc => 'http://purl.org/dc/elements/1.1/',
6
+ :owl => 'http://www.w3.org/2002/07/owl#' }
7
+ def self.ns; @ns; end
8
+
9
+ def self.parse uri, ns={}
10
+ URI.parse( (uri.to_s =~ /(\w+):(.*)/ and uri.to_s[0..6]!='http://' and uri.to_s[0..7]!='https://') ?
11
+ "#{QURI.ns.merge(ns)[$1.to_sym]}#{$2}" :
12
+ uri.to_s )
13
+ end
14
+
15
+ def self.compress uri, ns={}
16
+ QURI.ns.merge(ns).map.sort_by{|k,v| -v.to_s.size}.each do |k,v|
17
+ if uri.to_s.index(v) == 0
18
+ return "#{k}:#{uri.to_s[v.size..-1]}"
19
+ end
20
+ end
21
+ uri.to_s
22
+ end
23
+ end
24
+
25
+ module ID
26
+ def self.ns; QURI.ns; end
27
+
28
+ def compressed ns={}
29
+ RDF::ID.compress self, ns
30
+ end
31
+
32
+ def self.compress id, ns={}
33
+ bnode?(id) ? id.to_s : RDF::QURI.compress(id, ns)
34
+ end
35
+
36
+ def self.parse id, ns={}
37
+ bnode?(id) ? RDF::BNodeID.parse(id) : RDF::QURI.parse(id, ns)
38
+ end
39
+
40
+ def self.bnode?(id)
41
+ id.nil? or id == '*' or id.to_s[0..0] == '_'
42
+ end
43
+ end
44
+
45
+ class BNodeID
46
+ include RDF::ID
47
+ @count = 0
48
+ def self.count; @count; end
49
+ def self.count=c; @count=c; end
50
+
51
+ attr_reader :id
52
+ def initialize id=nil
53
+ @id = (id || "_:bnode#{RDF::BNodeID.count+=1}").to_s
54
+ end
55
+ def self.parse id
56
+ new(id=='*' ? nil : id)
57
+ end
58
+ def to_s; id.to_s; end
59
+
60
+ def == b
61
+ eql? b
62
+ end
63
+ def eql? b
64
+ b.is_a?(BNodeID) and self.id == b.id
65
+ end
66
+ def hash # Hack for Ruby 1.8.6
67
+ id.hash + self.class.hash
68
+ end
69
+ end
70
+ end
71
+
72
+ class URI::Generic
73
+ include RDF::ID # URIs are RDF IDs
74
+ end
75
+
76
+ def ID uri, ns={} # Shortcut to parse IDs
77
+ RDF::ID.parse uri, ns
78
+ end
79
+ def Namespace prefix, uri
80
+ RDF::QURI.ns[prefix.to_sym] = uri
81
+ end
data/lib/lightrdf.rb ADDED
@@ -0,0 +1,17 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module RDF
5
+ VERSION = '0.1'
6
+ end
7
+
8
+ require 'rubygems'
9
+ require 'active_support'
10
+ require 'uri'
11
+ require 'open3'
12
+ require 'open-uri'
13
+
14
+ require "lightrdf/quri"
15
+ require "lightrdf/parser"
16
+ require "lightrdf/graph"
17
+ require "lightrdf/node"
@@ -0,0 +1,3 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/lightrdf'
@@ -0,0 +1,125 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestLightRDF < Test::Unit::TestCase
4
+
5
+ def setup
6
+ Namespace :ex, 'http://www.example.com/ontology#'
7
+ Namespace :foaf, 'http://xmlns.com/foaf/0.1/'
8
+ end
9
+
10
+ def test_equality
11
+ assert Node('_:something') == Node(Node('_:something'))
12
+ assert ID('_:2') == ID('_:2')
13
+ assert ID('http://www.w3.org/1999/02/22-rdf-syntax-ns#type') == ID('rdf:type')
14
+ assert Node('_:2') == Node('_:2')
15
+ end
16
+
17
+ def test_type
18
+ assert ID('rdf:type').is_a?(RDF::ID)
19
+ assert ID('_:bnode').is_a?(RDF::ID)
20
+ assert ID('http://www.google.com').is_a?(RDF::ID)
21
+ assert URI.parse('http://www.google.com').is_a?(RDF::ID)
22
+ assert ID('*').is_a?(RDF::ID)
23
+ end
24
+
25
+ def test_random_node
26
+ assert Node(nil).is_a?(RDF::Node)
27
+ end
28
+
29
+ def test_graph
30
+ graph = RDF::Graph.new
31
+ assert_equal graph['rdf:type'], graph[Node('rdf:type')]
32
+ assert_equal graph['rdf:type'], graph['rdf:type']
33
+ assert_not_equal graph['foaf:name'], graph[Node('foaf::nick')]
34
+ assert_not_equal graph['rdf:type'], graph[Node('rdf:class')]
35
+ assert !graph['rdf:type'].eql?(graph[Node('rdf:class')])
36
+ end
37
+
38
+ def test_namespaces
39
+ assert_equal Node('http://www.example.com/ontology#test'), Node('ex:test')
40
+ end
41
+
42
+ def test_attributes
43
+ a = Node('ex:bob')
44
+ a.foaf::name = "Bob"
45
+ assert_equal "Bob", a.foaf::name.first
46
+ end
47
+
48
+ def test_query
49
+ a = Node('ex:bob')
50
+ a.foaf::name = "Bob"
51
+ a.foaf::age = "24"
52
+
53
+ b = Node('ex:alice')
54
+ b.foaf::name = "Alice"
55
+ b.foaf::age = "22"
56
+
57
+ g = RDF::Graph.new
58
+ g << a
59
+ g << b
60
+
61
+ assert_equal 1, g.find(nil, Node('foaf:age'), "22").size
62
+ assert_equal 2, g.find(Node('ex:bob'), nil, []).size
63
+ end
64
+
65
+ def test_addition
66
+ a = Node('ex:bob')
67
+ a.foaf::weblog = Node('http://www.awesomeweblogfordummies.com')
68
+ a.foaf::weblog << Node('http://www.anotherawesomeweblogfordummies.com')
69
+ assert_equal 2, a.foaf::weblog.size
70
+ assert a.foaf::weblog?(Node('http://www.awesomeweblogfordummies.com'))
71
+ end
72
+
73
+ def test_graph_merge
74
+ a = Node('ex:bob')
75
+ a.foaf::name = "Bob"
76
+
77
+ b = Node('ex:alice')
78
+ b.foaf::name = "Alice"
79
+
80
+ g1 = RDF::Graph.new
81
+ g2 = RDF::Graph.new
82
+ g1 << a
83
+ g2 << b
84
+
85
+ g3 = g1.merge(g2)
86
+
87
+ assert ["Alice"], g3[Node('ex:Alice')].foaf::name
88
+ assert ["Bob"], g3[Node('ex:bob')].foaf::name
89
+ assert_equal 2, g3.triples.size
90
+ end
91
+
92
+ def test_parsing
93
+ text = """
94
+ dc: http://purl.org/dc/elements/1.1/
95
+ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
96
+ ex: http://www.example.com/ontology#
97
+ foaf: http://xmlns.com/foaf/0.1/
98
+ *:
99
+ rdf:type: rdf:Resource
100
+ foaf:name: \"Bob\"
101
+ foaf:weblog:
102
+ \"http://www.awesomeweblogfordummies.com\"
103
+ http://www.anotherawesomeweblogfordummies.com:
104
+ dc:title: \"Another awesome blog\"
105
+ """
106
+
107
+ assert_equal 5, RDF::Parser.parse(:yarf, text).triples.size
108
+ end
109
+
110
+ def test_parsing_raptor
111
+ # Very naive testing -- it only helps to check that rapper is being executed
112
+ assert RDF::Parser.parse(:rdf, open('http://planetrdf.com/guide/rss.rdf').read).serialize(:ntriples).split("\n").size > 10
113
+ end
114
+
115
+ def test_serialization
116
+ a = Node('ex:bob')
117
+ a.foaf::name = "Bob"
118
+ a.foaf::age = "23"
119
+
120
+ g = RDF::Graph.new
121
+ g << a
122
+
123
+ assert 2, g.to_ntriples.split("\n").size
124
+ end
125
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lightrdf
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ version: "0.1"
9
+ platform: ruby
10
+ authors:
11
+ - "Jos\xC3\xA9 Ignacio"
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2010-10-06 00:00:00 +02:00
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: activesupport
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ segments:
27
+ - 2
28
+ - 0
29
+ - 2
30
+ version: 2.0.2
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: rubyforge
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 2
42
+ - 0
43
+ - 4
44
+ version: 2.0.4
45
+ type: :development
46
+ version_requirements: *id002
47
+ - !ruby/object:Gem::Dependency
48
+ name: hoe
49
+ prerelease: false
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 2
56
+ - 6
57
+ - 0
58
+ version: 2.6.0
59
+ type: :development
60
+ version_requirements: *id003
61
+ description: LightRDF is a gem that makes managing RDF data and graphs easily with a Ruby interface.
62
+ email:
63
+ - joseignacio.fernandez@gmail.com
64
+ executables:
65
+ - yarfp
66
+ extensions: []
67
+
68
+ extra_rdoc_files:
69
+ - History.txt
70
+ - Manifest.txt
71
+ files:
72
+ - History.txt
73
+ - Manifest.txt
74
+ - README.rdoc
75
+ - Rakefile
76
+ - bin/yarfp
77
+ - lib/lightrdf.rb
78
+ - lib/lightrdf/graph.rb
79
+ - lib/lightrdf/node.rb
80
+ - lib/lightrdf/parser.rb
81
+ - lib/lightrdf/quri.rb
82
+ - test/test_helper.rb
83
+ - test/test_lightrdf.rb
84
+ has_rdoc: true
85
+ homepage: http://github.com/josei/lightrdf
86
+ licenses: []
87
+
88
+ post_install_message: "**Remember to install raptor RDF tools and (optionally for RDF PNG output) Graphviz**"
89
+ rdoc_options:
90
+ - --main
91
+ - README.rdoc
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ segments:
99
+ - 0
100
+ version: "0"
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ requirements: []
109
+
110
+ rubyforge_project: lightrdf
111
+ rubygems_version: 1.3.6
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Light and easy library for managing RDF data and graphs
115
+ test_files:
116
+ - test/test_lightrdf.rb
117
+ - test/test_helper.rb