Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
82a733e
commit 895e299
Showing
9 changed files
with
234 additions
and
124 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
require 'clin' | ||
|
||
# Parent class for shell interaction. | ||
class Clin::ShellInteraction | ||
class << self | ||
attr_accessor :persist | ||
end | ||
|
||
attr_accessor :shell | ||
|
||
# @param shell [Clin::Shell] Shell starting the interaction. | ||
def initialize(shell) | ||
@shell = shell | ||
self.class.persist ||= {} | ||
end | ||
|
||
# @return [Boolean] | ||
def persist? | ||
self.class.persist[@shell] ||= false | ||
end | ||
|
||
# Mark the current shell to persist file interaction | ||
def persist! | ||
self.class.persist[@shell] = persist_answer | ||
end | ||
|
||
def persist_answer | ||
true | ||
end | ||
end | ||
|
||
require 'clin/shell_interaction/file_conflict' | ||
require 'clin/shell_interaction/yes_or_no' | ||
require 'clin/shell_interaction/choose' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
require 'clin' | ||
|
||
# Handle a choose question | ||
class Clin::ShellInteraction::Choose < Clin::ShellInteraction | ||
def run(statement, choices, default: nil, allow_initials: false) | ||
choices = convert_choices(choices) | ||
question = prepare_question(statement, choices, default: default, initials: allow_initials) | ||
loop do | ||
answer = @shell.ask(question, default: default) | ||
unless answer.nil? | ||
choices.each do |choice, _| | ||
if choice.casecmp(answer) == 0 || (allow_initials && choice[0].casecmp(answer[0]) == 0) | ||
return choice | ||
end | ||
end | ||
end | ||
print_choices_help(choices, allow_initials: allow_initials) | ||
end | ||
end | ||
|
||
protected def choice_message(choices, default: nil, initials: false) | ||
choices = choices.keys.map { |x| x == default ? x.to_s.upcase : x } | ||
msg = if initials | ||
choices.map { |x| x[0] }.join('') | ||
else | ||
choices.join(',') | ||
end | ||
"[#{msg}]" | ||
end | ||
|
||
protected def prepare_question(statement, choices, default: nil, initials: false) | ||
"#{statement} #{choice_message(choices, default: default, initials: initials)}" | ||
end | ||
|
||
# Convert the choices to a hash with key being the choice and value the description | ||
protected def convert_choices(choices) | ||
if choices.is_a? Array | ||
Hash[*choices.map { |k| [k, ''] }.flatten] | ||
elsif choices.is_a? Hash | ||
choices | ||
end | ||
end | ||
|
||
protected def print_choices_help(choices, allow_initials: false) | ||
puts 'Choose from:' | ||
used_initials = Set.new | ||
choices.each do |choice, description| | ||
suf = choice.to_s | ||
suf += ", #{description}" unless description.blank? | ||
line = if allow_initials && !used_initials.include?(choice[0]) | ||
used_initials << choice[0] | ||
" #{choice[0]} - #{suf}" | ||
else | ||
" #{suf}" | ||
end | ||
puts line | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
require 'clin' | ||
|
||
# Handle the file_conflict interaction with the user. | ||
class Clin::ShellInteraction::FileConflict < Clin::ShellInteraction | ||
def run(filename, default: nil, &block) | ||
choices = file_conflict_choices | ||
choices = choices.except(:diff) unless block_given? | ||
return true if persist? | ||
result = nil | ||
while result.nil? | ||
choice = @shell.choose("Overwrite '#{filename}'?", choices, | ||
default: default, allow_initials: true) | ||
result = handle_choice(choice, filename, &block) | ||
end | ||
result | ||
end | ||
|
||
protected def handle_choice(choice, filename, &block) | ||
case choice | ||
when :yes | ||
return true | ||
when :no | ||
return false | ||
when :always | ||
return persist! | ||
when :quit | ||
puts 'Aborting...' | ||
fail SystemExit | ||
when :diff | ||
show_diff(filename, block.call) | ||
return nil | ||
else | ||
return nil | ||
end | ||
end | ||
|
||
protected def file_conflict_choices | ||
{yes: 'Overwrite', | ||
no: 'Do not Overwrite', | ||
always: 'Override this and all the next', | ||
quit: 'Abort!', | ||
diff: 'Show the difference', | ||
help: 'Show this'} | ||
end | ||
|
||
protected def show_diff(old_file, new_content) | ||
Tempfile.open(File.basename(old_file)) do |f| | ||
f.write new_content | ||
f.rewind | ||
system %(#{Clin.diff_cmd} "#{old_file}" "#{f.path}") | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
require 'clin' | ||
|
||
# Handle a simple yes/no interaction | ||
class Clin::ShellInteraction::YesOrNo < Clin::ShellInteraction | ||
def run(statement, default: nil, persist: false) | ||
default = default.to_sym unless default.nil? | ||
options = [:yes, :no] | ||
if persist | ||
return true if persist? | ||
options << :always | ||
end | ||
choice = @shell.choose(statement, options, default: default, allow_initials: true) | ||
return persist! if choice == :always | ||
choice == :yes | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
require 'clin' | ||
|
||
RSpec.describe Clin::ShellInteraction::Choose do | ||
let(:shell) { Clin::Shell.new } | ||
subject { Clin::ShellInteraction::Choose.new(shell) } | ||
|
||
def expects_scan(message, *outputs) | ||
expect(shell).to receive(:scan).with(message).and_return(*outputs).exactly(outputs.size).times | ||
end | ||
|
||
describe '#choice_message' do | ||
let(:options) { {yes: '', no: '', maybe: ''} } | ||
it { expect(subject.send(:choice_message, options)).to eq('[yes,no,maybe]') } | ||
it { expect(subject.send(:choice_message, options, default: :yes)).to eq('[YES,no,maybe]') } | ||
it { expect(subject.send(:choice_message, options, default: :no)).to eq('[yes,NO,maybe]') } | ||
it { expect(subject.send(:choice_message, options, initials: true)).to eq('[ynm]') } | ||
it { expect(subject.send(:choice_message, options, default: :yes, initials: true)).to eq('[Ynm]') } | ||
it { expect(subject.send(:choice_message, options, default: :maybe, initials: true)).to eq('[ynM]') } | ||
end | ||
|
||
describe '#prepare_question' do | ||
let(:options) { {yes: '', no: '', maybe: ''} } | ||
it do | ||
expect(subject.send(:prepare_question, 'Is it true?', options)) | ||
.to eq('Is it true? [yes,no,maybe]') | ||
end | ||
end | ||
|
||
describe '#run' do | ||
let(:countries) { %w(usa france germany italy) } | ||
|
||
it 'ask for a choice and return the user reply' do | ||
expects_scan('Where are you from? [usa,france,germany,italy]', 'France') | ||
expect(subject.run('Where are you from?', countries)).to eq('france') | ||
end | ||
|
||
it 'ask for a choice and return the default' do | ||
expects_scan('Where are you from? [usa,france,GERMANY,italy]', '') | ||
expect(subject.run('Where are you from?', countries, default: 'germany')).to eq('germany') | ||
end | ||
|
||
it 'ask for a choice and user can reply with initials' do | ||
expects_scan('Where are you from? [ufgi]', 'i') | ||
expect(subject.run('Where are you from?', countries, allow_initials: true)).to eq('italy') | ||
end | ||
|
||
it 'keep asking until the answer is valid' do | ||
expects_scan('Where are you from? [usa,france,germany,italy]', 'spain', 'russia', 'france') | ||
expect(subject).to receive(:print_choices_help).twice | ||
expect(subject.run('Where are you from?', countries)).to eq('france') | ||
end | ||
end | ||
end |
Oops, something went wrong.