rdf 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,5 +1,4 @@
1
1
  # RDF.rb: Linked Data for Ruby
2
- [![Build Status](https://secure.travis-ci.org/ruby-rdf/rdf.png?branch=master)](http://travis-ci.org/ruby-rdf/rdf)
3
2
 
4
3
  This is a pure-Ruby library for working with [Resource Description Framework
5
4
  (RDF)][RDF] data.
@@ -10,6 +9,9 @@ This is a pure-Ruby library for working with [Resource Description Framework
10
9
  * <http://blog.datagraph.org/2010/04/parsing-rdf-with-ruby>
11
10
  * <http://blog.datagraph.org/2010/04/rdf-repository-howto>
12
11
 
12
+ [![Gem Version](https://badge.fury.io/rb/rdf.png)](http://badge.fury.io/rb/rdf)
13
+ [![Build Status](https://travis-ci.org/ruby-rdf/rdf.png?branch=master)](http://travis-ci.org/ruby-rdf/rdf)
14
+
13
15
  ## Features
14
16
 
15
17
  * 100% pure Ruby with minimal dependencies and no bloat.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.5
1
+ 1.0.6
data/lib/rdf/format.rb CHANGED
@@ -13,18 +13,18 @@ module RDF
13
13
  # RDF::Format.for("etc/doap.nt")
14
14
  # RDF::Format.for(:file_name => "etc/doap.nt")
15
15
  # RDF::Format.for(:file_extension => "nt")
16
- # RDF::Format.for(:content_type => "text/plain")
16
+ # RDF::Format.for(:content_type => "application/n-triples")
17
17
  #
18
18
  # @example Obtaining serialization format MIME types
19
- # RDF::Format.content_types #=> {"text/plain" => [RDF::NTriples::Format]}
19
+ # RDF::Format.content_types #=> {"application/n-triples" => [RDF::NTriples::Format]}
20
20
  #
21
21
  # @example Obtaining serialization format file extension mappings
22
22
  # RDF::Format.file_extensions #=> {:nt => [RDF::NTriples::Format]}
23
23
  #
24
24
  # @example Defining a new RDF serialization format class
25
25
  # class RDF::NTriples::Format < RDF::Format
26
- # content_type 'text/plain', :extension => :nt
27
- # content_encoding 'ascii'
26
+ # content_type 'application/n-triples', :extension => :nt
27
+ # content_encoding 'utf-8'
28
28
  #
29
29
  # reader RDF::NTriples::Reader
30
30
  # writer RDF::NTriples::Writer
@@ -249,7 +249,7 @@ module RDF
249
249
  #
250
250
  # @example
251
251
  #
252
- # RDF::NTriples::Format.name => "NTriples"
252
+ # RDF::NTriples::Format.name => "N-Triples"
253
253
  #
254
254
  # @return [Symbol]
255
255
  def self.name
@@ -355,7 +355,7 @@ module RDF
355
355
  # match cannot be unambigiously found otherwise.
356
356
  #
357
357
  # @example
358
- # RDF::NTriples::Format.detect("<a> <b> <c> .") => true
358
+ # RDF::NTriples::Format.detect("<a> <b> <c> .") #=> true
359
359
  #
360
360
  # @param [String] sample Beginning several bytes (~ 1K) of input.
361
361
  # @return [Boolean]
@@ -401,14 +401,21 @@ module RDF
401
401
  |ct, cl| (cl.include?(self) && ct != @@content_type[self]) ? ct : nil }].flatten.compact
402
402
  else
403
403
  @@content_type[self] = type
404
- (@@content_types[type] ||= []) << self
404
+ @@content_types[type] ||= []
405
+ @@content_types[type] << self unless @@content_types[type].include?(self)
405
406
 
406
407
  if extensions = (options[:extension] || options[:extensions])
407
408
  extensions = [extensions].flatten.map(&:to_sym)
408
- extensions.each { |ext| (@@file_extensions[ext] ||= []) << self }
409
+ extensions.each do |ext|
410
+ @@file_extensions[ext] ||= []
411
+ @@file_extensions[ext] << self unless @@file_extensions[ext].include?(self)
412
+ end
409
413
  end
410
414
  if aliases = (options[:alias] || options[:aliases])
411
- aliases = [aliases].flatten.each { |a| (@@content_types[a] ||= []) << self }
415
+ aliases = [aliases].flatten.each do |a|
416
+ @@content_types[a] ||= []
417
+ @@content_types[a] << self unless @@content_types[a].include?(self)
418
+ end
412
419
  end
413
420
  end
414
421
  end
@@ -9,9 +9,8 @@ module RDF
9
9
  #
10
10
  # @return [Boolean]
11
11
  def empty?
12
- empty = true
13
- each { empty = false; break }
14
- empty
12
+ each {return false}
13
+ true
15
14
  end
16
15
 
17
16
  ##
@@ -17,10 +17,10 @@ module RDF
17
17
  # enumerable.has_quad?([subject, predicate, object, context])
18
18
  #
19
19
  # @example Checking whether a specific value exists
20
- # enumerable.has_subject?(RDF::URI("http://rdf.rubyforge.org/"))
20
+ # enumerable.has_subject?(RDF::URI("http://rubygems.org/gems/rdf"))
21
21
  # enumerable.has_predicate?(RDF::DC.creator)
22
- # enumerable.has_object?(RDF::Literal("Hello!", :language => :en))
23
- # enumerable.has_context?(RDF::URI("http://rubyforge.org/"))
22
+ # enumerable.has_object?(RDF::Literal("A Ruby library for working with Resource Description Framework (RDF) data.", :language => :en))
23
+ # enumerable.has_context?(RDF::URI("http://ar.to/#self"))
24
24
  #
25
25
  # @example Enumerating all statements
26
26
  # enumerable.each_statement do |statement|
@@ -17,7 +17,7 @@ module RDF
17
17
  # This method delegates to the protected {RDF::Queryable#query_pattern} method for the
18
18
  # actual lower-level query pattern matching implementation.
19
19
  #
20
- # @example
20
+ # @example Querying for statements having a given predicate
21
21
  # queryable.query([nil, RDF::DOAP.developer, nil])
22
22
  # queryable.query(:predicate => RDF::DOAP.developer)
23
23
  #
@@ -9,13 +9,13 @@ module RDF
9
9
  # graph = Graph.new("http://rubygems.org/")
10
10
  #
11
11
  # @example Loading graph data from a URL (1)
12
- # require 'rdf/raptor' # for RDF/XML support
12
+ # require 'rdf/rdfxml' # for RDF/XML support
13
13
  #
14
14
  # graph = RDF::Graph.new("http://www.bbc.co.uk/programmes/b0081dq5.rdf")
15
15
  # graph.load!
16
16
  #
17
17
  # @example Loading graph data from a URL (2)
18
- # require 'rdf/raptor' # for RDF/XML support
18
+ # require 'rdf/rdfxml' # for RDF/XML support
19
19
  #
20
20
  # graph = RDF::Graph.load("http://www.bbc.co.uk/programmes/b0081dq5.rdf")
21
21
  #
@@ -181,15 +181,6 @@ module RDF
181
181
  named? ? context.to_s : "default"
182
182
  end
183
183
 
184
- ##
185
- # Returns `true` if this graph contains no RDF statements.
186
- #
187
- # @return [Boolean]
188
- # @see RDF::Enumerable#empty?
189
- def empty?
190
- !@data.has_context?(context || false)
191
- end
192
-
193
184
  ##
194
185
  # Returns `true` if this graph has an anonymous context, `false` otherwise.
195
186
  #
@@ -196,7 +196,7 @@ module RDF
196
196
  # Returns the element at `index`.
197
197
  #
198
198
  # @example
199
- # RDF::List[1, 2, 3][0] #=> 1
199
+ # RDF::List[1, 2, 3][0] #=> RDF::Literal(1)
200
200
  #
201
201
  # @param [Integer] index
202
202
  # @return [RDF::Term]
@@ -299,10 +299,12 @@ module RDF
299
299
  end
300
300
 
301
301
  ##
302
- # Returns the element at `index`.
302
+ # Returns a slice of a list.
303
303
  #
304
304
  # @example
305
- # RDF::List[1, 2, 3].at(0) #=> 1
305
+ # RDF::List[1, 2, 3].slice(0) #=> RDF::Literal(1),
306
+ # RDF::List[1, 2, 3].slice(0, 2) #=> RDF::List[1, 2],
307
+ # RDF::List[1, 2, 3].slice(0..2) #=> RDF::List[1, 2, 3]
306
308
  #
307
309
  # @return [RDF::Term]
308
310
  # @see http://ruby-doc.org/core-1.9/classes/Array.html#M000462
@@ -331,10 +333,10 @@ module RDF
331
333
  protected :slice_with_range
332
334
 
333
335
  ##
334
- # Returns the element at `index`.
336
+ # Returns element at `index` with default.
335
337
  #
336
338
  # @example
337
- # RDF::List[1, 2, 3].fetch(0) #=> 1
339
+ # RDF::List[1, 2, 3].fetch(0) #=> RDF::Literal(1)
338
340
  # RDF::List[1, 2, 3].fetch(4) #=> IndexError
339
341
  # RDF::List[1, 2, 3].fetch(4, nil) #=> nil
340
342
  # RDF::List[1, 2, 3].fetch(4) { |n| n*n } #=> 16
@@ -358,6 +360,7 @@ module RDF
358
360
  #
359
361
  # @example
360
362
  # RDF::List[1, 2, 3].at(0) #=> 1
363
+ # RDF::List[1, 2, 3].at(4) #=> nil
361
364
  #
362
365
  # @return [RDF::Term]
363
366
  # @see http://ruby-doc.org/core-1.9/classes/Array.html#M000419
@@ -374,7 +377,7 @@ module RDF
374
377
  # Returns the first element in this list.
375
378
  #
376
379
  # @example
377
- # RDF::List[*(1..10)].first #=> 1
380
+ # RDF::List[*(1..10)].first #=> RDF::Literal(1)
378
381
  #
379
382
  # @return [RDF::Term]
380
383
  def first
@@ -385,7 +388,7 @@ module RDF
385
388
  # Returns the second element in this list.
386
389
  #
387
390
  # @example
388
- # RDF::List[*(1..10)].second #=> 2
391
+ # RDF::List[*(1..10)].second #=> RDF::Literal(2)
389
392
  #
390
393
  # @return [RDF::Term]
391
394
  def second
@@ -396,7 +399,7 @@ module RDF
396
399
  # Returns the third element in this list.
397
400
  #
398
401
  # @example
399
- # RDF::List[*(1..10)].third #=> 3
402
+ # RDF::List[*(1..10)].third #=> RDF::Literal(4)
400
403
  #
401
404
  # @return [RDF::Term]
402
405
  def third
@@ -407,7 +410,7 @@ module RDF
407
410
  # Returns the fourth element in this list.
408
411
  #
409
412
  # @example
410
- # RDF::List[*(1..10)].fourth #=> 4
413
+ # RDF::List[*(1..10)].fourth #=> RDF::Literal(4)
411
414
  #
412
415
  # @return [RDF::Term]
413
416
  def fourth
@@ -418,7 +421,7 @@ module RDF
418
421
  # Returns the fifth element in this list.
419
422
  #
420
423
  # @example
421
- # RDF::List[*(1..10)].fifth #=> 5
424
+ # RDF::List[*(1..10)].fifth #=> RDF::Literal(5)
422
425
  #
423
426
  # @return [RDF::Term]
424
427
  def fifth
@@ -429,7 +432,7 @@ module RDF
429
432
  # Returns the sixth element in this list.
430
433
  #
431
434
  # @example
432
- # RDF::List[*(1..10)].sixth #=> 6
435
+ # RDF::List[*(1..10)].sixth #=> RDF::Literal(6)
433
436
  #
434
437
  # @return [RDF::Term]
435
438
  def sixth
@@ -440,7 +443,7 @@ module RDF
440
443
  # Returns the seventh element in this list.
441
444
  #
442
445
  # @example
443
- # RDF::List[*(1..10)].seventh #=> 7
446
+ # RDF::List[*(1..10)].seventh #=> RDF::Literal(7)
444
447
  #
445
448
  # @return [RDF::Term]
446
449
  def seventh
@@ -451,7 +454,7 @@ module RDF
451
454
  # Returns the eighth element in this list.
452
455
  #
453
456
  # @example
454
- # RDF::List[*(1..10)].eighth #=> 8
457
+ # RDF::List[*(1..10)].eighth #=> RDF::Literal(8)
455
458
  #
456
459
  # @return [RDF::Term]
457
460
  def eighth
@@ -462,7 +465,7 @@ module RDF
462
465
  # Returns the ninth element in this list.
463
466
  #
464
467
  # @example
465
- # RDF::List[*(1..10)].ninth #=> 9
468
+ # RDF::List[*(1..10)].ninth #=> RDF::Literal(9)
466
469
  #
467
470
  # @return [RDF::Term]
468
471
  def ninth
@@ -473,7 +476,7 @@ module RDF
473
476
  # Returns the tenth element in this list.
474
477
  #
475
478
  # @example
476
- # RDF::List[*(1..10)].tenth #=> 10
479
+ # RDF::List[*(1..10)].tenth #=> RDF::Literal(10)
477
480
  #
478
481
  # @return [RDF::Term]
479
482
  def tenth
@@ -484,7 +487,7 @@ module RDF
484
487
  # Returns the last element in this list.
485
488
  #
486
489
  # @example
487
- # RDF::List[1, 2, 3].last #=> 3
490
+ # RDF::List[*(1..10)].last #=> RDF::Literal(10)
488
491
  #
489
492
  # @return [RDF::Term]
490
493
  # @see http://ruby-doc.org/core-1.9/classes/Array.html#M000422
@@ -676,7 +679,7 @@ module RDF
676
679
  #
677
680
  # @example
678
681
  # RDF::List[].to_a #=> []
679
- # RDF::List[1, 2, 3].to_a #=> [1, 2, 3]
682
+ # RDF::List[1, 2, 3].to_a #=> [RDF::Literal(1), RDF::Literal(2), RDF::Literal(3)]
680
683
  #
681
684
  # @return [Array]
682
685
  def to_a
@@ -687,7 +690,7 @@ module RDF
687
690
  # Returns the elements in this list as a set.
688
691
  #
689
692
  # @example
690
- # RDF::List[1, 2, 3].to_set #=> Set[1, 2, 3]
693
+ # RDF::List[1, 2, 3].to_set #=> Set[RDF::Literal(1), RDF::Literal(2), RDF::Literal(3)]
691
694
  #
692
695
  # @return [Set]
693
696
  def to_set
@@ -15,6 +15,9 @@ module RDF; class Literal
15
15
  @datatype = RDF::URI(options[:datatype] || self.class.const_get(:DATATYPE))
16
16
  @string = options[:lexical] if options.has_key?(:lexical)
17
17
  @string ||= value if value.is_a?(String)
18
+ @has_timezone = @string.nil? || if md = @string.match(GRAMMAR)
19
+ !!md[2] # If lexical value contains timezone
20
+ end
18
21
  @object = case
19
22
  when value.is_a?(::DateTime) then value
20
23
  when value.respond_to?(:to_datetime) then value.to_datetime # Ruby 1.9+
@@ -33,6 +36,35 @@ module RDF; class Literal
33
36
  self
34
37
  end
35
38
 
39
+ ##
40
+ # Returns the timezone part of arg as a simple literal. Returns the empty string if there is no timezone.
41
+ #
42
+ # @return [RDF::Literal]
43
+ # @see http://www.w3.org/TR/sparql11-query/#func-tz
44
+ def tz
45
+ zone = @has_timezone ? object.zone : ""
46
+ zone = "Z" if zone == "+00:00"
47
+ RDF::Literal(zone)
48
+ end
49
+
50
+ ##
51
+ # Returns the timezone part of arg as an xsd:dayTimeDuration, or `nil`
52
+ # if lexical form of literal does not include a timezone.
53
+ #
54
+ # @return [RDF::Literal]
55
+ def timezone
56
+ if tz == 'Z'
57
+ RDF::Literal("PT0S", :datatype => RDF::XSD.dayTimeDuration)
58
+ elsif md = tz.to_s.match(/^([+-])?(\d+):(\d+)?$/)
59
+ plus_minus, hour, min = md[1,3]
60
+ plus_minus = nil unless plus_minus == "-"
61
+ hour = hour.to_i
62
+ min = min.to_i
63
+ res = "#{plus_minus}PT#{hour}H#{"#{min}M" if min > 0}"
64
+ RDF::Literal(res, :datatype => RDF::XSD.dayTimeDuration)
65
+ end
66
+ end
67
+
36
68
  ##
37
69
  # Returns `true` if the value adheres to the defined grammar of the
38
70
  # datatype.
@@ -45,6 +77,8 @@ module RDF; class Literal
45
77
  super && object && value !~ %r(\A0000)
46
78
  end
47
79
 
80
+ ##
81
+ # Returns the `timezone` of the literal. If the
48
82
  ##
49
83
  # Returns the value as a string.
50
84
  #
@@ -56,6 +56,36 @@ module RDF; class Literal
56
56
  (d = to_d) && d > 0 ? self : RDF::Literal(d.abs)
57
57
  end
58
58
 
59
+ ##
60
+ # Returns the number with no fractional part that is closest to the argument. If there are two such numbers, then the one that is closest to positive infinity is returned. An error is raised if arg is not a numeric value.
61
+ #
62
+ # @return [RDF::Literal]
63
+ def round
64
+ self.class.new(to_d.round)
65
+ end
66
+
67
+ ##
68
+ # Returns the smallest integer greater than or equal to `self`.
69
+ #
70
+ # @example
71
+ # RDF::Literal(1).ceil #=> RDF::Literal(1)
72
+ #
73
+ # @return [RDF::Literal]
74
+ def ceil
75
+ self.class.new(to_d.ceil)
76
+ end
77
+
78
+ ##
79
+ # Returns the largest integer less than or equal to `self`.
80
+ #
81
+ # @example
82
+ # RDF::Literal(1).floor #=> RDF::Literal(1)
83
+ #
84
+ # @return [RDF::Literal]
85
+ def floor
86
+ self.class.new(to_d.floor)
87
+ end
88
+
59
89
  ##
60
90
  # Returns `true` if the value is zero.
61
91
  #
@@ -118,7 +118,7 @@ module RDF; class Literal
118
118
  end
119
119
 
120
120
  ##
121
- # Returns the smallest integer greater than or equal to `self`.
121
+ # Returns the smallest number greater than or equal to `self`.
122
122
  #
123
123
  # @example
124
124
  # RDF::Literal(1.2).ceil #=> RDF::Literal(2)
@@ -129,11 +129,11 @@ module RDF; class Literal
129
129
  # @return [RDF::Literal]
130
130
  # @since 0.2.3
131
131
  def ceil
132
- RDF::Literal(to_f.ceil)
132
+ self.class.new(to_f.ceil)
133
133
  end
134
134
 
135
135
  ##
136
- # Returns the largest integer less than or equal to `self`.
136
+ # Returns the largest number less than or equal to `self`.
137
137
  #
138
138
  # @example
139
139
  # RDF::Literal(1.2).floor #=> RDF::Literal(1)
@@ -144,7 +144,7 @@ module RDF; class Literal
144
144
  # @return [RDF::Literal]
145
145
  # @since 0.2.3
146
146
  def floor
147
- RDF::Literal(to_f.floor)
147
+ self.class.new(to_f.floor)
148
148
  end
149
149
 
150
150
  ##
@@ -153,7 +153,15 @@ module RDF; class Literal
153
153
  # @return [RDF::Literal]
154
154
  # @since 0.2.3
155
155
  def abs
156
- (f = to_f) && f > 0 ? self : RDF::Literal(f.abs)
156
+ (f = to_f) && f > 0 ? self : self.class.new(f.abs)
157
+ end
158
+
159
+ ##
160
+ # Returns the number with no fractional part that is closest to the argument. If there are two such numbers, then the one that is closest to positive infinity is returned. An error is raised if arg is not a numeric value.
161
+ #
162
+ # @return [RDF::Literal]
163
+ def round
164
+ self.class.new(to_f.round)
157
165
  end
158
166
 
159
167
  ##
@@ -83,7 +83,15 @@ module RDF; class Literal
83
83
  # @return [RDF::Literal]
84
84
  # @since 0.2.3
85
85
  def abs
86
- (n = to_i) && n > 0 ? self : RDF::Literal(n.abs)
86
+ (n = to_i) && n > 0 ? self : self.class.new(n.abs)
87
+ end
88
+
89
+ ##
90
+ # Returns `self`.
91
+ #
92
+ # @return [RDF::Literal]
93
+ def round
94
+ self
87
95
  end
88
96
 
89
97
  ##
@@ -144,6 +144,46 @@ module RDF; class Literal
144
144
  end
145
145
  end
146
146
 
147
+ ##
148
+ # Returns the absolute value of `self`.
149
+ #
150
+ # @return [RDF::Literal]
151
+ # @raise [NotImplementedError] unless implemented in subclass
152
+ def abs
153
+ raise NotImplementedError
154
+ end
155
+
156
+ ##
157
+ # Returns the number with no fractional part that is closest to the argument. If there are two such numbers, then the one that is closest to positive infinity is returned. An error is raised if arg is not a numeric value.
158
+ #
159
+ # @return [RDF::Literal]
160
+ # @raise [NotImplementedError] unless implemented in subclass
161
+ def round
162
+ raise NotImplementedError
163
+ end
164
+
165
+ ##
166
+ # Returns the smallest integer greater than or equal to `self`.
167
+ #
168
+ # @example
169
+ # RDF::Literal(1).ceil #=> RDF::Literal(1)
170
+ #
171
+ # @return [RDF::Literal]
172
+ def ceil
173
+ self
174
+ end
175
+
176
+ ##
177
+ # Returns the largest integer less than or equal to `self`.
178
+ #
179
+ # @example
180
+ # RDF::Literal(1).floor #=> RDF::Literal(1)
181
+ #
182
+ # @return [RDF::Literal]
183
+ def floor
184
+ self
185
+ end
186
+
147
187
  ##
148
188
  # Returns the value as an integer.
149
189
  #
@@ -53,7 +53,7 @@ module RDF
53
53
  # RDF::Literal.new(123).datatype #=> XSD.integer
54
54
  # RDF::Literal.new(9223372036854775807).datatype #=> XSD.integer
55
55
  # RDF::Literal.new(3.1415).datatype #=> XSD.double
56
- # RDF::Literal.new(Time.now).datatype #=> XSD.dateTime
56
+ # RDF::Literal.new(Time.now).datatype #=> XSD.time
57
57
  # RDF::Literal.new(Date.new(2010)).datatype #=> XSD.date
58
58
  # RDF::Literal.new(DateTime.new(2010)).datatype #=> XSD.dateTime
59
59
  #
@@ -97,6 +97,8 @@ module RDF
97
97
  ##
98
98
  # @private
99
99
  def self.new(value, options = {})
100
+ raise ArgumentError, "datatype with language" if options[:language] && (options[:datatype] || RDF.langString).to_s != RDF.langString.to_s
101
+
100
102
  klass = case
101
103
  when !self.equal?(RDF::Literal)
102
104
  self # subclasses can be directly constructed without type dispatch
@@ -122,9 +124,9 @@ module RDF
122
124
  literal
123
125
  end
124
126
 
125
- TRUE = RDF::Literal.new(true).freeze
126
- FALSE = RDF::Literal.new(false).freeze
127
- ZERO = RDF::Literal.new(0).freeze
127
+ TRUE = RDF::Literal.new(true)
128
+ FALSE = RDF::Literal.new(false)
129
+ ZERO = RDF::Literal.new(0)
128
130
 
129
131
  # @return [Symbol] The language tag (optional).
130
132
  attr_accessor :language
@@ -142,6 +144,11 @@ module RDF
142
144
  # @option options [URI] :datatype (nil)
143
145
  # @option options [Boolean] :validate (false)
144
146
  # @option options [Boolean] :canonicalize (false)
147
+ # @raise [ArgumentError]
148
+ # if there is a language and datatype is no rdf:langString
149
+ # or datatype is rdf:langString and there is no language
150
+ # @see http://www.w3.org/TR/rdf11-concepts/#section-Graph-Literal
151
+ # @see http://www.w3.org/TR/rdf11-concepts/#section-Datatypes
145
152
  def initialize(value, options = {})
146
153
  @object = value
147
154
  @string = options[:lexical] if options[:lexical]
@@ -149,6 +156,10 @@ module RDF
149
156
  @language = options[:language].to_s.to_sym if options[:language]
150
157
  @datatype = RDF::URI(options[:datatype]) if options[:datatype]
151
158
  @datatype ||= self.class.const_get(:DATATYPE) if self.class.const_defined?(:DATATYPE)
159
+
160
+ # Ignore rdf:langString if there is a language
161
+ @datatype = nil if @language && @datatype == RDF.langString
162
+ raise ArgumentError, "datatype of rdf:langString requires a language" if !@language && @datatype == RDF::langString
152
163
  end
153
164
 
154
165
  ##
@@ -186,7 +197,24 @@ module RDF
186
197
  #
187
198
  # @return [Fixnum]
188
199
  def hash
189
- to_s.hash
200
+ @hash ||= to_s.hash
201
+ end
202
+
203
+
204
+ ##
205
+ # Returns a hash code for the value.
206
+ #
207
+ # @return [Fixnum]
208
+ def value_hash
209
+ @value_hash ||= value.hash
210
+ end
211
+
212
+ ##
213
+ # @private
214
+ def freeze
215
+ hash.freeze
216
+ value_hash.freeze
217
+ super
190
218
  end
191
219
 
192
220
  ##
@@ -224,9 +252,9 @@ module RDF
224
252
  true
225
253
  when self.has_language? && self.language.to_s.downcase == other.language.to_s.downcase
226
254
  # Literals with languages can compare if languages are identical
227
- self.value == other.value
255
+ self.value_hash == other.value_hash && self.value == other.value
228
256
  when (self.simple? || self.datatype == XSD.string) && (other.simple? || other.datatype == XSD.string)
229
- self.value == other.value
257
+ self.value_hash == other.value_hash && self.value == other.value
230
258
  when other.comperable_datatype?(self) || self.comperable_datatype?(other)
231
259
  # Comoparing plain with undefined datatypes does not generate an error, but returns false
232
260
  # From data-r2/expr-equal/eq-2-2.
@@ -235,21 +263,35 @@ module RDF
235
263
  type_error("unable to determine whether #{self.inspect} and #{other.inspect} are equivalent")
236
264
  end
237
265
  when String
238
- self.plain? && self.value.eql?(other)
266
+ self.simple? && self.value.eql?(other)
239
267
  else false
240
268
  end
241
269
  end
242
270
  alias_method :===, :==
243
271
 
244
272
  ##
245
- # Returns `true` if this is a plain literal.
273
+ # Returns `true` if this is a plain literal. A plain literal
274
+ # may have a language, but may not have a datatype. For
275
+ # all practical purposes, this includes xsd:string literals
276
+ # too.
246
277
  #
247
278
  # @return [Boolean] `true` or `false`
248
279
  # @see http://www.w3.org/TR/rdf-concepts/#dfn-plain-literal
249
280
  def plain?
250
- language.nil? && datatype.nil?
281
+ has_language? ?
282
+ (datatype || RDF.langString) == RDF.langString :
283
+ (datatype || XSD.string) == XSD.string
284
+ end
285
+
286
+ ##
287
+ # Returns `true` if this is a simple literal.
288
+ # A simple literal has no datatype or language.
289
+ #
290
+ # @return [Boolean] `true` or `false`
291
+ # @see http://www.w3.org/TR/sparql11-query/#simple_literal
292
+ def simple?
293
+ !has_language? && !has_datatype?
251
294
  end
252
- alias_method :simple?, :plain?
253
295
 
254
296
  ##
255
297
  # Returns `true` if this is a language-tagged literal.
@@ -132,7 +132,9 @@ module RDF
132
132
  # If other is a Literal, reverse test to consolodate complex type checking logic
133
133
  other == self
134
134
  else
135
- other.respond_to?(:node?) && other.node? && other.respond_to?(:id) && @id == other.id
135
+ other.respond_to?(:node?) && other.node? &&
136
+ self.hash == other.hash &&
137
+ other.respond_to?(:id) && @id == other.id
136
138
  end
137
139
  end
138
140
  alias_method :===, :==
@@ -9,6 +9,7 @@ module RDF
9
9
  # RDF::Statement.new(s, p, o)
10
10
  #
11
11
  # @example Creating an RDF statement with a context
12
+ # uri = RDF::URI("http://example/")
12
13
  # RDF::Statement.new(s, p, o, :context => uri)
13
14
  #
14
15
  # @example Creating an RDF statement from a `Hash`
data/lib/rdf/model/uri.rb CHANGED
@@ -522,7 +522,7 @@ module RDF
522
522
  # @param [RDF::URI] other
523
523
  # @return [Boolean] `true` or `false`
524
524
  def eql?(other)
525
- other.is_a?(URI) && self == other
525
+ other.is_a?(URI) && self.hash == other.hash && self == other
526
526
  end
527
527
 
528
528
  ##
@@ -544,6 +544,7 @@ module RDF
544
544
  # If other is a Literal, reverse test to consolodate complex type checking logic
545
545
  other == self
546
546
  when String then to_s == other
547
+ when URI then hash == other.hash && to_s == other.to_s
547
548
  when URI, Addressable::URI then to_s == other.to_s
548
549
  else other.respond_to?(:to_uri) && to_s == other.to_uri.to_s
549
550
  end
@@ -104,7 +104,7 @@ module RDF; class Query
104
104
  # To match triples only in the default context, set context to `false`.
105
105
  #
106
106
  # @example
107
- # Pattern.new(:s, :p, :o).execute(RDF::Repository.load('data.nt'))
107
+ # Pattern.new(:s, :p, :o).execute(RDF::Repository.load('etc/doap.nt'))
108
108
  #
109
109
  # @param [RDF::Queryable] queryable
110
110
  # the graph or repository to query
@@ -156,7 +156,12 @@ module RDF; class Query
156
156
  # pattern with the corresponding terms in the given `statement`.
157
157
  #
158
158
  # @example
159
- # pattern.solution(statement)
159
+ # pattern = Pattern.new(:s, :p, :o)
160
+ # solution = pattern.solution(statement)
161
+ #
162
+ # pattern[:s] #=> statement.subject
163
+ # pattern[:p] #=> statement.predicate
164
+ # pattern[:o] #=> statement.object
160
165
  #
161
166
  # @param [RDF::Statement] statement
162
167
  # an RDF statement to bind terms from
@@ -25,7 +25,7 @@ class RDF::Query
25
25
  undef_method(*instance_methods.
26
26
  map(&:to_s).
27
27
  select {|m| m =~ /^\w+$/}.
28
- reject {|m| %w(object_id dup instance_eval inspect to_s class).include?(m) || m[0,2] == '__'}.
28
+ reject {|m| %w(object_id dup instance_eval inspect to_s class should should_not pretty_print).include?(m) || m[0,2] == '__'}.
29
29
  map(&:to_sym))
30
30
 
31
31
  include Enumerable
@@ -145,7 +145,7 @@ class RDF::Query
145
145
  # @return [RDF::Term]
146
146
  # @since 0.3.0
147
147
  def []=(name, value)
148
- @bindings[name.to_sym] = value
148
+ @bindings[name.to_sym] = value.is_a?(RDF::Term) ? value : RDF::Literal(value)
149
149
  end
150
150
 
151
151
  ##
@@ -4,13 +4,13 @@ module RDF; class Query
4
4
  #
5
5
  # @example Filtering solutions using a hash
6
6
  # solutions.filter(:author => RDF::URI("http://ar.to/#self"))
7
- # solutions.filter(:author => "Arto Bendiken")
8
- # solutions.filter(:author => [RDF::URI("http://ar.to/#self"), "Arto Bendiken"])
7
+ # solutions.filter(:author => "Gregg Kellogg")
8
+ # solutions.filter(:author => [RDF::URI("http://ar.to/#self"), "Gregg Kellogg"])
9
9
  # solutions.filter(:updated => RDF::Literal(Date.today))
10
10
  #
11
11
  # @example Filtering solutions using a block
12
12
  # solutions.filter { |solution| solution.author.literal? }
13
- # solutions.filter { |solution| solution.title =~ /^SPARQL/ }
13
+ # solutions.filter { |solution| solution.title.to_s =~ /^SPARQL/ }
14
14
  # solutions.filter { |solution| solution.price < 30.5 }
15
15
  # solutions.filter { |solution| solution.bound?(:date) }
16
16
  # solutions.filter { |solution| solution.age.datatype == RDF::XSD.integer }
@@ -93,7 +93,11 @@ module RDF; class Query
93
93
  self.reject! do |solution|
94
94
  solution = solution.is_a?(Solution) ? solution : Solution.new(solution)
95
95
  results = criteria.map do |name, value|
96
- solution[name] == value
96
+ case value
97
+ when Array then value.any? {|v| solution[name] == v}
98
+ when Regexp then solution[name].to_s.match(value)
99
+ else solution[name] == value
100
+ end
97
101
  end
98
102
  !results.all?
99
103
  end
@@ -12,7 +12,7 @@ class RDF::Query
12
12
  # var.name #=> :g2166151240
13
13
  #
14
14
  # @example Unbound variables match any value
15
- # var === 42 #=> true
15
+ # var === RDF::Literal(42) #=> true
16
16
  #
17
17
  # @example Creating a bound variable
18
18
  # var = RDF::Query::Variable.new(:y, 123)
@@ -20,10 +20,12 @@ class RDF::Query
20
20
  # var.value #=> 123
21
21
  #
22
22
  # @example Bound variables match only their actual value
23
+ # var = RDF::Query::Variable.new(:y, 123)
23
24
  # var === 42 #=> false
24
25
  # var === 123 #=> true
25
26
  #
26
27
  # @example Getting the variable name
28
+ # var = RDF::Query::Variable.new(:y, 123)
27
29
  # var.named? #=> true
28
30
  # var.name #=> :y
29
31
  # var.to_sym #=> :y
data/lib/rdf/query.rb CHANGED
@@ -89,7 +89,7 @@ module RDF
89
89
  # @return [RDF::Query::Solutions]
90
90
  # the resulting solution sequence
91
91
  # @see RDF::Query#execute
92
- def self.execute(queryable, patterns = nil, options = {}, &block)
92
+ def self.execute(queryable, patterns = {}, options = {}, &block)
93
93
  self.new(patterns, options, &block).execute(queryable, options)
94
94
  end
95
95
 
@@ -243,6 +243,9 @@ module RDF
243
243
  # or named contexts.
244
244
  # The name of `false` will only match against the default context.
245
245
  #
246
+ # If the query nas no patterns, it returns a single empty solution as
247
+ # per SPARQL 1.1 _Empty Group Pattern_.
248
+ #
246
249
  # @param [RDF::Queryable] queryable
247
250
  # the graph or repository to query
248
251
  # @param [Hash{Symbol => Object}] options
@@ -259,6 +262,7 @@ module RDF
259
262
  # @return [RDF::Query::Solutions]
260
263
  # the resulting solution sequence
261
264
  # @see http://www.holygoat.co.uk/blog/entry/2005-10-25-1
265
+ # @see http://www.w3.org/TR/sparql11-query/#emptyGroupPattern
262
266
  def execute(queryable, options = {})
263
267
  validate!
264
268
  options = options.dup
@@ -271,6 +275,9 @@ module RDF
271
275
  # the first pattern
272
276
  @solutions = options[:solutions] || (Solutions.new << RDF::Query::Solution.new({}))
273
277
 
278
+ # If there are no patterns, just return the empty solution
279
+ return @solutions if empty?
280
+
274
281
  patterns = @patterns
275
282
  context = options.fetch(:context, options.fetch(:name, self.context))
276
283
 
@@ -237,13 +237,6 @@ module RDF
237
237
  false
238
238
  end
239
239
 
240
- ##
241
- # @private
242
- # @see RDF::Countable#empty?
243
- def empty?
244
- @data.empty?
245
- end
246
-
247
240
  ##
248
241
  # @private
249
242
  # @see RDF::Countable#count
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-04-12 00:00:00.000000000 Z
14
+ date: 2013-05-01 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: addressable
@@ -45,6 +45,24 @@ dependencies:
45
45
  none: false
46
46
  prerelease: false
47
47
  type: :development
48
+ - !ruby/object:Gem::Dependency
49
+ name: rdf-rdfxml
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: !binary |-
55
+ MA==
56
+ none: false
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: !binary |-
62
+ MA==
63
+ none: false
64
+ prerelease: false
65
+ type: :development
48
66
  - !ruby/object:Gem::Dependency
49
67
  name: rspec
50
68
  version_requirements: !ruby/object:Gem::Requirement