Skip to content

Commit cb4e1f9

Browse files
committed
* In CLI runner, if messages are set, display them. This is in preference to text formatting in the command.
* Change `CLI::Option#required` to `CLI::Option#use` with states `:disabled`, `:removed`, `:required`, and `:optional`. * Allow commands to override option use using `option_use`.
1 parent 247cba7 commit cb4e1f9

4 files changed

Lines changed: 83 additions & 55 deletions

File tree

bin/rdf

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,18 @@ abort options.banner if ARGV.empty? && !options.options[:evaluate]
99

1010
# Add option_parser to parsed options to enable help
1111
begin
12-
RDF::CLI.exec(options.args, option_parser: options, **options.options)
12+
messages = {}
13+
RDF::CLI.exec(options.args, option_parser: options, **options.options.merge(messages: messages))
14+
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}"}
21+
end
22+
end
23+
end
1324
rescue ArgumentError => e
1425
RDF::CLI.abort e.message
1526
end

lib/rdf/cli.rb

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -100,25 +100,28 @@ class Option
100100
# @return [:text, :textarea, :radio, :checkbox, :select, :url, :url2, :none]
101101
attr_reader :control
102102

103-
# Is this a required option?
104-
# @return [Boolean]
105-
attr_reader :required
103+
# Use of this option
104+
# @return [:optional, :disabled, :removed, :required]
105+
attr_accessor :use
106106

107107
##
108108
# Create a new option with optional callback.
109109
#
110110
# @param [Symbol] symbol
111111
# @param [Array<String>] on
112+
# @param [String] datatype
113+
# @param [String] control
112114
# @param [String] description
115+
# @param [[:optional, :disabled, :removed, :required]] use
113116
# @yield value which may be used within `OptionParser#on`
114117
# @yieldparam [Object] value The option value as parsed using `on` argument
115118
# @yieldparam [OptionParser] options (nil) optional OptionParser
116119
# @yieldreturn [Object] a possibly modified input value
117120
def initialize(symbol: nil, on: nil, datatype: nil, control: nil,
118-
description: nil, required: false, **options, &block)
121+
description: nil, use: :optional, **options, &block)
119122
raise ArgumentError, "symbol is a required argument" unless symbol
120123
raise ArgumentError, "on is a required argument" unless on
121-
@symbol, @on, @datatype, @control, @description, @required, @callback = symbol.to_sym, Array(on), datatype, control, description, required, block
124+
@symbol, @on, @datatype, @control, @description, @use, @callback = symbol.to_sym, Array(on), datatype, control, description, use, block
122125
end
123126

