rubyrdf 0.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.
Files changed (53) hide show
  1. data/History.txt +4 -0
  2. data/License.txt +24 -0
  3. data/Manifest.txt +52 -0
  4. data/README.txt +79 -0
  5. data/Rakefile +4 -0
  6. data/config/hoe.rb +70 -0
  7. data/config/requirements.rb +15 -0
  8. data/lib/rdf/blank_node.rb +41 -0
  9. data/lib/rdf/exceptions.rb +26 -0
  10. data/lib/rdf/format/ntriples.rb +493 -0
  11. data/lib/rdf/graph/base.rb +118 -0
  12. data/lib/rdf/graph/memory.rb +146 -0
  13. data/lib/rdf/graph/tests.rb +137 -0
  14. data/lib/rdf/namespace.rb +90 -0
  15. data/lib/rdf/plain_literal_node.rb +36 -0
  16. data/lib/rdf/query/binding.rb +68 -0
  17. data/lib/rdf/query/executer.rb +42 -0
  18. data/lib/rdf/query/result.rb +54 -0
  19. data/lib/rdf/query.rb +54 -0
  20. data/lib/rdf/triple.rb +61 -0
  21. data/lib/rdf/typed_literal_node.rb +39 -0
  22. data/lib/rdf/uri_node.rb +35 -0
  23. data/lib/rdf/version.rb +9 -0
  24. data/lib/rubyrdf.rb +59 -0
  25. data/log/debug.log +0 -0
  26. data/script/console +11 -0
  27. data/script/destroy +14 -0
  28. data/script/generate +14 -0
  29. data/script/txt2html +74 -0
  30. data/setup.rb +1585 -0
  31. data/tasks/deployment.rake +34 -0
  32. data/tasks/environment.rake +7 -0
  33. data/tasks/website.rake +17 -0
  34. data/test/helper.rb +14 -0
  35. data/test/test_blank_node.rb +67 -0
  36. data/test/test_format_ntriples.rb +247 -0
  37. data/test/test_graph_base.rb +71 -0
  38. data/test/test_graph_memory.rb +146 -0
  39. data/test/test_namespace.rb +130 -0
  40. data/test/test_plain_literal_node.rb +83 -0
  41. data/test/test_query.rb +49 -0
  42. data/test/test_query_binding.rb +84 -0
  43. data/test/test_query_result.rb +111 -0
  44. data/test/test_rdf.rb +56 -0
  45. data/test/test_triple.rb +147 -0
  46. data/test/test_typed_literal_node.rb +61 -0
  47. data/test/test_uri_node.rb +45 -0
  48. data/website/index.html +169 -0
  49. data/website/index.txt +92 -0
  50. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  51. data/website/stylesheets/screen.css +138 -0
  52. data/website/template.html.erb +48 -0
  53. metadata +139 -0
