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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bb99255dac04c747b9392d7f487ec45e31ef79bc
4
- data.tar.gz: 213f13c8b38eaf7a8324c6002c7bf412f22933e3
3
+ metadata.gz: 9a77462d7aa84383474aec0d885fcff326293c41
4
+ data.tar.gz: fc3ff787ef744bb281ce34a3a9d415232fd4081a
5
5
  SHA512:
6
- metadata.gz: 00d4369281d4945fa1252c79c284928655d5f57c5b96c094be795025cd3db52b8a2f693a52d692cb0e1646921d5cc8137c47039acd1dfb63ec18918a55162176
7
- data.tar.gz: 94df47f23a50f7c45f40456d444e2a133df3d71b778663184998824e7496f08b78f5419d2a233697a338b43e4efe31466bb887aef3de0a8be6616e2b7f7363dc
6
+ metadata.gz: 0e257e359a05481b608dad6d3fd783e723485aaa7dd7cee828fdc3e9202b2625541fb3bc324dc4fe6703ec487c62667bbbef1f2febcddb357bee3b3abf74eea2
7
+ data.tar.gz: fe307ff319e3c78a1aec5d014b6a57dce05b4cdac64c88dad09fdab9d7d6f238f31ff64e5cf33a1ac8110530de3477755e5e5b860683517adf11cd0a819ed626
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.2.7
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 do
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
- self.on('-V', '--version', 'Display the RDF.rb version and exit.') do
12
- puts RDF::VERSION; exit
13
- end
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
- 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)
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
- # $stdout.puts "Parsed #{count} statements"
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
- # Argument datatype, which may be enumerated string values
91
- # @return [Class, Array<String>]
95
+ # Potential values (for select or radio) or Ruby datatype
96
+ # @return [Class, Array<String>]
92
97
  attr_reader :datatype
93
98
 
94
- # Allows multiple comma-spearated values.
95
- # @return [Boolean]
96
- attr_reader :multiple
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 [Class, Array<String>] datatype of value
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, description: nil, datatype: String, multiple: false, &block)
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, @description, @datatype, @multiple, @callback = symbol.to_sym, Array(on), description, datatype, multiple, block
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
- @callback ? @callback.call(arg) : arg
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
- # @private
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
- $stdout.puts "Parsed #{count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
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
- help: "lengths [options] [args...]\nreturns statement lengths",
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
- $stdout.puts statement.to_s.size
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
- help: "objects [options] [args...]\nreturns unique objects",
203
+ control: :none,
204
+ help: "objects [options] [args...]\nreturns unique objects serialized in N-Triples format",
159
205
  lambda: ->(argv, opts) do
160
- $stdout.puts "Objects"
206
+ opts[:output].puts "Objects"
161
207
  repository.each_object do |object|
162
- $stdout.puts object.to_ntriples
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
- help: "predicates [options] [args...]\nreturns unique predicates",
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
- $stdout.puts "Predicates"
219
+ opts[:output].puts "Predicates"
172
220
  repository.each_predicate do |predicate|
173
- $stdout.puts predicate.to_ntriples
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 each parsed statement to N-Triples, or the specified output format",
227
+ description: "Serialize using output-format (or N-Triples)",
179
228
  parse: true,
180
- help: "serialize [options] [args...]\nserialize output using specified format (or n-triples if not specified)",
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] || $stdout
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
- help: "subjects [options] [args...]\nreturns unique subjects",
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
- $stdout.puts "Subjects"
246
+ opts[:output].puts "Subjects"
197
247
  repository.each_subject do |subject|
198
- $stdout.puts subject.to_ntriples
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
- $stdout.puts "Input is " + (repository.valid? ? "" : "in") + "valid"
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
- # @yield [options]
224
- # @yieldparam [OptionParser]
225
- # @return [OptionParser]
226
- def self.options(&block)
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::ERROR
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
- # Add default Reader and Writer options
241
- RDF::Reader.options.each do |cli_opt|
242
- next if opts.has_key?(cli_opt.symbol)
243
- on_args = cli_opt.on || []
244
- on_args << cli_opt.description if cli_opt.description
245
- options.on(*on_args) do |arg|
246
- opts[cli_opt.symbol] = cli_opt.call(arg)
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
- RDF::Writer.options.each do |cli_opt|
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
- # Command-specific options
259
- if block_given?
260
- case block.arity
261
- when 1 then block.call(options)
262
- else options.instance_eval(&block)
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
- options.on("--input-format FORMAT", "--format FORMAT", "Format of input file, uses heuristic if not specified") do |arg|
276
- unless reader = RDF::Reader.for(arg.downcase.to_sym)
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
- # Add format-specific reader options
281
- reader.options.each do |cli_opt|
282
- next if opts.has_key?(cli_opt.symbol)
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
- options.on("--output-format FORMAT", "Format of output file, defaults to NTriples") do |arg|
297
- unless writer = RDF::Writer.for(arg.downcase.to_sym)
298
- self.abort "No writer found for #{arg.downcase.to_sym}. Available writers:\n #{self.formats(writer: true).join("\n ")}"
413
+ begin
414
+ args = options.parse!(args)
415
+ rescue OptionParser::InvalidOption, OptionParser::InvalidArgument, ArgumentError => e
416
+ abort e
299
417
  end
300
418
 
301
- # Add format-specific writer options
302
- writer.options.each do |cli_opt|
303
- next if opts.has_key?(cli_opt.symbol)
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
- options.on_tail("-h", "--help", "Show this message") do
314
- self.usage(options)
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: self.options, **options)
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
- cmds, args = args.partition {|e| commands.include?(e.to_s)}
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
- abort "No command given"
458
+ raise ArgumentError, "No command given"
351
459
  end
352
460
 
353
461
  if cmds.first == 'help'
354
462
  on_cmd = cmds[1]
355
- if on_cmd && COMMANDS.fetch(on_cmd.to_sym, {})[:help]
356
- usage(option_parser, banner: "Usage: #{self.basename.split('/').last} #{COMMANDS[on_cmd.to_sym][:help]}")
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
- $stdout.puts "Parsed #{repository.count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
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
- # @return [Array<String>] list of executable commands
386
- def self.commands
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.keys.map(&:to_s).sort
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).each.
422
- select {|f| (reader ? f.reader : (writer ? f.writer : (f.reader || f.writer)))}.
423
- inject({}) do |memo, reader|
424
- memo.merge(reader.to_sym => reader.name)
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: :ntriples, encoding: Encoding::UTF_8, **options, &block)
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
- r = RDF::Reader.for(format)
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.") {true},
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.") {true},
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
 
@@ -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.7
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-07-28 00:00:00.000000000 Z
13
+ date: 2017-08-17 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: link_header