math with instance variables - ruby

I have this class:
class Account
attr_accessor :balance
def initialize(balance)
#balance = balance
end
def credit(amount)
#balance += amount
end
def debit(amount)
#balance -= amount
end
end
Then, for example, later in the program:
bank_account = Account.new(200)
bank_account.debit(100)
If I call the debit method with the "-=" operator in it (as shown in the class above) the program fails with the following message:
bank2.rb:14:in `debit': undefined method `-' for "200":String (NoMethodError)
from bank2.rb:52:in `<main>'
But if I remove the minus sign and just make it #balance = amount, then it works. Obviously I want it to subtract, but I can't figure out why it doesn't work. Can math not be done with instance variables?

Your value passed into initialize() is a string, rather than an integer. Cast it to an int via .to_i.
def initialize(balance)
# Cast the parameter to an integer, no matter what it receives
# and the other operators will be available to it later
#balance = balance.to_i
end
Likewise, if the parameter passed to debit() and credit() is a string, cast it to an int.
def credit(amount)
#balance += amount.to_i
end
def debit(amount)
#balance -= amount.to_i
end
Finally, I'll add that if you plan to set #balance outside the initialize() method, it is recommended to define its setter to call .to_i implicitly.
def balance=(balance)
#balance = balance.to_i
end
Note: This assumes you want and only intend to use integer values. Use .to_f if you need floating point values.

Most likely, you did
bank_account = Account.new("200")
You should actually do
bank_account = Account.new(200)

try with
def credit(amount)
#balance += amount.to_i
end
def debit(amount)
#balance -= amount.to_i
end
or pass a number as the parameter (the error says that you are passing a string)

Related

Cannot get a method instance to function; How to use an Each Do method in ruby?

I have an specific method that I would like to call so that I can see the balances for 'accounts'. The method is;
def report_balances(accounts)
accounts.each do |account|
puts account.balance
end
end
I am not sure but I have either built the above method incorrectly or I am calling it incorrectly or maybe I have placed the method correctly in my code.
class BankAccount
attr_reader :balance
def initialize(balance)
#balance = balance
end
def deposit(amount)
#balance += amount if amount >= 0
end
def withdraw(amount)
#balance -= amount if #balance >= amount
end
end
class SavingsAccount < BankAccount
attr_reader :number_of_withdrawals
APY = 0.0017
def initialize(balance)
super(balance) # calls the parent method
#number_of_withdrawals = 0 # then continues here
end
def end_of_month_closeout
if #balance > 0
interest_gained = (#balance * APY) / 12
#balance += interest_gained
end
#number_of_withdrawals = 0
end
def report_balances(accounts)
accounts.each do |account|
puts account.balance
end
end
end
I would like to see the balances of the objects:
my_account = SavingsAccount.new(100)
and
account = BankAccount.new(2500)
by calling
'report_balances(accounts)'
How would this be accomplished?
Think of my_account = SavingsAccount.new(100) as creating a new account, but what you're asking is I want to see all the balances of a list of accounts. Since each account has a balance, you can do:
[my_account, other_account].each do |account|
puts account.balance
end
I'd recommend moving your report_balances method to a class method or out of that class all together but that's a topic for a different discussion.

Passing argument in instance variable in Ruby gets error

class Account
attr_reader :name
attr_reader :balance
def initialize(name, balance=100)
#name = name
#balance = balance
end
public
def display_balance(pin_number)
if pin_number == pin
puts "Balance: $#{#balance}."
else
puts pin_error
end
end
def withdraw(pin_number,amount)
if pin_number == #pin
#balance -= amount
puts "Withdrew #{amount}."
else
puts pin_error
end
end
def deposit(pin_number,amount)
if pin_number ==#pin
#balance+=amount
puts"Deposited"
else
puts pin_error
end
end
private
def pin
#pin = 1
end
def pin_error
return "Access denied: incorrect PIN."
end
end
checking_account=Account.new("bob",200)
checking_account.deposit(1,20)
When i try deposit i get an error on pin, but when i remove the # in the pin checks and treat it as a normal variable it works. In codeacademy it shows that the correct way to check the pin is with
if pin_number==#pin
yet it doesn't work, even though it should, why is that?
pin_number==#pin will return false, because you never set #pin - so it will still be nil.
Instead of using a private method, you could do something like this:
def initialize(name, pin, balance=100)
#name = name
#pin = pin
#balance = balance
end
# ...
checking_account=Account.new("bob", 1, 200)
checking_account.deposit(1, 20)
yet it doesn't work, even though it should, why is that?
Because your #pin is never initialized. Something must be different between your code and what they have at codecademy (they likely initialize #pin in the initializer, which you do not).
but when i remove the # in the pin checks and treat it as a normal variable
Wrong.. This is not a variable. When you make it pin instead of #pin, it starts pointing to that private method of yours. Which returns a pin number. That's why it works.
Note that your code will work as-is, if you simply display_balance before making a deposit. This is called "call order dependency" and it's bad.

How do I make my class attribute not be able to access instance variable directly?

How can I make my code work so that the balance attribute doesn't access the instance variable(I believe it's #balance) directly? Can someone explain what it means for an attribute to access an instance variable?
I'm new to using Ruby and just got into learning about Ruby classes. In this chapter, my objectives is to understand the concept of instance variables, demonstrate the us of getter and setter methods, understand how to use instance methods, and understand the concept of encapsulation.
class BankAccount
attr_accessor :balance
def initialize(balance)
#balance = balance
end
def withdraw(amount)
if (balance >= amount)
#balance = balance - amount
end
end
end
In Ruby attr_reader :balance is more or less just a convenience version of the following method:
def balance
#balance
end
Similarly, attr_writer :balance is just a short form for
def balance=(value)
#balance = value
end
And attr_accessor :balance is short for attr_reader :balance plus attr_writer :balance.
So as you can see attr_reader accessing the instance variable is nothing special, e.g. in your code you also access the instance variable in #initalize and #withdraw.
You need to clarify why you wouldn't want to access it directly. And what that even means. Because you can either access the instance variable using #balance or not, there is no indirect in my opinion.
Define your own version of balance reader method:
class BankAccount
attr_accessor :balance
def initialize(balance)
#balance = balance
end
def withdraw(amount)
if (balance >= amount)
#balance = balance - amount
end
end
def balance
'balance from method directly'
end
end
Now when you call balance method, it'd read your defined one and return value accordingly.

Ruby Check Class Owner From Other Inheritance With Default Library

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

LearnStreet Ruby for Beginners Lesson 9.12

Hi i'm stuck on defining instance methods. The tutorial asks:
Define a method called balance in the class BankAccount which returns the balance.
The code has:
class BankAccount
def initialize(balance)
#balance = balance
end
# your code here
end
I'm really confused as to what the question is asking. Any help would be appreciated...
The tutorial is asking you to define a "getter" for the BankAccount class:
class BankAccount
def initialize(balance)
#balance = balance
end
def balance
#balance # remember that the result of last sentence gets returned in ruby
end
end
Then, you can do
bankAccount = BankAccount.new 22 # the value 22 is the balance that gets passed to initialize
bankAccount.balance # 22
Now, if what the tutorial is asking is a class method (to me it's not really clear), you should do:
def self.balance # self is here so you define the method in the class
#balance
end
Then you can do BankAccount.balance
Ok, let's take the example code:
class BankAccount
def initialize(balance)
#balance = balance
end
# your code here
end
in here you are defining a BankAccount class which define the BankAccount#initialize method (also called constructor) that will be automatically called on the creation of a BankAccount object via BankAccount::new:
BankAccount.new( 123 )
In the above example #balance will be set to 123. #balance is an instance variable (notice the # before the name) which means that you can access it per-object within any method you define.
To return that variable, as the exercise ask, you can use the keyword return within the BankAccount#balance method as follows:
def balance
return #balance
end
The Ruby syntax also allows you to omit return (as it is intended to always return the last evaluated expression from a method) leading to a more concise syntax:
def balance
#balance
end
For this kind of getter-methods (= methods that return an instance variable) there is an easily utility: attr_reader that you can use as follows:
class BankAccount
attr_reader :balance
def initialize(balance)
#balance = balance
end
end
But don't worry, you'll probably learn about the above very soon.
Happy learning.
class BankAccount
attr_reader :balance
def initialize(balance)
#balance = balance
end
end
add attr_reader :balance

Resources