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,146 @@
1
+ require 'set'
2
+
3
+ module RDF
4
+ module Graph
5
+ # An in-memory, pure Ruby implementation of an RDF graph. This graph implementation also serves as an example of the interface that every graph implementation should have.
6
+ class Memory < Base
7
+ include RDF::Query::Executer
8
+
9
+ # Creates a new Graph.
10
+ def initialize
11
+ super
12
+ @triples = Set.new
13
+ @idx_s = Hash.new{|h, k| h[k] = Set.new}
14
+ @idx_sp = Hash.new{|h, k| h[k] = Set.new}
15
+ @idx_blank = Hash.new(0)
16
+ end
17
+
18
+ # Adds a triple to the graph. Will accept an instance of Triple, or three parameters for the subject, predicate, and object.
19
+ def add(*triple)
20
+ triple = Triple.construct(*triple)
21
+
22
+ # perform some syntax checks
23
+ raise RDF::UnassociatedBlankNodeError, "#{triple.subject} is not associated with this graph" if RDF::BlankNode?(triple.subject) && triple.subject.graph != self
24
+ raise RDF::UnassociatedBlankNodeError, "#{triple.object} is not associated with this graph" if RDF::BlankNode?(triple.object) && triple.object.graph != self
25
+
26
+ index_subject(triple)
27
+ index_subject_and_predicate(triple)
28
+ index_blank(triple.subject) if RDF::BlankNode?(triple.subject)
29
+ index_blank(triple.object) if RDF::BlankNode?(triple.object)
30
+ @triples << triple
31
+ end
32
+
33
+ # Deletes +triple+ from the graph. Will accept an instance of Triple, or three parameters for the subject, predicate, and object.
34
+ def delete(*triple)
35
+ triple = Triple.construct(*triple)
36
+ unindex_subject(triple)
37
+ unindex_subject_and_predicate(triple)
38
+ unindex_blank(triple.subject) if RDF::BlankNode?(triple.subject)
39
+ unindex_blank(triple.object) if RDF::BlankNode?(triple.object)
40
+ @triples.delete(triple)
41
+ end
42
+
43
+ # Returns true if graph contains +triple+. Will accept an instance of Triple, or three parameters for the subject, predicate, and object.
44
+ def include?(*triple)
45
+ triple = Triple.construct(*triple)
46
+ @triples.include?(triple)
47
+ end
48
+
49
+ # Iterates through all of the triples in this graph.
50
+ def each(&b)
51
+ @triples.each(&b)
52
+ end
53
+
54
+ # Returns the number of triples in this graph.
55
+ def size
56
+ @triples.size
57
+ end
58
+
59
+ private
60
+ def index_subject(triple)
61
+ @idx_s[triple.subject] ||= Set.new
62
+ @idx_s[triple.subject] << triple.predicate
63
+ end
64
+
65
+ def index_subject_and_predicate(triple)
66
+ @idx_sp[[triple.subject, triple.predicate]] ||= Set.new
67
+ @idx_sp[[triple.subject, triple.predicate]] << triple.object
68
+ end
69
+
70
+ def index_blank(node)
71
+ @idx_blank[node] += 1
72
+ end
73
+
74
+ def unindex_subject(triple)
75
+ if @idx_s[triple.subject]
76
+ @idx_s[triple.subject].delete(triple.predicate)
77
+ @idx_s.delete(triple.subject) if @idx_s[triple.subject].empty?
78
+ end
79
+ end
80
+
81
+ def unindex_subject_and_predicate(triple)
82
+ sp = [triple.subject, triple.predicate]
83
+ if @idx_sp[sp]
84
+ @idx_sp[sp].delete(triple.object)
85
+ @idx_sp.delete(sp) if @idx_sp[sp].empty?
86
+ end
87
+ end
88
+
89
+ def unindex_blank(node)
90
+ if @idx_blank[node] > 0
91
+ @idx_blank[node] -= 1
92
+ @idx_blank.delete(node) if @idx_blank[node] == 0 && @idx_blank.key?(node)
93
+ end
94
+ end
95
+
96
+ def match_binding(s, p, o, b)
97
+ if (s.is_a?(Symbol) || (RDF::BlankNode?(s) && !@idx_blank.key?(s))) && !b.bound?(s)
98
+ result = @idx_s.keys.inject(RDF::Query::Result.new) do |r, node|
99
+ binding = b.clone
100
+ binding[s] = node
101
+ r.bindings << binding
102
+ r
103
+ end
104
+ result = match(s, p, o, result)
105
+ result
106
+ else
107
+ bind_predicate(s, p, o, b)
108
+ end
109
+ end
110
+
111
+ def bind_predicate(s, p, o, b)
112
+ if p.is_a?(Symbol) && !b.bound?(p)
113
+ result = @idx_s[b[s]].inject(RDF::Query::Result.new) do |r, node|
114
+ binding = b.clone
115
+ binding[p] = node
116
+ r.bindings << binding
117
+ r
118
+ end
119
+ result = match(s, p, o, result)
120
+ result
121
+ else
122
+ bind_object(s, p, o, b)
123
+ end
124
+ end
125
+
126
+ def bind_object(s, p, o, b)
127
+ if (o.is_a?(Symbol) || (RDF::BlankNode?(o) && !@idx_blank.key?(o))) && !b.bound?(o)
128
+ result = @idx_sp[[b[s],b[p]]].inject(RDF::Query::Result.new) do |r, node|
129
+ binding = b.clone
130
+ binding[o] = node
131
+ r.bindings << binding
132
+ r
133
+ end
134
+
135
+ match(s, p, o, result)
136
+ else
137
+ if @triples.include?(Triple.new(b[s], b[p], b[o]))
138
+ RDF::Query::Result.success!(b)
139
+ else
140
+ RDF::Query::Result.new
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,137 @@
1
+ module RDF
2
+ module Graph
3
+ module Tests
4
+ ##== add ==##
5
+ def test_add_should_insert_triple
6
+ @graph.add(RDF::Triple.new(EX::a, EX::b, EX::c))
7
+ assert @graph.include?(RDF::Triple.new(EX::a, EX::b, EX::c)),
8
+ "Add should accept an RDF::Triple as a parameter"
9
+ end
10
+
11
+ def test_add_should_insert_spo
12
+ @graph.add(EX::a, EX::b, EX::c)
13
+ assert @graph.include?(RDF::Triple.new(EX::a, EX::b, EX::c)),
14
+ "Add should accept three nodes as parameters"
15
+ end
16
+
17
+ def test_add_should_raise_error_for_unassociated_blank_node_subject
18
+ assert_raise(RDF::UnassociatedBlankNodeError) {
19
+ @graph.add(RDF::Graph::Memory.new.new_blank_node('x'), EX::b, EX::c)
20
+ }
21
+ end
22
+
23
+ def test_add_should_raise_error_for_unassociated_blank_node_object
24
+ assert_raise(RDF::UnassociatedBlankNodeError) {
25
+ @graph.add(EX::a, EX::b, RDF::Graph::Memory.new.new_blank_node('x'))
26
+ }
27
+ end
28
+
29
+ ##== include? ==##
30
+ def test_include_should_accept_spo
31
+ @graph.add(RDF::Triple.new(EX::a, EX::b, EX::c))
32
+ assert @graph.include?(EX::a, EX::b, EX::c)
33
+ end
34
+
35
+ ##== delete ==##
36
+ def test_delete_should_remove_triple
37
+ @graph.add(RDF::Triple.new(EX::a, EX::b, EX::c))
38
+ @graph.delete(RDF::Triple.new(EX::a, EX::b, EX::c))
39
+ assert !@graph.include?(RDF::Triple.new(EX::a, EX::b, EX::c))
40
+ end
41
+
42
+ def test_delete_should_remove_spo
43
+ @graph.add(RDF::Triple.new(EX::a, EX::b, EX::c))
44
+ @graph.delete(EX::a, EX::b, EX::c)
45
+ assert !@graph.include?(RDF::Triple.new(EX::a, EX::b, EX::c))
46
+ end
47
+
48
+ ##== empty? ==##
49
+ def test_empty_should_be_true_for_empty_graph
50
+ assert @graph.empty?
51
+ end
52
+
53
+ def test_empty_should_not_be_true_for_non_empty_graph
54
+ @graph.add(RDF::Triple.new(EX::a, EX::b, EX::c))
55
+ assert !@graph.empty?
56
+ end
57
+
58
+ ##== new_blank_node ==##
59
+ def test_new_blank_node_should_be_a_blank_node
60
+ assert RDF::BlankNode?(@graph.new_blank_node('test'))
61
+ end
62
+
63
+ def test_new_blank_node_should_set_graph
64
+ assert_equal @graph, @graph.new_blank_node('test').graph
65
+ end
66
+
67
+ def test_new_blank_node_should_set_name
68
+ assert_equal 'test', @graph.new_blank_node('test').name
69
+ end
70
+
71
+ ##== each ==##
72
+ def test_each_should_iterate_all_nodes
73
+ triples = [RDF::Triple.new(EX::a, EX::b, EX::c),
74
+ RDF::Triple.new(EX::d, EX::e, EX::f),
75
+ RDF::Triple.new(EX::g, EX::h, EX::i)]
76
+ @graph.add(triples[0])
77
+ @graph.add(triples[1])
78
+ @graph.add(triples[2])
79
+
80
+ @graph.each do |t|
81
+ assert triples.delete(t)
82
+ end
83
+ assert_equal [], triples
84
+ end
85
+
86
+ ##== size ==##
87
+ def test_size_should_be_zero_for_empty_graph
88
+ assert_equal 0, @graph.size
89
+ end
90
+
91
+ def test_size_should_be_number_of_triples
92
+ @graph.add(EX::a, EX::b, EX::c)
93
+ @graph.add(EX::d, EX::e, EX::f)
94
+ assert 2, @graph.size
95
+ end
96
+
97
+ ##== merge ==##
98
+ def test_merge_should_copy_triples
99
+ new_graph = RDF::Graph::Memory.new
100
+ new_graph.add(EX::a, EX::b, new_graph.new_blank_node('x'))
101
+ @graph.merge(new_graph)
102
+ assert @graph.execute(RDF::Query.new.where(EX::a, EX::b, :a)).success?
103
+ end
104
+
105
+ def test_merge_should_associate_blank_nodes_with_new_graph
106
+ new_graph = RDF::Graph::Memory.new
107
+ x = new_graph.new_blank_node('x')
108
+ new_graph.add(EX::a, EX::b, x)
109
+ @graph.merge(new_graph)
110
+ assert_equal @graph, @graph.execute(RDF::Query.new.where(EX::a, EX::b, :a)).bindings.to_a.first[:a].graph
111
+ end
112
+
113
+ ##== execute ==##
114
+ def query_executer_test_case
115
+ @graph.add(EX::a, EX::b, EX::c)
116
+ @graph.add(EX::c, EX::e, EX::f)
117
+ q = RDF::Query.new
118
+ q.where(EX::a, EX::b, :a)
119
+ q.where(:a, EX::e, EX::f)
120
+
121
+ @graph.execute(q)
122
+ end
123
+
124
+ def test_execute_should_return_result
125
+ assert query_executer_test_case.is_a?(RDF::Query::Result)
126
+ end
127
+
128
+ def test_execute_should_succeed
129
+ assert query_executer_test_case.success?
130
+ end
131
+
132
+ def test_execute_should_assign_variable
133
+ assert_equal EX::c, query_executer_test_case.bindings.to_a.first[:a]
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,90 @@
1
+ module RDF
2
+ # An RDF namespace that can be used to generate URI references easily.
3
+ #
4
+ # Example:
5
+ #
6
+ # RDF::Namespace.register(:ex, 'http://example.com/')
7
+ # EX::Test #=> 'http://example.com/Test'
8
+ # EX::test #=> 'http://example.com/test'
9
+ module Namespace
10
+ @@prefixes = {}
11
+ @@bases = {}
12
+
13
+ class << self
14
+ # Returns an array of all registered prefixes.
15
+ def prefixes
16
+ @@prefixes.keys
17
+ end
18
+
19
+ # Returns true if the specified +prefix+ is registered.
20
+ def registered?(prefix)
21
+ @@prefixes.key?(prefix)
22
+ end
23
+
24
+ # Registers +prefix+ as a nickname for +base+, and either finds an existing module,
25
+ # or creates a new module named +prefix+ that can be used for expansions.
26
+ def register(prefix, base)
27
+ raise ArgumentError, "prefix cannot be nil" if prefix.to_s.empty?
28
+
29
+ prefix, base = prefix.to_s.to_sym, base.to_s
30
+
31
+ raise ArgumentError, "base is invalid it should end with '/' or '#'" unless ['/', '#'].include?(base[-1,1])
32
+ if @@prefixes.has_key?(prefix)
33
+ $stderr.puts "Warning: Namespace #{prefix} is already defined"
34
+ return find_or_create_module(prefix)
35
+ end
36
+
37
+ @@prefixes[prefix] = base
38
+ @@bases[base] = prefix
39
+
40
+ mod = find_or_create_module(prefix)
41
+ class << mod
42
+ def method_missing(sym, *a, &b)
43
+ raise ArgumentError, "Unexpected arguments for Namespace.expand" if a.size > 0
44
+ RDF::UriNode.new(Namespace.expand(self, sym))
45
+ end
46
+
47
+ def const_missing(sym)
48
+ RDF::UriNode.new(Namespace.expand(self, sym))
49
+ end
50
+
51
+ [:type, :name, :id].each{|a| private(a)}
52
+ end
53
+ mod
54
+ end
55
+
56
+ # Returns the string resulting from looking up the prefix and appending the name
57
+ def expand(prefix, name)
58
+ prefix, name = prefix.to_s.downcase.to_sym, name.to_s
59
+ @@prefixes[prefix] + name
60
+ end
61
+
62
+ # Finds the last '/' or '#' and uses it to split the uri into base and name parts.
63
+ def split(uri)
64
+ uri = uri.to_s
65
+ i = uri.rindex(/\/|#/) + 1
66
+ [uri[0...i].to_s, uri[i..-1].to_s]
67
+ end
68
+
69
+ # Returns the assigned prefix name for a uri, if one exists
70
+ def prefix(uri)
71
+ @@bases[split(uri).first]
72
+ end
73
+
74
+ private
75
+ def find_or_create_module(name)
76
+ name = name.to_s.upcase
77
+
78
+ unless Object.const_defined?(name)
79
+ Object.const_set(name, Module.new)
80
+ end
81
+ Object.const_get(name)
82
+ end
83
+ end
84
+ end
85
+
86
+ Namespace.register(:rdf, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#')
87
+ Namespace.register(:rdfs, 'http://www.w3.org/2000/01/rdf-schema#')
88
+ Namespace.register(:owl, 'http://www.w3.org/2002/07/owl#')
89
+ Namespace.register(:xsd, 'http://www.w3.org/2001/XMLSchema#')
90
+ end
@@ -0,0 +1,36 @@
1
+ module RDF
2
+ # A plain literal node.
3
+ class PlainLiteralNode
4
+ # the lexical form of this LiteralNode.
5
+ attr_reader :lexical_form
6
+ # the language tag (if any) of this LiteralNode.
7
+ attr_reader :language_tag
8
+
9
+ # Creates a new literal node with the specified +lexical_form+ and +language_tag+.
10
+ def initialize(lexical_form, language_tag = nil)
11
+ @lexical_form = lexical_form
12
+ @language_tag = language_tag
13
+ end
14
+
15
+ # Returns true if +o+ is a PlainLiteralNode with the same +lexical_form+ and
16
+ # +language_tag+ (if any).
17
+ def ==(o)
18
+ if RDF::PlainLiteralNode?(o)
19
+ lexical_form == o.lexical_form &&
20
+ language_tag.to_s.downcase == o.language_tag.to_s.downcase
21
+ end
22
+ end
23
+
24
+ alias :eql? :==
25
+
26
+ # Returns a hash value for this PlainLiteralNode.
27
+ def hash
28
+ [-1025818701, lexical_form.hash, language_tag.to_s.downcase.hash].hash
29
+ end
30
+
31
+ # Returns the NTriples representation of this PlainLiteralNode.
32
+ def to_s
33
+ Format::NTriples.export_node(self)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,68 @@
1
+ module RDF
2
+ class Query
3
+ # A single binding of variables in an Graph.
4
+ class Binding
5
+ # Creates a new instange of Binding initializing it with +hash+, or an empty Hash if +hash+ is nil.
6
+ def initialize(hash = nil)
7
+ @hash = hash || {}
8
+ end
9
+
10
+ # Copy constructor that creates a deep copy of this binding.
11
+ def initialize_copy(o)
12
+ @hash = o.instance_variable_get('@hash').clone
13
+ end
14
+
15
+ # Returns true if +key+ is bound in this binding.
16
+ def bound?(key)
17
+ @hash.key?(key)
18
+ end
19
+
20
+ # Returns the value bound to +key+ if +key+ is bound in this binding, or +key+ otherwise.
21
+ def [](key)
22
+ @hash.key?(key) ? @hash[key] : key
23
+ end
24
+
25
+ # Binds +key+ to +value+ in this binding.
26
+ def []=(key, value)
27
+ @hash[key] = value
28
+ end
29
+
30
+ # Returns true if this binding is empty, false otherwise.
31
+ def empty?
32
+ @hash.empty?
33
+ end
34
+
35
+ # Returns true if +o+ is a Binding and it has the same bindings.
36
+ def ==(o)
37
+ if o.is_a?(Binding)
38
+ @hash.each_pair do |k,v|
39
+ return false unless o[k] == v
40
+ end
41
+
42
+ ohash = o.instance_variable_get('@hash')
43
+ ohash.each_pair do |k,v|
44
+ return false unless @hash[k] == v
45
+ end
46
+
47
+ true
48
+ end
49
+ end
50
+
51
+ alias :eql? :==
52
+
53
+ # Returns a hash value for this binding.
54
+ def hash
55
+ h = -901662846
56
+ @hash.each_pair do |k,v|
57
+ h ^= (h << 5) + k.hash + (h >> 2)
58
+ h ^= (h << 5) + v.hash + (h >> 2)
59
+ end
60
+ h
61
+ end
62
+
63
+ def to_s
64
+ "{#{@hash.collect{|k,v| "#{k} => #{v}"}.join(", ")}}"
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,42 @@
1
+ module RDF
2
+ class Query
3
+ # A mix-in module for graph implementations. If a Graph implementation
4
+ # cannot delegate the +execute+ method to the underlying triplestore, then
5
+ # this module can be mixed in to provide Query execution. The graph must
6
+ # only implement the +match+ method.
7
+ module Executer
8
+ # Returns the result of executing +query+ on this graph.
9
+ def execute(query)
10
+ result = nil
11
+
12
+ where_clause = query.where_clause
13
+ begin
14
+ s, p, o = where_clause.shift
15
+ result = match(s, p, o, result)
16
+ end while where_clause.any? && result.success?
17
+
18
+ result
19
+ end
20
+
21
+ # Performs a match of +s+, +p+, and +o+ and returns a set of bindings.
22
+ #
23
+ # If +s+, +p+, or +o+ are Ruby symbols, then they will be considered
24
+ # variables, and they will be bound to all possible nodes in the result.
25
+ #
26
+ # If +s+, +p+, or +o+ are BlankNodes that are unassociated with this
27
+ # graph, then they will be bound to all possible nodes in the result.
28
+ def match(s, p, o, result = nil)
29
+ if result.nil?
30
+ result = match_binding(s, p, o, RDF::Query::Binding.new)
31
+ result
32
+ else
33
+ result = result.bindings.inject(RDF::Query::Result.new) do |r, b|
34
+ r << match_binding(s, p, o, b)
35
+ r
36
+ end
37
+ result
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,54 @@
1
+ module RDF
2
+ class Query
3
+ # A set of bindings for a graph.
4
+ class Result
5
+ def self.success!(*a)
6
+ r = Result.new(*a)
7
+ r.success!
8
+ r
9
+ end
10
+
11
+ attr_reader :bindings
12
+
13
+ # Creates an instance of Result with the specified bindings.
14
+ def initialize(*bindings)
15
+ @success = false
16
+ @bindings = Set.new((bindings || []).flatten.reject{|b| b.empty?})
17
+ end
18
+
19
+ # Returns true if the executed query failed.
20
+ def failure?
21
+ !@success
22
+ end
23
+
24
+ # Returns true if the executed query succeeded.
25
+ def success?
26
+ @success
27
+ end
28
+
29
+ # Sets the success indicator to true.
30
+ def success!
31
+ @success = true
32
+ end
33
+
34
+ # Merges the Result +o+ into this Result.
35
+ def <<(o)
36
+ raise ArgumentError, "Must be RDF::Query::Result" unless o.is_a?(Result)
37
+ if o.success?
38
+ if failure?
39
+ success!
40
+ bindings.clear
41
+ end
42
+
43
+ o.bindings.each do |b|
44
+ bindings << b unless b.empty?
45
+ end
46
+ end
47
+ end
48
+
49
+ def to_s
50
+ "{#{bindings.collect{|b| b.to_s}.join(', ')}}"
51
+ end
52
+ end
53
+ end
54
+ end
data/lib/rdf/query.rb ADDED
@@ -0,0 +1,54 @@
1
+ module RDF
2
+ # An RDF graph query. This represents a complex query that joins several triples. To query for
3
+ # a simple triple, see the +match+ method on RDF::Graph::Memory.
4
+ #
5
+ # A Query is created then passed to the +execute+ method on a graph. In some graph
6
+ # implementations the execute method may delegate to the underlying triple store, but
7
+ # in other cases the +execute+ method may be mixed in from RDF::Query::Executer.
8
+ #
9
+ # At some point I'd like to steal the {query DSL}[http://projects.semwebcentral.org/cgi-bin/viewcvs.cgi/semitar/test/test_query.rb?rev=1.1.1.1&cvsroot=semitar&content-type=text/vnd.viewcvs-markup] from Semitar[http://semitar.projects.semwebcentral.org/].
10
+ #
11
+ # Example:
12
+ # q = RDF::Query.new.select(:a, :c).
13
+ # where(:a, RDF::type, :b).
14
+ # and(:b, RDFS::subClassOf, RDF::Property).
15
+ # and(:b, RDFS::comment, :c)
16
+ # result = some_graph.execute(q)
17
+ class Query
18
+ class << self
19
+ alias :select :new
20
+ end
21
+
22
+ # Creates a new Query.
23
+ def initialize
24
+ @select_clause = []
25
+ @where_clause = []
26
+ end
27
+
28
+ # Adds items to the select clause of this query. When this query is executed, the results will include
29
+ # bindings for these variables.
30
+ def select(*a)
31
+ @select_clause = a
32
+ self
33
+ end
34
+
35
+ # Returns the select clause for this query.
36
+ def select_clause
37
+ @select_clause.clone
38
+ end
39
+
40
+ # Adds the triple +s p o+ to the where clause of this query. When the query is executed, the where clause
41
+ # will constrain the results.
42
+ def where(s, p, o)
43
+ @where_clause << [s, p, o]
44
+ self
45
+ end
46
+
47
+ # Returns the where clause for this query.
48
+ def where_clause
49
+ @where_clause.clone
50
+ end
51
+
52
+ alias :and :where
53
+ end
54
+ end
data/lib/rdf/triple.rb ADDED
@@ -0,0 +1,61 @@
1
+ module RDF
2
+ # A triple.
3
+ class Triple
4
+ def self.construct(*a)
5
+ if a.size == 1
6
+ a[0] if a[0].is_a?(Triple)
7
+ elsif a.size == 3
8
+ Triple.new(*a)
9
+ else
10
+ raise ArgumentError, 'Expected triple or subject, predicate, and object'
11
+ end
12
+ end
13
+
14
+ # the subject of this triple
15
+ attr_reader :subject
16
+ # the predicate of this triple
17
+ attr_reader :predicate
18
+ # the object of this triple
19
+ attr_reader :object
20
+
21
+ # Creates a new triple with the specified +subject+, +predicate+, and +object+.
22
+ #
23
+ # Raises:
24
+ # ArgumentError:: if any of the parameters is nil.
25
+ # LiteralNodeSubjectError:: if +subject+ is a LiteralNode.
26
+ # LiteralNodePredicateError:: if +predicate+ is a LiteralNode.
27
+ # BlankNodePredicateError:: if +predicate+ is a BlankNode.
28
+ def initialize(subject, predicate, object)
29
+ @subject = subject
30
+ @predicate = predicate
31
+ @object = object
32
+
33
+ raise ArgumentError, 'subject must be a node' unless RDF::Node?(@subject)
34
+ raise ArgumentError, 'predicate must be a node' unless RDF::Node?(@predicate)
35
+ raise ArgumentError, 'object must be a node' unless RDF::Node?(@object)
36
+
37
+ raise RDF::InvalidSubjectError, "#{@subject} must be a blank or uri node" unless RDF::BlankNode?(@subject) || RDF::UriNode?(@subject)
38
+ raise RDF::InvalidPredicateError, "#{@predicate} must be a uri node" unless RDF::UriNode?(@predicate)
39
+ end
40
+
41
+ # Returns true if +o+ is a Triple with the same +subject+, +object+, and +predicate+.
42
+ def ==(o)
43
+ if RDF::Triple?(o)
44
+ subject == o.subject &&
45
+ predicate == o.predicate &&
46
+ object == o.object
47
+ end
48
+ end
49
+
50
+ alias :eql? :==
51
+
52
+ # Returns a hash value for this Triple.
53
+ def hash
54
+ [-78640401, subject.hash, predicate.hash, object.hash].hash
55
+ end
56
+
57
+ def to_s
58
+ "#{subject} #{predicate} #{object}."
59
+ end
60
+ end
61
+ end