lightrdf 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/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