rdf 2.0.0.beta1 → 2.0.0.beta2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d6b5ce1d8969fb6463efd82a7fe9826589bac799
4
- data.tar.gz: e0c0066693ddc81d5089bf63f7502527750d1380
3
+ metadata.gz: 93fe92b9d0141e5c9537bdd33df4ac48b16dd614
4
+ data.tar.gz: 48c1f403490e61487a6d66cc2b9434e1199ddd2f
5
5
  SHA512:
6
- metadata.gz: f866ac2a48e98e154bd786f6e0c30f5298e70c01de47847c660fded022f14ef58f19b0a727e20a53704d0d46f610cae4f53a08b5b2b94dc5f82d85da006e9d0b
7
- data.tar.gz: ccede9fff108419d7d0df702df4dd8e8a24bd53be1e0aa0bd1a1986158ff5dcdbfd7bb824eb36cbe1dc0ea41243732bb49f0aec5dbbf869721595911e7f39709
6
+ metadata.gz: 766b89bac9456158ba2e3bc4640e5b3e4c361e4a20ac72737d259146223f224523f059f302331edb9a58ade726a03fc3f45e0672a404e47f73b9044529548aaa
7
+ data.tar.gz: e5327946f1f6b1d41e05b262b8fb64a4efeec329aafb14107e2e46a92c15b0e7cd217fded4e4339f83f034b6f87a690d1c387ebe21f8923e731e2006e4fc4201
data/README.md CHANGED
@@ -79,12 +79,13 @@ Notably, {RDF::Queryable#query} and {RDF::Query#execute} are now completely symm
79
79
  When installed, RDF.rb includes a `rdf` shell script which acts as a wrapper to perform a number of different
80
80
  operations on RDF files using available readers and writers.
81
81
 
82
- * `serialize`: Parse an RDF input and re-serializing to [N-Triples][] or another available format using `--output-format` option.
83
82
  * `count`: Parse and RDF input and count the number of statements.
84
- * `subjects`: Returns unique subjects from parsed input.
85
- * `objects`: Returns unique objects from parsed input.
86
83
  * `predicates`: Returns unique objects from parsed input.
84
+ * `objects`: Returns unique objects from parsed input.
85
+ * `serialize`: Parse an RDF input and re-serializing to [N-Triples][] or another available format using `--output-format` option.
86
+ * `subjects`: Returns unique subjects from parsed input.
87
87
 
88
+ The `serialize` command can also be used to serialize as a vocabulary
88
89
  ## Examples
89
90
 
90
91
  require 'rdf'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0.beta1
1
+ 2.0.0.beta2
data/bin/rdf CHANGED
@@ -11,8 +11,19 @@ options = RDF::CLI.options do
11
11
  self.on('-V', '--version', 'Display the RDF.rb version and exit.') do
12
12
  puts RDF::VERSION; exit
13
13
  end
14
+
15
+ ARGV.select {|a| RDF::CLI.commands.include?(a)}.each do |cmd|
16
+ # Load command-specific options
17
+ Array(RDF::CLI::COMMANDS[cmd.to_sym][:options]).each do |cli_opt|
18
+ on_args = cli_opt.on || []
19
+ on_args << cli_opt.description if cli_opt.description
20
+ self.on(*on_args) do |arg|
21
+ self.options[cli_opt.symbol] = cli_opt.call(arg)
22
+ end
23
+ end
24
+ end
14
25
  end
15
26
 
16
27
  abort options.banner if ARGV.empty? && !options.options[:evaluate]
17
28
 
18
- RDF::CLI.exec_command(command = ARGV.shift, ARGV, options.options)
29
+ RDF::CLI.exec(ARGV, options.options)
data/lib/rdf/cli.rb CHANGED
@@ -1,24 +1,19 @@
1
1
  require 'rdf'
2
2
  require 'rdf/ntriples'
3
3
  require 'rdf/nquads'
4
+ require 'rdf/vocab/writer'
4
5
  require 'logger'
5
6
  require 'optparse'
6
7
  begin
7
- gem 'linkeddata'
8
8
  require 'linkeddata'
9
9
  rescue LoadError
10
10
  # Silently load without linkeddata, but try some others
11
- %w(rdfa rdfxml turtle).each do |ser|
11
+ %w(reasoner rdfa rdfxml turtle json/ld ld/patch).each do |ser|
12
12
  begin
13
- require "rdf/#{ser}"
13
+ require ser.include?('/') ? ser : "rdf/#{ser}"
14
14
  rescue LoadError
15
15
  end
16
16
  end
17
-
18
- begin
19
- require 'json/ld'
20
- rescue LoadError
21
- end
22
17
  end
23
18
 
24
19
  class OptionParser
@@ -29,6 +24,10 @@ end
29
24
  module RDF
30
25
  # Individual formats can modify options by updating {Reader.options} or {Writer.options}. Format-specific commands are taken from {Format.cli_commands} for each loaded format, which returns an array of lambdas taking arguments and options.
31
26
  #
27
+ # Other than `help`, all commands parse an input file.
28
+ #
29
+ # Multiple commands may be added in sequence to execute a pipeline.
30
+ #
32
31
  # @example Creating Reader-specific options:
33
32
  # class Reader
34
33
  # def self.options
@@ -38,7 +37,11 @@ module RDF
38
37
  # datatype: TrueClass,
39
38
  # on: ["--canonicalize"],
40
39
  # description: "Canonicalize input/output.") {true},
41
- # ...
40
+ # RDF::CLI::Option.new(
41
+ # symbol: :uri,
42
+ # datatype: RDF::URI,
43
+ # on: ["--uri STRING"],
44
+ # description: "URI.") {|v| RDF::URI(v)},
42
45
  # ]
43
46
  # end
44
47
  #
@@ -46,18 +49,27 @@ module RDF
46
49
  # class Format
47
50
  # def self.cli_commands
48
51
  # {
49
- # count: ->(argv, opts) do
50
- # count = 0
51
- # RDF::CLI.parse(argv, opts) do |reader|
52
- # reader.each_statement do |statement|
53
- # count += 1
54
- # end
55
- # end
56
- # $stdout.puts "Parsed #{count} statements"
57
- # end,
52
+ # count: {
53
+ # description: "",
54
+ # parse: true,
55
+ # lambda: ->(argv, opts) {}
56
+ # },
58
57
  # }
59
58
  # end
60
59
  #
60
+ # @example Adding a command manually
61
+ # class MyCommand
62
+ # RDF::CLI.add_command(:count, description: "Count statements") do |argv, opts|
63
+ # count = 0
64
+ # RDF::CLI.parse(argv, opts) do |reader|
65
+ # reader.each_statement do |statement|
66
+ # count += 1
67
+ # end
68
+ # end
69
+ # $stdout.puts "Parsed #{count} statements"
70
+ # end
71
+ # end
72
+ #
61
73
  # Format-specific commands should verify that the reader and/or output format are appropriate for the command.
62
74
  class CLI
63
75
 
@@ -107,78 +119,102 @@ module RDF
107
119
 
108
120
  # @private
109
121
  COMMANDS = {
110
- count: ->(argv, opts) do
111
- start = Time.new
112
- count = 0
113
- self.parse(argv, opts) do |reader|
114
- reader.each_statement do |statement|
115
- count += 1
122
+ count: {
123
+ description: "Count statements in parsed input",
124
+ parse: false,
125
+ help: "count [options] [args...]\nreturns number of parsed statements",
126
+ lambda: ->(argv, opts) do
127
+ unless repository.count > 0
128
+ start = Time.new
129
+ count = 0
130
+ self.parse(argv, opts) do |reader|
131
+ reader.each_statement do |statement|
132
+ count += 1
133
+ end
134
+ end
135
+ secs = Time.new - start
136
+ $stdout.puts "Parsed #{count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
116
137
  end
117
138
  end
118
- secs = Time.new - start
119
- $stdout.puts "Parsed #{count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
120
- end,
121
- help: ->(argv, opts) do
122
- self.usage(self.options)
123
- end,
124
- lenghts: ->(argv, opts) do
125
- self.parse(argv, opts) do |reader|
126
- reader.each_statement do |statement|
139
+ },
140
+ help: {
141
+ description: "This message",
142
+ parse: false,
143
+ lambda: ->(argv, opts) {self.usage(self.options)}
144
+ },
145
+ lengths: {
146
+ description: "Lengths of each parsed statement",
147
+ parse: true,
148
+ help: "lengths [options] [args...]\nreturns statement lengths",
149
+ lambda: ->(argv, opts) do
150
+ repository.each_statement do |statement|
127
151
  $stdout.puts statement.to_s.size
128
152
  end
129
153
  end
130
- end,
131
- objects: ->(argv, opts) do
132
- $stdout.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
133
- self.parse(argv, opts) do |reader|
134
- reader.each_statement do |statement|
135
- $stdout.puts statement.object.to_ntriples
154
+ },
155
+ objects: {
156
+ description: "Serialize each parsed object to N-Triples",
157
+ parse: true,
158
+ help: "objects [options] [args...]\nreturns unique objects",
159
+ lambda: ->(argv, opts) do
160
+ $stdout.puts "Objects"
161
+ repository.each_object do |object|
162
+ $stdout.puts object.to_ntriples
136
163
  end
137
164
  end
138
- end,
139
- predicates: ->(argv, opts) do
140
- $stdout.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
141
- self.parse(argv, opts) do |reader|
142
- reader.each_statement do |statement|
143
- $stdout.puts statement.predicate.to_ntriples
165
+ },
166
+ predicates: {
167
+ description: "Serialize each parsed predicate to N-Triples",
168
+ parse: true,
169
+ help: "predicates [options] [args...]\nreturns unique predicates",
170
+ lambda: ->(argv, opts) do
171
+ $stdout.puts "Predicates"
172
+ repository.each_predicate do |predicate|
173
+ $stdout.puts predicate.to_ntriples
144
174
  end
145
175
  end
146
- end,
147
- serialize: ->(argv, opts) do
148
- writer_class = RDF::Writer.for(opts[:output_format]) || RDF::NTriples::Writer
149
- out = opts[:output] || $stdout
150
- out.set_encoding(Encoding::UTF_8) if out.respond_to?(:set_encoding) && RUBY_PLATFORM == "java"
151
- opts = opts.merge(prefixes: {})
152
- writer_opts = opts.merge(standard_prefixes: true)
153
- self.parse(argv, opts) do |reader|
176
+ },
177
+ serialize: {
178
+ description: "Serialize each parsed statement to N-Triples, or the specified output format",
179
+ parse: true,
180
+ help: "serialize [options] [args...]\nserialize output using specified format (or n-triples if not specified)",
181
+ lambda: ->(argv, opts) do
182
+ writer_class = RDF::Writer.for(opts[:output_format]) || RDF::NTriples::Writer
183
+ out = opts[:output] || $stdout
184
+ opts = opts.merge(prefixes: {})
185
+ writer_opts = opts.merge(standard_prefixes: true)
154
186
  writer_class.new(out, writer_opts) do |writer|
155
- writer << reader
187
+ writer << repository
156
188
  end
157
189
  end
158
- end,
159
- subjects: ->(argv, opts) do
160
- $stdout.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
161
- self.parse(argv, opts) do |reader|
162
- reader.each_statement do |statement|
163
- $stdout.puts statement.subject.to_ntriples
190
+ },
191
+ subjects: {
192
+ description: "Serialize each parsed subject to N-Triples",
193
+ parse: true,
194
+ help: "subjects [options] [args...]\nreturns unique subjects",
195
+ lambda: ->(argv, opts) do
196
+ $stdout.puts "Subjects"
197
+ repository.each_subject do |subject|
198
+ $stdout.puts subject.to_ntriples
164
199
  end
165
200
  end
166
- end,
167
- validate: ->(argv, opts) do
168
- start = Time.new
169
- count = 0
170
- valid = true
171
- self.parse(argv, opts) do |reader|
172
- reader.each_statement do |statement|
173
- count += 1
174
- valid = false if statement.invalid?
175
- end
201
+ },
202
+ validate: {
203
+ description: "Validate parsed input",
204
+ parse: true,
205
+ help: "validate [options] [args...]\nvalidates parsed input (may also be used with --validate)",
206
+ lambda: ->(argv, opts) do
207
+ $stdout.puts "Input is " + (repository.valid? ? "" :"in") + "valid"
176
208
  end
177
- secs = Time.new - start
178
- $stdout.puts "Validated #{count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
179
- end
209
+ }
180
210
  }
181
211
 
212
+ class << self
213
+ # Repository containing parsed statements
214
+ # @return [RDF::Repository]
215
+ attr_accessor :repository
216
+ end
217
+
182
218
  ##
183
219
  # @return [String]
184
220
  def self.basename() File.basename($0) end
@@ -226,7 +262,7 @@ module RDF
226
262
  else options.instance_eval(&block)
227
263
  end
228
264
  end
229
- options.banner ||= "Usage: #{self.basename} [options] command [args...]"
265
+ options.banner = "Usage: #{self.basename} command+ [options] [args...]"
230
266
 
231
267
  options.on('-d', '--debug', 'Enable debug output for troubleshooting.') do
232
268
  opts[:logger].level = Logger::DEBUG
@@ -276,6 +312,7 @@ module RDF
276
312
 
277
313
  options.on_tail("-h", "--help", "Show this message") do
278
314
  self.usage(options)
315
+ exit(0)
279
316
  end
280
317
 
281
318
  begin
@@ -289,7 +326,8 @@ module RDF
289
326
 
290
327
  ##
291
328
  # Output usage message
292
- def self.usage(options)
329
+ def self.usage(options, banner: nil)
330
+ options.banner = banner if banner
293
331
  $stdout.puts options
294
332
  $stdout.puts "Note: available commands and options may be different depending on selected --input-format and/or --output-format."
295
333
  $stdout.puts "Available commands:\n\t#{self.commands.join("\n\t")}"
@@ -297,15 +335,47 @@ module RDF
297
335
  end
298
336
 
299
337
  ##
300
- # @param [#to_sym] command
338
+ # Execute one or more commands, parsing input as necessary
339
+ #
301
340
  # @param [Array<String>] args
302
341
  # @return [Boolean]
303
- def self.exec_command(command, args, options = {})
304
- unless commands.include?(command.to_s)
305
- abort "unknown command `#{command}'"
342
+ def self.exec(args, options = {})
343
+ out = options[:output] || $stdout
344
+ out.set_encoding(Encoding::UTF_8) if out.respond_to?(:set_encoding) && RUBY_PLATFORM == "java"
345
+ cmds, args = args.partition {|e| commands.include?(e.to_s)}
346
+
347
+ if cmds.empty?
348
+ usage(options)
349
+ abort "No command given"
350
+ end
351
+
352
+ if cmds.first == 'help'
353
+ on_cmd = cmds[1]
354
+ if on_cmd && COMMANDS.fetch(on_cmd.to_sym, {})[:help]
355
+ usage(self.options, banner: "Usage: #{self.basename.split('/').last} #{COMMANDS[on_cmd.to_sym][:help]}")
356
+ else
357
+ usage(self.options)
358
+ end
359
+ return
360
+ end
361
+
362
+ @repository = RDF::Repository.new
363
+
364
+ # Parse input files if any command requires it
365
+ if cmds.any? {|c| COMMANDS[c.to_sym][:parse]}
366
+ start = Time.new
367
+ count = 0
368
+ self.parse(args, options) do |reader|
369
+ @repository << reader
370
+ end
371
+ secs = Time.new - start
372
+ $stdout.puts "Parsed #{repository.count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
306
373
  end
307
374
 
308
- COMMANDS[command.to_sym].call(args, options)
375
+ # Run each command in sequence
376
+ cmds.each do |command|
377
+ COMMANDS[command.to_sym][:lambda].call(args, options)
378
+ end
309
379
  rescue ArgumentError => e
310
380
  abort e.message
311
381
  end
@@ -316,8 +386,9 @@ module RDF
316
386
  # First, load commands from other formats
317
387
  unless @commands_loaded
318
388
  RDF::Format.each do |format|
319
- format.cli_commands.each do |command, lambda|
320
- COMMANDS[command] ||= lambda
389
+ format.cli_commands.each do |command, options|
390
+ options = {lambda: options} unless options.is_a?(Hash)
391
+ add_command(command, options)
321
392
  end
322
393
  end
323
394
  @commands_loaded = true
@@ -325,11 +396,29 @@ module RDF
325
396
  COMMANDS.keys.map(&:to_s).sort
326
397
  end
327
398
 
399
+ ##
400
+ # Add a command.
401
+ #
402
+ # @param [#to_sym] command
403
+ # @param [Hash{Symbol => String}] options
404
+ # @option options [String] description
405
+ # @option options [String] help string to display for help
406
+ # @option options [Boolean] parse parse input files in to Repository, or not.
407
+ # @option options [Array<RDF::CLI::Option>] options specific to this command
408
+ # @yield argv, opts
409
+ # @yieldparam [Array<String>] argv
410
+ # @yieldparam [Hash] opts
411
+ # @yieldreturn [void]
412
+ def self.add_command(command, options = {}, &block)
413
+ options[:lambda] = block if block_given?
414
+ COMMANDS[command.to_sym] ||= options
415
+ end
416
+
328
417
  ##
329
418
  # @return [Array<String>] list of available formats
330
419
  def self.formats(reader: false, writer: false)
331
- f = RDF::Format.each.
332
- select {|f| (reader ? f.reader : (writer ? f.writer : true))}.
420
+ f = RDF::Format.sort_by(&:to_sym).each.
421
+ select {|f| (reader ? f.reader : (writer ? f.writer : (f.reader || f.writer)))}.
333
422
  inject({}) do |memo, reader|
334
423
  memo.merge(reader.to_sym => reader.name)
335
424
  end
data/lib/rdf/format.rb CHANGED
@@ -361,7 +361,7 @@ module RDF
361
361
 
362
362
  ##
363
363
  # Hash of CLI commands appropriate for this format
364
- # @return [Hash{Symbol => Lambda(Array, Hash)}]
364
+ # @return [Hash{Symbol => {description: String, lambda: Lambda(Array, Hash)}}]
365
365
  def self.cli_commands
366
366
  {}
367
367
  end
@@ -761,6 +761,15 @@ module RDF
761
761
  end
762
762
  end
763
763
 
764
+ ##
765
+ # @note this instantiates an writer; it could probably be done more
766
+ # efficiently by refactoring `RDF::Reader` and/or `RDF::Format` to expose
767
+ # a list of valid format symbols.
768
+ def respond_to_missing?(name, include_private = false)
769
+ return RDF::Writer.for(name.to_s[3..-1].to_sym) if name.to_s[0,3] == 'to_'
770
+ super
771
+ end
772
+
764
773
  ##
765
774
  # @private
766
775
  # @param [Symbol, #to_sym] method
@@ -31,14 +31,11 @@ module RDF
31
31
  include Queryable
32
32
  include Enumerable
33
33
 
34
- def method_missing(method, *args)
35
- self.to_a if method.to_sym == :to_ary
36
- end
37
-
38
34
  # Make sure returned arrays are also queryable
39
35
  def to_a
40
36
  return super.to_a.extend(RDF::Queryable, RDF::Enumerable)
41
37
  end
38
+ alias_method :to_ary, :to_a
42
39
  end
43
40
  end
44
- end
41
+ end
@@ -260,6 +260,16 @@ module RDF
260
260
  super
261
261
  end
262
262
  end
263
+
264
+ ##
265
+ # @note this instantiates an entire reader; it could probably be done more
266
+ # efficiently by refactoring `RDF::Reader` and/or `RDF::Format` to expose
267
+ # a list of valid format symbols.
268
+ def respond_to_missing?(name, include_private = false)
269
+ return RDF::Reader.for(name.to_s[5..-1].to_sym) if name.to_s[0,5] == 'from_'
270
+ super
271
+ end
272
+
263
273
  protected
264
274
 
265
275
  ##
@@ -20,22 +20,37 @@ module RDF
20
20
  # Executes the given block in a transaction.
21
21
  #
22
22
  # @example running a transaction
23
- # repository.transaction do |tx|
23
+ # repository.transaction(mutable: true) do |tx|
24
24
  # tx.insert [RDF::URI("http://rubygems.org/gems/rdf"), RDF::RDFS.label, "RDF.rb"]
25
25
  # end
26
26
  #
27
27
  # Raising an error within the transaction block causes automatic rollback.
28
28
  #
29
- # @param mutable [Boolean]
30
- # allows changes to the transaction, otherwise it is a read-only snapshot of the underlying repository.
31
- # @yield [tx]
32
- # @yieldparam [RDF::Transaction] tx
33
- # @yieldreturn [void] ignored
34
- # @return [self]
29
+ # @example manipulating a live transaction
30
+ # tx = repository.transaction(mutable: true)
31
+ # tx.insert [RDF::URI("http://rubygems.org/gems/rdf"), RDF::RDFS.label, "RDF.rb"]
32
+ # tx.execute
33
+ #
34
+ # @overload transaction(mutable: false)
35
+ # @param mutable [Boolean]
36
+ # @return [RDF::Transaction] an open transaction; the client is
37
+ # responsible for closing the transaction via #execute or #rollback
38
+ #
39
+ # @overload transaction(mutable: false, &block)
40
+ # @param mutable [Boolean]
41
+ # allows changes to the transaction, otherwise it is a read-only
42
+ # snapshot of the underlying repository.
43
+ # @yield [tx]
44
+ # @yieldparam [RDF::Transaction] tx
45
+ # @yieldreturn [void] ignored
46
+ # @return [self]
47
+ #
35
48
  # @see RDF::Transaction
36
49
  # @since 0.3.0
37
50
  def transaction(mutable: false, &block)
38
51
  tx = begin_transaction(mutable: mutable)
52
+ return tx unless block_given?
53
+
39
54
  begin
40
55
  case block.arity
41
56
  when 1 then block.call(tx)
@@ -27,7 +27,7 @@ module RDF; class Literal
27
27
  when 'INF' then 1/0.0
28
28
  when '-INF' then -1/0.0
29
29
  when 'NaN' then 0/0.0
30
- else Float(value) rescue nil
30
+ else Float(value.sub(/\.[eE]/, '.0E')) rescue nil
31
31
  end
32
32
  when value.is_a?(::Float) then value
33
33
  when value.respond_to?(:to_f) then value.to_f
@@ -162,13 +162,13 @@ module RDF
162
162
  # @see http://www.w3.org/TR/rdf11-concepts/#section-Graph-Literal
163
163
  # @see http://www.w3.org/TR/rdf11-concepts/#section-Datatypes
164
164
  def initialize(value, options = {})
165
- @object = value
165
+ @object = value.freeze
166
166
  @string = options[:lexical] if options[:lexical]
167
167
  @string = value if !defined?(@string) && value.is_a?(String)
168
- @string = @string.encode(Encoding::UTF_8) if @string
168
+ @string = @string.encode(Encoding::UTF_8).freeze if @string
169
169
  @object = @string if @string && @object.is_a?(String)
170
170
  @language = options[:language].to_s.downcase.to_sym if options[:language]
171
- @datatype = RDF::URI(options[:datatype]) if options[:datatype]
171
+ @datatype = RDF::URI(options[:datatype]).freeze if options[:datatype]
172
172
  @datatype ||= self.class.const_get(:DATATYPE) if self.class.const_defined?(:DATATYPE)
173
173
  @datatype ||= @language ? RDF.langString : RDF::XSD.string
174
174
  raise ArgumentError, "datatype of rdf:langString requires a language" if !@language && @datatype == RDF::langString
@@ -425,7 +425,8 @@ module RDF
425
425
  gsub("\n", '\\n').
426
426
  gsub("\r", '\\r').
427
427
  gsub("\f", '\\f').
428
- gsub('"', '\\"')
428
+ gsub('"', '\\"').
429
+ freeze
429
430
  end
430
431
 
431
432
  ##
@@ -433,7 +434,7 @@ module RDF
433
434
  #
434
435
  # @return [String]
435
436
  def to_s
436
- @object.to_s
437
+ @object.to_s.freeze
437
438
  end
438
439
 
439
440
  ##
@@ -442,7 +443,7 @@ module RDF
442
443
  # @return [String]
443
444
  # @since 1.1.6
444
445
  def humanize(lang = :en)
445
- to_s
446
+ to_s.freeze
446
447
  end
447
448
 
448
449
  ##
@@ -55,7 +55,7 @@ module RDF
55
55
  # @return [RDF::Node]
56
56
  # @since 0.2.0
57
57
  def self.intern(id)
58
- (cache[id = id.to_s] ||= self.new(id)).freeze
58
+ (cache[(id = id.to_s).to_sym] ||= self.new(id)).freeze
59
59
  end
60
60
 
61
61
  ##
@@ -82,7 +82,7 @@ module RDF
82
82
  # @param [#to_s] id
83
83
  def initialize(id = nil)
84
84
  id = nil if id.to_s.empty?
85
- @id = (id || "g#{__id__.to_i.abs}").to_s
85
+ @id = (id || "g#{__id__.to_i.abs}").to_s.freeze
86
86
  end
87
87
 
88
88
  ##
@@ -56,14 +56,6 @@ module RDF
56
56
  super
57
57
  end
58
58
 
59
- ##
60
- # Returns a base representation of `self`.
61
- #
62
- # @return [RDF::Value]
63
- def to_base
64
- self
65
- end
66
-
67
59
  ##
68
60
  # Returns `true` if `self` is a {RDF::Term}.
69
61
  #
@@ -85,7 +77,7 @@ module RDF
85
77
  #
86
78
  # @return [Sring]
87
79
  def to_base
88
- RDF::NTriples.serialize(self)
80
+ RDF::NTriples.serialize(self).freeze
89
81
  end
90
82
 
91
83
  ##
data/lib/rdf/model/uri.rb CHANGED
@@ -143,7 +143,7 @@ module RDF
143
143
  # @return [RDF::URI] an immutable, frozen URI object
144
144
  def self.intern(*args)
145
145
  str = args.first
146
- (cache[str = str.to_s] ||= self.new(*args)).freeze
146
+ (cache[(str = str.to_s).to_sym] ||= self.new(*args)).freeze
147
147
  end
148
148
 
149
149
  ##
@@ -229,6 +229,7 @@ module RDF
229
229
  if @value.encoding != Encoding::UTF_8
230
230
  @value = @value.dup if @value.frozen?
231
231
  @value.force_encoding(Encoding::UTF_8)
232
+ @value.freeze
232
233
  end
233
234
  else
234
235
  %w(
@@ -815,7 +816,7 @@ module RDF
815
816
  path,
816
817
  ("?#{query}" if query),
817
818
  ("##{fragment}" if fragment)
818
- ].compact.join("")
819
+ ].compact.join("").freeze
819
820
  end
