rdf 0.1.10 → 0.2.0
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/CONTRIBUTORS +3 -0
- data/README +68 -34
- data/VERSION +1 -1
- data/etc/doap.nt +16 -0
- data/lib/rdf.rb +24 -18
- data/lib/rdf/format.rb +47 -22
- data/lib/rdf/mixin/countable.rb +42 -0
- data/lib/rdf/mixin/durable.rb +2 -0
- data/lib/rdf/mixin/enumerable.rb +72 -50
- data/lib/rdf/mixin/inferable.rb +2 -3
- data/lib/rdf/mixin/mutable.rb +51 -107
- data/lib/rdf/mixin/queryable.rb +43 -11
- data/lib/rdf/mixin/readable.rb +2 -0
- data/lib/rdf/mixin/writable.rb +130 -0
- data/lib/rdf/model/graph.rb +47 -14
- data/lib/rdf/model/literal.rb +12 -2
- data/lib/rdf/model/node.rb +51 -2
- data/lib/rdf/model/resource.rb +7 -10
- data/lib/rdf/model/statement.rb +59 -41
- data/lib/rdf/model/uri.rb +68 -8
- data/lib/rdf/model/value.rb +26 -31
- data/lib/rdf/ntriples/reader.rb +7 -6
- data/lib/rdf/ntriples/writer.rb +90 -0
- data/lib/rdf/query.rb +6 -6
- data/lib/rdf/query/pattern.rb +13 -15
- data/lib/rdf/query/solution.rb +1 -1
- data/lib/rdf/query/variable.rb +3 -1
- data/lib/rdf/reader.rb +2 -1
- data/lib/rdf/repository.rb +97 -47
- data/lib/rdf/util.rb +4 -0
- data/lib/rdf/util/aliasing.rb +51 -0
- data/lib/rdf/util/cache.rb +131 -0
- data/lib/rdf/version.rb +3 -4
- data/lib/rdf/vocab.rb +11 -10
- data/lib/rdf/vocab/cert.rb +13 -0
- data/lib/rdf/vocab/geo.rb +13 -0
- data/lib/rdf/vocab/rsa.rb +12 -0
- data/lib/rdf/writer.rb +102 -109
- metadata +23 -15
data/lib/rdf/ntriples/writer.rb
CHANGED
@@ -27,6 +27,90 @@ module RDF::NTriples
|
|
27
27
|
class Writer < RDF::Writer
|
28
28
|
format RDF::NTriples::Format
|
29
29
|
|
30
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
31
|
+
ESCAPE_PLAIN = /\A[\x20-\x21\x23-\x5B\x5D-\x7E]*\z/m.freeze
|
32
|
+
ESCAPE_ASCII = /\A[\x00-\x7F]*\z/m.freeze
|
33
|
+
|
34
|
+
##
|
35
|
+
# @param [String] string
|
36
|
+
# @return [String]
|
37
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
38
|
+
def self.escape(string)
|
39
|
+
case
|
40
|
+
when string =~ ESCAPE_PLAIN # a shortcut for the simple case
|
41
|
+
string
|
42
|
+
when string.respond_to?(:ascii_only?) && string.ascii_only?
|
43
|
+
StringIO.open do |buffer|
|
44
|
+
string.each_byte { |u| buffer << escape_ascii(u) }
|
45
|
+
buffer.string
|
46
|
+
end
|
47
|
+
when string.respond_to?(:each_codepoint)
|
48
|
+
StringIO.open do |buffer|
|
49
|
+
string.each_codepoint { |u| buffer << escape_unicode(u) }
|
50
|
+
buffer.string
|
51
|
+
end
|
52
|
+
else # works in Ruby 1.8.x, too
|
53
|
+
StringIO.open do |buffer|
|
54
|
+
string.scan(/./mu) { |c| buffer << escape_unicode(u = c.unpack('U*').first) }
|
55
|
+
buffer.string
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# @param [Integer, #ord] u
|
62
|
+
# @return [String]
|
63
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
64
|
+
def self.escape_unicode(u)
|
65
|
+
case (u = u.ord)
|
66
|
+
when (0x00..0x7F) # ASCII 7-bit
|
67
|
+
escape_ascii(u)
|
68
|
+
when (0x80..0xFFFF) # Unicode BMP
|
69
|
+
escape_utf16(u)
|
70
|
+
when (0x10000..0x10FFFF) # Unicode
|
71
|
+
escape_utf32(u)
|
72
|
+
else
|
73
|
+
raise ArgumentError.new("expected a Unicode codepoint in (0x00..0x10FFFF), but got 0x#{u.to_s(16)}")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# @param [Integer, #ord] u
|
79
|
+
# @return [String]
|
80
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
81
|
+
def self.escape_ascii(u)
|
82
|
+
case (u = u.ord)
|
83
|
+
when (0x00..0x08) then escape_utf16(u)
|
84
|
+
when (0x09) then "\\t"
|
85
|
+
when (0x0A) then "\\n"
|
86
|
+
when (0x0B..0x0C) then escape_utf16(u)
|
87
|
+
when (0x0D) then "\\r"
|
88
|
+
when (0x0E..0x1F) then escape_utf16(u)
|
89
|
+
when (0x22) then "\\\""
|
90
|
+
when (0x5C) then "\\\\"
|
91
|
+
when (0x7F) then escape_utf16(u)
|
92
|
+
when (0x00..0x7F) then u.chr
|
93
|
+
else
|
94
|
+
raise ArgumentError.new("expected an ASCII character in (0x00..0x7F), but got 0x#{u.to_s(16)}")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# @param [Integer, #ord] u
|
100
|
+
# @return [String]
|
101
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
102
|
+
def self.escape_utf16(u)
|
103
|
+
sprintf("\\u%04X", u.ord)
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# @param [Integer, #ord] u
|
108
|
+
# @return [String]
|
109
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
110
|
+
def self.escape_utf32(u)
|
111
|
+
sprintf("\\U%08X", u.ord)
|
112
|
+
end
|
113
|
+
|
30
114
|
##
|
31
115
|
# Returns the serialized N-Triples representation of the given RDF
|
32
116
|
# value.
|
@@ -121,5 +205,11 @@ module RDF::NTriples
|
|
121
205
|
quoted(escaped(literal.to_s))
|
122
206
|
end
|
123
207
|
end
|
208
|
+
|
209
|
+
##
|
210
|
+
# @private
|
211
|
+
def escaped(string)
|
212
|
+
self.class.escape(string)
|
213
|
+
end
|
124
214
|
end
|
125
215
|
end
|
data/lib/rdf/query.rb
CHANGED
@@ -43,13 +43,13 @@ module RDF
|
|
43
43
|
|
44
44
|
include ::Enumerable
|
45
45
|
|
46
|
-
# @return [Hash{Symbol => Variable}]
|
46
|
+
# @return [Hash{Symbol => RDF::Query::Variable}]
|
47
47
|
attr_reader :variables
|
48
48
|
|
49
|
-
# @return [Array<Pattern>]
|
49
|
+
# @return [Array<RDF::Query::Pattern>]
|
50
50
|
attr_reader :patterns
|
51
51
|
|
52
|
-
# @return [Array<Hash{Symbol => Value}>] An unordered sequence of query solutions.
|
52
|
+
# @return [Array<Hash{Symbol => RDF::Value}>] An unordered sequence of query solutions.
|
53
53
|
attr_accessor :solutions
|
54
54
|
|
55
55
|
# @return [Hash]
|
@@ -81,7 +81,7 @@ module RDF
|
|
81
81
|
# @return [Enumerator]
|
82
82
|
def each_solution(&block)
|
83
83
|
unless block_given?
|
84
|
-
|
84
|
+
enum_for(:each_solution)
|
85
85
|
else
|
86
86
|
solutions.each do |solution|
|
87
87
|
block.call(solution.is_a?(Solution) ? solution : Solution.new(solution))
|
@@ -138,7 +138,7 @@ module RDF
|
|
138
138
|
raise ArgumentError.new("wrong number of arguments (0 for 1)")
|
139
139
|
else
|
140
140
|
# TODO: support for descending sort, e.g. order(:s => :asc, :p => :desc)
|
141
|
-
variables.map!
|
141
|
+
variables.map!(&:to_sym)
|
142
142
|
solutions.sort! do |a, b|
|
143
143
|
a = variables.map { |variable| a[variable].to_s }
|
144
144
|
b = variables.map { |variable| b[variable].to_s }
|
@@ -157,7 +157,7 @@ module RDF
|
|
157
157
|
# @return [Query]
|
158
158
|
def project(*variables)
|
159
159
|
unless variables.empty?
|
160
|
-
variables.map!
|
160
|
+
variables.map!(&:to_sym)
|
161
161
|
solutions.each do |bindings|
|
162
162
|
bindings.delete_if { |k, v| !variables.include?(k) } # FIXME
|
163
163
|
end
|
data/lib/rdf/query/pattern.rb
CHANGED
@@ -20,19 +20,17 @@ class RDF::Query
|
|
20
20
|
# @param [Hash{Symbol => Object}] options
|
21
21
|
# @option options [Boolean] :optional (false)
|
22
22
|
def initialize(subject = nil, predicate = nil, object = nil, options = {})
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
@object = object.is_a?(Symbol) ? Variable.new(object) : object
|
35
|
-
end
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# @private
|
28
|
+
def initialize!
|
29
|
+
@context = Variable.new(@context) if @context.is_a?(Symbol)
|
30
|
+
@subject = Variable.new(@subject) if @subject.is_a?(Symbol)
|
31
|
+
@predicate = Variable.new(@predicate) if @predicate.is_a?(Symbol)
|
32
|
+
@object = Variable.new(@object) if @object.is_a?(Symbol)
|
33
|
+
super
|
36
34
|
end
|
37
35
|
|
38
36
|
##
|
@@ -108,7 +106,7 @@ class RDF::Query
|
|
108
106
|
#
|
109
107
|
# @return [Boolean]
|
110
108
|
def bound?
|
111
|
-
!variables.empty? && variables.values.all?
|
109
|
+
!variables.empty? && variables.values.all?(&:bound?)
|
112
110
|
end
|
113
111
|
|
114
112
|
##
|
@@ -124,7 +122,7 @@ class RDF::Query
|
|
124
122
|
#
|
125
123
|
# @return [Boolean]
|
126
124
|
def unbound?
|
127
|
-
!variables.empty? && variables.values.all?
|
125
|
+
!variables.empty? && variables.values.all?(&:unbound?)
|
128
126
|
end
|
129
127
|
|
130
128
|
##
|
data/lib/rdf/query/solution.rb
CHANGED
@@ -22,7 +22,7 @@ class RDF::Query
|
|
22
22
|
#
|
23
23
|
class Solution
|
24
24
|
# Undefine all superfluous instance methods:
|
25
|
-
undef_method
|
25
|
+
undef_method(*(instance_methods.map(&:to_sym) - [:__id__, :__send__, :__class__, :__eval__, :object_id, :instance_eval, :inspect, :class, :is_a?]))
|
26
26
|
|
27
27
|
include Enumerable
|
28
28
|
|
data/lib/rdf/query/variable.rb
CHANGED
data/lib/rdf/reader.rb
CHANGED
@@ -35,6 +35,7 @@ module RDF
|
|
35
35
|
# @see RDF::Writer
|
36
36
|
class Reader
|
37
37
|
extend ::Enumerable
|
38
|
+
include RDF::Readable
|
38
39
|
include ::Enumerable
|
39
40
|
|
40
41
|
##
|
@@ -148,7 +149,7 @@ module RDF
|
|
148
149
|
|
149
150
|
##
|
150
151
|
# @yield [triple]
|
151
|
-
# @yieldparam [Array(Value)]
|
152
|
+
# @yieldparam [Array(RDF::Value)]
|
152
153
|
# @return [Enumerator]
|
153
154
|
def each_triple(&block)
|
154
155
|
begin
|
data/lib/rdf/repository.rb
CHANGED
@@ -41,10 +41,17 @@ module RDF
|
|
41
41
|
# repository.clear!
|
42
42
|
#
|
43
43
|
class Repository
|
44
|
+
include RDF::Countable
|
44
45
|
include RDF::Enumerable
|
45
|
-
include RDF::Durable
|
46
|
-
include RDF::Mutable
|
47
46
|
include RDF::Queryable
|
47
|
+
include RDF::Mutable
|
48
|
+
include RDF::Durable
|
49
|
+
|
50
|
+
##
|
51
|
+
# Returns the options passed to this repository when it was constructed.
|
52
|
+
#
|
53
|
+
# @return [Hash{Symbol => Object}]
|
54
|
+
attr_reader :options
|
48
55
|
|
49
56
|
##
|
50
57
|
# Returns the {URI} of this repository.
|
@@ -94,10 +101,7 @@ module RDF
|
|
94
101
|
@title = @options.delete(:title)
|
95
102
|
|
96
103
|
# Provide a default in-memory implementation:
|
97
|
-
if self.class.equal?(RDF::Repository)
|
98
|
-
@data = []
|
99
|
-
send(:extend, Implementation)
|
100
|
-
end
|
104
|
+
send(:extend, Implementation) if self.class.equal?(RDF::Repository)
|
101
105
|
|
102
106
|
if block_given?
|
103
107
|
case block.arity
|
@@ -128,6 +132,13 @@ module RDF
|
|
128
132
|
##
|
129
133
|
# @see RDF::Repository
|
130
134
|
module Implementation
|
135
|
+
##
|
136
|
+
# @private
|
137
|
+
def self.extend_object(obj)
|
138
|
+
obj.instance_variable_set(:@data, {})
|
139
|
+
super
|
140
|
+
end
|
141
|
+
|
131
142
|
##
|
132
143
|
# Returns `true` if this repository supports `feature`.
|
133
144
|
#
|
@@ -143,76 +154,115 @@ module RDF
|
|
143
154
|
end
|
144
155
|
|
145
156
|
##
|
146
|
-
#
|
147
|
-
#
|
148
|
-
# @return [Boolean]
|
149
|
-
# @see RDF::Durable#durable?
|
157
|
+
# @private
|
158
|
+
# @see RDF::Durable#durable?
|
150
159
|
def durable?
|
151
160
|
false
|
152
161
|
end
|
153
162
|
|
154
163
|
##
|
155
|
-
#
|
156
|
-
#
|
157
|
-
# @return [Boolean]
|
158
|
-
# @see RDF::Enumerable#empty?
|
164
|
+
# @private
|
165
|
+
# @see RDF::Countable#empty?
|
159
166
|
def empty?
|
160
167
|
@data.empty?
|
161
168
|
end
|
162
169
|
|
163
170
|
##
|
164
|
-
#
|
165
|
-
#
|
166
|
-
# @return [Integer]
|
167
|
-
# @see RDF::Enumerable#count
|
171
|
+
# @private
|
172
|
+
# @see RDF::Countable#count
|
168
173
|
def count
|
169
|
-
|
174
|
+
count = 0
|
175
|
+
@data.each do |c, ss|
|
176
|
+
ss.each do |s, ps|
|
177
|
+
ps.each do |p, os|
|
178
|
+
count += os.size
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
count
|
170
183
|
end
|
171
184
|
|
172
185
|
##
|
173
|
-
#
|
174
|
-
#
|
175
|
-
# @param [Statement] statement
|
176
|
-
# @return [Boolean]
|
177
|
-
# @see RDF::Enumerable#has_statement?
|
186
|
+
# @private
|
187
|
+
# @see RDF::Enumerable#has_statement?
|
178
188
|
def has_statement?(statement)
|
179
|
-
|
189
|
+
s, p, o, c = statement.to_quad
|
190
|
+
@data.has_key?(c) &&
|
191
|
+
@data[c].has_key?(s) &&
|
192
|
+
@data[c][s].has_key?(p) &&
|
193
|
+
@data[c][s][p].include?(o)
|
180
194
|
end
|
181
195
|
|
182
196
|
##
|
183
|
-
#
|
184
|
-
#
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
197
|
+
# @private
|
198
|
+
# @see RDF::Enumerable#each_statement
|
199
|
+
def each_statement(&block)
|
200
|
+
if block_given?
|
201
|
+
# Note that to iterate in a more consistent fashion despite
|
202
|
+
# possible concurrent mutations to `@data`, we use `#dup` to make
|
203
|
+
# shallow copies of the nested hashes before beginning the
|
204
|
+
# iteration over their keys and values.
|
205
|
+
@data.dup.each do |c, ss|
|
206
|
+
ss.dup.each do |s, ps|
|
207
|
+
ps.dup.each do |p, os|
|
208
|
+
os.dup.each do |o|
|
209
|
+
block.call(RDF::Statement.new(s, p, o, :context => c))
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
else
|
215
|
+
enum_statement
|
216
|
+
end
|
191
217
|
end
|
192
218
|
|
219
|
+
alias_method :each, :each_statement
|
220
|
+
|
193
221
|
##
|
194
|
-
#
|
195
|
-
#
|
196
|
-
|
197
|
-
|
222
|
+
# @private
|
223
|
+
# @see RDF::Enumerable#has_context?
|
224
|
+
def has_context?(value)
|
225
|
+
@data.keys.compact.include?(value)
|
226
|
+
end
|
227
|
+
|
228
|
+
##
|
229
|
+
# @private
|
230
|
+
# @see RDF::Enumerable#each_context
|
231
|
+
def each_context(&block)
|
232
|
+
block_given? ? @data.keys.compact.each(&block) : enum_context
|
233
|
+
end
|
234
|
+
|
235
|
+
protected
|
236
|
+
|
237
|
+
##
|
238
|
+
# @private
|
239
|
+
# @see RDF::Mutable#insert
|
198
240
|
def insert_statement(statement)
|
199
|
-
|
241
|
+
unless has_statement?(statement)
|
242
|
+
s, p, o, c = statement.to_quad
|
243
|
+
@data[c] ||= {}
|
244
|
+
@data[c][s] ||= {}
|
245
|
+
@data[c][s][p] ||= []
|
246
|
+
@data[c][s][p] << o
|
247
|
+
end
|
200
248
|
end
|
201
249
|
|
202
250
|
##
|
203
|
-
#
|
204
|
-
#
|
205
|
-
# @param [RDF::Statement] statement
|
206
|
-
# @return [void]
|
251
|
+
# @private
|
252
|
+
# @see RDF::Mutable#delete
|
207
253
|
def delete_statement(statement)
|
208
|
-
|
254
|
+
if has_statement?(statement)
|
255
|
+
s, p, o, c = statement.to_quad
|
256
|
+
@data[c][s][p].delete(o)
|
257
|
+
@data[c][s].delete(p) if @data[c][s][p].empty?
|
258
|
+
@data[c].delete(s) if @data[c][s].empty?
|
259
|
+
@data.delete(c) if @data[c].empty?
|
260
|
+
end
|
209
261
|
end
|
210
262
|
|
211
263
|
##
|
212
|
-
#
|
213
|
-
#
|
214
|
-
# @return [void]
|
215
|
-
# @see RDF::Mutable#clear
|
264
|
+
# @private
|
265
|
+
# @see RDF::Mutable#clear
|
216
266
|
def clear_statements
|
217
267
|
@data.clear
|
218
268
|
end
|
data/lib/rdf/util.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
module RDF; module Util
|
2
|
+
module Aliasing
|
3
|
+
##
|
4
|
+
# Helpers for late-bound instance method aliasing.
|
5
|
+
#
|
6
|
+
# Anything that extends this module will obtain an `alias_method` class
|
7
|
+
# method that creates late-bound instance method aliases instead of the
|
8
|
+
# default early-bound aliases created by Ruby's `Module#alias_method`.
|
9
|
+
#
|
10
|
+
# This is useful because RDF.rb mixins typically alias a number of
|
11
|
+
# overridable methods. For example, `RDF::Enumerable#count` has the
|
12
|
+
# aliases `#size` and `#length`. Normally if implementing classes were
|
13
|
+
# to override the default method, the aliased methods would still be
|
14
|
+
# bound to the mixin's original reference implementation rather than the
|
15
|
+
# new overridden method. Mixing in this module into the implementing
|
16
|
+
# class fixes this problem.
|
17
|
+
#
|
18
|
+
# @example Using late-bound aliasing in a module
|
19
|
+
# module MyModule
|
20
|
+
# extend RDF::Util::Aliasing::LateBound
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @example Using late-bound aliasing in a class
|
24
|
+
# class MyClass
|
25
|
+
# extend RDF::Util::Aliasing::LateBound
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# @see http://en.wikipedia.org/wiki/Name_binding
|
29
|
+
# @since 0.2.0
|
30
|
+
module LateBound
|
31
|
+
##
|
32
|
+
# Makes `new_name` a late-bound alias of the method `old_name`.
|
33
|
+
#
|
34
|
+
# @example Aliasing the `#count` method to `#size` and `#length`
|
35
|
+
# alias_method :size, :count
|
36
|
+
# alias_method :length, :count
|
37
|
+
#
|
38
|
+
# @param [Symbol, #to_sym] new_name
|
39
|
+
# @param [Symbol, #to_sym] old_name
|
40
|
+
# @return [void]
|
41
|
+
# @see http://ruby-doc.org/core/classes/Module.html#M001653
|
42
|
+
def alias_method(new_name, old_name)
|
43
|
+
new_name, old_name = new_name.to_sym, old_name.to_sym
|
44
|
+
self.__send__(:define_method, new_name) do |*args, &block|
|
45
|
+
__send__(old_name, *args, &block)
|
46
|
+
end
|
47
|
+
return self
|
48
|
+
end
|
49
|
+
end # module LateBound
|
50
|
+
end # module Aliasing
|
51
|
+
end; end # module RDF::Util
|