How do I properly override instance methods in Ruby? - ruby

I've been learning Ruby for a class and have been writing a sample game. Here is a bit of one of my classes:
class Player
def askIfTake
puts("Would you like to take a card? > ")
input = gets.chomp
input.downcase!
if input == "y" or input == "yes"
return 1
elsif input == "n" or input == "no"
return 0
else
puts("Invalid input. Please type y or n.")
return askIfTake
end
end
end
I then have another class:
class PlayerAI < Player
def initialize
super
end
def askIfTake
puts("this is an AI")
return rand(2)
end
end
The problem is, when I create an instance of PlayerAI, and attempt to call askIfTake from that instance, it calls the method declared in the Player class. Why is this happening?

This is not possible. I tested it (knowing it would be fine) and it worked for me:
>> PlayerAI.new
=> #<PlayerAI:0x00000103889308>
>> PlayerAI.new.askIfTake
this is an AI
You are doing this in the correct way. Check if you have any spelling mistakes. Incidentally, in Ruby the method usually uses underscores: PlayerAI.new.ask_if_take. Or better, with a question mark: PlayerAI.new.will_take?.

Related

Ruby - Testing a method that calls itself in Minitest

I'm having trouble developing unit tests for a method that calls itself (a game loop) in Ruby using minitest. What I've attempted has been stubbing the method I'm trying to call in said game loop with my input. Here's the game loop:
#main game loop
def playRound
#draw board
#board.printBoard
#get input
playerInput = gets.chomp #returns user input without ending newline
#interpret input, quitting or beginning set selection for a player
case playerInput
when "q"
quit
when "a", "l"
set = getPlayerSet()
if(playerInput == "a")
player = 1
else
player = 2
end
when "h"
if #hintsEnabled
giveHint
playRound
else
puts "Hints are disabled"
playRound
end
else
puts "Input not recognized."
end
if(set != nil)
#have board test set
checkSet(set, player)
end
#check if player has quitted or there are no more valid sets
unless #quitted || #board.boardComplete
playRound
end
end
Much of it is ultimately irrelevant, all I'm trying to test is that this switch statement is calling the correct methods. Currently I'm trying to circumvent the loop by stubbing the called method to raise an error (which my test assers_raise's):
def test_playRound_a_input_triggers_getPlayerSet
#game.stub :getPlayerSet, raise(StandardError) do
assert_raises(StandardError) do
simulate_stdin("") {
#game.playRound
}
end
end
end
This approach does not seem to work, however, as Minitest is recording the results of the above test as an error with the message
E
Error:
TestGame#test_playRound_a_input_triggers_getPlayerSet:
StandardError: StandardError
test_game.rb:136:in `test_playRound_a_input_triggers_getPlayerSet'
If anyone has any advice or direction for me it would be massively appreciated as I can't tell what's going wrong
I'm not very familiar with minitest, but I expect you need to wrap the raise(exception) in a block, otherwise your test code is raising the exception immediately in your test (not as a result of the stubbed method being called).
Something like:
class CustomTestError < RuntimeError; end
def test_playRound_a_input_triggers_getPlayerSet
raise_error = -> { raise(CustomTestError) }
#game.stub(:getPlayerSet, raise_error) do
assert_raises(CustomTestError) do
simulate_stdin("") {
#game.playRound
}
end
end
end
-- EDIT --
Sometimes when i'm having difficulty testing a method it's a sign that I should refactor things to be easier to test (and thus have a cleaner, simpler interface, possibly be easier to understand later).
I don't code games and don't know what's typical for a game loop, but that method looks very difficult to test. I'd try to break it into a couple steps where each step/command can be easily tested in isolation. One option for this would be to define a method for each command and use send. This would allow you to test that each command works separately from your input parsing and separately from the game loop itself.
COMMANDS = {
q: :quit,
# etc..
}.stringify_keys.freeze
def play_round # Ruby methods should be snake_case rather than camelCase
#board.print_board
run_command(gets.chomp)
play_round unless #quitted || #board.board_complete
end
def run_command(input)
command = parse_input_to_command(input)
run_command(command)
end
def parse_input_to_command(input)
COMMANDS[input] || :bad_command
end
def run_command(command)
send("run_#{command}")
end
# Then a method for each command, e.g.
def run_bad_input
puts "Input not recognized"
end
However, for this type of problem I really like a functional approach, where each command is just a stateless function that you pass state into and get new state back. These could either mutate their input state (eww) or return a new copy of the board with updated state (yay!). Something like:
COMMANDS = {
# All state change must be done on board. To be a functional pattern, you should not mutate the board but return a new one. For this I invent a `.copy()` method that takes attributes to update as input.
q: -> {|board| board.copy(quitted: true) },
h: -> HintGiver.new, # If these commands were complex, they could live in a separate class entirely.
bad_command: -> {|board| puts "Unrecognized command"; board },
#
}.stringify_keys.freeze
def play_round
#board.print_board
command = parse_input_to_command(gets.chomp)
#board = command.call(#board)
play_round unless #board.quitted || #board.board_complete
end
def parse_input_to_command(input)
COMMANDS[input] || COMMANDS[:bad_command]
end

Dynamic methods using define_method and eval

I've put together two sample classes implemented in a couple of different ways which pretty well mirrors what I want to do in my Rails model. My concern is that I don't know what, if any are the concerns of using either method. And I've only found posts which explain how to implement them or a general warning to avoid/ be careful when using them. What I have not found is a clear explanation of how to accomplish this safely, and what I'm being careful of or why I should avoid this pattern.
class X
attr_accessor :yn_sc, :um_sc
def initialize
#yn_sc = 0
#um_sc = 0
end
types = %w(yn um)
types.each do |t|
define_method("#{t}_add") do |val|
val = ActiveRecord::Base.send(:sanitize_sql_array, ["%s", val])
eval("##{t}_sc += #{val}")
end
end
end
class X
attr_accessor :yn_sc, :um_sc
def initialize
#yn_sc = 0
#um_sc = 0
end
types = %w(yn um)
types.each do |t|
# eval <<-EVAL also works
self.class_eval <<-EVAL
def #{t}_add(val)
##{t}_sc += val
end
EVAL
end
end
x = X.new
x.yn_add(1) #=> x.yn_sc == 1 for both
Well, your code looks realy safe. But imagine a code based on user input. It might be look something like
puts 'Give me an order, sir!'
order = gets.chomp
eval(order)
What will happen if our captain will go wild and order us to 'rm -rf ~/'? Sad things for sure!
So take a little lesson. eval is not safe because it evaluates every string it receives.
But there's another reason not to use eval. Sometimes it evaluates slower than alternatives. Look here if interested.

Undefined method in console?

Here comes another Codecademy question:
The following challenge has been presented.
Define two methods in the editor:
A greeter method that takes a single string parameter, name, and
returns a string greeting that person. (Make sure to use return and
don't use print or puts.)
A by_three? method that takes a single integer parameter, number, and
returns true if that number is evenly divisible by three and false if
not. Remember, it's a Ruby best practice to end method names that
produce boolean values with a question mark.
The code I put in re: was..
def greeter(name)
return "Greet #{name}"
end
def by_three?(x)
if x % 3==0
returns true
else
return false
end
greeter("Brant")
by_three?(6)
The console then gives me the following error:
Did you define your greeter method?
It seems like I have. Am I wrong?
this would be it:
def greeter(name)
"Greet #{name}"
end
def by_three?(x)
x % 3 == 0
end
greeter("Brant") # => "Greet Brant"
by_three?(6) # => true
It looks like you did not add "end" after your else statement. Here you go.
#For the greeter method, i decided to use this format
def greeter(name)
return name
end
greeter("Hello Jane, good morning")
def by_three?(number)
if number % 3 != 1
return true
else
return false
end #Add end here to end your statement
end
by_three?(5)

Function calls in hash come up empty in Ruby

I've been sifting through the prior questions and answers on stackoverflow, and I have gotten most of my question figured out. I figured out that I can't place a function call within a hash, without placing it within a proc, or a similar container.
What I'm ultimately trying to do is have a menu displayed, grab user input, and then iterate through the hash, and run the specified function:
def Main()
menu_titles = {"Answer1" => Proc.new{Choice1()}}
Menu(menu_titles)
end
def Choice1()
puts "Response answer"
end
def Menu(menu_titles)
menu_titles.each_with_index do |(key, value),index|
puts "#{index+1}. #{key}"
end
user_input = 0
menu_titles.each_with_index do |(key, value), index|
if index.eql?(user_input)
menu_titles[value]
break
end
end
end
Main()
The issue I'm having right now is that I'm not entering the functions that my hash calls for. Whether I use a return or a "puts", I either get a blank line or nothing at all. If anyone has other recommendations about my code, I'm all ears also. To be honest, I don't like using procs, but that's mostly because I don't entirely know how they work and where to use them.
Right now for my menus I have:
user_input = 1
if user_input == 1
Choice1()
...
end
Here's how I would refactor this:
class Menu
attr_reader :titles
# initialize sets up a hard-coded titles instance variable,
# but it could easily take an argument.
def initialize
#titles = {
"Answer1" => Proc.new{ puts "choice 1" },
"Answer2" => Proc.new{ puts "choice 2" }
}
end
# This is the only public instance method in your class,
# which should give some idea about what the class is for
# to whoever reads your code
def choose
proc_for_index(display_for_choice)
end
private
# returns the index of the proc.
def display_for_choice
titles.each_with_index { |(key,value), index| puts "#{index + 1}. #{key}" }
gets.chomp.to_i - 1 # gets will return the string value of user input (try it in IRB)
end
# first finds the key for the selected index, then
# performs the hash lookup.
def proc_for_index(index)
titles[titles.keys[index]]
end
end
If you're serious about Ruby (or object-oriented programming in general), I would highly recommend learning about the advantages of packaging your code into behavior-specific classes. This example allows you to do this:
menu = Menu.new
proc = menu.choose
#=> 1. Answer1
#=> 2. Answer2
2 #(user input)
proc.call
#=> choice 2
And you could actually run it on one line:
Menu.new.choose.call

Ruby metaprogramming, how does RSpec's 'should' work?

I was reading up on RSpec and I was trying to figure out how RSpec's "should" was implemented.
Could someone give a hand on how the meta nature of this function works?
The code is located here:
http://github.com/dchelimsky/rspec/blob/master/lib/spec/expectations/extensions/kernel.rb
TIA,
-daniel
Clarification:
target.should == 5
How did target's value get passed along to "should", which in turn was "=="'d against 5?
Take a look at class OperatorMatcher.
It all boils down to Ruby allowing you to leave out periods and parenthesis. What you are really writing is:
target.should.send(:==, 5)
That is, send the message should to the object target, then send the message == to whatever should returns.
The method should is monkey patched into Kernel, so it can be received by any object. The Matcher returned by should holds the actual which in this case is target.
The Matcher implements the method == which does the comparison with the expected which, in this case, is the number 5. A cut down example that you can try yourself:
module Kernel
def should
Matcher.new(self)
end
end
class Matcher
def initialize(actual)
#actual = actual
end
def == expected
if #actual == expected
puts "Hurrah!"
else
puts "Booo!"
end
end
end
target = 4
target.should == 5
=> Booo!
target = 5
target.should == 5
=> Hurrah!

Resources