buildingbrowsergames

A Simple Combat System (Ruby on Rails)

Introduction

This entry is based on a Building Browsergames blog entry: A Simple Combat System (PHP)

It pays to at least skim over the original entry before going over the Ruby on Rails version below.

Monsters and Hitpoints and Forest, Oh My!

The PHP version of this entry starts off by creating a way to store monsters within the game and adding some new stats (maxhp and currenthp). First off, we’ll use the generator to create the new model we want:

> ruby script/generate model Monster name:string attack:integer defense:integer
magic:integer gold:integer max_hp:integer cur_hp:integer

All by itself the generator did a fine job with the creation of the migration for Monster. We provided enough information about the fields to put in each one when we called the generator from the command line. But the migration to add the new stats to the user and add example monsters isn’t just adding a table with some fields, it’s performing a whole set of operations so we’ll edit the migration file to add some more to it:

class CreateMonsters < ActiveRecord::Migration
  def self.up
    create_table :monsters do |t|
      t.string :name
      t.integer :attack
      t.integer :defense
      t.integer :magic
      t.integer :gold
      t.integer :max_hp
      t.integer :cur_hp
 
      t.timestamps
    end
 
    # Add the maximum hitpoints and current hitpoints stats to the user.
    # Note: Setting the defaults here will set default values for any existing
    # users already in the database as well as for any new ones created. So we
    # won't have to update the before_create method in the User model to set 
    # defaults for these values.
    add_column :users, :max_hp, :integer, :default => 10
    add_column :users, :cur_hp, :integer, :default => 10
 
    # Create some example monsters.
    Monster.create(:name => 'Crazy Eric', :attack => 2, :defense => 2, 
      :max_hp => 8, :cur_hp => 8, :gold => 5)
    Monster.create(:name => 'Lazy Russell', :attack => 1, :defense => 0, 
      :max_hp => 4, :cur_hp => 4, :gold => 20)
    Monster.create(:name => 'Hard Hitting Louis', :attack => 4, :defense => 3, 
      :max_hp => 10, :cur_hp => 10, :gold => 5)
  end
 
  def self.down
    remove_column :users, :cur_hp
    remove_column :users, :max_hp
 
    drop_table :monsters
  end
end

This is one of our best examples of migration so far because you can see new tables being created, existing ones being revised, and additional code that exists just to give us a starting point in our application. Next we’ll add the two new stats to the welcome page so they are visible when a user is logged in.

<h1>Welcome To The Game</h1>
 
<% if logged_in? %>
  <h2><%= @current_user.login %></h2>
 
  <ul>
    <li>Attack: <strong><%= @current_user.attack %></strong></li>
    <li>Defense: <strong><%= @current_user.defense %></strong></li>
    <li>Magic: <strong><%= @current_user.magic %></strong></li>
    <li>Gold in hand: <strong><%= @current_user.gold %></strong></li>
    <li>Current HP: <strong><%= @current_user.cur_hp %>/<%= @current_user.max_hp %></strong></li>
  </ul>
 
  <%= link_to "The Forest", :controller => "forest" %>
<% end %>

Even though I haven’t created the controller to handle the forest yet, I’ve gone ahead and added a link to it above.

The Forest

> ruby script/generate controller Forest index

Now it’s time to fill in the code for the forest so we can actually encounter something there. First we’ll take a crack at the view in app/views/forest/index.html.erb:

<h1>The Forest</h1>
 
<p>You've encountered a <%= @monster.name %>!</p>
 
<%= link_to "Attack", :controller => "forest", :action => "attack" %>
<%= link_to "Run Away", :controller => "forest", :action => "run_away" %>

One thing you might note in the upcoming code is that I’ve added a “before_filter” to the code. That’s a callback that Rails provides you in your controller, much like ActiveRecord had callbacks for certain points within the lifetime of a record, a controller has callbacks that can get called before and after each action. That makes it easy to have something called before each and every action within this controller. In this case we just tell it to run the login_required function which restful_authentication provided. It will check to see if the user is logged in before any action on the page and if not, redirect to the root page for the application.

class ForestController < ApplicationController
  # We don't want anyone other than logged in users going to the forest.
  before_filter :login_required
 
  def index
    current_user
 
    @monster = Monster.random_encounter
  end
end

