rdf 3.0.11 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHORS +1 -1
  3. data/README.md +127 -95
  4. data/UNLICENSE +1 -1
  5. data/VERSION +1 -1
  6. data/etc/doap.nt +79 -85
  7. data/lib/rdf.rb +35 -23
  8. data/lib/rdf/changeset.rb +80 -19
  9. data/lib/rdf/cli.rb +7 -7
  10. data/lib/rdf/format.rb +17 -10
  11. data/lib/rdf/mixin/enumerable.rb +4 -3
  12. data/lib/rdf/mixin/mutable.rb +5 -15
  13. data/lib/rdf/mixin/queryable.rb +12 -4
  14. data/lib/rdf/mixin/transactable.rb +2 -2
  15. data/lib/rdf/mixin/writable.rb +9 -14
  16. data/lib/rdf/model/dataset.rb +1 -1
  17. data/lib/rdf/model/graph.rb +7 -4
  18. data/lib/rdf/model/list.rb +5 -5
  19. data/lib/rdf/model/literal.rb +3 -3
  20. data/lib/rdf/model/statement.rb +32 -9
  21. data/lib/rdf/model/uri.rb +53 -32
  22. data/lib/rdf/nquads.rb +6 -6
  23. data/lib/rdf/ntriples.rb +7 -5
  24. data/lib/rdf/ntriples/reader.rb +29 -7
  25. data/lib/rdf/ntriples/writer.rb +10 -1
  26. data/lib/rdf/query.rb +27 -35
  27. data/lib/rdf/query/hash_pattern_normalizer.rb +14 -12
  28. data/lib/rdf/query/pattern.rb +51 -18
  29. data/lib/rdf/query/solution.rb +20 -1
  30. data/lib/rdf/query/solutions.rb +15 -5
  31. data/lib/rdf/query/variable.rb +17 -5
  32. data/lib/rdf/reader.rb +76 -25
  33. data/lib/rdf/repository.rb +32 -18
  34. data/lib/rdf/transaction.rb +1 -1
  35. data/lib/rdf/util.rb +6 -5
  36. data/lib/rdf/util/cache.rb +2 -2
  37. data/lib/rdf/util/coercions.rb +60 -0
  38. data/lib/rdf/util/file.rb +20 -10
  39. data/lib/rdf/util/logger.rb +6 -6
  40. data/lib/rdf/util/uuid.rb +4 -4
  41. data/lib/rdf/vocab/owl.rb +401 -86
  42. data/lib/rdf/vocab/rdfs.rb +81 -18
  43. data/lib/rdf/vocab/rdfv.rb +147 -1
  44. data/lib/rdf/vocab/writer.rb +41 -3
  45. data/lib/rdf/vocab/xsd.rb +203 -2
  46. data/lib/rdf/vocabulary.rb +73 -15
  47. data/lib/rdf/writer.rb +33 -11
  48. metadata +34 -28
data/lib/rdf.rb CHANGED
@@ -6,6 +6,9 @@ require 'time'
6
6
  require 'rdf/version'
7
7
  require 'rdf/extensions'
8
8
 
9
+ # When loading, issue deprecation warning on forthcoming unsupported versions of Ruby
10
+ warn "[DEPRECATION] Ruby 2.4+ required in next version 3.1 of RDF.rb" if RUBY_VERSION < "2.4"
11
+
9
12
  module RDF
10
13
  # RDF mixins
11
14
  autoload :Countable, 'rdf/mixin/countable'
@@ -56,16 +59,18 @@ module RDF
56
59
  # RDF vocabularies
57
60
  autoload :Vocabulary, 'rdf/vocabulary'
58
61
  autoload :StrictVocabulary, 'rdf/vocabulary'
59
- VOCABS = Dir.glob(File.expand_path("../rdf/vocab/*.rb", __FILE__)).map { |f| File.basename(f)[0...-(File.extname(f).size)].to_sym } rescue []
60
62
 
