Creating The Bank (Ruby on Rails)

As always, it pays to quickly scan the original PHP version of this tutorial entry. That entry is available here: Creating The Bank (PHP)

In order to add a bank to the game we know we need a new page to handle the user interaction. So, does that go into an existing controller? At present I think the answer is no, we’ll generate a new controller just for the bank (like we did for the forest):

> ruby script/generate controller Bank index

In addition to something to handle the UI, we also need someplace to keep track of the money in the bank so we’ll add a new field for that to the user:

> ruby script/generate migration AddBankgcToUser bankgc:integer

The migration we just generated will handle adding the new field to all existing users and any new ones that join, but we need to edit it to add a default value of zero for the field:

add_column :users, :bankgc, :integer, :default => 0

Be sure to run the migration to change the database table:

> rake db:migrate

Now we need some functionality to let us deposit money into the “bank” and withdraw it again later. To do that we’ll add two new functions into our User model (app/models/user.rb):

def deposit(amount)
  if (amount < 0 or amount > self.gold)
    amount = self.gold
  end
 
  self.bankgc += amount
  self.gold -= amount
  self.save
 
  amount
end
 
def withdraw(amount)
  if (amount < 0 or amount > self.bankgc)
    amount = self.bankgc
  end
 
  self.bankgc -= amount
  self.gold += amount
  self.save
 
  amount
end

One of the only things new and worth noting here is that the revised amount is referenced on the last line of each function. In Ruby that makes it a return value so we can see exactly how much really was moved when the user specifies a ridiculous value (i.e. attempting to withdraw 20000 with only 5 in the bank). We’ll use that in the controller code later.

Next we need to fill in the view. It will both inform the user of how much money he/she has in the bank and in hand and take input via a form and buttons to indicate which way the transfer should occur. Here’s the code for the view (app/views/bank/index.html.erb):

<p>Welcome to the bank. You currently have 
<strong><%= @current_user.bankgc %></strong> gold in the bank, 
and <strong><%= @current_user.gold %></strong> gold in hand.</p>
 
<% form_tag('/bank/do_some_banking', :method => :post) do %>
  <%= text_field_tag 'amount' %><br/>
  <%= submit_tag 'Deposit' %><%= submit_tag 'Withdraw' %>
<% end %>
<p><%= link_to 'Home', :controller => 'welcome' %></p>
 
<script type="text/javascript">
  window.onload = function() {
    document.getElementById('amount').focus();
  }
</script>

One thing slightly different about this form is that we have one form with multiple different submit buttons. We can differentiate between them when the controller gets the submitted results of the form so we know what to do. Another thing to note about this form is that it isn’t a form_for, but instead a form_tag. form_for makes it easy to have a set of fields and populate them from a model object, then gather up the results and put them into a hash which can be assigned directly back to the same type of model object to create a new one or edit the values in it. But we don’t have a model for this page, we just one one value (a number in this case) returned to the controller so form_tag is what makes sense.

Lastly we will pull all these pieces together in the controller. We’ll use the hash of parameters created from the user filling in the form and figure out which of the new methods on the user model to call. As before, the model is doing the real work, the view is taking the user’s input, and the controller is gluing the two together. Here’s the code for the Bank controller we generated earlier (app/controllers/bank_controller.rb):

class BankController < ApplicationController
  def index
    current_user
  end
 
  def do_some_banking
    if params[:commit] == "Deposit"
      amount = current_user.deposit(params[:amount].to_i)
 
      flash[:notice] = "You deposited #{amount} gold into your bank account. Your total in the bank is now #{@current_user.bankgc}."
    else
      amount = current_user.withdraw(params[:amount].to_i)
 
      flash[:notice] = "You withdraw #{amount} gold from your bank account. Your total gold in hand is now #{@current_user.gold}."
    end
 
    render :action => "index"
  end
end

The code consists of getting the current user and then performing actions on that user. The code we added to the model gives us the new features we need and the view got the values into the hash so we can find them. The controller is only responsible for getting the user’s input out of the hash and passing it on to the correct functions in the model. It also acts as a translator to take responses from the model and turn them into messages which can be displayed by the view. As I mentioned before, we can look in the hash coming from the view and the value of the params[:commit] tells the code whether the user clicked on the “Deposit” or “Withdraw” button.

Our final touch is adding the bank as a destination to the welcome page (for a logged in user). Add this below the corresponding link to the forest in app/views/welcome/index.html.erb:

<%= link_to "The Bank", :controller => "bank" %>

Extra Credit

  1. “rake –tasks” tells you about all the commands rake supports. We’ve used “rake db:migrate” many times but there are actually several rake functions to manipulate the database. In particular I’ll point out “rake db:migrate:redo”. I used it while working on this particular entry because the first time I built it I forgot to set a default value for bankgc in the user, so I went back and added the default value and used the “rake db:migrate:redo” to undo the last migration (it calls the self.down in the migration) and then run the migration again to correct the error. Then I had the new field on each user and it had a default value of zero for all of them.
  2. form_for and form_tag are both helper functions. Helper functions are Rails methods designed to help you generate HTML with less pain, and in the case of form_for, generate HTML that follows certain patterns (e.g. the naming of fields in the form) so that it will work together with code to make some activity simpler.

Wish there was more?

I'm considering writing an ebook - click here.

.

John Munsch is a professional software developer with over 20 years experience. He created a series of game development sites (XPlus and DevGames.com) on his own before co-founding GameDev.net in 1999. The blog for his PBBG work is located at MadGamesLab.com.

Tags: ,

Thursday, October 2nd, 2008 buildingbrowsergames, code, medieval
  • Mattias

    I get a Routing Error:
    No route matches "/bank" with {:method=>:get}

    I have quadruple checked letter by letter, and comparing differences with the other controllers. I can't find the problem :O
    Please help.

  • JohnMunsch

    That doesn't sound like an error in your code, more like an error in the file naming. Did you name it "app/controllers/bank_controller.rb"? If not then it isn't able to find the controller with that name. Also it is expecting to find the views in "app/views/bank".

    It might help to download the current version of the code from here: http://code.google.com/p/bu...

    That has everything I've done in it so far, including the bank. You could compare yours against that and perhaps find the problem. Failing that, please contact me at john.munsch@gmail.com.

  • Ruby on Rails is sweet but have you thought about using Simple Scripts? This is also good if not better.

  • JohnMunsch

    current_user is a function that was provided to us by the restful_authentication system. Whenever we call it, it uses the ID of the logged in user (if any) that is stored in the session to look up a user record and assign it to the variable @current_user.

    If you follow the instructions in The Login Page (Ruby on Rails) Part 2 (http://buildingbrowsergames... you will be able to call this function from any of your controllers to pull the latest version of the user into a variable accessible from the controller and from the view that the controller will forward to.

    If my explanation didn't help or you have any more questions, please ask.

  • Janus

    Can't figure out this current_user

blog comments powered by Disqus

About

Building Browsergames is a blog about browsergames(also known as PBBG's). It's geared towards the beginner to intermediate developer who has an interest in building their own browsergame.

Sponsors

Got Something to Say?

Send an e-mail to luke@buildingbrowsergames.com, or get in touch through Twitter at http://twitter.com/bbrowsergames