In addition we have to add the random_encounter method to the Monster model so we can call it to get a random monster. Note again that I’ve put game logic down in the model and kept the controller clean of all database interaction and logic. It’s just glue between the views which display things/take input and the models which handle all the data access and game logic.

def self.random_encounter
  # For a single random record I'm using the technique from here:
  # http://robzon.aenima.pl/2007/12/selecting-random-row-from-table.html
 
  random_monster = self.find(:first, :offset => (self.count * rand).to_i)
 
  # We'll set the number of hitpoints for the monster to the max before unleashing
  # the freshly retrieved creature out on the world. Note, this is just a copy
  # of the database record. There could be dozens of these in the system all at
  # the same time. As long as none of them is saved back to the database each one
  # can be changed independently (e.g. taking damage), because you're only changing
  # the in-memory copy.
  random_monster.cur_hp = random_monster.max_hp
 
  random_monster
end

At this point we’ve added some new stuff to our app and we can try it out. Don’t forget, if you haven’t already run the migration we created earlier to add the monsters table to the database then we need to run it now before we hit the pages that require the new data and fields:

> rake db:migrate

Here’s what our welcome page looks like now when the user is logged in:

And here’s the new forest page we see after hitting the link to go there:

One thing worth trying if you want to see the restful_authentication system in action is logging out of the game and then trying to go directly to the http://localhost:3000/forest page. When you do you’ll immediately find yourself redirected to the home page for the application thanks to the before_filter we added earlier.

Since the links for user actions (i.e. attack, run away) aren’t hooked up yet, clicking on them just gives you a Rails error. We’ll fix that by expanding the Forest controller to handle the user’s actions and by adding the ability to conduct combat to the User model:

class ForestController < ApplicationController
  # We don't want anyone other than logged in users going to the forest or executing
  # actions inside the forest.
  before_filter :login_required
 
  def index
    current_user
 
    # Get a random monster for us to encounter in the forest.
    @monster = Monster.random_encounter
 
    # Save the monster in our session.
    session[:monster_id] = @monster.id
  end
 
  def attack
    current_user
 
    # Pull the monster we were fighting from the session.
    @monster = Monster.find(session[:monster_id])
 
    # Fight the monster and get the transcript of the fight.
    @combat = @current_user.fight(@monster)
  end
 
  # Running away couldn't be much easier, just send the user back to the welcome page.
  def run_away
    session[:monster_id] = nil
 
    redirect_to :controller => "welcome"
  end
end

You might notice that in addition to adding the attack and run_away actions, I also changed the index action. It still asks for a random monster for the encounter in the forest, but now it saves that monster in the session. That way we aren’t sending the monster ID to the user with the page (the PHP code sends it as a hidden field) and then using it after we get it back. Remember, you can never send something to the user and implicitly trust what you get back. It could have been modified (e.g. the user might have swapped out the monster you picked for an easier or harder one). By storing the monster in the session we avoid that security hole. But remember how long your session lasts, if the session times out, is it OK to lose the information in it or is it something that needs to be saved to the database instead?

We wrote the fight function into the controller without really knowing how it works, which is a good thing because details of things like combat shouldn’t be up at the level of a controller. Instead it belongs down in the model layer. Specifically, I’ve put it into the User model:

def alive?
  self.cur_hp > 0
end
 
def fight(monster)
  combat = Array.new
 
  turn_number = 0
 
  # This is a small workaround for the fact that we aren't getting the user's name yet
  # when they signup. All we have for the user is the login.
  if (self.name.empty?)
    self.name = self.login
  end
 
  while (self.alive? and monster.alive?)
    turn_number += 1
 
    # Switch the roles of attacker and defender back and forth.
    if (turn_number % 2 != 0)
      attacker = monster
      defender = self
    else
      attacker = self
      defender = monster
    end
 
    if (attacker.attack > defender.defense)
      damage = attacker.attack - defender.defense
 
      # We allow damage to take you below zero hitpoints. Presumably the funeral is
      # closed casket in those cases.
      defender.cur_hp -= damage
 
      # We'll only make a turn record for those cases where something actually happens.
      turn = Hash.new
      turn["attacker"] = attacker.name
      turn["defender"] = defender.name
      turn["damage"] = damage
 
      # Save this turn in the combat transcript.
      combat << turn
    end
  end
 
  # Somebody is dead, let's see who and take appropriate action.
  if (self.alive?)
    # Yay, you lived. You get gold.
    self.gold += monster.gold
  else
    # There aren't any penalties for losing at the moment.
  end
 
  # We only save the user's record back to the database. The monster needs to
  # stay unchanged in the database no matter how much damage we did to it so
  # the next one we make will still be pristine.
  self.save
 
  # Return the results of combat.
  combat
