Building An Atm Simulator In Ruby
It happens to be a powerful and expressive language by any standard. This blog posts details my very first Ruby project. Just like the title says, it would be a replica of the ATM (Automated Teller Machine) somewhere down your street.
- There are users.
- Users have accounts - one account per head.
- Accounts have balances - total available balance and minimum balance.
- Accounts have passwords.
- There must be a commandline Interface.
From the above, we sure know we’d need some classes. Here are the ones we would be creating :
Prompter-> Ask the user some questions via the cli.
Customer-> A user - this includes his account information.1
Atm-> This is the main container for our app.
Security must be built into any app. Ours isn’t an exception. We want to make sure there are only authorized usage of our application. No trolls. Else we end up in some serious legal suit. Authentication is done by providing a valid debit card and a matching password - for the card. Hence to save passwords, we’d be making use of a database - of a sort. A (flat) file would serve as our database. This is for several reasons :
- Ruby is a good language for text processing, this is an opportunity to demonstrate that strength.
- We are not enterprise level
Our db file would contain some data in a specialized format our app understands. Each line would represent a user which would ultimately be converted into a
Here is what a sample line looks like :
5011; 0000-1234-5678-5011; The Real Clown; ?2313! ; 60_000; 3_000. They are in the following order when delimited by
- The last 4 digits of a user’s debit card.
- The 16 digits of a user’s debit card.
- The user’s full name.
- The total amount (s)he has in the account.
- The minimum amount that must be left in the account - Bank policy.
Since we are working on a
cli app, we need to ask for certain inputs from the user which we then respond to. Here is time to put our
Prompter to work.
gets is built in function that reads input from the keyboard. We show the user a question, then we wait till we get a reply. This is more like What would you like to do question we get on a real life machine.
Next up is the app container itself
In Ruby, the constructor method is
__construct like in PHP or the class name as in Java. Basic OO - We inject the prompter into the class, this is so as we can switch to something more sophisticated when we reach enterprise level.
get_all_customer_details is simply fetching everything from the database and dumping them into an array in our app. This is so as we can get easily get all the data for the users without constantly digging into the file. The line that says
/\w+/ is a regular expression that tells us to only get lines that contain words from the file (our database). Remember this method was called in the constructor.
start method tells our atm to do some real work. First we ask for the last four digits om his/her card. This is always unique so it helps us simulate a card insertion (into the slot). Then we
throw an exception if the user provides some digit more or less than 4 in it’s length -
raise is Ruby’s equivalent to PHP or Java’s
There is something interesting in the
get_customer_details_by_last_four_digits method. This method shows us many nice stuffs about the ruby language :
- Everything is an object.
- Reads a lot like regular english
This is quite large but quite self explanatory. We check if the user provided the right password. If yes, save the current user to the instance variable
current_customer - which is actually more a sort of session stuff.
bootstrap_atm_commands simply print some information to the screen while waiting for the user to enter some response, so as to perform the requested action. Our atm understands some basic commands - 0 for account balance, 1 to withdraw some cash (this command in turn prompts the user to specify how much he’d like to withdraw), 2 is for logging out.
The most interesting parts here are the extra methods we called on the customer instance -
@current_customer.withdraw!. The method
can_withdraw would return a
boolean which is actually why it has a ? in it’s method definition - a standard ruby practice by the way WHILE the
withdraw! method would actually reduce the balance of the customer.
Great!!! But what does the
Customer object look like ? Nothing complex if you ask.
For the curious minded, here are the exceptions definition
With this, our application is complete and works. Tests should be written obviously especially the
Customer object since we have more fine grained control over it - unlike others where there is a lot of prompting and reading stuffs from the keyboard.
To test out the atm, a dummy file - say
app.rb - should be created with the following content :
Make sure you make the file executable, then run
./app.rb. Or just
 Our app has one account per head. This is by design. In the real world, users can have multiple account.
Atm class obviously goes against SRP (Single Responsibility Principle). It does password validation via the
is_password_valid method. It loads all users from the database file (
get_all_customer_details method). This two operations can be refactored into their own specific classes - say a
FileReader (or something).