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 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