end

We’re done with the User model, but we need to tweak the Monster model one more time to add a function to determine if the monster is alive too:

def alive?
  self.cur_hp > 0
end

Finally we have to have a page that displays the results of combat. Rather than wedging it into the index page for the forest, I felt it was easier to just have another page for it (app/views/forest/attack.html.erb):

<h2>Combat Results</h2>
 
<p>
<% @combat.each do |t| %>
  <%= "#{t['attacker']} hit #{t['defender']} for #{t['damage']} damage." %><br/>
<% end %>
</p>
 
<% if @current_user.alive? %>
<p>You killed <strong><%= @monster.name %></strong>! You gained <strong><%= @monster.gold %></strong> gold.</p>
 
<%= link_to "Explore Again", :controller => "forest" %>
<%= link_to "Home", :controller => "welcome" %>
<% else %>
<p>You were killed by <strong><%= @monster.name %></strong>.</p>
<% end %>

Now we can go ahead and click the “Attack” and “Run Away” links on the forest page to see what happens. Here’s an example of clicking the Attack link:

Extra Credit

  1. Download it. If you’ve only been following along so far by just reading the text about how to build all the pieces, it’s time for you to go get the code from the Subversion repository on Google and download it so you can see what I’ve done. The thing I think you should notice in particular is just how few files we are talking about and how little is in each file. Here’s the contents of the app directory (where almost all of you code goes):

    See the recent post Now On Google Code! for instructions on downloading the code.

  2. I talked about “sessions” in the code above but I didn’t really go into what they are. For a little more info about sessions, read something like Understanding Sessions or for a more in depth look at what sessions are in general Session @ Wikipedia.

Tuesday, September 16th, 2008 buildingbrowsergames, code, rubyonrails No Comments

Building Browsergames: Integrating weapons into our combat system (Perl)

Even though we’ve built a combat system and a weapons system, we haven’t done anything yet to make them work together. Today, we’re going to be modifying our combat system so that it takes into account the ‘attack’ attribute of the user’s current primary weapon.

This is a quick, easy change - all we have to do is retrieve the ‘attack’ stat for the user’s primary weapon, and then add it onto their attack stat for our combat calculations. Here’s the code that retrieves a players stats, from forest.cgi:

28
29
30
31
32
33
my %player = (
	name		=>	$username,
	attack		=>	stats::getStat('atk',$userID),
	defence		=>	stats::getStat('def',$userID),
	curhp		=>	stats::getStat('curhp',$userID)
);

If you’ve been following along with all of the tutorials so far, you might have noticed a slight problem: we can’t retrieve weapon stats yet. Thanks to our DRY changes from earlier, it’s easy to write that code:

1
2
3
4
5
6
7
8
9
10
11
package weaponstats;
use DBI;
 
use statsDRY;
 
sub getWeaponStat {
	my ($statName,$userID) = @_;
	return statsDRY::getStatDRY('Weapon',$statName,$userID);
}
 
1;

It’s even easier because we aren’t going to be allowing users to change the stats - which means we only need to write the code to retrieve them(and not update them).

With that finished, we’re going to make sure that forest.cgi includes the new code:

8
use weaponstats;

Because our combat formula is as simple as it is(attack - defence), all we need to do is modify the ‘attack’ value that we stored in the %player hash with the attack for their primary weapon:

my $phand = stats::getStat('phand',$userID);
$player{attack} += weaponstats::getWeaponStat('atk',$phand);

And with that small piece of code added, we’re done! If the weapon has an attack value, it will be added to the player’s attack when they fight the monster - even if it’s negative.

Extra Credit

  • Make it so that only positive attack values are added to the player’s attack.

There was a small bug with the stats retrieval code found in this version of the tutorial - it has been fixed in the latest revision of the Google Code Repository. Make sure to update if you’ve been using it!

Monday, September 15th, 2008 buildingbrowsergames, code, perl No Comments