820
821
 
821
822
  ##
@@ -273,5 +273,11 @@ class RDF::Query
273
273
  super # raises NoMethodError
274
274
  end
275
275
  end
276
+
277
+ ##
278
+ # @return [Boolean]
279
+ def respond_to_missing?(name, include_private = false)
280
+ @bindings.has_key?(name.to_sym) || super
281
+ end
276
282
  end # Solution
277
283
  end # RDF::Query
@@ -16,7 +16,13 @@ module RDF; module Util
16
16
  def logger(options = {})
17
17
  logger = options.fetch(:logger, @logger)
18
18
  logger = @options[:logger] if logger.nil? && @options
19
- logger = (@options || options)[:logger] = $stderr if logger.nil?
19
+ if logger.nil?
20
+ # Unless otherwise specified, use $stderr
21
+ logger = (@options || options)[:logger] = $stderr
22
+
23
+ # Reset log_statistics so that it's not inherited across different instances
24
+ logger.log_statistics.clear if logger.respond_to?(:log_statistics)
25
+ end
20
26
  logger = (@options || options)[:logger] = ::Logger.new(::File.open(::File::NULL, "w")) unless logger # Incase false was used, which is frozen
21
27
  logger.extend(LoggerBehavior) unless logger.is_a?(LoggerBehavior)
