rdf 0.1.10 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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