Building Browsergames: Integrating weapons into our combat system (PHP)

For all that we’ve built both a combat system and a weapons system, we haven’t yet done anything that allows the weapon a user has selected as their primary weapon to affect the combat system. Today, we will be integrating weapons into our combat system, so that the stats of the weapon actually affect the combat results.

This is actually a lot easier than it sounds - all we have to do is retrieve the weapon’s ‘attack’ stat, use it to modify the damage that the player does, and we’re finished! We’ll open up forest.php, to the piece of code that retrieves our player’s attack:

21
22
23
24
25
26
$player = array (
	name		=>	$_SESSION['username'],
	attack 		=>	getStat('atk',$userID),
	defence		=>	getStat('def',$userID),
	curhp		=>	getStat('curhp',$userID)
);

Unfortunately, we don’t yet have a weapon stats retrieval system - but thanks to our DRY changes from earlier, that’s an easy piece of code to add:

1
2
3
4
5
6
7
8
9
<?php
 
require_once 'stats-dry.php';
 
function getWeaponStat($statName,$weaponID) {
	return getStatDRY('Weapon',$statName,$weaponID);
}
 
?>

Because we won’t be allowing users to modify the stats of weapons in the game, we only need to add the getWeaponStat function. Save that file as weapon-stats.php, and go back to forest.php. First off, we’ll require our weapon stats code:

4
require_once 'weapon-stats.php';

Because we’re using a simple combat formula, we can directly modify the player’s attack attribute in the associative array that we are using to store their stats. Here’s how we would retrieve their primary weapon, and then add it’s attack to the player’s:

28
29
30
$phand = getStat('phand',$userID);
$atk = getWeaponStat('atk',$phand);
$player['attack'] += $atk;

And with that small piece of code added, we’re finished! If the weapon has an attack value(even if it’s negative), it will be added to the player’s when they attack the monster.

Extra Credit

  • Make the combat system display the name of the weapon when the player attacks - e.g. “You attack <monster> with your <weapon> for 10 damage!”.

There was a small bug with the stats retrieval code found in this version of the tutorial - it has been fixed in the latest revision of the Google Code Repository. Make sure to update if you’ve been using it!

Friday, September 12th, 2008 buildingbrowsergames, code, php No Comments

Displaying A User’s Stats (Ruby on Rails)

Introduction

This entry is based on the Building Browsergames blog entry: Displaying A User’s Stats (PHP)

It’s a pretty good idea to at least skim quickly through the original article before you go through this one, just to familiarize yourself with what we’re going to do.

Second Verse Same As The First

> ruby script/generate migration AddGoldToUser gold:integer

To add a new stat in the database for the amount of gold held we’ll create and edit a new migration. You saw something just like this a few entries back in this series:

class AddGoldToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :gold, :integer, :default => 0
 
    # Any existing users will get the new stat just like new users. If we had 
    # to do any special setup for the value though, we could do it here.
  end
 
  def self.down
    remove_column :users, :gold
  end
end

Note that a default value for the gold stat has been added above. The code as generated won’t have that. Be sure to run the new migration so we get our database schema updated:

> rake db:migrate

Now we’ll change the index page to display the various stats that we’ve assigned to our user whenever he/she logs in.

<h1>Welcome To The Game</h1>
 
<% if logged_in? %>
<h2><%= @current_user.login %></h2>
 
<ul>
  <li>Attack: <strong><%= @current_user.attack %></strong></li>
  <li>Defense: <strong><%= @current_user.defense %></strong></li>
  <li>Magic: <strong><%= @current_user.magic %></strong></li>
  <li>Gold in hand: <strong><%= @current_user.gold %></strong></li>
</ul>
<% end %>
The welcome page now displays both the user\'s login name and his/her stats.

The welcome page now displays both the user's login name and his/her stats.

Note that neither the controller nor the view have ever accessed the database or set any values. Instead they get the user model which represents a particular user and call functions on it to get its values. This is exactly the kind of thing we want to see, controllers and views which are ignorant on details (like SQL) and depend upon a well written model to handle them. This will give us compartmentalized code that is easier to understand, to test, and to modify.