61
- # Use const_missing instead of autoload to load most vocabularies so we can provide deprecation messages
62
- def self.const_missing(constant)
63
- if VOCABS.include?(constant.to_s.downcase.to_sym)
64
- require "rdf/vocab/#{constant.to_s.downcase}"
65
- const_get(constant)
66
- else
67
- super
68
- end
63
+ VOCABS = {
64
+ owl: {uri: "http://www.w3.org/2002/07/owl#", class_name: "OWL"},
65
+ rdfs: {uri: "http://www.w3.org/2000/01/rdf-schema#", class_name: "RDFS"},
66
+ rdfv: {uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", class_name: "RDFV"},
67
+ xsd: {uri: "http://www.w3.org/2001/XMLSchema#", class_name: "XSD"},
68
+ }
69
+
70
+ # Autoload vocabularies
71
+ VOCABS.each do |id, params|
72
+ v = (params[:class_name] ||= id.to_s.upcase).to_sym
73
+ autoload v, File.expand_path("../rdf/vocab/#{id}", __FILE__)
69
74
  end
70
75
 
71
76
  # Utilities
@@ -76,8 +81,8 @@ module RDF
76
81
  #
77
82
  # @param (see RDF::Resource#initialize)
78
83
  # @return [RDF::Resource]
79
- def self.Resource(*args, &block)
80
- Resource.new(*args, &block)
84
+ def self.Resource(*args)
85
+ Resource.new(*args)
81
86
  end
82
87
 
83
88
  ##
@@ -85,8 +90,8 @@ module RDF
85
90
  #
86
91
  # @param (see RDF::Node#initialize)
87
92
  # @return [RDF::Node]
88
- def self.Node(*args, &block)
89
- Node.new(*args, &block)
93
+ def self.Node(*args)
94
+ Node.new(*args)
90
95
  end
91
96
 
92
97
  ##
@@ -94,8 +99,15 @@ module RDF
94
99
  #
95
100
  # @param (see RDF::URI#initialize)
96
101
  # @return [RDF::URI]
97
- def self.URI(uri, *args, &block)
98
- uri.respond_to?(:to_uri) ? uri.to_uri : URI.new(uri, *args, &block)
102
+ def self.URI(*args)
103
+ if args.first.respond_to?(:to_uri)
104
+ args.first.to_uri
105
+ elsif args.first.is_a?(Hash)
106
+ URI.new(**args.first)
107
+ else
108
+ opts = args.last.is_a?(Hash) ? args.pop : {}
109
+ URI.new(*args, **opts)
110
+ end
99
111
  end
100
112
 
101
113
  ##
@@ -103,10 +115,10 @@ module RDF
103
115
  #
104
116
  # @param (see RDF::Literal#initialize)
105
117
  # @return [RDF::Literal]
106
- def self.Literal(literal, *args, &block)
118
+ def self.Literal(literal, **options)
107
119
  case literal
108
120
  when RDF::Literal then literal
109
- else Literal.new(literal, *args, &block)
121
+ else Literal.new(literal, **options)
110
122
  end
111
123
  end
112
124
 
@@ -116,7 +128,7 @@ module RDF
116
128
  # @param (see RDF::Graph#initialize)
117
129
  # @return [RDF::Graph]
118
130
  def self.Graph(**options, &block)
119
- Graph.new(options, &block)
131
+ Graph.new(**options, &block)
120
132
  end
121
133
 
122
134
  ##
@@ -168,11 +180,11 @@ module RDF
168
180
  # @option options [RDF::Resource] :graph_name (nil)
169
181
  # @return [RDF::Statement]
170
182
  #
171
- def self.Statement(*args)
172
- if args.empty?
183
+ def self.Statement(*args, **options)
184
+ if args.empty? && options.empty?
173
185
  RDF[:Statement]
174
186
  else
175
- Statement.new(*args)
187
+ Statement.new(*args, **options)
176
188
  end
177
189
  end
178
190
 
@@ -190,8 +202,8 @@ module RDF
190
202
  #
191
203
  # @param (see RDF::Vocabulary#initialize)
192
204
  # @return [Class]
193
- def self.StrictVocabulary(prefix)
194
- StrictVocabulary.create(prefix)
205
+ def self.StrictVocabulary(uri)
206
+ StrictVocabulary.create(uri)
195
207
  end
196
208
 
197
209
  ##
@@ -31,10 +31,11 @@ module RDF
31
31
  #
32
32
  # @since 2.0.0
33
33
  class Changeset
34
- include RDF::Mutable
34
+ # include RDF::Mutable
35
+ include RDF::Util::Coercions
35
36
 
36
37
  ##
37
- # Applies a changeset to the given mutable RDF::Enumerable .
38
+ # Applies a changeset to the given {RDF::Mutable} object.
38
39
  #
39
40
  # @param [RDF::Mutable] mutable
40
41
  # @param [Hash{Symbol => Object}] options
@@ -42,7 +43,7 @@ module RDF
42
43
  # @yieldparam [RDF::Changeset] changes
43
44
  # @return [void]
44
45
  def self.apply(mutable, **options, &block)
45
- self.new(&block).apply(mutable, options)
46
+ self.new(&block).apply(mutable, **options)
46
47
  end
47
48
 
48
49
  ##
@@ -98,6 +99,24 @@ module RDF
98
99
  false
99
100
  end
100
101
 
102
+ ##
103
+ # Returns `false` as changesets are not {RDF::Writable}.
104
+ #
105
+ # @return [Boolean]
106
+ # @see RDF::Writable#writable?
107
+ def writable?
108
+ false
109
+ end
110
+
111
+ ##
112
+ # Returns `false` as changesets are not {RDF::Mutable}.
113
+ #
114
+ # @return [Boolean]
115
+ # @see RDF::Mutable#mutable?
116
+ def mutable?
117
+ false
118
+ end
119
+
101
120
  ##
102
121
  # Applies this changeset to the given mutable RDF::Enumerable.
103
122
  #
@@ -127,35 +146,77 @@ module RDF
127
146
 
128
147
  ##
129
148
  # Outputs a developer-friendly representation of this changeset to
130
- # `stderr`.
149
+ # `$stderr`.
131
150
  #
132
151
  # @return [void]
133
152
  def inspect!
134
153
  $stderr.puts(self.inspect)
135
154
  end
136
155
 
137
- protected
138
-
139
156
  ##
140
- # Appends an RDF statement to the sequence to insert when applied.
157
+ # Returns the sum of both the `inserts` and `deletes` counts.
141
158
  #
142
- # @param [RDF::Statement] statement
143
- # @return [void]
144
- # @see RDF::Writable#insert_statement
145
- def insert_statement(statement)
146
- self.inserts << statement
159
+ # @return [Integer]
160
+ def count
161
+ inserts.count + deletes.count
147
162
  end
148
163
 
164
+ # Append statements to `inserts`. Statements _should_ be constant
165
+ # as variable statements will at best be ignored or at worst raise
166
+ # an error when applied.
167
+ #
168
+ # @param statements [Enumerable, RDF::Statement] Some statements
169
+ # @return [self]
170
+ def insert(*statements)
171
+ coerce_statements(statements) do |stmts|
172
+ append_statements :inserts, stmts
173
+ end
174
+
175
+ self
176
+ end
177
+ alias_method :insert!, :insert
178
+ alias_method :<<, :insert
179
+
180
+ # Append statements to `deletes`. Statements _may_ contain
181
+ # variables, although support will depend on the {RDF::Mutable}
182
+ # target.
183
+ #
184
+ # @param statements [Enumerable, RDF::Statement] Some statements
185
+ # @return [self]
186
+ def delete(*statements)
187
+ coerce_statements(statements) do |stmts|
188
+ append_statements :deletes, stmts
189
+ end
190
+
191
+ self
192
+ end
193
+ alias_method :delete!, :delete
194
+ alias_method :>>, :delete
195
+
196
+ private
197
+
149
198
  ##
150
- # Appends an RDF statement to the sequence to delete when applied.
199
+ # Append statements to the appropriate target. This is a little
200
+ # shim to go in between the other shim and the target.
151
201
  #
152
- # @param [RDF::Statement] statement
153
- # @return [void]
154
- # @see RDF::Mutable#delete_statement
155
- def delete_statement(statement)
156
- self.deletes << statement
202
+ # @param target [Symbol] the method to send
203
+ # @param arg [Enumerable, RDF::Statement]
204
+ #
205
+ def append_statements(target, arg)
206
+ # coerce to an enumerator
207
+ stmts = case
208
+ when arg.is_a?(RDF::Statement)
209
+ [arg]
210
+ when arg.respond_to?(:each_statement)
211
+ arg.each_statement
212
+ when arg.respond_to?(:each)
213
+ arg
214
+ else
215
+ raise ArgumentError, "Invalid statement: #{arg.class}"
216
+ end
217
+
218
+ stmts.each { |s| send(target) << s }
157
219
  end
158
220
 
159
- undef_method :load, :update, :clear
160
221
  end # Changeset
161
222
  end # RDF
@@ -175,7 +175,7 @@ module RDF
175
175
  unless repository.count > 0
176
176
  start = Time.new
177
177
  count = 0
178
- self.parse(argv, opts) do |reader|
178
+ self.parse(argv, **opts) do |reader|
179
179
  reader.each_statement do |statement|
180
180
  count += 1
181
181
  end
@@ -240,7 +240,7 @@ module RDF
240
240
  out = opts[:output]
241
241
  opts = opts.merge(prefixes: {})
242
242
  writer_opts = opts.merge(standard_prefixes: true)
243
- writer_class.new(out, writer_opts) do |writer|
243
+ writer_class.new(out, **writer_opts) do |writer|
244
244
  writer << repository
245
245
  end
246
246
  end
@@ -262,7 +262,7 @@ module RDF
262
262
  description: "Validate parsed input",
263
263
  control: :none,
264
264
  parse: true,
265
- help: "validate [options] [args...]\nvalidates parsed input (may also be used with --validate)",
265
+ help: "validate [options] [args...]\nvalidates resulting repository (may also be used with --validate to check for parse-time errors)",
266
266
  lambda: ->(argv, opts) do
267
267
  opts[:output].puts "Input is " + (repository.valid? ? "" : "in") + "valid"
268
268
  end,
@@ -501,7 +501,7 @@ module RDF
501
501
  if cmds.any? {|c| COMMANDS[c.to_sym][:parse]}
502
502
  start = Time.new
503
503
  count = 0
504
- self.parse(args, options) do |reader|
504
+ self.parse(args, **options) do |reader|
505
505
  @repository << reader
506
506
  end
507
507
  secs = Time.new - start
@@ -575,7 +575,7 @@ module RDF
575
575
  RDF::Format.each do |format|
576
576
  format.cli_commands.each do |command, options|
577
577
  options = {lambda: options} unless options.is_a?(Hash)
578
- add_command(command, options)
578
+ add_command(command, **options)
579
579
  end
580
580
  end
581
581
  @commands_loaded = true
@@ -637,13 +637,13 @@ module RDF
637
637
  r = RDF::Reader.for(format|| {sample: sample})
638
638
  raise ArgumentError, "Unknown format for evaluated input" unless r
639
639
  (@readers ||= []) << r
640
- r.new(input, options) do |reader|
640
+ r.new(input, **options) do |reader|
641
641
  yield(reader)
642
642
  end
643
643
  else
644
644
  options[:format] = format if format
645
645
  files.each do |file|
646
- RDF::Reader.open(file, options) do |reader|
646
+ RDF::Reader.open(file, **options) do |reader|
647
647
  (@readers ||= []) << reader.class.to_s
648
648
  yield(reader)
649
649
  end
@@ -122,7 +122,7 @@ module RDF
122
122
  sample = case sample
123
123
  when Proc then sample.call.to_s
124
124
  else sample.dup.to_s
125
- end.force_encoding(Encoding::ASCII_8BIT)
125
+ end.dup.force_encoding(Encoding::ASCII_8BIT)
126
126
  # Given a sample, perform format detection across the appropriate formats, choosing the last that matches
127
127
  # Return last format that has a positive detection
128
128
  formats = formats.select {|f| f.detect(sample)}
@@ -145,10 +145,10 @@ module RDF
145
145
  # @param [String, RDF::URI] filename
146
146
  # @return [Class]
147
147
  #
148
- # @overload for(**options)
148
+ # @overload for(options)
149
149
  # Finds an RDF serialization format class based on various options.
150
150
  #
151
- # @param [Hash{Symbol => Object}] options
151
+ # @param [Hash{Symbol => Object}] options ({})
152
152
  # @option options [String, #to_s] :file_name (nil)
153
153
  # @option options [Symbol, #to_sym] :file_extension (nil)
154
154
  # @option options [String, #to_s] :content_type (nil)
@@ -164,19 +164,26 @@ module RDF
164
164
  # @yieldreturn [String] another way to provide a sample, allows lazy for retrieving the sample.
165
165
  #
166
166
  # @return [Class]
167
- def self.for(*args, **options, &block)
167
+ def self.for(*arg, &block)
168
+ case arg.length
169
+ when 0 then arg = nil
170
+ when 1 then arg = arg.first
171
+ else
172
+ raise ArgumentError, "Format.for accepts zero or one argument, got #{arg.length}."
173
+ end
174
+
175
+ options = arg.is_a?(Hash) ? arg : {}
168
176
  options = {sample: block}.merge(options) if block_given?
169
- formats = case args.first
177
+ formats = case arg
170
178
  when String, RDF::URI
171
179
  # Find a format based on the file name
172
- self.each(file_name: args.first, **options).to_a
180
+ self.each(file_name: arg, **options).to_a
173
181
  when Symbol
174
182
  # Try to find a match based on the full class name
175
183
  # We want this to work even if autoloading fails
176
- fmt = args.first
177
- classes = self.each(options).select {|f| f.symbols.include?(fmt)}
184
+ classes = self.each(**options).select {|f| f.symbols.include?(arg)}
178
185
  if classes.empty?
179
- classes = case fmt
186
+ classes = case arg
180
187
  when :ntriples then [RDF::NTriples::Format]
181
188
  when :nquads then [RDF::NQuads::Format]
182
189
  else []
@@ -184,7 +191,7 @@ module RDF
184
191
  end
185
192
  classes
186
193
  else
187
- self.each(options.merge(all_if_none: false)).to_a
194
+ self.each(**options.merge(all_if_none: false)).to_a
188
195
  end
189
196
 
190
197
  # Return the last detected format
@@ -17,7 +17,7 @@ module RDF
17
17
  # enumerable.has_quad?([subject, predicate, object, graph_name])
18
18
  #
19
19
  # @example Checking whether a specific value exists
20
- # enumerable.has_subject?(RDF::URI("http://rubygems.org/gems/rdf"))
20
+ # enumerable.has_subject?(RDF::URI("https://rubygems.org/gems/rdf"))
21
21
  # enumerable.has_predicate?(RDF::RDFS.label)
22
22
  # enumerable.has_object?(RDF::Literal("A Ruby library for working with Resource Description Framework (RDF) data.", language: :en))
23
23
  # enumerable.has_graph?(RDF::URI("http://ar.to/#self"))
@@ -71,6 +71,7 @@ module RDF
71
71
  # * `:literal_equality' preserves [term-equality](https://www.w3.org/TR/rdf11-concepts/#dfn-literal-term-equality) for literals. Literals are equal only if their lexical values and datatypes are equal, character by character. Literals may be "inlined" to value-space for efficiency only if `:literal_equality` is `false`.
72
72
  # * `:validity` allows a concrete Enumerable implementation to indicate that it does or does not support valididty checking. By default implementations are assumed to support validity checking.
73
73
  # * `:skolemize` supports [Skolemization](https://www.w3.org/wiki/BnodeSkolemization) of an `Enumerable`. Implementations supporting this feature must implement a `#skolemize` method, taking a base URI used for minting URIs for BNodes as stable identifiers and a `#deskolemize` method, also taking a base URI used for turning URIs having that prefix back into the same BNodes which were originally skolemized.
74
+ # * `:rdfstar` supports RDF* where statements may be subjects or objects of other statements.
74
75
  #
75
76
  # @param [Symbol, #to_sym] feature
76
77
  # @return [Boolean]
@@ -220,7 +221,7 @@ module RDF
220
221
  def each_triple
221
222
  if block_given?
222
223
  each_statement do |statement|
223
- yield *statement.to_triple
224
+ yield(*statement.to_triple)
224
225
  end
225
226
  end
226
227
  enum_triple
@@ -282,7 +283,7 @@ module RDF
282
283
  def each_quad
283
284
  if block_given?
284
285
  each_statement do |statement|
285
- yield *statement.to_quad
286
+ yield(*statement.to_quad)
286
287
  end
287
288
  end
288
289
  enum_quad
@@ -9,6 +9,7 @@ module RDF
9
9
  extend RDF::Util::Aliasing::LateBound
10
10
  include RDF::Readable
11
11
  include RDF::Writable
12
+ include RDF::Util::Coercions
12
13
 
13
14
  ##
14
15
  # Returns `true` if `self` is mutable.
@@ -37,10 +38,10 @@ module RDF
37
38
  # @option options [RDF::Resource] :graph_name
38
39
  # Set set graph name of each loaded statement
39
40
  # @return [void]
40
- def load(url, graph_name: nil, **options)
41
+ def load(url, graph_name: nil, **options)
41
42
  raise TypeError.new("#{self} is immutable") if immutable?
42
43
 
43
- Reader.open(url, {base_uri: url}.merge(options)) do |reader|
44
+ Reader.open(url, base_uri: url, **options) do |reader|
44
45
  if graph_name
45
46
  statements = []
46
47
  reader.each_statement do |statement|
@@ -154,20 +155,9 @@ module RDF
154
155
  def delete(*statements)
155
156
  raise TypeError.new("#{self} is immutable") if immutable?
156
157
 
157
- statements.map! do |value|
158
- case
159
- when value.respond_to?(:each_statement)
160
- delete_statements(value)
161
- nil
162
- when (statement = Statement.from(value)).constant?
163
- statement
164
- else
165
- delete_statements(query(value))
166
- nil
167
- end
158
+ coerce_statements(statements, query: true, constant: true) do |value|
159
+ delete_statements(value)
168
160
  end
169
- statements.compact!
170
- delete_statements(statements) unless statements.empty?
171
161
 
172
162
  return self
173
163
  end