22
28
  logger
@@ -267,6 +273,13 @@ module RDF; module Util
267
273
  super
268
274
  end
269
275
  end
276
+
277
+ def respond_to_missing?(name, include_private = false)
278
+ return true if
279
+ [:fatal, :error, :warn, :info, :debug, :level, :sev_threshold]
280
+ .include?(name.to_sym)
281
+ super
282
+ end
270
283
  end
271
284
  end # Logger
272
285
  end; end # RDF::Util
@@ -0,0 +1,176 @@
1
+ require 'rdf'
2
+ require 'rdf/vocabulary'
3
+
4
+ module RDF
5
+ ##
6
+ # Vocabulary format specification. This can be used to generate a Ruby class definition from a loaded vocabulary.
7
+ #
8
+ class Vocabulary
9
+ class Format < RDF::Format
10
+ content_encoding 'utf-8'
11
+ writer { RDF::Vocabulary::Writer }
12
+ end
13
+
14
+ class Writer < RDF::Writer
15
+ include RDF::Util::Logger
16
+ format RDF::Vocabulary::Format
17
+
18
+ attr_accessor :class_name, :module_name
19
+
20
+ def self.options
21
+ [
22
+ RDF::CLI::Option.new(
23
+ symbol: :class_name,
24
+ datatype: String,
25
+ on: ["--class-name NAME"],
26
+ description: "Name of created Ruby class (vocabulary format)."),
27
+ RDF::CLI::Option.new(
28
+ symbol: :module_name,
29
+ datatype: String,
30
+ on: ["--module-name NAME"],
31
+ description: "Name of Ruby module containing class-name (vocabulary format)."),
32
+ RDF::CLI::Option.new(
33
+ symbol: :strict,
34
+ datatype: TrueClass,
35
+ on: ["--strict"],
36
+ description: "Make strict vocabulary"
37
+ ) {true},
38
+ RDF::CLI::Option.new(
39
+ symbol: :extra,
40
+ datatype: String,
41
+ on: ["--extra URIEncodedJSON"],
42
+ description: "URI Encoded JSON representation of extra data"
43
+ ) {|arg| ::JSON.parse(::URI.decode(arg))},
44
+ ]
45
+ end
46
+
47
+ ##
48
+ # Initializes the writer.
49
+ #
50
+ # @param [IO, File] output
51
+ # the output stream
52
+ # @param [Hash{Symbol => Object}] options = ({})
53
+ # any additional options. See {RDF::Writer#initialize}
54
+ # @option options [RDF::URI] :base_uri
55
+ # URI of this vocabulary
56
+ # @option options [String] :class_name
57
+ # Class name for this vocabulary
58
+ # @option options [String] :module_name ("RDF")
59
+ # Module name for this vocabulary
60
+ # @option options [Hash] extra
61
+ # Extra properties to add to the output (programatic only)
62
+ # @option options [String] patch
63
+ # An LD Patch to run against the graph before writing
64
+ # @option options [Boolean] strict (false)
65
+ # Create an RDF::StrictVocabulary instead of an RDF::Vocabulary
66
+ # @yield [writer] `self`
67
+ # @yieldparam [RDF::Writer] writer
68
+ # @yieldreturn [void]
69
+ def initialize(output = $stdout, options = {}, &block)
70
+ options.fetch(:base_uri) {raise ArgumentError, "base_uri option required"}
71
+ @graph = RDF::Repository.new
72
+ super
73
+ end
74
+
75
+ def write_triple(subject, predicate, object)
76
+ @graph << RDF::Statement(subject, predicate, object)
77
+ end
78
+
79
+ # Generate vocabulary
80
+ #
81
+ def write_epilogue
82
+ class_name = options[:class_name]
83
+ module_name = options.fetch(:module_name, "RDF")
84
+ source = options.fetch(:location, base_uri)
85
+ strict = options.fetch(:strict, false)
86
+
87
+ # Passing a graph for the location causes it to serialize the written triples.
88
+ vocab = RDF::Vocabulary.from_graph(@graph,
89
+ url: base_uri,
90
+ class_name: class_name,
91
+ extra: options[:extra])
92
+
93
+ @output.print %(# -*- encoding: utf-8 -*-
94
+ # frozen_string_literal: true
95
+ # This file generated automatically using vocab-fetch from #{source}
96
+ require 'rdf'
97
+ module #{module_name}
98
+ # @!parse
99
+ # # Vocabulary for <#{base_uri}>
100
+ # class #{vocab.name} < RDF::#{"Strict" if strict}Vocabulary
101
+ # end
102
+ class #{vocab.name} < RDF::#{"Strict" if strict}Vocabulary("#{base_uri}")
103
+ ).gsub(/^ /, '')
104
+
105
+ # Split nodes into Class/Property/Datatype/Other
106
+ term_nodes = {
107
+ class: {},
108
+ property: {},
109
+ datatype: {},
110
+ other: {}
111
+ }
112
+
113
+ vocab.each.to_a.sort.each do |term|
114
+ name = term.to_s[base_uri.length..-1].to_sym
115
+ kind = begin
116
+ case term.type.to_s
117
+ when /Class/ then :class
118
+ when /Property/ then :property
119
+ when /Datatype/ then :datatype
120
+ else :other
121
+ end
122
+ rescue KeyError
123
+ # This can try to resolve referenced terms against the previous version of this vocabulary, which may be strict, and fail if the referenced term hasn't been created yet.
124
+ :other
125
+ end
126
+ term_nodes[kind][name] = term.attributes
127
+ end
128
+
129
+ {
130
+ class: "Class definitions",
131
+ property: "Property definitions",
132
+ datatype: "Datatype definitions",
133
+ other: "Extra definitions"
134
+ }.each do |tt, comment|
135
+ next if term_nodes[tt].empty?
136
+ @output.puts "\n # #{comment}"
137
+ term_nodes[tt].each {|name, attributes| from_node name, attributes, tt}
138
+ end
139
+
140
+ # Query the vocabulary to extract property and class definitions
141
+ @output.puts " end\nend"
142
+ end
143
+
144
+ private
145
+ ##
146
+ # Turn a node definition into a property/term expression
147
+ def from_node(name, attributes, term_type)
148
+ op = term_type == :property ? "property" : "term"
149
+
150
+ components = [" #{op} #{name.to_sym.inspect}"]
151
+ attributes.keys.sort_by(&:to_s).map(&:to_sym).each do |key|
152
+ next if key == :vocab
153
+ value = Array(attributes[key])
154
+ component = key.inspect.start_with?(':"') ? "#{key.inspect} => " : "#{key.to_s}: "
155
+ value = value.first if value.length == 1
156
+ component << if value.is_a?(Array)
157
+ '[' + value.map {|v| serialize_value(v, key)}.sort.join(", ") + "]"
158
+ else
159
+ serialize_value(value, key)
160
+ end
161
+ components << component
162
+ end
163
+ @output.puts components.join(",\n ")
164
+ end
165
+
166
+ def serialize_value(value, key)
167
+ case key.to_s
168
+ when "comment", /:/
169
+ "%(#{value.gsub('(', '\(').gsub(')', '\)')}).freeze"
170
+ else
171
+ "#{value.inspect}.freeze"
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -263,27 +263,24 @@ module RDF
263
263
  end
