Skip to content

Instantly share code, notes, and snippets.

@pvdb
Last active September 4, 2018 17:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pvdb/16b9345fab18f98d0441be857e4352c9 to your computer and use it in GitHub Desktop.
Save pvdb/16b9345fab18f98d0441be857e4352c9 to your computer and use it in GitHub Desktop.
idiomatic Enumerable extensions to filter elements by consent
# rubocop:disable all
# https://github.com/pvdb/consenter
require 'io/console'
class Consenter # :nodoc:
VALID_RESPONSES = {
'y' => 'yes to this',
'n' => 'no to this',
'Y' => 'yes to this and all remaining',
'N' => 'no to this and all remaining',
'q' => 'quit',
'?' => 'help',
}.freeze
private_constant :VALID_RESPONSES
HELP = VALID_RESPONSES.map { |k, v|
"#{k} - #{v}"
}.join($/).freeze
private_constant :HELP
ANSWERS = VALID_RESPONSES.keys.join(',').freeze
private_constant :ANSWERS
def initialize(prompt = '%s', inspector = :to_s)
@prompt = prompt + ' [' + ANSWERS + '] '
@inspector = inspector
end
def consent_for?(arg)
key_pressed = \
case
when @yes_to_all then 'y'
when @no_to_all then 'n'
else
loop do
description = case @inspector
when Symbol then arg.send(@inspector)
when Method then @inspector.call(arg)
end
IO.console.print format(@prompt, description)
case answer = (key = IO.console.gets) ? key.strip : nil
when *['y', 'n'] then break answer
when 'Y' then @yes_to_all = true and break 'y'
when 'N' then @no_to_all = true and break 'n'
when 'q' then break nil
when nil then break nil # CTRL-D
when '?' then IO.console.puts HELP
else IO.console.puts HELP
end
end
end
key_pressed && key_pressed == 'y'
end
private :consent_for?
def for(enumerable, &block)
@yes_to_all = false
@no_to_all = false
enumerable.each do |arg|
case consent_for? arg
when true then block.yield(arg) # user pressed 'y' or 'Y'
when false then nil # user pressed 'n' or 'N'
when nil then break # user pressed 'q'
end
end
end
end
module Enumerable # :nodoc:
def each_consented(prompt = '%s', inspector = :to_s)
unless block_given?
return to_enum(__method__, prompt) do size; end
end
Consenter.new(prompt, inspector).for(self) do |*args|
yield(*args)
end
end
end
if $0 == __FILE__
%w[foo bar qux].each_consented('Do you like "%s"', :upcase) do |thing|
puts thing
end
end
# rubocop:enable all
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment