Im having a bit of trouble testing some random behaviour in rspec. I have a method on a class that should change one of the classes instance variables if a randomly generated number equals 10. I cant find anyway to correctly test this with rspec.
Here is the code for the class
class Airport
DEFAULT_CAPACITY = 20
attr_reader :landed_planes, :capacity
attr_accessor :weather
def initialize(capacity=DEFAULT_CAPACITY,weather = "clear")
#landed_planes = []
#capacity = capacity
#weather = weather
end
def stormy
if rand(10) == 10 then #weather = "stormy" end
end
end
does anyone know of a way i could write test for the stormy method?
One option is start rspec with rspec --seed 123 this will ensure that your random numbers are always predictable. But this will effect all subsequent calls to rand, shuffle, sample and so on.
Another way is to change your class to inject the randnumber generator:
class Airport
DEFAULT_CAPACITY = 20
attr_reader :landed_planes, :capacity
attr_accessor :weather
def initialize(capacity=DEFAULT_CAPACITY,weather = "clear", randomizer = ->(n) { rand(n)})
#landed_planes = []
#capacity = capacity
#weather = weather
#randomizer = randomizer
end
def stormy
if #randomizer.call(10) == 10 then #weather = "stormy" end
end
end
Related
class Airport
attr_reader :plane
CAPACITY = 20
def initialize
#plane = []
#capacity = CAPACITY
end
#can land a plane
def land(plane)
fail 'plane is full' if #plane.count >= #capacity
#plane << plane
end
def take_off
fail 'weather is stormy' if #weather
#plane
end
def weather
#weather = weather
end
end
class Weather
def stormy?
[true, false].sample
end
end
I am new to Ruby. I am a little confused in how to use the method of the Weather class in my Airport class.
I want the take_off method in my Airport class to fail when the weather is reported to be stormy. I am unsure where to begin in understanding object interactions. Any help would be greatly appreciated, thank you
If you want to fail if weather is stormy, you have to call its stormy? method:
def take_off
fail 'weather is stormy' if #weather.stormy?
# do something
end
But in order to get the above working, weather needs to be an instance of Weather. I would simply pass it to the constructor. In addition, I would make it a settable attribute, so you can easily update it:
class Airport
attr_accessor :weather
attr_reader :plane
CAPACITY = 20
def initialize(weather)
#weather = weather
#plane = []
#capacity = CAPACITY
end
# ...
end
I would also make the weather a little more deterministic, e.g. by having a wind force attribute:
class Weather
attr_reader :wind_force
def initialize(wind_force)
#wind_force = wind_force
end
def stormy?
wind_force >= 10
end
end
Usage:
weather = Weather.new(3)
airport = Airport.new(weather)
airport.take_off # no exception
airport.weather = Weather.new(12)
airport.take_off # RuntimeError: weather is stormy
I'm trying to make this test pass and am not sure how to go about making this pass.
TEST
def test_it_is_thirsty_by_default
vampire = Vampire.new("Count von Count")
assert vampire.thirsty?
end
def test_it_is_not_thirsty_after_drinking
vampire = Vampire.new("Elizabeth Bathory")
vampire.drink
refute vampire.thirsty?
end
CODE
def thirsty?
true
end
def drink
thirsty? === false
end
It is giving a failure message on the last test:
Failed refutation, no message given
What am I missing? My thought is that initially, the vampire is thirsty(true) and then defined a method that would then make the vampire not thirsty(false).
EDIT
Even if I reassign the drink method to:
thirsty? = false
I get a syntax error pointing to the = sign.
You're missing a couple things, most importantly some kind of writer method that allows you to store that fact that #thirsty is getting updated inside of your drink method call
There's a couple different ways to do this but I've shown one below with a few notes:
require 'test/unit'
class Vampire
def initialize(name)
#name = name
#thirsty = true # true by default
end
def drink
#thirsty = false # updates #thirsty for the respective instance
end
def thirsty?
#thirsty
end
end
class VampireTests < Test::Unit::TestCase
def test_it_is_thirsty_by_default
vampire = Vampire.new("Count von Count")
assert vampire.thirsty?
end
def test_it_is_not_thirsty_after_drinking
vampire = Vampire.new("Elizabeth Bathory")
vampire.drink
refute vampire.thirsty?
end
end
I wondering of how to check the owner of certain method/class from other class.
For example:
class Value
attr_accessor :money
def initialize
#money = 0.0
end
def get_money
return self.money
end
def transfer_money(target, amount)
self.money -= amount
target.money += amount
end
end
class Nation
attr_accessor :value
def initialize
#value = Value.new
end
end
class Nation_A < Nation
def initialize
super
end
def pay_tribute_to_decendant_country
value.transfer_money(Nation_B.value, 500)
end
end
class Nation_B < Nation
def initialize
super
end
def pay_tribute_to_decendant_country
value.transfer_money(Nation_C.value, 200)
end
end
class Nation_C < Nation
def initialize
super
end
def pay_tribute_to_decendant_country
value.transfer_money(Nation_A.value, 300)
end
end
Yea, makes no sense how the decendant goes in a circle, but I'd like to implement the idea that different subclass has different argument.
The list is pretty long (I've installed at least 40 of these already with much more complex desendant branches and much more methods that call transfer_money from Value class). Then I have some idea to implement to the structure. I'd like to add currency, but to override all transfer_money method call would be a tremendous task for me to apply. Therefore I create a hash table that generate the call for me.
class Nation
def self.get_descendants
ObjectSpace.each_object(Class).select { |klass| klass < self }
end
end
module Additional_Value
currency_table = {}
min = 50
max = 100
def self.range (min, max)
rand * (max-min) + min
end
Nation.get_descendants.each do |derived_classes|
currency_table[derived_classes] == self.range min, max
end
end
class Value
attr_accessor :currency
def initialize
#money = 0
#currency = Additional_Value::currency_table
end
def transfer_money(target, amount)
self.money -= amount
amount = amount * #currency[self.class.owner] / #currency[target.class.owner]
target.money += amount
end
end
and I need to figure out how to define owner class. I tried using the caller, but it returns me string / array of string instead of object, method or calle work only for the same method, the 'sender' gem gives me an idea, but it's written in C, and I need to use the default library due to my circumstances.
Greatly appreciated.
Edit:
I'll rewrite the problem in a shorter way:
class Slave
def who_is_the_owner_of_me
return self.class.owner unless self.class.owner.nil?
end
end
class Test
attr_accessor :testing
def initialize
#testing = Slave.new
end
end
class Test2 < Test1
end
a = Test.new
b = Test2.new
c = Slave.new
a.testing.who_is_the_owner_of_me #=> Test
b.testing.who_is_the_owner_of_me #=> Test2
c.who_is_the_owner_of_me #=> main
I just tested attr_accessor against equivalent getter/setter-methods:
class A
# we define two R/W attributes with accessors
attr_accessor :acc, :bcc
# we define two attributes with getter/setter-functions
def dirA=(d); #dirA=d; end
def dirA; #dirA; end
def dirB=(d); #dirB=d; end
def dirB; #dirB; end
end
varA = A.new
startT = 0
dirT = 0
accT = 0
# now we do 100 times the same benchmarking
# where we do the same assignment operation
# 50000 times
100.times do
startT = Time.now.to_f
50000.times do |i|
varA.dirA = i
varA.dirB = varA.dirA
end
dirT += (Time.now.to_f - startT)
startT = Time.now.to_f
50000.times do |i|
varA.acc = i
varA.bcc = varA.acc
end
accT += (Time.now.to_f - startT)
end
puts "direct: %10.4fs" % (dirT/100)
puts "accessor: %10.4fs" % (accT/100)
Program output is:
direct: 0.2640s
accessor: 0.1927s
So the attr_accessor is significantly faster. could someone please share some wisdom, why this is so?
Without deeply understanding the differences, I can at least say that attr_accessor (and attr_reader and attr_writer) are implemented in C, as you can see by toggling the source on that page. Your methods will be implemented in Ruby, and Ruby methods have more call overhead than native C functions.
Here's an article explaining why Ruby method dispatch tends to be slow.
I'm creating a card game with multiple classes. Currently, I'm using global variables to hold the $shuffled_deck, $players_hand, and $dealers_hand variables, but I worry when using global variables (perhaps, needlessly) and would prefer to use instance variables.
I've been reading around, but nothing is really clicking. Can anyone help point me in the right direction with this?
Using instance variables I haven't been able to save the #players_hand and #dealers_hand to be able to use them in other classes. For instance, I have #players_hand from the Player class. I have the Dealer class draw a card, but I can't pull that #players_hand into the Dealer class to add the two together.
My current code is:
class Blackjack
def initialize
#player = Player.new
#dealer = Dealer.new
end
end
class Dealer
def initialize
#deck = Deck.new
$dealers_hand = 0
end
def hit_dealer
#deck.hit_dealer
end
def hit_player
#deck.hit_player
end
def draw_card
#hit = $shuffled_deck
end
def shuffle
#deck.suits
end
end
class Player
def initialize
$players_hand = 0
end
end
class Deck
def suits
#code that shuffled the deck..
$shuffled_deck = #shuffled_deck
end
def hit_player
#hit = $shuffled_deck.pop
end
def hit_dealer
#hit = $shuffled_deck.pop
end
end
using your example you can do it like this
class Blackjack
attr_reader :player, :dealer
def initialize
#player = Player.new
#dealer = Dealer.new
end
end
class Dealer
def dealers_hand #the long java way of a getter
#dealers_hand
end
#and now the short ruby way
attr_reader :dealers_hand #if you only need to read the attribute
attr_writer :dealers_hand #if you only need to write the attribute
attr_accessor: dealers_hand #if you need both
def initialize
#deck = Deck.new
#dealers_hand = 5
end
def hit_dealer
#deck.hit_dealer
end
def hit_player
#deck.hit_player
end
def draw_card
#hit = $shuffled_deck
end
def shuffle
#deck.suits
end
end
class Player
attr_reader :players_hand
def initialize
#players_hand = 0
end
end
class Deck
def suits
attr_reader :shuffled_deck
#shuffled_deck = #shuffled_deck
end
def hit_player
#hit = $shuffled_deck.pop
end
def hit_dealer
#hit = $shuffled_deck.pop
end
end
game = Blackjack.new
p game.dealer.dealers_hand
game.dealer.dealers_hand = 4
p game.dealer.dealers_hand
You want to use attr_reader, attr_writer, or attr_accessor. Here's how they work:
attr_reader :players_hand: Allows you to write some_player.players_hand to get the value of that player's players_hand instance variable
attr_writer :players_hand: Allows you to write some_player.players_hand = 0 to set the variable to 0
attr_accessor :players_hand: Allows you to both read and write, as though you'd used both attr_reader and attr_writer.
Incidentally, all these do is write methods for you. If you wanted, you could do it manually like this:
class Player
def initialize
#players_hand = 0
end
def players_hand
#players_hand
end
def players_hand=(new_value)
#players_hand = new_value
end
end