124127
def call(arg, options)
@@ -139,7 +142,7 @@ def to_hash
139142
datatype: (datatype.is_a?(Class) ? datatype.name : datatype),
140143
control: control,
141144
description: description,
142-
required: required
145+
use: use
143146
}
144147
end
145148
end
@@ -151,12 +154,14 @@ def to_hash
151154
# * `lambda` code run to execute command.
152155
# * `filter` Option values that must match for command to be used
153156
# * `control` Used to indicate how (if) command is displayed
154-
# * `options` an optional array of `RDF::CLI::Option` describing command-specific options, which may also be used to remove general options.
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.
155159
# @return [Hash{Symbol => Hash{Symbol => Object}}]
156160
COMMANDS = {
157161
count: {
158162
description: "Count statements in parsed input",
159163
parse: false,
164+
control: :none,
160165
help: "count [options] [args...]\nreturns number of parsed statements",
161166
lambda: ->(argv, opts) do
162167
unless repository.count > 0
@@ -171,9 +176,7 @@ def to_hash
171176
opts[:output].puts "Parsed #{count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
172177
end
173178
end,
174-
options: [
175-
RDF::CLI::Option.new(symbol: :output_format, on: [])
176-
]
179+
option_use: {output_format: :disabled}
177180
},
178181
help: {
179182
description: "This message",
@@ -184,44 +187,41 @@ def to_hash
184187
lengths: {
185188
description: "Lengths of each parsed statement",
186189
parse: true,
190+
control: :none,
187191
help: "lengths [options] [args...]\nreturns lengths of each parsed statement",
188192
lambda: ->(argv, opts) do
189193
opts[:output].puts "Lengths"
190194
repository.each_statement do |statement|
191195
opts[:output].puts statement.to_s.size
192196
end
193197
end,
194-
options: [
195-
RDF::CLI::Option.new(symbol: :output_format, on: [])
196-
]
198+
option_use: {output_format: :disabled}
197199
},
198200
objects: {
199201
description: "Serialize each parsed object to N-Triples",
200202
parse: true,
203+
control: :none,
201204
help: "objects [options] [args...]\nreturns unique objects serialized in N-Triples format",
202205
lambda: ->(argv, opts) do
203206
opts[:output].puts "Objects"
204207
repository.each_object do |object|
205208
opts[:output].puts object.to_ntriples
206209
end
207210
end,
208-
options: [
209-
RDF::CLI::Option.new(symbol: :output_format, on: [])
210-
]
211+
option_use: {output_format: :disabled}
211212
},
212213
predicates: {
213214
parse: true,
214215
description: "Serialize each parsed predicate to N-Triples",
216+
control: :none,
215217
help: "predicates [options] [args...]\nreturns unique predicates serialized in N-Triples format",
216218
lambda: ->(argv, opts) do
217219
opts[:output].puts "Predicates"
218220
repository.each_predicate do |predicate|
219221
opts[:output].puts predicate.to_ntriples
220222
end
221223
end,
222-
options: [
223-
RDF::CLI::Option.new(symbol: :output_format, on: [])
224-
]
224+
option_use: {output_format: :disabled}
225225
},
226226
serialize: {
227227
description: "Serialize using output-format (or N-Triples)",
@@ -239,6 +239,7 @@ def to_hash
239239
},
240240
subjects: {
241241
parse: true,
242+
control: :none,
242243
description: "Serialize each parsed subject to N-Triples",
243244
help: "subjects [options] [args...]\nreturns unique subjects serialized in N-Triples format",
244245
lambda: ->(argv, opts) do
@@ -247,20 +248,17 @@ def to_hash
247248
opts[:output].puts subject.to_ntriples
248249
end
249250
end,
250-
options: [
251-
RDF::CLI::Option.new(symbol: :output_format, on: [])
252-
]
251+
option_use: {output_format: :disabled}
253252
},
254253
validate: {
255254
description: "Validate parsed input",
255+
control: :none,
256256
parse: true,
257257
help: "validate [options] [args...]\nvalidates parsed input (may also be used with --validate)",
258258
lambda: ->(argv, opts) do
259259
opts[:output].puts "Input is " + (repository.valid? ? "" : "in") + "valid"
260260
end,
261-
options: [
262-
RDF::CLI::Option.new(symbol: :output_format, on: [])
263-
]
261+
option_use: {output_format: :disabled}
264262
}
265263
}
266264

@@ -376,8 +374,15 @@ def self.options(argv, format: nil)
376374
# Replace any existing option with the same symbol
377375
cli_opts.delete_if {|cli_opt| cli_opt.symbol == option.symbol}
378376

379-
# Add the option, unless `on` is empty
380-
cli_opts.unshift(option) unless option.on.empty?
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
381386
end
382387
end
383388

@@ -392,7 +397,7 @@ def self.options(argv, format: nil)
392397

393398
if format == :json
394399
# Return options
395-
OPTIONS.map(&:to_hash)
400+
cli_opts.map(&:to_hash)
396401
else
397402
options.banner = "Usage: #{self.basename} command+ [options] [args...]"
398403

@@ -437,9 +442,10 @@ def self.usage(options, cmd_opts = {}, banner: nil)
437442
# @param [Array<String>] args
438443
# @param [IO] output
439444
# @param [OptionParser] option_parser
445+
# @param [Hash{Symbol => Hash{Symbol => Array[String]}}] messages used for confeying non primary-output which is structured.
440446
# @param [Hash{Symbol => Object}] options
441447
# @return [Boolean]
442-
def self.exec(args, output: $stdout, option_parser: nil, **options)
448+
def self.exec(args, output: $stdout, option_parser: nil, messages: {}, **options)
443449
option_parser ||= self.options(args)
444450
options[:logger] ||= option_parser.options[:logger]
445451
output.set_encoding(Encoding::UTF_8) if output.respond_to?(:set_encoding) && RUBY_PLATFORM == "java"
@@ -470,7 +476,7 @@ def self.exec(args, output: $stdout, option_parser: nil, **options)
470476
COMMANDS[c.to_sym].fetch(:filter, {}).each do |opt, val|
471477
if options[opt].to_s != val.to_s
472478
usage(option_parser, banner: "Command #{c.inspect} requires #{opt}: #{val}, not #{options.fetch(opt, 'null')}")
473-
return
479+
raise ArgumentError, "Incompatible command #{c} used with option #{opt}=#{options[opt]}"
474480
end
475481
end
476482
end
@@ -496,7 +502,7 @@ def self.exec(args, output: $stdout, option_parser: nil, **options)
496502

497503
# Run each command in sequence
498504
cmds.each do |command|
499-
COMMANDS[command.to_sym][:lambda].call(args, output: output, **options)
505+
COMMANDS[command.to_sym][:lambda].call(args, output: output, **options.merge(messages: messages))
500506
end
501507

502508
if options[:statistics]

lib/rdf/vocab/writer.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ def self.options
2323
symbol: :class_name,
2424
datatype: String,
2525
control: :text,
26-
on: ["--class-name NAME", :REQUIRED],
27-
required: true,
26+
on: ["--class-name NAME"],
27+
use: :required,
2828
description: "Name of created Ruby class (vocabulary format)."),
2929
RDF::CLI::Option.new(
3030
symbol: :module_name,

spec/cli_spec.rb

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
}
1313

