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.
@@ -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
@@ -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
- Enumerator.new(self, :each_solution)
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! { |variable| variable.to_sym }
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! { |variable| variable.to_sym }
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
@@ -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
- case subject
24
- when Hash
25
- options = subject
26
- subject = options.delete(:subject)
27
- predicate = options.delete(:predicate)
28
- object = options.delete(:object)
29
- initialize(subject, predicate, object, options)
30
- else
31
- @options = options || {}
32
- @subject = subject.is_a?(Symbol) ? Variable.new(subject) : subject
33
- @predicate = predicate.is_a?(Symbol) ? Variable.new(predicate) : predicate
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? { |var| var.bound? }
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? { |var| var.unbound? }
125
+ !variables.empty? && variables.values.all?(&:unbound?)
128
126
  end
129
127
 
130
128
  ##
@@ -22,7 +22,7 @@ class RDF::Query
22
22
  #
23
23
  class Solution
24
24
  # Undefine all superfluous instance methods:
25
- undef_method *(instance_methods.map { |s| s.to_sym } - [:__id__, :__send__, :__class__, :__eval__, :object_id, :instance_eval, :inspect, :class, :is_a?])
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
 
@@ -38,7 +38,9 @@ class RDF::Query
38
38
  # var = RDF::Query::Variable.new(:y, 123)
39
39
  # var.to_s #=> "?y=123"
40
40
  #
41
- class Variable < RDF::Value
41
+ class Variable
42
+ include RDF::Value
43
+
42
44
  # @return [Symbol] The variable's name.
43
45
  attr_accessor :name
44
46
 
@@ -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
@@ -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
- # Returns `false` to indicate that this repository is nondurable.
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
- # Returns `true` if this repository contains no RDF statements.
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
- # Returns the number of RDF statements in this repository.
165
- #
166
- # @return [Integer]
167
- # @see RDF::Enumerable#count
171
+ # @private
172
+ # @see RDF::Countable#count
168
173
  def count
169
- @data.size
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
- # Returns `true` if this repository contains the given RDF statement.
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
- @data.include?(statement)
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
- # Enumerates each RDF statement in this repository.
184
- #
185
- # @yield [statement]
186
- # @yieldparam [Statement] statement
187
- # @return [Enumerator]
188
- # @see RDF::Enumerable#each_statement
189
- def each(&block)
190
- @data.each(&block)
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
- # Inserts the given RDF statement into the underlying storage.
195
- #
196
- # @param [RDF::Statement] statement
197
- # @return [void]
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
- @data.push(statement.dup) unless @data.include?(statement)
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
- # Deletes the given RDF statement from the underlying storage.
204
- #
205
- # @param [RDF::Statement] statement
206
- # @return [void]
251
+ # @private
252
+ # @see RDF::Mutable#delete
207
253
  def delete_statement(statement)
208
- @data.delete(statement)
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
- # Deletes all RDF statements from this repository.
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
@@ -0,0 +1,4 @@
1
+ module RDF; module Util
2
+ autoload :Aliasing, 'rdf/util/aliasing'
3
+ autoload :Cache, 'rdf/util/cache'
4
+ end; end
@@ -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