By this point, you should be starting to get a little more comfortable with how Rails lays out things. You’ll see HTML in the views with the occasional reference to variables we need to display (e.g. @current_user). Those variables are setup in a controller method which is called before we get the view to render (e.g. index). The controller will rely upon the model (e.g. User) to handle all the nitty gritty details of loading and storing data and also rules for that data (e.g. no more than two weapons per user). You’ll see this again in the next installment as we turn to adding combat to our game.

Extra Credit

  1. I’m told that I’m a pretty good teacher, in person, but this is not in person and there are probably a lot of times I pick the wrong things to touch lightly on and to belabor. So, a few good books, a very few, is a good idea both for their reference value and because they tend to cover material I don’t cover or give you another chance to see the same things in a different way. Here are two I recommend: The Rails Way (Addison-Wesley Professional Ruby Series) (the best reference text by far) and RailsSpace: Building a Social Networking Website with Ruby on Rails (Addison-Wesley Professional Ruby Series) (a complete social network site built in Rails 1.2, a little bit of the code is older but still one of the best all around books out there).


Thursday, September 11th, 2008 buildingbrowsergames, code, rubyonrails No Comments

Adding Stats (Ruby on Rails)

Introduction

This entry is based on the Building Browsergames blog entry: Adding Stats (PHP)

It doesn’t hurt to have at least skimmed through the original blog entry about the PHP version before proceeding to the the Ruby on Rails version below.

Setting The User’s Defaults

Now, having skimmed through the PHP version of this, you can ignore most of what happened there. We don’t have to do anything special to use our templating system, it just works. We don’t need to go get the ID of the user after creating a new one, ActiveRecord handles that for us. As soon as we’ve done a User.create or User.new plus a save of the record, the ID will already be filled in for you to use. So where does that leave us?

Rails subscribes to a pattern for developing applications known as Model-View-Controller, I’ll link it to the Wikipedia entry for the topic because frankly I couldn’t find any short simple explanation of it. So hopefully as you see me use it through this series of tutorials you’ll pick up the basics by osmosis. The way MVC is relevant to this is that there is no way we are going to add new stats to a user or assign default values within the registration page or any other page. In Rails, those are views and they exist not to contain game logic but rather to simply display information and take input from the user. That’s it. Code to change the user belongs in the User model, so that’s where we’ll put it.

In this case what we want to do is assign a new set of default values (since the defaults we set in the migration that added the stats isn’t what we wanted). We could add another migration to alter the default values but that’s only necessary if we’ve got existing users we need to fix in the database and the only user’s we’ve got are test ones. If you only need to fix new users, we can just add a before_create function to the User model (app/models/user.rb):

def before_create
  self.attack = 5
  self.defense = 5
  self.magic = 5
end

Here’s the User model with our new code in place. Note that virtually all the code here came from restful_authentication when it created the User model in the first place. We just added the code above to it:

require 'digest/sha1'
 
class User < ActiveRecord::Base
  include Authentication
  include Authentication::ByPassword
  include Authentication::ByCookieToken
 
  validates_presence_of     :login
  validates_length_of       :login,    :within => 3..40
  validates_uniqueness_of   :login,    :case_sensitive => false
  validates_format_of       :login,    :with => RE_LOGIN_OK, :message => MSG_LOGIN_BAD
 
  validates_format_of       :name,     :with => RE_NAME_OK,  :message => MSG_NAME_BAD, :allow_nil => true
  validates_length_of       :name,     :maximum => 100
 
  validates_presence_of     :email
  validates_length_of       :email,    :within => 6..100 #r@a.wk
  validates_uniqueness_of   :email,    :case_sensitive => false
  validates_format_of       :email,    :with => RE_EMAIL_OK, :message => MSG_EMAIL_BAD
 
  # HACK HACK HACK -- how to do attr_accessible from here?
  # prevents a user from submitting a crafted form that bypasses activation
  # anything else you want your user to change should be added here.
  attr_accessible :login, :email, :name, :password, :password_confirmation
 
  # Authenticates a user by their login name and unencrypted password.  Returns the user or nil.
  #
  # uff.  this is really an authorization, not authentication routine.  
  # We really need a Dispatch Chain here or something.
  # This will also let us return a human error message.
  #
  def self.authenticate(login, password)
    u = find_by_login(login) # need to get the salt
    u && u.authenticated?(password) ? u : nil
  end
 
  protected
 
  def before_create
    self.attack = 5
    self.defense = 5
    self.magic = 5
  end
end

