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
Related
I'm attempting to adapt the method-chaining example cited in this posting (Method chaining and lazy evaluation in Ruby) to work with an object that implements the Enumerable class (Implement a custom Enumerable collection class in Ruby )
Coffee class:
class Coffee
attr_accessor :name
attr_accessor :strength
def initialize(name, strength)
#name = name
#strength = strength
end
def <=>(other_coffee)
self.strength <=> other_coffee.strength
end
def to_s
"<name: #{name}, strength: #{strength}>"
end
end
Criteria class:
class Criteria
def initialize(klass)
#klass = klass
end
def criteria
#criteria ||= {:conditions => {}}
end
# only show coffee w/ this strength
def strength(strength)
criteria[:strength] = strength
self
end
# if there are multiple coffees, choose the first n=limit
def limit(limit)
criteria[:limit] = limit
self
end
# allow collection enumeration
def each(&block)
#klass.collection.select { |c| c[:strength] == criteria[:strength] }.each(&block)
end
end
CoffeeShop class:
class CoffeeShop
include Enumerable
def self.collection
#collection=[]
#collection << Coffee.new("Laos", 10)
#collection << Coffee.new("Angkor", 7)
#collection << Coffee.new("Nescafe", 1)
end
def self.limit(*args)
Criteria.new(self).limit(*args)
end
def self.strength(*args)
Criteria.new(self).strength(*args)
end
end
When I run this code:
CoffeeShop.strength(10).each { |c| puts c.inspect }
I get an error:
criteria.rb:32:in block in each': undefined method '[]' for #<Coffee:0x007fd25c8ec520 #name="Laos", #strength=10>
I'm certain that I haven't defined the Criteria.each method correctly, but I'm not sure how to correct it. How do I correct this?
Moreover, the each method doesn't support the limit as currently written. Is there a better way to filter the array such that it is easier to support both the strength and limit?
Other coding suggestions are appreciated.
Your Coffee class defines method accessors for name and strength. For a single coffee object, you can thus get the attributes with
coffee.name
# => "Laos"
coffee.strength
# => 10
In your Criteria#each method, you try to access the attributes using the subscript operator, i.e. c[:strength] (with c being an Instance of Coffee in this case). Now, on your Coffee class, you have not implemented the subscript accessor which resulting in the NoMethodError you see there.
You could thus either adapt your Criteria#each method as follows:
def each(&block)
#klass.collection.select { |c| c.strength == criteria[:strength] }.each(&block)
end
or you could implement the subscript operators on your Coffee class:
class Coffee
attr_accessor :name
attr_accessor :strength
# ...
def [](key)
public_send(key)
end
def []=(key, value)
public_send(:"#{key}=", value)
end
end
Noe, as an addendum, you might want to extend your each method in any case. A common (and often implicitly expected) pattern is that methods like each return an Enumerator if no block was given. This allows patterns like CoffeeShop.strength(10).each.group_by(&:strength).
You can implement this b a simple on-liner in your method:
def each(&block)
return enum_for(__method__) unless block_given?
#klass.collection.select { |c| c.strength == criteria[:strength] }.each(&block)
end
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
I'm quite new to ruby. I'm used to Java and C++ though. What I was trying to understand is how to use polymorphism of the language. And also rules of inheritance. I've made a simple working code but I can't understand how good is it. Are there any other ways? There are always other ways in ruby as far as I know.
What it does: compares children like children if they belong to the same class. Otherwise compares them like their parent.
Sorry if I've wasted your time. Googling didn't help, I can't make a good query.
class Hero
public
#redefine comparison like this just for the sake of having it use protected method
def <=> hero
#mass <=> hero.get_mass
end
def initialize mass
#mass = mass
end
protected
#this protected method is used for inheritance testing purposes
def get_mass
#mass
end
end
class Finn < Hero
def initialize mass, sword_length
#mass = mass
#sword_length = sword_length
end
# THIS! Am I doing this right?
def <=> finn
if finn.class == self.class
(#mass+#sword_length) <=> (finn.get_mass+finn.get_sword_length)
else
super(finn)
end
end
# probably not...
protected
def get_sword_length
#sword_length
end
end
class Jake < Hero
def initialize mass, fist_length
#mass = mass
#fist_length = fist_length
end
def <=> jake
if jake.class == self.class
(#mass+#fist_length) <=> (jake.get_mass+jake.get_fist_length)
else
super(jake)
end
end
protected
def get_fist_length
#fist_length
end
end
hero1 = Finn.new(10, 80)
hero4 = Jake.new(50, 18)
puts " error? "+(hero1<=>hero4).to_s
A couple of notes:
Typically you would use an accessor of the same name as your instance variable rather than your get style naming.
Typically you would not check the class, but rather what the object can do.
class Hero
def initialize(mass)
#mass = mass
end
def <=>(hero)
mass <=> hero.mass
end
protected #I put this as protected since you did, but I would likely leave it public
attr_reader :mass
end
class Finn < Hero
def initialize(mass, sword_length)
#mass = mass
#sword_length = sword_length
end
def <=>(finn)
if finn.respond_to? :sword_length
(mass + sword_length) <=> (finn.mass + finn.sword_length)
else
super(finn)
end
end
protected
attr_reader :sword_length
end
class Jake < Hero
def initialize(mass, fist_length)
#mass = mass
#fist_length = fist_length
end
#edited
def <=>(jake)
if jake.respond_to? :fist_length
(mass + fist_length) <=> (jake.mass + jake.fist_length)
else
super(jake)
end
end
protected
attr_reader :fist_length
end
I have tried to refactor your code, Here is how it looks:-
class Hero
attr_reader :mass
# more info in attr_reader and attr_accessor is here http://stackoverflow.com/questions/5046831/why-use-rubys-attr-accessor-attr-reader-and-attr-writer
def initialize mass
#mass = mass
end
def <=> hero
mass <=> hero.mass
end
end
class Finn < Hero
attr_reader :sword_length
def initialize mass, sword_length
#sword_length = sword_length
super(mass)
end
def <=> finn
if finn.class == self.class
(#mass+#sword_length) <=> (finn.mass+finn.sword_length)
else
super(finn)
end
end
end
class Jake < Hero
attr_reader :fist_length
def initialize mass, fist_length
#fist_length = fist_length
super(mass)
end
def <=> jake
#alternate dense way to do it
jake.class == self.class ? ((#mass+#fist_length) <=> (jake.mass+jake.fist_length)) : super(jake)
end
end
hero1 = Finn.new(10, 80)
hero4 = Jake.new(50, 18)
puts " error? "+(hero1<=>hero4).to_s
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'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