@@ -0,0 +1,493 @@
1
+ require 'stringio'
2
+
3
+ module RDF
4
+ module Format
5
+ # Import/Exports a graph to NTriples format.
6
+ class NTriples
7
+ # Raised when there is a syntax error in an input NTriples file.
8
+ class SyntaxError < RDF::Error; end
9
+
10
+ # Exports a +graph+ to +file+ in NTriples format.
11
+ def self.export(graph, file)
12
+ graph.each do |t|
13
+ file.write(export_node(t.subject))
14
+ file.write(' ')
15
+ file.write(export_node(t.predicate))
16
+ file.write(' ')
17
+ file.write(export_node(t.object))
18
+ file.puts('.')
19
+ end
20
+ end
21
+
22
+ # Returns a string NTriples representation of +node+.
23
+ def self.export_node(node)
24
+ case
25
+ when RDF::UriNode?(node)
26
+ "<#{node.uri}>"
27
+ when RDF::BlankNode?(node)
28
+ "_:#{node.name}"
29
+ when RDF::PlainLiteralNode?(node)
30
+ "\"#{node.lexical_form}\"" + (node.language_tag.to_s.empty? ? '' : "@#{node.language_tag}")
31
+ when RDF::TypedLiteralNode?(node)
32
+ "\"#{node.lexical_form}\"^^<#{node.datatype_uri}>"
33
+ else
34
+ raise SyntaxError, "Unknown node type"
35
+ end
36
+ end
37
+
38
+ # Imports NTriples input +file+ to +graph+ or creates a new graph if +graph+ is nil.
39
+ def self.import(file, graph = nil)
40
+ NTriples.new(file, graph).import
41
+ end
42
+
43
+ # Imports a string NTriples representation of a node into +graph+ or creates a new graph if +graph+ is nil.
44
+ def self.import_node(str, graph = nil)
45
+ NTriples.new(StringIO.new(str), graph).import_node
46
+ end
47
+
48
+ # Creates an instance of NTriples for importing an NTriples +file+ into +graph+.
49
+ def initialize(file, graph = nil)
50
+ @lineno = 1
51
+ @charno = 1
52
+ @file = file.respond_to?(:getc) ? file : StringIO.new(file.to_s)
53
+ stretch_buf_to(1)
54
+ @graph = graph.nil? ? RDF::Graph::Memory.new : graph
55
+ end
56
+
57
+ # Runs the import for this NTriples instance, and returns the graph.
58
+ def import
59
+ lines unless @file.eof?
60
+ @graph
61
+ end
62
+
63
+ # Runs the import for a single node for this NTriples instance, and returns the node.
64
+ def import_node
65
+ if ws?
66
+ wses
67
+ end
68
+
69
+ object
70
+ end
71
+
72
+ private
73
+ def position
74
+ "line: #{@lineno}, char: #{@charno}"
75
+ end
76
+
77
+ # All of the internal parsing methods follow
78
+ def stretch_buf_to(x)
79
+ @buf ||= ''
80
+ goal = x - @buf.length
81
+ while goal > 0 && !@file.eof?
82
+ @buf << @file.getc.chr
83
+ goal -= 1
84
+ @charno += 1
85
+ end
86
+ end
87
+
88
+ def consume(str = nil)
89
+ if str.nil?
90
+ stretch_buf_to(2)
91
+ @buf.slice!(0).chr
92
+ elsif test(str)
93
+ stretch_buf_to(str.length + 1)
94
+
95
+ if @buf.length >= str.length
96
+ @buf.slice!(0, str.length)
97
+ end
98
+ else
99
+ raise SyntaxError, "Expected #{str} at #{position}"
100
+ end
101
+ end
102
+
103
+ def test(str)
104
+ stretch_buf_to(str.length)
105
+ @buf[0, str.length] == str
106
+ end
107
+
108
+ def peek
109
+ @buf[0, 1]
110
+ end
111
+
112
+ def unescape(str)
113
+ str
114
+ end
115
+
116
+ ## Parsing Methods
117
+ def lines
118
+ line
119
+ while line?
120
+ line
121
+ end
122
+ end
123
+
124
+ def line
125
+ @charno = 1
126
+ if ws?
127
+ wses
128
+ end
129
+
130
+ case
131
+ when comment?
132
+ comment
133
+ eoln
134
+ when triple?
135
+ triple
136
+ eoln
137
+ when eoln? || @file.eof?
138
+ eoln
139
+ else
140
+ raise SyntaxError, "Expected comment, triple, or empty line at #{position}"
141
+ end
142
+ @lineno += 1
143
+ end
144
+
145
+ def wses
146
+ str = ws
147
+ while ws?
148
+ str << ws
149
+ end
150
+ str
151
+ end
152
+
153
+ def ws
154
+ if space? || tab?
155
+ consume
156
+ else
157
+ raise SyntaxError, "Expected space or tab at #{position}"
158
+ end
159
+ end
160
+
161
+ def comment
162
+ str = consume('#')
163
+ while character_no_cr_or_lf?
164
+ str = consume
165
+ end
166
+ str
167
+ end
168
+
169
+ def triple
170
+ sub = subject
171
+ wses
172
+ pred = predicate
173
+ wses
174
+ obj = object
175
+ if ws?
176
+ wses
177
+ end
178
+ consume('.')
179
+ if ws?
180
+ wses
181
+ end
182
+ @graph.add(RDF::Triple.new(sub, pred, obj))
183
+ end
184
+
185
+ def subject
186
+ if uriRef?
187
+ uriRef
188
+ elsif nodeId?
189
+ nodeId
190
+ else
191
+ raise SyntaxError, "Expected uriRef or nodeId at #{position}"
192
+ end
193
+ end
194
+
195
+ def predicate
196
+ uriRef
197
+ end
198
+
199
+ def object
200
+ if uriRef?
201
+ uriRef
202
+ elsif nodeId?
203
+ nodeId
204
+ elsif lit_string?
205
+ literal
206
+ end
207
+ end
208
+
209
+ def uriRef
210
+ consume('<')
211
+ node = RDF::UriNode.new(absoluteUri)
212
+ consume('>')
213
+ node
214
+ end
215
+
216
+ def absoluteUri
217
+ str = character
218
+ while character? && !test('>')
219
+ str << character
220
+ end
221
+ unescape(str)
222
+ end
223
+
224
+ def nodeId
225
+ consume('_:')
226
+ RDF::BlankNode.new(name, @graph)
227
+ end
228
+
229
+ def name
230
+ str = ''
231
+ if cap_az? || az?
232
+ str << consume
233
+ else
234
+ raise SyntaxError, "Expected A-Z or a-z at #{position}"
235
+ end
236
+
237
+ while cap_az? || az? || num?
238
+ str << consume
239
+ end
240
+ str
241
+ end
242
+
243
+ def literal
244
+ str = lit_string
245
+ if lang?
246
+ lit_lang = lang
247
+ elsif datatype?
248
+ lit_dt = datatype
249
+ end
250
+
251
+ if lit_dt
252
+ RDF::TypedLiteralNode.new(str, lit_dt)
253
+ else
254
+ RDF::PlainLiteralNode.new(str, lit_lang)
255
+ end
256
+ end
257
+
258
+ def lit_string
259
+ consume('"')
260
+ str = string
261
+ consume('"')
262
+ unescape(str)
263
+ end
264
+
265
+ def string
266
+ str = ''
267
+ backslash = false
268
+ while character? && (!test('"') || backslash)
269
+ char = character
270
+ if char == '\\' && !backslash
271
+ backslash = true
272
+ else
273
+ if backslash
274
+ backslash = false
275
+ char = convert_backslash(char)
276
+ end
277
+
278
+ str << char
279
+ end
280
+ end
281
+ str
282
+ end
283
+
284
+ def convert_backslash(char)
285
+ case char
286
+ when 'n'
287
+ "\n"
288
+ when 'r'
289
+ "\r"
290
+ when 't'
291
+ "\t"
292
+ when 'u'
293
+ small_unicode_value
294
+ when 'U'
295
+ long_unicode_value
296
+ else
297
+ char
298
+ end
299
+ end
300
+
301
+ def small_unicode_value
302
+ value = ''
303
+ while peek =~ /[A-Fa-f0-9]/
304
+ value << consume
305
+ end
306
+
307
+ if value.size <= 4
308
+ value.hex.utf8
309
+ else
310
+ raise SyntaxError, 'Expected no more than four hexadecimal characters'
311
+ end
312
+ end
313
+
314
+ def long_unicode_value
315
+ value = ''
316
+ while peek =~ /[A-Fa-f0-9]/
317
+ value << consume
318
+ end
319
+
320
+ if value.size >= 5 && value.size <= 8
321
+ value.hex.utf8
322
+ else
323
+ raise SyntaxError, 'Expected between five and eight hexadecimal characters'
324
+ end
325
+ end
326
+
327
+ def lang
328
+ consume('@')
329
+ language
330
+ end
331
+
332
+ def language
333
+ str = ''
334
+ if az?
335
+ str << consume
336
+ else
337
+ raise SyntaxError, "Expected a-z at #{position}"
338
+ end
339
+
340
+ while az? && !test('-')
341
+ str << consume
342
+ end
343
+
344
+ while test('-')
345
+ str << consume
346
+ if az? || num?
347
+ str << consume
348
+ else
349
+ raise SyntaxError, "Expected a-z or number at #{position}"
350
+ end
351
+
352
+ while az? || num?
353
+ str << consume
354
+ end
355
+ end
356
+ str
357
+ end
358
+
359
+ def datatype
360
+ consume('^^')
361
+ uriRef.uri
362
+ end
363
+
364
+ def character
365
+ if character?
366
+ consume
367
+ else
368
+ raise SyntaxError, "Expected character at #{position}"
369
+ end
370
+ end
371
+
372
+ def eoln
373
+ str = ''
374
+ if cr?
375
+ str = consume
376
+ if lf?
377
+ str << consume
378
+ end
379
+ elsif lf?
380
+ str = consume
381
+ elsif !@file.eof?
382
+ raise SyntaxError, "Expected cr, lf, or crlf at #{position}"
383
+ end
384
+ str
385
+ end
386
+
387
+ ## Test Methods
388
+ def line?
389
+ ws? || comment? || uriRef? || nodeId? || eoln?
390
+ end
391
+
392
+ def comment?
393
+ test('#')
394
+ end
395
+
396
+ def triple?
397
+ subject?
398
+ end
399
+
400
+ def subject?
401
+ uriRef? || nodeId?
402
+ end
403
+
404
+ def predicate?
405
+ uriRef?
406
+ end
407
+
408
+ def object?
409
+ uriRef? || nodeId? || lit_string?
410
+ end
411
+
412
+ def uriRef?
413
+ test('<')
414
+ end
415
+
416
+ def nodeId?
417
+ test("_:")
418
+ end
419
+
420
+ def lit_string?
421
+ test('"')
422
+ end
423
+
424
+ def lang?
425
+ test("@")
426
+ end
427
+
428
+ def datatype?
429
+ test("^^")
430
+ end
431
+
432
+ def language_tag?
433
+ ('a'..'z').include?(peek)
434
+ end
435
+
436
+ def ws?
437
+ space? || tab?
438
+ end
439
+
440
+ def eoln?
441
+ cr? || lf?
442
+ end
443
+
444
+ def space?
445
+ peek == 0x20.chr
446
+ end
447
+
448
+ def cr?
449
+ peek == 0xD.chr
450
+ end
451
+
452
+ def lf?
453
+ peek == 0xA.chr
454
+ end
455
+
456
+ def tab?
457
+ peek == 0x9.chr
458
+ end
459
+
460
+ def string?
461
+ @file.eof? || character?
462
+ end
463
+
464
+ def cap_az?
465
+ ('A'..'Z').include?(peek)
466
+ end
467
+
468
+ def az?
469
+ ('a'..'z').include?(peek)
470
+ end
471
+
472
+ def num?
473
+ ('0'..'9').include?(peek)
474
+ end
475
+
476
+ def name?
477
+ cap_az? || az?
478
+ end
479
+
480
+ def absoluteUri?
481
+ character?
482
+ end
483
+
484
+ def character_no_cr_or_lf?
485
+ character? && !(cr? || lf?)
486
+ end
487
+
488
+ def character?
489
+ (0x20.chr .. 0x7E.chr).include?(peek)
490
+ end
491
+ end
492
+ end
493
+ end
@@ -0,0 +1,118 @@
1
+ module RDF
2
+ module Graph
3
+ # Base class for graph implementations. This class provides basic
4
+ # functionality and common interface for all graphs.
5
+ class Base
6
+ # Exports the triples for this graph in +format+ to +file+. If +file+ is
7
+ # nil, then the export is returned as a string.
8
+ #
9
+ # There is one format provided by default (:ntriples), but the supported
10
+ # formats are depended upon the particular graph implementation, and graph
11
+ # implementations can override this method to use a (presumably) faster
12
+ # export function from the underlying triple store--as long as it
13
+ # preserves the interface expectations.
14
+ #
15
+ # If a graph implementation overrides this method, and the underlying
16
+ # triple store does not support one of the default formats
17
+ # (see RDF::Format), then it is recommended that the implementation make
18
+ # use of the RDF::Format classes to provide exports for the default
19
+ # formats.
20
+ #
21
+ # *Raises*:: <tt></tt>
22
+ # UnsupportedFormatError:: if +format+ is not supported for export
23
+ def export(format = :ntriples, file = nil)
24
+ file ||= StringIO.new
25
+
26
+ case format
27
+ when :ntriples
28
+ RDF::Format::NTriples.export(self, file)
29
+ else
30
+ raise UnsupportedFormatError
31
+ end
32
+
33
+ file.string if file.is_a?(StringIO)
34
+ end
35
+
36
+ # Imports the triples from +file+ in +format+ to this graph.
37
+ #
38
+ # This method will not merge the graphs, meaning the blank nodes from
39
+ # +file+ are not renamed as they are imported. If you want to merge two
40
+ # graphs, then use merge.
41
+ #
42
+ # There is one format provided by default (:ntriples), but the supported
43
+ # formats are depended upon the particular graph implementation, and graph
44
+ # implementations can override this method to use a (presumably) faster
45
+ # import function from the underlying triple store--as long as it
46
+ # preserves the interface expectations.
47
+ #
48
+ # If a graph implementation overrides this method, and the underlying
49
+ # triple store does not support one of the default formats
50
+ # (see RDF::Format), then it is recommended that the implementation make
51
+ # use of the RDF::Format classes to provide imports for the default
52
+ # formats.
53
+ #
54
+ # *Raises*:: <tt></tt>
55
+ # ArgumentError:: if +file+ is nil
56
+ # UnsupportedFormatError:: if +format+ is not supported for import
57
+ def import(file, format = :ntriples)
58
+ raise ArgumentError, "file cannot be blank" if file.nil?
59
+
60
+ case format
61
+ when :ntriples
62
+ RDF::Format::NTriples.import(file, self)
63
+ else
64
+ raise UnsupportedFormatError
65
+ end
66
+ end
67
+
68
+ # Merges +graph+ into the current graph. In doing so it will rename all
69
+ # of the blank nodes so they don't collide with any existing blank nodes.
70
+ def merge(graph)
71
+ map = {}
72
+ graph.each do |t|
73
+ s, p, o = t.subject, t.predicate, t.object
74
+ if RDF::BlankNode?(s)
75
+ map[s] = new_blank_node(s.name) unless map.key?(s)
76
+ s = map[s]
77
+ end
78
+ if RDF::BlankNode?(o)
79
+ map[o] = new_blank_node(o.name) unless map.key?(o)
80
+ o = map[o]
81
+ end
82
+
83
+ add(s, p, o)
84
+ end
85
+ end
86
+
87
+ # Returns true if the triple (+s+, +p+, +o+) is in this graph, false
88
+ # otherwise.
89
+ def include?(*triple)
90
+ triple = Triple.construct(*triple)
91
+ return false if variable?(triple.subject) || variable?(triple.object)
92
+ execute(RDF::Query.new.where(triple.subject, triple.predicate, triple.object)).success?
93
+ end
94
+
95
+ def each
96
+ result = execute(RDF::Query.new.where(:s, :p, :o))
97
+ result.bindings.each do |b|
98
+ yield Triple.new(b[:s], b[:p], b[:o])
99
+ end
100
+ end
101
+
102
+ # Returns true if this graph is empty, false otherwise.
103
+ def empty?
104
+ size == 0
105
+ end
106
+
107
+ # Creates a new BlankNode associated with this graph.
108
+ def new_blank_node(name)
109
+ RDF::BlankNode.new(name, self)
110
+ end
111
+
112
+ private
113
+ def variable?(x)
114
+ RDF::BlankNode?(x) && x.graph != self
115
+ end
116
+ end
117
+ end
118
+ end