0

I have a program that uses a method for verification, if that verification failed I would like to return to the method it was called from, for example:

def obtain_pokemon_name
  print 'Enter Pokemon: '
  pokemon = gets.chomp.capitalize
  obtain_basic_attack(pokemon)
end

def obtain_basic_attack(poke)
  print 'Enter basic attack: '
  basic_attack = gets.chomp.downcase
  check_attacks(poke, basic_attack)
  obtain_spec_attack(poke)
end

def obtain_spec_attack(poke)
  print 'Enter special attack: '
  spec_attack = gets.chomp.downcase
  check_attacks(poke, spec_attack)
end

def check_attacks(pokemon, attack)
  if POKEMON_ATTACKS[pokemon][attack] == nil
    puts "#{attack} is not one of #{pokemon}'s attacks, try again.."
    return # to where this function was called
  else
    attack
  end
end

begin
  obtain_pokemon_name
rescue => e
  puts "Failed with error code: #{e}"
end

When this is run:

Enter Pokemon: arbok
Enter basic attack: eat
eat is not one of Arbok's attacks, try again..
Enter special attack: test
test is not one of Arbok's attacks, try again..

Attack list:

POKEMON_ATTACKS = {
    'Bulbasaur' => {'tackle' => 10.9, 'vine whip' => 15.4, 'power whip' => 21.4, 'seed bomb' => 12.5, 'sludge bomb' => 19.2},
    'Ivysaur' => {'razor leaf' => 10.3, 'vine whip' => 15.4, 'power whip' => 21.4, 'sludge bomb' => 19.2, 'solar beam' => 13.3},
    'Kakuna' => {'bug bite' => 13.3, 'poison sting' => 10.3, 'struggle' => 8.8},
    'Beedrill' => {'bug bite' => 13.3, 'poison jab' => 14.3, 'aerial ace' => 8.6, 'sludge bomb' => 19.2, 'x-scissor' => 14.3},
    'Pidgey' => {'quick attack' => 7.5, 'tackle' => 10.9, 'aerial ace' => 8.6, 'air cutter' => 7.6, 'twister' => 5.6},
    'Ekans' => {'acid' => 9.5, 'poison sting' => 10.3, 'gunk shot' => 20.0, 'sludge bomb' => 19.2, 'wrap' => 3.8},
    'Arbok' => {'acid' => 9.5, 'bite' => 12.0, 'dark pulse' => 12.9, 'gunk shot' => 20.0, 'sludge wave' => 17.6},
}

So my question is, if the attack is not present in the data, how can I return back to the calling method? So for instance if I call arbok and his attack is tackle if it doesn't exist in the hash, how would I return to the obtain_basic_attack(poke) method?

Rubyist
  • 19
  • 3
  • I suggest you take a look at recursion. Here's a good analogy: http://stackoverflow.com/questions/717725/understanding-recursion – Un3qual Jul 29 '16 at 18:31

1 Answers1

2

RIght here:

   puts "#{attack} is not one of #{pokemon}'s attacks, try again.."
    return # to where this function was called

you should call the original method again. i.e.

  if POKEMON_ATTACKS[pokemon][attack] == nil
    puts "#{attack} is not one of #{pokemon}'s attacks, try again.."
    return obtain_spec_attack(poke)

You could alternatively add this logic to obtain_spec_attack:

def obtain_spec_attack(poke)
  loop do
    print 'Enter special attack: '
    spec_attack = gets.chomp.downcase
    attack_found = check_attacks(poke, spec_attack)
    if attack_found
      break attack_found # this will return attack_found from the loop
    else
      puts "attack not found"
    end
  end
end

edit

looking at your question again, I realize you want to return to a method multiple levels up. You could use the approaches I've already outlined, or alternatively use rescue:

def obtain_basic_attack(poke)
  begin
    print 'Enter basic attack: '
    basic_attack = gets.chomp.downcase
    check_attacks(poke, basic_attack)
    obtain_spec_attack(poke)
  rescue AttackNotFoundError
    retry # runs the 'begin' block again
  end
end

def obtain_spec_attack(poke)
  print 'Enter special attack: '
  spec_attack = gets.chomp.downcase
  check_attacks(poke, spec_attack)
end

def check_attacks(pokemon, attack)
  if POKEMON_ATTACKS[pokemon][attack] == nil
    puts "#{attack} is not one of #{pokemon}'s attacks, try again.."
    raise AttackNotFoundError
  else
    attack
  end
end

In order to use a custom error like AttackNotFoundError, you need to define the error class somewhere:

class AttackNotFoundError < StandardError; end

You could use any error really, such as raise StandardError, but it's better to restrict what errors you're rescuing so that you don't accidentally rescue an unrelated error.

engineersmnky
  • 25,495
  • 2
  • 36
  • 52
max pleaner
  • 26,189
  • 9
  • 66
  • 118
  • If you look at the code, there's more then one calling method. However the loop would probably work – Rubyist Jul 29 '16 at 18:38
  • you can also use rescue. I'll add an example for that. – max pleaner Jul 29 '16 at 18:45
  • That's a genius idea!!! I didn't even think about rescue clauses, thank you so much! – Rubyist Jul 29 '16 at 19:00
  • Sure, by the way something like `if POKEMON_ATTACKS[pokemon][attack] == nil` is usually unnecessary, you can probably remove the `== nil` and `if` will evaluate if it is truthy/falsy – max pleaner Jul 29 '16 at 20:25
  • I can't decide whether this is an abuse of exceptions or not. Idiomatically, you would probably rather use `throw`/`catch` instead. – Jörg W Mittag Jul 30 '16 at 01:33