I'm trying to improve the release_bike method.
I have gone into irb, and the first guard condition works, and the release_working_bikes work, but the second guard condition keeps on return nil in irb, even when I run a feature test and know that there is only a broken bike available.
Is there something wrong with the way I'm phrasing my second fail line, or is there a flaw in broken_bikes ?
The release_bike method should work as follows;
if there are no bikes in the docking station, then there should be a warning saying - No bikes available
if there are bikes in the docking station, but they are ALL broken, then there should be a warning saying - No working bikes available
if there are some working bikes, then release_bike should release one of the workign bikes.
Below are the two classes, that are involved;
require_relative 'bike'
class DockingStation
DEFAULT_CAPACITY = 20
attr_reader :capacity, :bikes
def initialize(capacity = DEFAULT_CAPACITY)
#bikes = []
#capacity = capacity
end
def release_bike
fail 'No bikes available' if empty?
fail 'No working bikes available' unless broken_bikes
release_working_bikes
end
def dock(bike)
fail 'Docking Station Full' if full?
#bikes << bike
end
private
def working_bikes
#bikes.each { |bike| return bike unless bike.broken? }
end
def broken_bikes
not_working = []
not_working << #bikes.each { |bike| return bike if bike.broken? }
not_working.empty?
end
def release_working_bikes
bike = working_bikes
#bikes.delete(bike)
end
def full?
#bikes.count >= #capacity
end
def empty?
#bikes.empty?
end
end
class Bike
attr_accessor :broken
def initialize
#broken = false
end
def working?
#working
end
def report_broken
#broken = true
end
def broken?
#broken
end
end
As already pointed out in comments, you're trying to check if all bikes are broken, so why not name your method all_bikes_broken? . See comments in code.
require_relative 'bike'
class DockingStation
DEFAULT_CAPACITY = 20
attr_reader :capacity, :bikes
def initialize(capacity = DEFAULT_CAPACITY)
#bikes = []
#capacity = capacity
end
def release_bike
fail 'No bikes available' if empty?
fail 'No working bikes available' unless all_bikes_broken?
release_working_bikes
end
def dock(bike)
fail 'Docking Station Full' if full?
#bikes << bike
end
private
def working_bikes
#this will select only bikes which are NOT broken
#bikes.reject{ |bike| bike.broken? }
end
def all_bikes_broken?
#this is shorthand for #bikes.all?{ |bike| bike.broken? }
#it says send :broken? method to each instance of bike.
#.all? returns true only if all instances return true, otherwise false.
#bikes.all?(&:broken?)
end
def release_working_bikes
bike = working_bikes
#bikes.delete(working_bikes.first)
#or you could do .last but order probably doesn't matter here.
end
def full?
#bikes.count >= #capacity
end
def empty?
#bikes.empty?
end
end
class Bike
attr_accessor :broken
def initialize
#broken = false
end
def working?
#working
end
def report_broken
#broken = true
end
def broken?
#broken
end
end
Related
I tried to write a wrapper for ets with which you can read and write structures in etc, question is: how to make id to be generated automatically
defmodule StructTable do
defstruct id: 0, data: nil
def create_table do
:ets.new(__MODULE__, [:orderedset, :named_table, {:keypos, 1}])
end
def insert_into_table(%__MODULE__{ id: id, data: data}) do
if hd(:ets.lookup(__MODULE__, id)) == false do
:ets.insert(__MODULE__, {id,data})
else IO.puts("already exists")
end
end
def select_data(iid) do
hd(:ets.lookup(__MODULE__, iid))
end
def select_all do
:ets.tab2list(__MODULE__)
end
end
I used :ets.last() to get the last key and added one to it; if it was an empty table, it gets an id of 1. Table type is ordered_set, which sorts keys.
defmodule StructTable do
defstruct id: 0, data: nil
def create_table do
:ets.new(__MODULE__, [:ordered_set, :public, :named_table])
end
def insert_into_table(%__MODULE__{data: data}) do
:ets.insert(__MODULE__, {generate_id(), data})
end
def select_all do
:ets.tab2list(__MODULE__)
end
def generate_id() do
if :ets.tab2list(__MODULE__) == [] do
1
else
(:ets.last(__MODULE__) ) + 1
end
end
def select_data(iid) do
hd(:ets.lookup(__MODULE__, iid))
end
end
im new with ruby and i tried to put an array into initialize method but its not work like that, so how to put an array with this argument ? thanks
class User
attr_accessor :name, :friends
def initialize(name, friends)
#name = name
#friends = friends
end
def friendNbr
return friends.count
end
def isFriendWith(value)
friends.each do |user|
if (user.name == value)
return "Yes, #{name} is friend with #{user.name}"
end
end
return "No, #{name} is not friend with #{value}"
end
end
jane = User.new("Jane", [boris, francois, carlos, alice])
bob = User.new("Bob", [jane, boris, missy])
alice = User.new("Alice", [bob, jane])
# bob.isFriendWith("Jane")
# jane.isFriendWith("Alice")
# alice.isFriendWith("Carlos")
You have multiple ways of solving your problem :
First friends could be array of names (string)
class User
attr_accessor :name, :friends
def initialize(name, friends)
#name = name
#friends = friends
end
def friendNbr
return friends.count
end
def isFriendWith(value)
friends.each do |friend_name|
if (friend_name == value)
return "Yes, #{name} is friend with #{friend_name}"
end
end
return "No, #{name} is not friend with #{friend_name}"
end
end
jane = User.new("Jane", ["Boris", "Francois", "Carlos", "Alice"])
bob = User.new("Bob", ['Jane', 'Boris', 'Missy'])
alice = User.new("Alice", ['Bob', 'Jane'])
bob.isFriendWith("Jane")
jane.isFriendWith("Alice")
alice.isFriendWith("Carlos")
Other way is to pass object as you did but in this case you can only pass object already instanciated. In this second choice you can add a method addFriend and first create User and then add them friends.
class User
attr_accessor :name, :friends
def initialize(name, friends)
#name = name
#friends = friends
end
def friendNbr
return friends.count
end
def isFriendWith(value)
friends.each do |user|
if (user.name == value)
return "Yes, #{name} is friend with #{user.name}"
end
end
return "No, #{name} is not friend with #{value}"
end
def addFriend(...)
...
end
end
jane = User.new("Jane", [])
bob = User.new("Bob", [jane])
alice = User.new("Alice", [bob, jane])
bob.isFriendWith("Jane")
jane.isFriendWith("Alice")
alice.isFriendWith("Carlos")
class Player
def initialize(hp, attack, defence, gold)
#hp = hp
#attack = attack
#defence = defence
#gold = gold
#inventory = inventory
end
def inventory
#inventory = []
end
def buy(item)
if #gold >= item.price
#gold-=item.price
puts "You hand over #{item.price} gold, and get the #{item.name}."
puts "You have #{gold} gold left over."
#inventory.push([item.name,item.price,item.attack,item.defence])
puts "ITEMS IN INV: #{#inventory}" # output: ITEMS IN INV: [["Dagger", 4, 1, 0], ["Mucky Tunic", 2, 0, 2]]
else
puts "You can't afford this item."
end
end
end
player = Player.new(10,1,2,6)
puts player.inventory.inspect # output: []
The inventory.push line pushes the element to the array while it is inside the method, but when returned outside the method, the inventory array is empty. This is confusing because other variables that were changed inside that method in the same way come back as altered.
sample output when printed from inside the buy method:
ITEMS IN INV: [["Dagger", 4, 1, 0], ["Mucky Tunic", 2, 0, 2]]
output with player.inventory.inspect outside of the method:
[]
Whenever you call your inventory method:
def inventory
#inventory = []
end
... it assigns a new (empty) array to #inventory, thus overwriting any existing items.
The correct way is to either assign #inventory in initialize and just return it from within the getter:
class Player
def initialize(hp, attack, defence, gold)
#hp = hp
#attack = attack
#defence = defence
#gold = gold
#inventory = []
end
def inventory
#inventory
end
# ...
end
or to not assign it at all in initialize and use the conditional assignment operator:
class Player
def initialize(hp, attack, defence, gold)
#hp = hp
#attack = attack
#defence = defence
#gold = gold
end
def inventory
#inventory ||= []
end
# ...
end
which will assign [] to #inventory only if it was nil or false (i.e. the first time you call inventory).
A getter that just returns the corresponding instance variable (as in the former example) can also be created via attr_reader:
class Player
attr_reader :inventory
# ...
end
I figured it out 10 seconds after posting this, after messing about with it for an hour.
I needed to add inventory to def initialize, and then pass an empty array to player = Player.new(10,1,2,6) so it became player = Player.new(10,1,2,6,[]).
I still don't know why this works.
I started yesterday with Ruby and have a problem.
I have two classes: Patient and Patient_history.
Patient can have multiple histories (from different appointments).
Here is what i have now:
class Patient
attr_accessor :name,
:surname,
:histories
def initialize(*)
#histories = []
end
def create_patient
#name = create_name_or_surname( "name")
#surname = create_name_or_surname("surname")
end
def create_name_or_surname( name_or_surname)
#not relevant in this case
end
def add_history(history)
#histories.push(history)
end
def print_patient
puts "name: #{#name}"
puts "surname : #{#surname}"
## I wish to do sth like:
## puts "history date: #{#histories[1].date}"
## to print content of Patient_history
end
end
And
class Patient_history
attr_accessor :date,
:description
def create_history
#date = create_date_or_desc("What is the date today?")
#description = create_date_or_desc("Write about an illness:")
end
end
with a line:
p patient
after setting history and patient values i get:
What is your name?
john
What is your surname?
smith
What is the date today?
12/12/2016
Write about an illness:
sick
#<Patient:0x007ffcb50ab518 #histories=[#
<Patient_history:0x007ffcb50aad98 #date="12/12/2016",
#description="sick">], #name="john", #surname="smith">
Can you give me a hint of what to do?
Question is a little vague, do you wish to print out the list of patient histories? You can iterate over each history using #each.
def print_patient
puts "name: #{#name}"
puts "surname : #{#surname}"
histories.each do |history|
puts "History Date: #{history.date}"
puts history.description
end
end
I'm trying to set up a program to help me take care of grading for students in a class. I've set it up to make a class of student then to read in from the file (something I'm not very familiar with in Ruby) via an array. My programming experience is in java so if there are errors that can be explained by that I apologize. Thank you in advance for your help.
class Student
def initialize(str_LastName, str_FirstName, arr_Score)
#str_LastName = str_LastName
#str_FirstName = str_FirstName
#arr_Score = arr_Score
str_Grade = ""
int_OutOf = 415
end
def get_LastName
str_LastName
end
def get_FirstName
str_FirstName
end
def get_Grade
str_Grade
end
def set_TotalScore()
sum = 0
arr_Score.each do |item|
sum += item
end
arr_Score[12] = sum
end
def set_Grade
if arr_Score[12]/int_OutOf >= 0.9
str_Grade = "A"
elsif arr_Score[12]/int_OutOf >= 0.8
str_Grade = "B"
elsif arr_Score[12]/int_OutOf >= 0.7
str_Grade = "C"
elsif arr_Score[12]/int_OutOf >= 0.6
str_Grade = "D"
else
str_Grade = "F"
end
end
end
def main
file_name = "Grades"
arr_students = Array.new(31)
arr_scores = Array.new(12)
int_i = 0
file_io = open(file_name).readlines.each do |line|
array = line.split(",").map(&:strip)
student = Student.new(array[0],array[1],array[2..-2]) #the final element in the array is for the final score
arr_students[int_i] = student
puts "read #{arr_students[int_i]}"
end
file_name = "Graded"
file_io = open(file_name,"a+")
arr_students.each do |student|
set_TotalScore
set_Grade
file.io_write(student)
puts "write #{student}"
end
end
main if __FILE__==$0
Here is my run at it. I tried to stay true in general to the original intent of your code while introducing more Rubyish ways of doing things.
class Student
def initialize(firstname, lastname, *scores)
#firstname, #lastname, #scores = firstname, lastname, scores
end
def total_score
#scores.map(&:to_i).inject(:+)
end
def grade
raise "TOO HIGH!" if total_score > MAX_SCORE
case total_score / MAX_SCORE
when 0.9..1.0; "A"
when 0.8...0.9; "B"
when 0.7...0.8; "C"
when 0.6...0.7; "D"
else "F"
end
end
def to_s
"#{#lastname}, #{#firstname}: #{total_score}, #{grade}"
end
end
MAX_SCORE = 415.0
DATA.each_line do |line|
arr = line.split(",").map(&:strip)
student = Student.new *arr
puts student
end
__END__
Herb,Goldberg,22,99,44,22,88,88
Mark,Sullivan,77,88,88,44,33
You can read and write to files like this(not tested):
outfile = File.open("Graded", "a+")
File.open("Grades").each_line do |line|
...
outfile.puts student
end
outfile.close
We can not easily reproduce your code because you open a file called "Grades" and we do not have or know of its content.
You should also add some code to first check whether your file exists, before continuing - right now your script exits with a Errno::ENOENT.
I would also suggest putting the logic in main into your class instead - let your class handle everything.
In the part:
if __FILE__ == $PROGRAM_NAME
end
You can then simply initialize your class with a simple call such as:
Foobar.new(ARGV)
You described the "Grades" file but I did not understand what you wrote - it would be easier if you could link in to a sample, like via a pastie or gist, then link it in; and to also say what the part is that is not working, which is also unclear.
The style issues are secondary, I consider your code ok - the other poster here does not.
You should go through codecademy to get your ruby syntax down.
To access your initialized instance variables (#str_LastName (which should be #last_name), etc) you need to use "attr_reader :str_LastName", preferably at the top of the class. That'll definite you getter (setter is attr_writer, both is attr_accessor).
You can also do a sum on an array like this: [1,4,6,7].inject(:+).
Does Java not allow case statements? You should use that in set_grade. You also don't need to initialize str_Grade. In set grade, you could do #grade_letter ||= "A", and then calling set_grade will return that value on each call.
I didn't look through your main method. It's ugly though. Ruby methods probably shouldn't be more than 5 lines long.