264
264
 
265
265
  ##
266
- # Load a vocabulary, optionally from a separate location.
266
+ # Load an RDFS vocabulary, optionally from a separate location.
267
267
  #
268
268
  # @param [URI, #to_s] url
269
269
  # @param [String] class_name
270
270
  # The class_name associated with the vocabulary, used for creating the class name of the vocabulary. This will create a new class named with a top-level constant based on `class_name`.
271
- # @param [URI, #to_s] location
272
- # Location from which to load the vocabulary, if not from `uri`.
271
+ # @param [RDF::Queryable, URI, #to_s] location
272
+ # Location from which to load the vocabulary, or Queryable containing already loaded vocabulary triples, if not from `uri`.
273
273
  # @param [Array<Symbol>, Hash{Symbol => Hash}] extra
274
274
  # Extra terms to add to the vocabulary. In the first form, it is an array of symbols, for which terms are created. In the second, it is a Hash mapping symbols to property attributes, as described in {RDF::Vocabulary.property}.
275
275
  # @param [String] patch
276
276
  # A patch to run on the graph after loading. Requires the `ld-patch` gem to be available.
277
277
  # @return [RDF::Vocabulary] the loaded vocabulary
278
+ # @deprecated Use Vocabulary.from_graph
278
279
  def load(url, class_name: nil, location: nil, extra: nil, patch: nil)
