rdf 2.2.7 → 2.2.8
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/VERSION +1 -1
- data/bin/rdf +15 -19
- data/lib/rdf/cli.rb +311 -139
- data/lib/rdf/reader.rb +17 -3
- data/lib/rdf/vocab/writer.rb +5 -0
- data/lib/rdf/writer.rb +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a77462d7aa84383474aec0d885fcff326293c41
|
4
|
+
data.tar.gz: fc3ff787ef744bb281ce34a3a9d415232fd4081a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e257e359a05481b608dad6d3fd783e723485aaa7dd7cee828fdc3e9202b2625541fb3bc324dc4fe6703ec487c62667bbbef1f2febcddb357bee3b3abf74eea2
|
7
|
+
data.tar.gz: fe307ff319e3c78a1aec5d014b6a57dce05b4cdac64c88dad09fdab9d7d6f238f31ff64e5cf33a1ac8110530de3477755e5e5b860683517adf11cd0a819ed626
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.2.
|
1
|
+
2.2.8
|
data/bin/rdf
CHANGED
@@ -3,28 +3,24 @@ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'rdf/cli'
|
5
5
|
|
6
|
-
options = RDF::CLI.options
|
7
|
-
self.on('-v', '--verbose', 'Enable verbose output. May be given more than once.') do
|
8
|
-
self.options[:logger].level = Logger::INFO
|
9
|
-
end
|
6
|
+
options = RDF::CLI.options(ARGV)
|
10
7
|
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
abort options.banner if ARGV.empty? && !options.options[:evaluate]
|
9
|
+
|
10
|
+
# Add option_parser to parsed options to enable help
|
11
|
+
begin
|
12
|
+
messages = {}
|
13
|
+
RDF::CLI.exec(options.args, option_parser: options, **options.options.merge(messages: messages))
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
self.options[cli_opt.symbol] = cli_opt.call(arg)
|
15
|
+
unless messages.empty?
|
16
|
+
$stdout.puts "Messages:"
|
17
|
+
messages.each do |kind, term_messages|
|
18
|
+
term_messages.each do |term, messages|
|
19
|
+
$stdout.puts "#{kind} #{term}"
|
20
|
+
messages.each {|m| $stdout.puts " #{m}"}
|
22
21
|
end
|
23
22
|
end
|
24
23
|
end
|
24
|
+
rescue ArgumentError => e
|
25
|
+
RDF::CLI.abort e.message
|
25
26
|
end
|
26
|
-
|
27
|
-
abort options.banner if ARGV.empty? && !options.options[:evaluate]
|
28
|
-
|
29
|
-
# Add option_parser to parsed options to enable help
|
30
|
-
RDF::CLI.exec(ARGV, options.options.merge(option_parser: options))
|
data/lib/rdf/cli.rb
CHANGED
@@ -17,13 +17,20 @@ rescue LoadError
|
|
17
17
|
end
|
18
18
|
|
19
19
|
class OptionParser
|
20
|
+
# Actual parsed options
|
20
21
|
def options; @options || {}; end
|
21
22
|
def options=(value); @options = value; end
|
23
|
+
|
24
|
+
# Arguments remaining after extracting options
|
25
|
+
def args; @args || []; end
|
26
|
+
def args=(value); @args = value; end
|
22
27
|
end
|
23
28
|
|
24
29
|
module RDF
|
25
30
|
# 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.
|
26
31
|
#
|
32
|
+
# Status updates should be logged to `opts[:logger].info`. More complicated information can be added to `:messages` key within `opts`, if present.
|
33
|
+
#
|
27
34
|
# Other than `help`, all commands parse an input file.
|
28
35
|
#
|
29
36
|
# Multiple commands may be added in sequence to execute a pipeline.
|
@@ -34,12 +41,10 @@ module RDF
|
|
34
41
|
# [
|
35
42
|
# RDF::CLI::Option.new(
|
36
43
|
# symbol: :canonicalize,
|
37
|
-
# datatype: TrueClass,
|
38
44
|
# on: ["--canonicalize"],
|
39
45
|
# description: "Canonicalize input/output.") {true},
|
40
46
|
# RDF::CLI::Option.new(
|
41
47
|
# symbol: :uri,
|
42
|
-
# datatype: RDF::URI,
|
43
48
|
# on: ["--uri STRING"],
|
44
49
|
# description: "URI.") {|v| RDF::URI(v)},
|
45
50
|
# ]
|
@@ -66,7 +71,7 @@ module RDF
|
|
66
71
|
# count += 1
|
67
72
|
# end
|
68
73
|
# end
|
69
|
-
#
|
74
|
+
# options[:logger].info "Parsed #{count} statements"
|
70
75
|
# end
|
71
76
|
# end
|
72
77
|
#
|
@@ -87,41 +92,76 @@ module RDF
|
|
87
92
|
# @return [String]
|
88
93
|
attr_reader :description
|
89
94
|
|
90
|
-
#
|
91
|
-
# @return
|
95
|
+
# Potential values (for select or radio) or Ruby datatype
|
96
|
+
# @return [Class, Array<String>]
|
92
97
|
attr_reader :datatype
|
93
98
|
|
94
|
-
#
|
95
|
-
# @return [
|
96
|
-
attr_reader :
|
99
|
+
# Associated HTML form control
|
100
|
+
# @return [:text, :textarea, :radio, :checkbox, :select, :url, :url2, :none]
|
101
|
+
attr_reader :control
|
102
|
+
|
103
|
+
# Use of this option
|
104
|
+
# @return [:optional, :disabled, :removed, :required]
|
105
|
+
attr_accessor :use
|
97
106
|
|
98
107
|
##
|
99
108
|
# Create a new option with optional callback.
|
100
109
|
#
|
101
110
|
# @param [Symbol] symbol
|
102
111
|
# @param [Array<String>] on
|
112
|
+
# @param [String] datatype
|
113
|
+
# @param [String] control
|
103
114
|
# @param [String] description
|
104
|
-
# @param [
|
105
|
-
# @param [Boolean] multiple can have multiple comma-separated values
|
115
|
+
# @param [[:optional, :disabled, :removed, :required]] use
|
106
116
|
# @yield value which may be used within `OptionParser#on`
|
107
117
|
# @yieldparam [Object] value The option value as parsed using `on` argument
|
118
|
+
# @yieldparam [OptionParser] options (nil) optional OptionParser
|
108
119
|
# @yieldreturn [Object] a possibly modified input value
|
109
|
-
def initialize(symbol: nil, on: nil,
|
120
|
+
def initialize(symbol: nil, on: nil, datatype: nil, control: nil,
|
121
|
+
description: nil, use: :optional, **options, &block)
|
110
122
|
raise ArgumentError, "symbol is a required argument" unless symbol
|
111
123
|
raise ArgumentError, "on is a required argument" unless on
|
112
|
-
@symbol, @on, @
|
124
|
+
@symbol, @on, @datatype, @control, @description, @use, @callback = symbol.to_sym, Array(on), datatype, control, description, use, block
|
113
125
|
end
|
114
126
|
|
115
|
-
def call(arg)
|
116
|
-
|
127
|
+
def call(arg, options)
|
128
|
+
if @callback
|
129
|
+
case @callback.arity
|
130
|
+
when 1 then @callback.call(arg)
|
131
|
+
when 2 then @callback.call(arg, options)
|
132
|
+
end
|
133
|
+
else
|
134
|
+
arg
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Return version of commands appropriate for use in JSON
|
139
|
+
def to_hash
|
140
|
+
{
|
141
|
+
symbol: symbol,
|
142
|
+
datatype: (datatype.is_a?(Class) ? datatype.name : datatype),
|
143
|
+
control: control,
|
144
|
+
description: description,
|
145
|
+
use: use
|
146
|
+
}
|
117
147
|
end
|
118
148
|
end
|
119
149
|
|
120
|
-
#
|
150
|
+
# Built-in commands. Other commands are imported from the Format class of different readers/writers using {RDF::Format#cli_commands}. `COMMANDS` is a Hash who's keys are commands that may be executed by {RDF::CLI.exec}. The value is a hash containing the following keys:
|
151
|
+
# * `description` used for providing information about the command.
|
152
|
+
# * `parse` Boolean value to determine if input files should automatically be parsed into `repository`.
|
153
|
+
# * `help` used for the CLI help output.
|
154
|
+
# * `lambda` code run to execute command.
|
155
|
+
# * `filter` Option values that must match for command to be used
|
156
|
+
# * `control` Used to indicate how (if) command is displayed
|
157
|
+
# * `options` an optional array of `RDF::CLI::Option` describing command-specific options.
|
158
|
+
# * `option_use`: A hash of option symbol to option usage, used for overriding the default status of an option for this command.
|
159
|
+
# @return [Hash{Symbol => Hash{Symbol => Object}}]
|
121
160
|
COMMANDS = {
|
122
161
|
count: {
|
123
162
|
description: "Count statements in parsed input",
|
124
163
|
parse: false,
|
164
|
+
control: :none,
|
125
165
|
help: "count [options] [args...]\nreturns number of parsed statements",
|
126
166
|
lambda: ->(argv, opts) do
|
127
167
|
unless repository.count > 0
|
@@ -133,54 +173,63 @@ module RDF
|
|
133
173
|
end
|
134
174
|
end
|
135
175
|
secs = Time.new - start
|
136
|
-
|
176
|
+
opts[:output].puts "Parsed #{count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
|
137
177
|
end
|
138
|
-
end
|
178
|
+
end,
|
179
|
+
option_use: {output_format: :disabled}
|
139
180
|
},
|
140
181
|
help: {
|
141
182
|
description: "This message",
|
142
183
|
parse: false,
|
184
|
+
control: :none,
|
143
185
|
lambda: ->(argv, opts) {self.usage(self.options)}
|
144
186
|
},
|
145
187
|
lengths: {
|
146
188
|
description: "Lengths of each parsed statement",
|
147
189
|
parse: true,
|
148
|
-
|
190
|
+
control: :none,
|
191
|
+
help: "lengths [options] [args...]\nreturns lengths of each parsed statement",
|
149
192
|
lambda: ->(argv, opts) do
|
193
|
+
opts[:output].puts "Lengths"
|
150
194
|
repository.each_statement do |statement|
|
151
|
-
|
195
|
+
opts[:output].puts statement.to_s.size
|
152
196
|
end
|
153
|
-
end
|
197
|
+
end,
|
198
|
+
option_use: {output_format: :disabled}
|
154
199
|
},
|
155
200
|
objects: {
|
156
201
|
description: "Serialize each parsed object to N-Triples",
|
157
202
|
parse: true,
|
158
|
-
|
203
|
+
control: :none,
|
204
|
+
help: "objects [options] [args...]\nreturns unique objects serialized in N-Triples format",
|
159
205
|
lambda: ->(argv, opts) do
|
160
|
-
|
206
|
+
opts[:output].puts "Objects"
|
161
207
|
repository.each_object do |object|
|
162
|
-
|
208
|
+
opts[:output].puts object.to_ntriples
|
163
209
|
end
|
164
|
-
end
|
210
|
+
end,
|
211
|
+
option_use: {output_format: :disabled}
|
165
212
|
},
|
166
213
|
predicates: {
|
167
|
-
description: "Serialize each parsed predicate to N-Triples",
|
168
214
|
parse: true,
|
169
|
-
|
215
|
+
description: "Serialize each parsed predicate to N-Triples",
|
216
|
+
control: :none,
|
217
|
+
help: "predicates [options] [args...]\nreturns unique predicates serialized in N-Triples format",
|
170
218
|
lambda: ->(argv, opts) do
|
171
|
-
|
219
|
+
opts[:output].puts "Predicates"
|
172
220
|
repository.each_predicate do |predicate|
|
173
|
-
|
221
|
+
opts[:output].puts predicate.to_ntriples
|
174
222
|
end
|
175
|
-
end
|
223
|
+
end,
|
224
|
+
option_use: {output_format: :disabled}
|
176
225
|
},
|
177
226
|
serialize: {
|
178
|
-
description: "Serialize
|
227
|
+
description: "Serialize using output-format (or N-Triples)",
|
179
228
|
parse: true,
|
180
|
-
help: "serialize [options] [args...]\nserialize output using specified format (or
|
229
|
+
help: "serialize [options] [args...]\nserialize output using specified format (or N-Triples if not specified)",
|
181
230
|
lambda: ->(argv, opts) do
|
182
231
|
writer_class = RDF::Writer.for(opts[:output_format]) || RDF::NTriples::Writer
|
183
|
-
out = opts[:output]
|
232
|
+
out = opts[:output]
|
184
233
|
opts = opts.merge(prefixes: {})
|
185
234
|
writer_opts = opts.merge(standard_prefixes: true)
|
186
235
|
writer_class.new(out, writer_opts) do |writer|
|
@@ -189,26 +238,102 @@ module RDF
|
|
189
238
|
end
|
190
239
|
},
|
191
240
|
subjects: {
|
192
|
-
description: "Serialize each parsed subject to N-Triples",
|
193
241
|
parse: true,
|
194
|
-
|
242
|
+
control: :none,
|
243
|
+
description: "Serialize each parsed subject to N-Triples",
|
244
|
+
help: "subjects [options] [args...]\nreturns unique subjects serialized in N-Triples format",
|
195
245
|
lambda: ->(argv, opts) do
|
196
|
-
|
246
|
+
opts[:output].puts "Subjects"
|
197
247
|
repository.each_subject do |subject|
|
198
|
-
|
248
|
+
opts[:output].puts subject.to_ntriples
|
199
249
|
end
|
200
|
-
end
|
250
|
+
end,
|
251
|
+
option_use: {output_format: :disabled}
|
201
252
|
},
|
202
253
|
validate: {
|
203
254
|
description: "Validate parsed input",
|
255
|
+
control: :none,
|
204
256
|
parse: true,
|
205
257
|
help: "validate [options] [args...]\nvalidates parsed input (may also be used with --validate)",
|
206
258
|
lambda: ->(argv, opts) do
|
207
|
-
|
208
|
-
end
|
259
|
+
opts[:output].puts "Input is " + (repository.valid? ? "" : "in") + "valid"
|
260
|
+
end,
|
261
|
+
option_use: {output_format: :disabled}
|
209
262
|
}
|
210
263
|
}
|
211
264
|
|
265
|
+
# Options to setup, may be modified by selected command. Options are also read from {RDF::Reader#options} and {RDF::Writer#options}. When a specific input- or ouput-format is selected, options are also discovered from the associated subclass reader or writer.
|
266
|
+
# @return [Array<RDF::CLI::Option>]
|
267
|
+
OPTIONS = ([
|
268
|
+
RDF::CLI::Option.new(
|
269
|
+
symbol: :debug,
|
270
|
+
control: :checkbox,
|
271
|
+
datatype: TrueClass,
|
272
|
+
on: ["-d", "--debug"],
|
273
|
+
description: 'Enable debug output for troubleshooting.'),
|
274
|
+
RDF::CLI::Option.new(
|
275
|
+
symbol: :verbose,
|
276
|
+
control: :checkbox,
|
277
|
+
datatype: TrueClass,
|
278
|
+
on: ['-v', '--verbose'],
|
279
|
+
description: 'Enable verbose output. May be given more than once.'),
|
280
|
+
RDF::CLI::Option.new(
|
281
|
+
symbol: :evaluate,
|
282
|
+
control: :none,
|
283
|
+
datatype: TrueClass,
|
284
|
+
on: ["-e", "--evaluate STRING"],
|
285
|
+
description: "Evaluate argument as RDF input, if no files are specified"),
|
286
|
+
RDF::CLI::Option.new(
|
287
|
+
symbol: :output,
|
288
|
+
control: :none,
|
289
|
+
on: ["-o", "--output FILE"],
|
290
|
+
description: "File to write output, defaults to STDOUT") {|arg| File.open(arg, "w")},
|
291
|
+
RDF::CLI::Option.new(
|
292
|
+
symbol: :format,
|
293
|
+
control: :select,
|
294
|
+
datatype: RDF::Format.select {|ft| ft.reader}.map(&:to_sym).sort,
|
295
|
+
on: ["--input-format FORMAT", "--format FORMAT"],
|
296
|
+
description: "Format of input file, uses heuristic if not specified"
|
297
|
+
) do |arg, options|
|
298
|
+
unless reader = RDF::Reader.for(arg.downcase.to_sym)
|
299
|
+
RDF::CLI.abort "No reader found for #{arg.downcase.to_sym}. Available readers:\n #{RDF::CLI.formats(reader: true).join("\n ")}"
|
300
|
+
end
|
301
|
+
|
302
|
+
# Add format-specific reader options
|
303
|
+
reader.options.each do |cli_opt|
|
304
|
+
next if options.options.has_key?(cli_opt.symbol)
|
305
|
+
on_args = cli_opt.on || []
|
306
|
+
on_args << cli_opt.description if cli_opt.description
|
307
|
+
options.on(*on_args) do |opt_arg|
|
308
|
+
options.options[cli_opt.symbol] = cli_opt.call(opt_arg)
|
309
|
+
end
|
310
|
+
end if reader
|
311
|
+
arg.downcase.to_sym
|
312
|
+
end,
|
313
|
+
RDF::CLI::Option.new(
|
314
|
+
symbol: :output_format,
|
315
|
+
control: :select,
|
316
|
+
datatype: RDF::Format.select {|ft| ft.writer}.map(&:to_sym).sort,
|
317
|
+
on: ["--output-format FORMAT"],
|
318
|
+
description: "Format of output file, defaults to NTriples"
|
319
|
+
) do |arg, options|
|
320
|
+
unless writer = RDF::Writer.for(arg.downcase.to_sym)
|
321
|
+
RDF::CLI.abort "No writer found for #{arg.downcase.to_sym}. Available writers:\n #{self.formats(writer: true).join("\n ")}"
|
322
|
+
end
|
323
|
+
|
324
|
+
# Add format-specific writer options
|
325
|
+
writer.options.each do |cli_opt|
|
326
|
+
next if options.options.has_key?(cli_opt.symbol)
|
327
|
+
on_args = cli_opt.on || []
|
328
|
+
on_args << cli_opt.description if cli_opt.description
|
329
|
+
options.on(*on_args) do |opt_arg|
|
330
|
+
options.options[cli_opt.symbol] = cli_opt.call(opt_arg)
|
331
|
+
end
|
332
|
+
end if writer
|
333
|
+
arg.downcase.to_sym
|
334
|
+
end,
|
335
|
+
] + RDF::Reader.options + RDF::Writer.options).uniq(&:symbol)
|
336
|
+
|
212
337
|
class << self
|
213
338
|
# Repository containing parsed statements
|
214
339
|
# @return [RDF::Repository]
|
@@ -220,117 +345,94 @@ module RDF
|
|
220
345
|
def self.basename() File.basename($0) end
|
221
346
|
|
222
347
|
##
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
|
348
|
+
# Return OptionParser set with appropriate options
|
349
|
+
#
|
350
|
+
# The yield return should provide one or more commands from which additional options will be extracted.
|
351
|
+
# @overload options(argv)
|
352
|
+
# @param [Array<String>] argv
|
353
|
+
# @return [OptionParser]
|
354
|
+
# @overload options(argv, format: :json)
|
355
|
+
# @param [Array<String>] argv
|
356
|
+
# @param [:json] format (:json)
|
357
|
+
# @return [Array<RDF::CLI::Option>]
|
358
|
+
# Returns discovered options
|
359
|
+
def self.options(argv, format: nil)
|
227
360
|
options = OptionParser.new
|
361
|
+
cli_opts = OPTIONS.dup
|
228
362
|
logger = Logger.new($stderr)
|
229
|
-
logger.level = Logger::
|
363
|
+
logger.level = Logger::WARN
|
230
364
|
logger.formatter = lambda {|severity, datetime, progname, msg| "#{severity} #{msg}\n"}
|
231
|
-
opts = options.options = {
|
232
|
-
debug: false,
|
233
|
-
evaluate: nil,
|
234
|
-
format: nil,
|
235
|
-
output: $stdout,
|
236
|
-
output_format: :ntriples,
|
237
|
-
logger: logger
|
238
|
-
}
|
365
|
+
opts = options.options = {logger: logger}
|
239
366
|
|
240
|
-
#
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
367
|
+
# Pre-load commands
|
368
|
+
load_commands
|
369
|
+
|
370
|
+
# Add options for the specified command(s)
|
371
|
+
cmds, args = argv.partition {|e| COMMANDS.include?(e.to_sym)}
|
372
|
+
cmds.each do |cmd|
|
373
|
+
Array(RDF::CLI::COMMANDS[cmd.to_sym][:options]).each do |option|
|
374
|
+
# Replace any existing option with the same symbol
|
375
|
+
cli_opts.delete_if {|cli_opt| cli_opt.symbol == option.symbol}
|
376
|
+
|
377
|
+
# Add the option, unless disabled or removed
|
378
|
+
cli_opts.unshift(option)
|
379
|
+
end
|
380
|
+
|
381
|
+
# Update usage of options for this command
|
382
|
+
RDF::CLI::COMMANDS[cmd.to_sym].fetch(:option_use, {}).each do |sym, use|
|
383
|
+
if opt = cli_opts.find {|cli_opt| cli_opt.symbol == sym}
|
384
|
+
opt.use = use
|
385
|
+
end
|
247
386
|
end
|
248
387
|
end
|
249
|
-
|
388
|
+
|
389
|
+
cli_opts.each do |cli_opt|
|
250
390
|
next if opts.has_key?(cli_opt.symbol)
|
251
391
|
on_args = cli_opt.on || []
|
252
392
|
on_args << cli_opt.description if cli_opt.description
|
253
393
|
options.on(*on_args) do |arg|
|
254
|
-
opts[cli_opt.symbol] = cli_opt.call(arg)
|
394
|
+
opts[cli_opt.symbol] = cli_opt.call(arg, options)
|
255
395
|
end
|
256
396
|
end
|
257
397
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
end
|
264
|
-
end
|
265
|
-
options.banner = "Usage: #{self.basename} command+ [options] [args...]"
|
266
|
-
|
267
|
-
options.on('-d', '--debug', 'Enable debug output for troubleshooting.') do
|
268
|
-
opts[:logger].level = Logger::DEBUG
|
269
|
-
end
|
270
|
-
|
271
|
-
options.on("-e", "--evaluate STRING", "Evaluate argument as RDF input, if no files are specified") do |arg|
|
272
|
-
opts[:evaluate] = arg
|
273
|
-
end
|
398
|
+
if format == :json
|
399
|
+
# Return options
|
400
|
+
cli_opts.map(&:to_hash)
|
401
|
+
else
|
402
|
+
options.banner = "Usage: #{self.basename} command+ [options] [args...]"
|
274
403
|
|
275
|
-
|
276
|
-
|
277
|
-
self.abort "No reader found for #{arg.downcase.to_sym}. Available readers:\n #{self.formats(reader: true).join("\n ")}"
|
404
|
+
options.on_tail('-V', '--version', 'Display the RDF.rb version and exit.') do
|
405
|
+
puts RDF::VERSION; exit(0)
|
278
406
|
end
|
279
407
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
on_args = cli_opt.on || []
|
284
|
-
on_args << cli_opt.description if cli_opt.description
|
285
|
-
options.on(*on_args) do |arg|
|
286
|
-
opts[cli_opt.symbol] = cli_opt.call(arg)
|
287
|
-
end
|
408
|
+
show_help = false
|
409
|
+
options.on_tail("-h", "--help", "Show this message") do
|
410
|
+
show_help = true
|
288
411
|
end
|
289
|
-
opts[:format] = arg.downcase.to_sym
|
290
|
-
end
|
291
|
-
|
292
|
-
options.on("-o", "--output FILE", "File to write output, defaults to STDOUT") do |arg|
|
293
|
-
opts[:output] = File.open(arg, "w")
|
294
|
-
end
|
295
412
|
|
296
|
-
|
297
|
-
|
298
|
-
|
413
|
+
begin
|
414
|
+
args = options.parse!(args)
|
415
|
+
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument, ArgumentError => e
|
416
|
+
abort e
|
299
417
|
end
|
300
418
|
|
301
|
-
#
|
302
|
-
|
303
|
-
|
304
|
-
on_args = cli_opt.on || []
|
305
|
-
on_args << cli_opt.description if cli_opt.description
|
306
|
-
options.on(*on_args) do |arg|
|
307
|
-
opts[cli_opt.symbol] = cli_opt.call(arg)
|
308
|
-
end
|
419
|
+
# Make sure options are processed first
|
420
|
+
if show_help
|
421
|
+
self.usage(options); exit(0)
|
309
422
|
end
|
310
|
-
opts[:output_format] = arg.downcase.to_sym
|
311
|
-
end
|
312
423
|
|
313
|
-
|
314
|
-
|
315
|
-
exit(0)
|
424
|
+
options.args = cmds + args
|
425
|
+
options
|
316
426
|
end
|
317
|
-
|
318
|
-
begin
|
319
|
-
options.parse!
|
320
|
-
rescue OptionParser::InvalidOption => e
|
321
|
-
abort e
|
322
|
-
end
|
323
|
-
|
324
|
-
options
|
325
427
|
end
|
326
428
|
|
327
429
|
##
|
328
430
|
# Output usage message
|
329
|
-
def self.usage(options, banner: nil)
|
431
|
+
def self.usage(options, cmd_opts = {}, banner: nil)
|
330
432
|
options.banner = banner if banner
|
331
433
|
$stdout.puts options
|
332
434
|
$stdout.puts "Note: available commands and options may be different depending on selected --input-format and/or --output-format."
|
333
|
-
$stdout.puts "Available commands:\n\t#{self.commands.join("\n\t")}"
|
435
|
+
$stdout.puts "Available commands:\n\t#{self.commands(**options.options).join("\n\t")}"
|
334
436
|
$stdout.puts "Available formats:\n\t#{(self.formats).join("\n\t")}"
|
335
437
|
end
|
336
438
|
|
@@ -339,27 +441,52 @@ module RDF
|
|
339
441
|
#
|
340
442
|
# @param [Array<String>] args
|
341
443
|
# @param [IO] output
|
444
|
+
# @param [OptionParser] option_parser
|
445
|
+
# @param [Hash{Symbol => Hash{Symbol => Array[String]}}] messages used for confeying non primary-output which is structured.
|
342
446
|
# @param [Hash{Symbol => Object}] options
|
343
447
|
# @return [Boolean]
|
344
|
-
def self.exec(args, output: $stdout, option_parser:
|
448
|
+
def self.exec(args, output: $stdout, option_parser: nil, messages: {}, **options)
|
449
|
+
option_parser ||= self.options(args)
|
450
|
+
options[:logger] ||= option_parser.options[:logger]
|
345
451
|
output.set_encoding(Encoding::UTF_8) if output.respond_to?(:set_encoding) && RUBY_PLATFORM == "java"
|
346
|
-
|
452
|
+
|
453
|
+
# Separate commands from file options; arguments already extracted
|
454
|
+
cmds, args = args.partition {|e| COMMANDS.include?(e.to_sym)}
|
347
455
|
|
348
456
|
if cmds.empty?
|
349
457
|
usage(option_parser)
|
350
|
-
|
458
|
+
raise ArgumentError, "No command given"
|
351
459
|
end
|
352
460
|
|
353
461
|
if cmds.first == 'help'
|
354
462
|
on_cmd = cmds[1]
|
355
|
-
|
356
|
-
|
463
|
+
cmd_opts = COMMANDS.fetch(on_cmd.to_s.to_sym, {})
|
464
|
+
if on_cmd && cmd_opts[:help]
|
465
|
+
usage(option_parser, cmd_opts: cmd_opts, banner: "Usage: #{self.basename.split('/').last} #{COMMANDS[on_cmd.to_sym][:help]}")
|
466
|
+
elsif on_cmd
|
467
|
+
usage(option_parser, cmd_opts: cmd_opts)
|
357
468
|
else
|
358
469
|
usage(option_parser)
|
359
470
|
end
|
360
471
|
return
|
361
472
|
end
|
362
473
|
|
474
|
+
# Make sure any selected command isn't filtered out
|
475
|
+
cmds.each do |c|
|
476
|
+
COMMANDS[c.to_sym].fetch(:filter, {}).each do |opt, val|
|
477
|
+
if options[opt].to_s != val.to_s
|
478
|
+
usage(option_parser, banner: "Command #{c.inspect} requires #{opt}: #{val}, not #{options.fetch(opt, 'null')}")
|
479
|
+
raise ArgumentError, "Incompatible command #{c} used with option #{opt}=#{options[opt]}"
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
# Hacks for specific options
|
485
|
+
options[:logger].level = Logger::INFO if options[:verbose]
|
486
|
+
options[:logger].level = Logger::DEBUG if options[:debug]
|
487
|
+
options[:format] = options[:format].to_sym if options[:format]
|
488
|
+
options[:output_format] = options[:output_format].to_sym if options[:output_format]
|
489
|
+
|
363
490
|
@repository = RDF::Repository.new
|
364
491
|
|
365
492
|
# Parse input files if any command requires it
|
@@ -370,21 +497,61 @@ module RDF
|
|
370
497
|
@repository << reader
|
371
498
|
end
|
372
499
|
secs = Time.new - start
|
373
|
-
|
500
|
+
options[:logger].info "Parsed #{repository.count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
|
374
501
|
end
|
375
502
|
|
376
503
|
# Run each command in sequence
|
377
504
|
cmds.each do |command|
|
378
|
-
COMMANDS[command.to_sym][:lambda].call(args, output: output, **options)
|
505
|
+
COMMANDS[command.to_sym][:lambda].call(args, output: output, **options.merge(messages: messages))
|
506
|
+
end
|
507
|
+
|
508
|
+
if options[:statistics]
|
509
|
+
options[:statistics][:reader] = @readers.first unless (@readers || []).empty?
|
510
|
+
options[:statistics][:count] = @repository.count
|
379
511
|
end
|
380
|
-
rescue ArgumentError => e
|
381
|
-
abort e.message
|
382
512
|
end
|
383
513
|
|
384
514
|
##
|
385
|
-
# @
|
386
|
-
|
515
|
+
# @overload commands(**options)
|
516
|
+
# @param [Hash{Symbol => Object}] options already set
|
517
|
+
# @return [Array<String>] list of executable commands
|
518
|
+
# @overload commands(format: :json, **options)
|
519
|
+
# @param [:json] format (:json)
|
520
|
+
# @param [Hash{Symbol => Object}] options already set
|
521
|
+
# @return [Array{Object}]
|
522
|
+
# Returns an array of commands including the command symbol
|
523
|
+
def self.commands(format: nil, **options)
|
387
524
|
# First, load commands from other formats
|
525
|
+
load_commands
|
526
|
+
|
527
|
+
case format
|
528
|
+
when :json
|
529
|
+
COMMANDS.map do |k, v|
|
530
|
+
v = v.merge(symbol: k, options: v.fetch(:options, []).map(&:to_hash))
|
531
|
+
v.delete(:lambda)
|
532
|
+
v.delete(:help)
|
533
|
+
v.delete(:options) if v[:options].empty?
|
534
|
+
v[:control] == :none ? nil : v
|
535
|
+
end.compact
|
536
|
+
else
|
537
|
+
# Subset commands based on filter options
|
538
|
+
cmds = COMMANDS.reject do |k, c|
|
539
|
+
c.fetch(:filter, {}).any? do |opt, val|
|
540
|
+
options[opt].to_s != val.to_s
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
sym_len = cmds.keys.map {|k| k.to_s.length}.max
|
545
|
+
cmds.keys.sort.map do |k|
|
546
|
+
"%*s: %s" % [sym_len, k, cmds[k][:description]]
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
##
|
552
|
+
# Load commands from formats
|
553
|
+
# @return [Hash{Symbol => Hash{Symbol => Object}}]
|
554
|
+
def self.load_commands
|
388
555
|
unless @commands_loaded
|
389
556
|
RDF::Format.each do |format|
|
390
557
|
format.cli_commands.each do |command, options|
|
@@ -394,7 +561,7 @@ module RDF
|
|
394
561
|
end
|
395
562
|
@commands_loaded = true
|
396
563
|
end
|
397
|
-
COMMANDS
|
564
|
+
COMMANDS
|
398
565
|
end
|
399
566
|
|
400
567
|
##
|
@@ -418,10 +585,10 @@ module RDF
|
|
418
585
|
##
|
419
586
|
# @return [Array<String>] list of available formats
|
420
587
|
def self.formats(reader: false, writer: false)
|
421
|
-
f = RDF::Format.sort_by(&:to_sym).
|
422
|
-
select {|
|
423
|
-
inject({}) do |memo,
|
424
|
-
memo.merge(
|
588
|
+
f = RDF::Format.sort_by(&:to_sym).
|
589
|
+
select {|ft| (reader ? ft.reader : (writer ? ft.writer : (ft.reader || ft.writer)))}.
|
590
|
+
inject({}) do |memo, r|
|
591
|
+
memo.merge(r.to_sym => r.name)
|
425
592
|
end
|
426
593
|
sym_len = f.keys.map {|k| k.to_s.length}.max
|
427
594
|
f.map {|s, t| "%*s: %s" % [sym_len, s, t]}
|
@@ -439,12 +606,17 @@ module RDF
|
|
439
606
|
# @yield [reader]
|
440
607
|
# @yieldparam [RDF::Reader]
|
441
608
|
# @return [nil]
|
442
|
-
def self.parse(files, evaluate: nil, format:
|
609
|
+
def self.parse(files, evaluate: nil, format: nil, encoding: Encoding::UTF_8, **options, &block)
|
443
610
|
if files.empty?
|
444
611
|
# If files are empty, either use options[:execute]
|
445
612
|
input = evaluate ? StringIO.new(evaluate) : $stdin
|
446
|
-
input.set_encoding(encoding)
|
447
|
-
|
613
|
+
input.set_encoding(encoding )
|
614
|
+
if !format
|
615
|
+
sample = input.read
|
616
|
+
input.rewind
|
617
|
+
end
|
618
|
+
r = RDF::Reader.for(format|| {sample: sample})
|
619
|
+
raise ArgumentError, "Unknown format for evaluated input" unless r
|
448
620
|
(@readers ||= []) << r
|
449
621
|
r.new(input, options) do |reader|
|
450
622
|
yield(reader)
|
data/lib/rdf/reader.rb
CHANGED
@@ -118,22 +118,27 @@ module RDF
|
|
118
118
|
symbol: :canonicalize,
|
119
119
|
datatype: TrueClass,
|
120
120
|
on: ["--canonicalize"],
|
121
|
+
control: :checkbox,
|
122
|
+
default: false,
|
121
123
|
description: "Canonicalize input/output.") {true},
|
122
124
|
RDF::CLI::Option.new(
|
123
125
|
symbol: :encoding,
|
124
126
|
datatype: Encoding,
|
127
|
+
control: :text,
|
125
128
|
on: ["--encoding ENCODING"],
|
126
129
|
description: "The encoding of the input stream.") {|arg| Encoding.find arg},
|
127
130
|
RDF::CLI::Option.new(
|
128
131
|
symbol: :intern,
|
129
132
|
datatype: TrueClass,
|
133
|
+
control: :none,
|
130
134
|
on: ["--intern"],
|
131
|
-
description: "Intern all parsed URIs.")
|
135
|
+
description: "Intern all parsed URIs."),
|
132
136
|
RDF::CLI::Option.new(
|
133
137
|
symbol: :prefixes,
|
134
138
|
datatype: Hash,
|
139
|
+
control: :none,
|
135
140
|
multiple: true,
|
136
|
-
on: ["--prefixes PREFIX,PREFIX"],
|
141
|
+
on: ["--prefixes PREFIX:URI,PREFIX:URI"],
|
137
142
|
description: "A comma-separated list of prefix:uri pairs.") do |arg|
|
138
143
|
arg.split(',').inject({}) do |memo, pfxuri|
|
139
144
|
pfx,uri = pfxuri.split(':', 2)
|
@@ -142,14 +147,23 @@ module RDF
|
|
142
147
|
end,
|
143
148
|
RDF::CLI::Option.new(
|
144
149
|
symbol: :base_uri,
|
150
|
+
control: :url,
|
145
151
|
datatype: RDF::URI,
|
146
152
|
on: ["--uri URI"],
|
147
153
|
description: "Base URI of input file, defaults to the filename.") {|arg| RDF::URI(arg)},
|
148
154
|
RDF::CLI::Option.new(
|
149
155
|
symbol: :validate,
|
150
156
|
datatype: TrueClass,
|
157
|
+
control: :checkbox,
|
151
158
|
on: ["--validate"],
|
152
|
-
description: "Validate input file.")
|
159
|
+
description: "Validate input file."),
|
160
|
+
RDF::CLI::Option.new(
|
161
|
+
symbol: :verifySSL,
|
162
|
+
datatype: TrueClass,
|
163
|
+
default: true,
|
164
|
+
control: :checkbox,
|
165
|
+
on: ["--[no-]verifySSL"],
|
166
|
+
description: "Verify SSL results on HTTP GET")
|
153
167
|
]
|
154
168
|
end
|
155
169
|
|
data/lib/rdf/vocab/writer.rb
CHANGED
@@ -22,22 +22,27 @@ module RDF
|
|
22
22
|
RDF::CLI::Option.new(
|
23
23
|
symbol: :class_name,
|
24
24
|
datatype: String,
|
25
|
+
control: :text,
|
25
26
|
on: ["--class-name NAME"],
|
27
|
+
use: :required,
|
26
28
|
description: "Name of created Ruby class (vocabulary format)."),
|
27
29
|
RDF::CLI::Option.new(
|
28
30
|
symbol: :module_name,
|
29
31
|
datatype: String,
|
32
|
+
control: :text,
|
30
33
|
on: ["--module-name NAME"],
|
31
34
|
description: "Name of Ruby module containing class-name (vocabulary format)."),
|
32
35
|
RDF::CLI::Option.new(
|
33
36
|
symbol: :strict,
|
34
37
|
datatype: TrueClass,
|
38
|
+
control: :checkbox,
|
35
39
|
on: ["--strict"],
|
36
40
|
description: "Make strict vocabulary"
|
37
41
|
) {true},
|
38
42
|
RDF::CLI::Option.new(
|
39
43
|
symbol: :extra,
|
40
44
|
datatype: String,
|
45
|
+
control: :none,
|
41
46
|
on: ["--extra URIEncodedJSON"],
|
42
47
|
description: "URI Encoded JSON representation of extra data"
|
43
48
|
) do |arg|
|
data/lib/rdf/writer.rb
CHANGED
@@ -119,17 +119,20 @@ module RDF
|
|
119
119
|
RDF::CLI::Option.new(
|
120
120
|
symbol: :canonicalize,
|
121
121
|
datatype: TrueClass,
|
122
|
+
control: :checkbox,
|
122
123
|
on: ["--canonicalize"],
|
123
124
|
description: "Canonicalize input/output.") {true},
|
124
125
|
RDF::CLI::Option.new(
|
125
126
|
symbol: :encoding,
|
126
127
|
datatype: Encoding,
|
128
|
+
control: :text,
|
127
129
|
on: ["--encoding ENCODING"],
|
128
130
|
description: "The encoding of the input stream.") {|arg| Encoding.find arg},
|
129
131
|
RDF::CLI::Option.new(
|
130
132
|
symbol: :prefixes,
|
131
133
|
datatype: Hash,
|
132
134
|
multiple: true,
|
135
|
+
control: :none,
|
133
136
|
on: ["--prefixes PREFIX,PREFIX"],
|
134
137
|
description: "A comma-separated list of prefix:uri pairs.") do |arg|
|
135
138
|
arg.split(',').inject({}) do |memo, pfxuri|
|
@@ -140,6 +143,7 @@ module RDF
|
|
140
143
|
RDF::CLI::Option.new(
|
141
144
|
symbol: :unique_bnodes,
|
142
145
|
datatype: TrueClass,
|
146
|
+
control: :checkbox,
|
143
147
|
on: ["--unique-bnodes"],
|
144
148
|
description: "Use unique Node identifiers.") {true},
|
145
149
|
]
|
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.2.
|
4
|
+
version: 2.2.8
|
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: 2017-
|
13
|
+
date: 2017-08-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: link_header
|