rdf 2.0.0.beta1 → 2.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|