280
+ warn "[DEPRECATION] Vocabulary.load is deprecated, use Vocabulary.from_graph instead. Called from #{Gem.location_of_caller.join(':')}"
279
281
  source = location || url
280
- vocab = if class_name
281
- Object.const_set(class_name, Class.new(self.create(url)))
282
- else
283
- Class.new(self.create(url))
284
- end
285
282
 
286
- graph = RDF::Repository.load(source)
283
+ graph = source.is_a?(RDF::Queryable) ? source : RDF::Repository.load(source)
287
284
 
288
285
  if patch
289
286
  begin
@@ -294,6 +291,27 @@ module RDF
294
291
  raise ArgumentError, "patching vocabulary requires the ld-patch gem"
295
292
  end
296
293
  end
294
+
295
+ from_graph(graph, url: url, class_name: nil, extra: extra)
296
+ end
297
+
298
+ ##
299
+ # Create a vocabulary from a graph or enumerable
300
+ #
301
+ # @param [RDF::Enumerable] graph
302
+ # @param [URI, #to_s] url
303
+ # @param [String] class_name
304
+ # The class_name associated with the vocabulary, used for creating the class name of the vocabulary. This will create a new class named with a top-level constant based on `class_name`.
305
+ # @param [Array<Symbol>, Hash{Symbol => Hash}] extra
306
+ # Extra terms to add to the vocabulary. In the first form, it is an array of symbols, for which terms are created. In the second, it is a Hash mapping symbols to property attributes, as described in {RDF::Vocabulary.property}.
307
+ # @return [RDF::Vocabulary] the loaded vocabulary
308
+ def from_graph(graph, url: nil, class_name: nil, extra: nil)
309
+ vocab = if class_name
310
+ Object.const_set(class_name, Class.new(self.create(url)))
311
+ else
312
+ Class.new(self.create(url))
313
+ end
314
+
297
315
  term_defs = {}
298
316
  graph.each do |statement|
299
317
  next unless statement.subject.uri? && statement.subject.start_with?(url)
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: 2.0.0.beta1
4
+ version: 2.0.0.beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arto Bendiken
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2016-02-22 00:00:00.000000000 Z
13
+ date: 2016-04-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: link_header
@@ -38,14 +38,14 @@ dependencies:
38
38
  requirements:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: '2.0'
41
+ version: '3.0'
42
42
  type: :runtime
43
43
  prerelease: false
44
44
  version_requirements: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: '2.0'
48
+ version: '3.0'
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: rdf-spec
51
51
  requirement: !ruby/object:Gem::Requirement
@@ -223,7 +223,6 @@ files:
223
223
  - lib/rdf.rb
224
224
  - lib/rdf/changeset.rb
225
225
  - lib/rdf/cli.rb
226
- - lib/rdf/cli/vocab-loader.rb
227
226
  - lib/rdf/format.rb
228
227
  - lib/rdf/mixin/countable.rb
229
228
  - lib/rdf/mixin/durable.rb
@@ -279,6 +278,7 @@ files:
279
278
  - lib/rdf/vocab/owl.rb
280
279
  - lib/rdf/vocab/rdfs.rb
281
280
  - lib/rdf/vocab/rdfv.rb