1414
#before(:each) {expect(RDF::CLI).not_to receive(:abort) }
15+
around(:each) do |example|
16+
orig_commands = RDF::CLI::COMMANDS.dup
17+
example.run
18+
RDF::CLI::COMMANDS.clear
19+
RDF::CLI::COMMANDS.merge!(orig_commands)
20+
end
1521

1622
describe "options" do
1723
it "sets debug logging with --debug" do
@@ -77,19 +83,29 @@
7783
end
7884
end
7985

86+
describe "with commands" do
87+
it "adds options from specified commands" do
88+
foo_opt = RDF::CLI::Option.new(symbol: :foo, on: [])
89+
RDF::CLI.add_command(:foo, {options: foo_opt})
90+
expect(RDF::CLI.options([:foo], format: :json)).to include(foo_opt.to_hash)
91+
end
92+
93+
it "replaces equivalent options from command" do
94+
verbose_opt = RDF::CLI::Option.new(
95+
symbol: :verbose,
96+
on: ['-v', '--verbose'],
97+
description: 'Replaced option.')
98+
RDF::CLI.add_command(:foo, {options: verbose_opt})
99+
expect(RDF::CLI.options([:foo], format: :json)).to include(verbose_opt.to_hash)
100+
end
101+
end
102+
80103
it "returns an array of Option format: :json" do
81104
expect(RDF::CLI.options([], format: :json)).to all(be_a(Hash))
82105
end
83106
end
84107

85108
describe ".commands" do
86-
around(:each) do |example|
87-
orig_commands = RDF::CLI::COMMANDS.dup
88-
example.run
89-
RDF::CLI::COMMANDS.clear
90-
RDF::CLI::COMMANDS.merge!(orig_commands)
91-
end
92-
93109
it "returns an array of strings" do
94110
expect(RDF::CLI.commands).to all(be_a(String))
95111
end
@@ -100,13 +116,6 @@
100116
end
101117

102118
describe ".add_command" do
103-
around(:each) do |example|
104-
orig_commands = RDF::CLI::COMMANDS.dup
105-
example.run
106-
RDF::CLI::COMMANDS.clear
107-
RDF::CLI::COMMANDS.merge!(orig_commands)
108-
end
109-
110119
it "adds a command" do
111120
RDF::CLI.add_command(:foo) do |argv, opts|
112121
$stdout.puts "Hello, World!"
@@ -116,13 +125,6 @@
116125
end
117126

118127
context "commands" do
119-
around(:each) do |example|
120-
orig_commands = RDF::CLI::COMMANDS.dup
121-
example.run
122-
RDF::CLI::COMMANDS.clear
123-
RDF::CLI::COMMANDS.merge!(orig_commands)
124-
end
125-
126128
describe "serialize" do
127129
after(:each) do
128130
$stdin = STDIN
@@ -205,6 +207,15 @@
205207
end
206208
end
207209

210+
it "complains if filtered command is attempted" do
211+
RDF::CLI.add_command(:foo, {filter: {output_format: :nquads}})
212+
expect do
213+
expect do
214+
RDF::CLI.exec(["foo"], {output_format: :ntriples})
215+
end.to raise_error(ArgumentError)
216+
end.to write(%(Command "foo" requires output_format: nquads, not ntriples)).to(:output)
217+
end
218+
208219
context "chaining" do
209220
TEST_FILES.each do |fmt, file|
210221
it "chains subjects and objects #{fmt}" do

0 commit comments

Comments
 (0)