Last active
September 4, 2018 17:59
-
-
Save pvdb/16b9345fab18f98d0441be857e4352c9 to your computer and use it in GitHub Desktop.
idiomatic Enumerable extensions to filter elements by consent
This file contains hidden or 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
# 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