281
+ - lib/rdf/vocab/writer.rb
282
282
  - lib/rdf/vocab/xsd.rb
283
283
  - lib/rdf/vocabulary.rb
284
284
  - lib/rdf/writer.rb
@@ -302,7 +302,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
302
302
  version: 1.3.1
303
303
  requirements: []
304
304
  rubyforge_project: rdf
305
- rubygems_version: 2.5.1
305
+ rubygems_version: 2.4.8
306
306
  signing_key:
307
307
  specification_version: 4
308
308
  summary: A Ruby library for working with Resource Description Framework (RDF) data.
@@ -1,192 +0,0 @@
1
- require 'rdf'
2
- require 'linkeddata'
3
- require 'optparse'
4
-
5
- module RDF
6
- # Utility class to load RDF vocabularies from files or their canonical
7
- # definitions and emit either a class file for RDF::StrictVocabulary,
8
- # RDF::Vocabulary or the raw RDF vocabulary
9
- class VocabularyLoader
10
- def initialize(class_name = nil)
11
- @class_name = class_name
12
- @module_name = "RDF"
13
- @output = $stdout
14
- @output_class_file = true
15
- @uri = nil
16
- @strict = true
17
- @extra = []
18
- end
19
- attr_accessor :class_name, :module_name, :output, :output_class_file
20
- attr_reader :uri, :source
21
-
22
- # Set the URI for the loaded RDF file - by default, sets the source as
23
- # well
24
- def uri=(uri)
25
- @uri = uri
26
- @source ||= uri
27
- end
28
-
29
- # Set the source for the loaded RDF - by default, sets the URI as well
30
- def source=(uri)
31
- @source = uri
32
- @uri ||= uri
33
- end
34
-
35
- # Set output
36
- def output=(out)
37
- @output = out
38
- end
39
-
40
- # Extra properties to define
41
- def extra=(extra)
42
- @extra = extra
43
- end
44
-
45
- # patch to run over loaded vocabulary
46
- def patch=(patch)
47
- @patch = patch
48
- end
49
-
50
- # Use StrictVocabulary or Vocabulary
51
- def strict=(strict)
52
- @strict = strict
53
- end
54
-
55
- # Parses arguments, for use in a command line tool
56
- def parse_options(argv)
57
- opti = OptionParser.new
58
- opti.banner = "Usage: #{File.basename($0)} [options] [uri]\nFetch an RDFS file and produce an RDF::StrictVocabulary with it.\n\n"
59
-
60
- opti.on("--uri URI", "The URI for the fetched RDF vocabulary") do |uri|
61
- self.uri = uri
62
- end
63
-
64
- opti.on("--source SOURCE", "The source URI or file for the vocabulary") do |uri|
65
- self.source = uri
66
- end
67
-
68
- opti.on("--class-name NAME", "The class name for the output StrictVocabulary subclass") do |name|
69
- self.class_name = name
70
- end
71
-
72
- opti.on("--module-name NAME", "The module name for the output StrictVocabulary subclass") do |name|
73
- self.module_name = name
74
- end
75
-
76
- opti.on("--raw", "Don't output an output file - just the RDF") do
77
- @output_class_file = false
78
- end
79
-
80
- opti.on_tail("--help", "This help text") do
81
- $stdout.puts opti
82
- exit 1
83
- end
84
-
85
- others = opti.parse(argv)
86
-
87
- if @class_name.nil? and @output_class_file
88
- raise "Class name (--class-name) is required!"
89
- end
90
-
91
- uri ||= others.first
92
- end
93
-
94
- # Parse command line arguments and run the load-and-emit process
95
- def go(argv)
96
- parse_options(argv)
97
- run
98
-
99
- if @output != $stdout
100
- @output.close
101
- end
102
- end
103
-
104
- ##
105
- # Turn a node definition into a property/term expression
106
- def from_node(name, attributes, term_type)
107
- op = term_type == :property ? "property" : "term"
108
-
109
- components = [" #{op} #{name.to_sym.inspect}"]
110
- attributes.keys.sort_by(&:to_s).each do |key|
111
- next if key == :vocab
112
- value = Array(attributes[key])
113
- component = key.is_a?(Symbol) ? "#{key}: " : ":#{key.inspect} => "
114
- value = value.first if value.length == 1
115
- component << if value.is_a?(Array)
116
- '[' + value.map {|v| serialize_value(v, key)}.sort.join(", ") + "]"
117
- else
118
- serialize_value(value, key)
119
- end
120
- components << component
121
- end
122
- @output.puts components.join(",\n ")
123
- end
124
-
125
- def serialize_value(value, key)
126
- case key
127
- when :comment, String
128
- "%(#{value.gsub('(', '\(').gsub(')', '\)')}).freeze"
129
- else
130
- "#{value.inspect}.freeze"
131
- end
132
- end
133
-
134
- # Actually executes the load-and-emit process - useful when using this
135
- # class outside of a command line - instantiate, set attributes manually,
136
- # then call #run
137
- def run
138
- @output.print %(# -*- encoding: utf-8 -*-
139
- # frozen_string_literal: true
140
- # This file generated automatically using vocab-fetch from #{source}
141
- require 'rdf'
142
- module #{module_name}
143
- # @!parse
144
- # # Vocabulary for <#{uri}>
145
- # class #{class_name} < RDF::#{"Strict" if @strict}Vocabulary
146
- # end
147
- class #{class_name} < RDF::#{"Strict" if @strict}Vocabulary("#{uri}")
148
- ).gsub(/^ /, '') if @output_class_file
149
-
150
- # Extract statements with subjects that have the vocabulary prefix and organize into a hash of properties and values
151
- vocab = RDF::Vocabulary.load(uri, location: source, extra: @extra, patch: @patch)
152
-
153
- # Split nodes into Class/Property/Datatype/Other
154
- term_nodes = {
155
- class: {},
156
- property: {},
157
- datatype: {},
158
- other: {}
159
- }
160
-
161
- vocab.each.to_a.sort.each do |term|
162
- name = term.to_s[uri.length..-1].to_sym
163
- kind = begin
164
- case term.type.to_s
165
- when /Class/ then :class
166
- when /Property/ then :property
167
- when /Datatype/ then :datatype
168
- else :other
169
- end
170
- rescue KeyError
171
- # This can try to resolve referenced terms against the previous version of this vocabulary, which may be strict, and fail if the referenced term hasn't been created yet.
172
- :other
173
- end
174
- term_nodes[kind][name] = term.attributes
175
- end
176
-
177
- {
178
- class: "Class definitions",
179
- property: "Property definitions",
180
- datatype: "Datatype definitions",
181
- other: "Extra definitions"
182
- }.each do |tt, comment|
183
- next if term_nodes[tt].empty?
184
- @output.puts "\n # #{comment}"
185
- term_nodes[tt].each {|name, attributes| from_node name, attributes, tt}
186
- end
187
-
188
- # Query the vocabulary to extract property and class definitions
189
- @output.puts " end\nend" if @output_class_file
190
- end
191
- end
192
- end