Ok, I think (not 100% sure) I have figured this out after spending hours playing around with this code.
What I have noticed in the original code I provided is that when an instance of the ‘Account’ class is created, only 2 values persist when the object initializes: ‘@name’ and ‘@balance’. So, there is no way to read the value of ‘@pin’ inside the private method after the object initializes.
So, I have come up with a few solutions that work.
Option 1:
Initialize with the private method ‘pin’ containing our ‘@pin’ instance variable:
# Initialize the object
def initialize(name, balance=100)
@name = name
@balance = balance
pin # Call private method 'pin' on initialization – reads the value of '@pin'
end
The private method ‘pin’ can stay as it is because we can read the value of ‘@pin’ during initialization. The value will persist on all instances created with this class – not very useful and we loose flexibility!
# Private method (PIN number)
private
def pin
@pin = 1234
end
Create a new instance of the class like this:
checking_account = Account.new("Bruce", 100_000)
Option 2:
Initialize with all the necessary arguments and instance variables
# Initialize the object with all required arguments
def initialize(name, pin, balance=100)
@name = name
@balance = balance
@pin = pin
end
Now the private method ‘pin’ needs adjusting and we have to remove the hard-coded value of ‘@pin’. We will now pass it’s value via the ‘pin’ parameter when we create a new instances of the class.
# Private method (PIN number)
private
def pin
# @pin = 1234
@pin # This value will now be assigned via the parameter
end
Now we can create a new instance of the class like this with the pin ‘1234’:
checking_account = Account.new("Bruce", 1234, 100_000)
I have found that with both techniques, the value of ‘@pin’ is protected and can’t be accessed or changed once an instance of the Class is created. This is the behavior we want from what I can understand.
Well, this is my understanding of it at this stage. Whether it’s correct, is another matter.

I have included all my code should anyone like to review it:
Uncomment lines one-at-a-time to see output
# Super class
class Account
# Readable attributes
attr_reader :name, :balance
# Boot the object and set values
def initialize(name, pin, balance=100)
# Instance variables: Only accessible from within instance of the class
@name = name
@balance = balance
@pin = pin
end
# Private method (PIN number)
private
def pin
@pin
end
# Private method (PIN error)
private
def pin_error
return "Access denied: incorrect PIN."
end
# Public method (Display balance)
public
def display_balance(pin)
# Check PIN number
if pin == @pin
puts "Balance: $#{@balance}."
else
puts pin_error
end
end
# Public method (Make a withdrawal)
public
def withdraw(pin, amount)
# Check PIN number
if pin == @pin
@balance -= amount
puts "Withdrew #{amount}. New balance: $#{@balance}."
else
puts pin_error
end
end
end
# Open an account (instance of the Account Class)
#checking_account = Account.new # => wrong number of arguments (0 for 2..3) (ArgumentError)
#checking_account = Account.new("Bruce") # => wrong number of arguments (1 for 2..3) (ArgumentError)
#checking_account = Account.new("Bruce", 1234) # => Works (with default balance)
checking_account = Account.new("Bruce", 1234, 1_000_000) # => Works (with custom balance)
# Test: Display Balance
#puts checking_account.display_balance # => wrong number of arguments (0 for 1) (ArgumentError)
#puts checking_account.display_balance(12345) # => Access denied: incorrect PIN.
puts checking_account.display_balance(1234) # => Works
# Test: Withdraw
#puts checking_account.withdraw # => wrong number of arguments (0 for 2) (ArgumentError)
#puts checking_account.withdraw(500) # => wrong number of arguments (1 for 2) (ArgumentError)
#puts checking_account.withdraw(12345, 500) # => Access denied: incorrect PIN.
#puts checking_account.withdraw(1234, 500) # => Works
# Test: Try and access private methods
#puts checking_account.pin # => private method `pin' called for #<Account> (NoMethodError)
#puts checking_account.pin_error # => private method `pin_error' called for #<Account> (NoMethodError)
# Test: Try and access @instance_variables
#puts pin # => undefined local variable or method `pin' for main:Object (NameError)
#puts balance # => undefined local variable or method `balance' for main:Object (NameError)