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 +4 -4
- data/README.md +4 -3
- data/VERSION +1 -1
- data/bin/rdf +12 -1
- data/lib/rdf/cli.rb +173 -84
- data/lib/rdf/format.rb +1 -1
- data/lib/rdf/mixin/enumerable.rb +9 -0
- data/lib/rdf/mixin/enumerator.rb +2 -5
- data/lib/rdf/mixin/mutable.rb +10 -0
- data/lib/rdf/mixin/transactable.rb +22 -7
- data/lib/rdf/model/literal/double.rb +1 -1
- data/lib/rdf/model/literal.rb +7 -6
- data/lib/rdf/model/node.rb +2 -2
- data/lib/rdf/model/term.rb +1 -9
- data/lib/rdf/model/uri.rb +3 -2
- data/lib/rdf/query/solution.rb +6 -0
- data/lib/rdf/util/logger.rb +14 -1
- data/lib/rdf/vocab/writer.rb +176 -0
- data/lib/rdf/vocabulary.rb +27 -9
- metadata +6 -6
- data/lib/rdf/cli/vocab-loader.rb +0 -192
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93fe92b9d0141e5c9537bdd33df4ac48b16dd614
|
4
|
+
data.tar.gz: 48c1f403490e61487a6d66cc2b9434e1199ddd2f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
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:
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
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:
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
self.usage(self.options)
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
131
|
-
objects:
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
139
|
-
predicates:
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
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
|
-
|
147
|
-
serialize:
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
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 <<
|
187
|
+
writer << repository
|
156
188
|
end
|
157
189
|
end
|
158
|
-
|
159
|
-
subjects:
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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
|
-
|
167
|
-
validate:
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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
|
-
|
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
|
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
|
-
#
|
338
|
+
# Execute one or more commands, parsing input as necessary
|
339
|
+
#
|
301
340
|
# @param [Array<String>] args
|
302
341
|
# @return [Boolean]
|
303
|
-
def self.
|
304
|
-
|
305
|
-
|
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
|
-
|
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,
|
320
|
-
|
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 :
|
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
|
data/lib/rdf/mixin/enumerable.rb
CHANGED
@@ -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
|
data/lib/rdf/mixin/enumerator.rb
CHANGED
@@ -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
|
data/lib/rdf/mixin/mutable.rb
CHANGED
@@ -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
|
-
# @
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
# @
|
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
|
data/lib/rdf/model/literal.rb
CHANGED
@@ -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
|
##
|
data/lib/rdf/model/node.rb
CHANGED
@@ -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
|
##
|
data/lib/rdf/model/term.rb
CHANGED
@@ -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
|
##
|
data/lib/rdf/query/solution.rb
CHANGED
@@ -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
|
data/lib/rdf/util/logger.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/rdf/vocabulary.rb
CHANGED
@@ -263,27 +263,24 @@ module RDF
|
|
263
263
|
end
|
264
264
|
|
265
265
|
##
|
266
|
-
# Load
|
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.
|
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-
|
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: '
|
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: '
|
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.
|
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.
|
data/lib/rdf/cli/vocab-loader.rb
DELETED
@@ -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
|