Extra Credit

  1. In this entry you saw one of the hooks ActiveRecord provides for us to perform actions at certain points within the lifecycle of an object (i.e. before_create). There are actually eight hooks in total for you to use. Visit one of several public repositories of Rails documentation (e.g. http://api.rubyonrails.org/) and read the documentation for ActiveRecord::Callbacks to see them all.
  2. As was mentioned in the previous extra credit item, http://api.rubyonrails.org/ is only one of several repositories of Rails and/or Ruby documentation. Here are a few others: RailsBrain.com, GotApi.com (this site offers documentation for many other APIs as well), and Noobkit Docs.

Wednesday, September 10th, 2008 buildingbrowsergames, code, rubyonrails No Comments

Putting It All Together (Ruby on Rails)

Introduction

This entry is based upon this entry from the Building Browsergames blog: Putting It All Together (PHP)

A Quick Recap

In past installments we provided a way for users to authenticate who they were (login, logout, signup) and have users with stats. The original article shows the SQL that was used to create the tables but we used migrations from our Rails code to do the same thing.

The example code we’ve already built is basically equivalent to what is built in the PHP entry so all we’re going to do is a few minor tweaks to match the version outlined in the article about the PHP version:

  • Our pages don’t have titles - We’ll use the layout feature of Rails to fix that.
  • Our pages don’t have any style - The PHP examples don’t have much style either, but at least they’ve got messages in green and red depending upon the type of message.

Note: One other difference is that restful_authentication logs you in immediately after registration. If the plan is not to require email confirmation, I prefer that we do it that way.

Let’s go through the changes required to make our code match the PHP version feature for feature so we’re up with the latest version.

Add Titles

We’re going to use another part of the templating engine in Rails to make it easy to have a title on every page. Rails has a concept called layouts. A layout takes the [something].html.erb files we’ve been creating and wraps them with another [something].html.erb file. It’s handy where you want a group of pages to all have the same headers, footers, navigation, etc. which, if you think about it, is most every website you go to. In order to wrap all the pages you create a layout called application.html.erb. A layout with that name in the layouts directory will be used as a default layout for the entire application. You can override the default easily if you need to though, one of the extra credit learning opportunities this week is about that. Here’s the app/views/layouts/application.html.erb I created:

<html>
  <head>
    <title><%= @title %></title>
  </head>
  <body>
    <%= yield  %>
  </body>
</html>

Now we can edit the various controllers (welcome_controller.rb, users_controller.rb, and sessions_controller.rb) adding assignments to @title in the methods that get called before we go to the view. For example:

class WelcomeController < ApplicationController
  def index
    # Call the function current_user to get the user assigned to the 
    # @current_user variable (if anyone is logged in). The
    # function comes with restful_authentication.
    current_user
 
    @title = "Welcome"
  end
end

Note that all we added here was an assignment to the @title variable in the controller action. Once the action is finished Rails will render the app/views/welcome/index.html.erb (with our new layout surrounding it) and the @title will go into the title of the page.

Add A Little Style

Styling message display is pretty easy, we just put a div tag with a class around the flash[:notice] output and we’re set. The class can then be styled any way we like:

<% if flash[:notice] %>
  <div class="flashNotice"><%= h flash[:notice] %></div>
<% end %>

To handle styling our entire game we’ll create a new file (public/stylesheets/pbbg.css). To get the stylesheet loaded we can add the following line within the head section of app/views/layouts/application.html.erb:

<%= stylesheet_link_tag "pbbg"  %>

Styling the errors that come out in the form whenever a user’s input fails validation isn’t much more complicated. If we take our registration form as an example, we can botch the password confirmation field and submit to see what we get:

<div class="errorExplanation" id="errorExplanation">
  <h2>3 errors prohibited this user from being saved</h2>
  <p>There were problems with the following fields:</p>
  <ul>
    <li>Password confirmation can't be blank</li>
    <li>Password can't be blank</li>
    <li>Password is too short (minimum is 6 characters)</li>
  </ul>
</div>
 
<form action="/users" method="post"> 
  <div style="margin:0;padding:0">
    <input name="authenticity_token" type="hidden" value="31249bde4aede2633a4391a44eafff49ec27ae17" />
  </div>
 
  <p><label for="login">Login</label><br/>
  <input id="user_login" name="user[login]" size="30" type="text" value="Testing" /></p>
 
  <p><label for="email">Email</label><br/>
  <input id="user_email" name="user[email]" size="30" type="text" value="sample@email.com" /></p>
 
  <p><label for="password">Password</label><br/>
  <div class="fieldWithErrors">
    <input id="user_password" name="user[password]" size="30" type="password" />
  </div></p>
 
  <p><label for="password_confirmation">Confirm Password</label><br/>
  <div class="fieldWithErrors">
    <input id="user_password_confirmation" name="user[password_confirmation]" size="30" type="password" />
  </div></p>
 
  <p><input name="commit" type="submit" value="Sign up" /></p>
</form>

That’s the error output and the form (with some of the error output wrapped through it) generated automatically by Rails. The thing to notice there is that Rails has inserted a couple of div tags automatically. One wraps the description of any and all errors (i.e. class=”errorExplanation”) and the other wraps individual fields which had errors (i.e. class=”fieldWithErrors”). By styling those two classes and even tags within each one (e.g. the h2 and ul tags in the explanation section) we can make the error output from Rails a little more appealing.

It probably wouldn’t take you a lot of effort to come up with better CSS to style the tags than what I present here, but at least it will give you a starting point (public/stylesheets/pbbg.css):

.errorExplanation {
  background-color:#ffcccc;
  border:1px solid red;
  vertical-align:top;
  padding: 10px;
}
 
.errorExplanation h2 {
  color: maroon;
}
 
.errorExplanation ul li {
  list-style: square;
}
 
.fieldWithErrors input[type="text"],
.fieldWithErrors input[type="password"] {
  border-color: red;
}
 
.flashNotice {
    border: 1px solid green;
    background-color: #ccffcc;
}

Here’s what the error page above looks like with that crude CSS applied to it. Note that not only is the error explanation styled, but that the fields that are in error are outlined in red:

The sign up form showing some simple CSS attributes applied to error messages and error fields.

The sign up form showing some simple CSS attributes applied to error messages and error fields.

Extra Credit

  1. You’ve already seen that you can easily have a default layout for the entire application. Watch Railscasts #7 - All About Layouts to learn about default layouts for any view in a particular controller and about how to override the default layout when you want something different for certain actions or even dynamically decide which layout to use.

Making Your Forms Auto-Focus (Ruby on Rails)

Introduction

This covers the same material as this entry from Building Browsergames: Making Your Forms Auto-Focus

Why Do We Have To Do Anything Different?

The advice is excellent and it works perfectly, but it has to be tweaked ever so slightly for Rails. Rails actually generates your form fields for you and names them itself in order to easily copy an object into the fields when populating a form and then make a new object out of the form inputs afterward. As a result, the form_for command you see in a page like app/views/users/new.html.erb:

<% form_for :user, :url => users_path do |f| -%>
  <p><%= label_tag 'login' %><br/>
  <%= f.text_field :login %></p>
 
  <p><%= label_tag 'email' %><br/>
  <%= f.text_field :email %></p>
 
  <p><%= label_tag 'password' %><br/>
  <%= f.password_field :password %></p>
 
  <p><%= label_tag 'password_confirmation', 'Confirm Password' %><br/>
  <%= f.password_field :password_confirmation %></p>
 
  <p><%= submit_tag 'Sign up' %></p>
<% end -%>

Generates HTML that looks like this:

<form action="/users" method="post">
  <div style="margin:0;padding:0"><input name="authenticity_token" 
type="hidden" value="41adf13eec9e99506a55c427ce59052a5543cf70" /></div>
  <p><label for="login">Login</label><br/>
  <input id="user_login" name="user[login]" size="30" type="text" /></p>
 
  <p><label for="email">Email</label><br/>
  <input id="user_email" name="user[email]" size="30" type="text" /></p>
 
  <p><label for="password">Password</label><br/>
  <input id="user_password" name="user[password]" size="30" type="password" /></p>
 
  <p><label for="password_confirmation">Confirm Password</label><br/>
  <input id="user_password_confirmation" name="user[password_confirmation]" size="30" type="password" /></p>
 
  <p><input name="commit" type="submit" value="Sign up" /></p>
</form>

So when we write our JavaScript, we need to make sure it specifies the correct element ID for the HTML that will actually be generated by Rails:

<script type="text/javascript">
window.onload = function() {
  document.getElementById('user_login').focus();
}
</script>

user_login is the name of the field that Rails will generate for login input in our case. If you aren’t sure of the name that will be used, just go to the page in your browser and view the page source. You can see the actual label used there.

Extra Credit

  1. Line #3 of the HTML code above features an “authenticity_token” that Rails inserted automatically for us. Find out what it does and why you want it.

Wednesday, September 3rd, 2008 buildingbrowsergames, code, rubyonrails, tutorial 2 Comments

Making Your Forms Remember Their Values (Ruby on Rails)

Introduction

This entry is based on an entry of the same name from Building Browsergames: Making Your Forms Remember Their Values

I Think We’re Already There

You may have noticed from our existing forms that it was remembering things like the login and feeding it back to the form automatically whenever there was an error. That’s because form_for in your view can take an object you created in the controller and use it to fill in the various fields as it builds them for the HTML. Likewise, when a form gets submitted, it automatically takes those fields and puts them into a Ruby hash which can be directly assigned to a new object to initialize it. That natural flow from controller to view and back to another controller when the user submits makes it easy to have forms “remember” their previous values whenever an error occurs or whenever they need to edit an existing record in the database.

<% form_for :user, :url => users_path do |f| -%>
  <p><%= label_tag 'login' %><br/>
  <%= f.text_field :login %></p>
 
  <p><%= label_tag 'email' %><br/>
  <%= f.text_field :email %></p>
 
  <p><%= label_tag 'password' %><br/>
  <%= f.password_field :password %></p>
 
  <p><%= label_tag 'password_confirmation', 'Confirm Password' %><br/>
  <%= f.password_field :password_confirmation %></p>
 
  <p><%= submit_tag 'Sign up' %></p>
<% end -%>

Simply by specifying the text field as “f.text_field :login” pulls the username from @user (which was supplied by the new function in app/controllers/users_controller.rb) and if there is any value on @user for that field then it is used to fill in the field as the page is built. You don’t have to do any of this yourself. Just learn and follow the standard Rails conventions for these things and it will handle the rest automatically.

If you’d like to see it in action, start entering in some intentionally wrong values into the sign up form and submit it. What you will see is how you get bounced in a loop between the new and create functions in the UsersController (app/controllers/users_controller.rb) and the app/views/users/new.html.erb. As you do so, even though it is may be erroneous, what you enter in for login and email will be preserved between one submission and the next and filled back into the form for you to correct and submit again (though Rails is noticing that the destination fields are password fields and it doesn’t fill those in for security reasons).

Extra Credit

  1. Dizzy offers several excellent one page cheatsheets you can print out for quick reference on the topics of forms, migrations, and ActionMailer. You might want to download the PDF for the FormHelper cheatsheet and print it out for quick reference.

Tuesday, September 2nd, 2008 buildingbrowsergames, code, rubyonrails, tutorial No Comments

Getting Started With A Templating System (Ruby on Rails)

Introduction

This entry is based on the Building Browsergames entry: Getting Started With A Templating System (PHP)

Since we’re using Ruby on Rails, we’re don’t have to select a templating system, there’s one already built into Rails itself. It offers many different features to make it easy to build web pages out of parts, cache parts of pages or whole pages, etc. As with most (probably all) parts of Rails, alternatives are available to what’s built in but you’ll be satisfied with what’s already there for quite a while.

I particularly want to amplify something Luke said in the original posting. He thought a lot of developers were fans of developing their own templating systems. Wow. If that is in fact true, then you are seriously misguided if you go down that path. What’s built into Rails and the Smarty templating system Luke used for his PHP work is more than enough to get your pages done. Do not waste your time on side projects like building a templating system, use the time to improve your overall programming skills or build your game. If you do, you might be one of the few percent who actually finishes a project and others get to play it.

Using Rails Views

The design of Rails is already about separating the view which builds our HTML from the controller which connects the model to the view. So displaying the user’s name in the view is as simple as adding some code like the following to the already existing app/views/welcome/index.html.erb:

<%= render :partial => 'users/user_bar' %>
 
<% if flash[:notice] %>
  <%= h flash[:notice] %>
<% end %>
 
<h1>Welcome To The Game</h1>
 
<% if logged_in? %>
  <p>Hello, <%= @current_user.login %>!</p>
<%