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