Simple RJS Example

Wasabi Pisum Sativum

Working on a contractual project on the weekend, I spent some time integrating some nifty new technology into a website. The technology was JavaScript, the crusty old web technology which has been crawling around the internet since 1995. Since it's inception JavaScript has played the central role in everything from annoying internet pop-ups to shiny WEB 2.0 sliding divs, and now they've slapped a Remote on the front of it and call it RJS.

I found a plethora of examples on the internet, but nothing that walked me from start to finish through using a RJS template in Rails. I hope that someday the following saves someone else from that void.

Take any old Rails application and add a time controller to it.



# script/generate controller time index
exists  app/controllers/
exists  app/helpers/
create  app/views/time
exists  test/functional/
create  app/controllers/time_controller.rb
create  test/functional/time_controller_test.rb
create  app/helpers/time_helper.rb
create  app/views/time/index.rhtml

The index action will be the one that Rails serves up when you head to the /time url. Modify the app/views/time/index.rhtml to look like:



<%= javascript_include_tag :defaults %>
<div id="current_time"><%= render :partial=>'time/current' %></div>
<%= link_to_remote 'refresh', :url=> {:action=>'now'} %>

Take a second to take that in, because it's the entire meat of the application. We need to include the default JavaScripts because RJS depends heavily on the Prototype library, next we created a div with the id current_time which we will use in our script magic in a second. The last line gives us a link to the now action in the time controller. We don't have that action yet, so open up app/controllers/time_controller.rb and add the new action



class TimeController < ApplicationController
  def index
  end

  def now
  end
end

The controller actions are anything but complicated, but now we need that action to return something. Usually the action looks for app/views/time/now.rhtml but execute and run app/views/time/now.rjs if it does not find it. Don't be silly and try to name your RJS templates the same as some other usable action within your application. Create app/views/time/now.rjs to read



page.replace_html 'current_time', :partial=>'time/current'

This template takes the current page, and replaces the current_time element with whatever is returned from the time/current partial. The partial is rendered from both the RJS template and the index.rhtml template is to ensure the DRYness of our logic. The app/views/time/_current.rhtml template can be as simple as



<%= Time.now().to_s %>

Now if you head to /time on your locally running WEBrick you'll see the current time, but if you mash the 'refresh' all the JavaScript magic replaces the current_time div with your partial. If you examine /time/now you'll see the RJS template is returning a script that is then run by the client's browser. You can start layering on some parameter passing and some data like XML and quickly these RJS templates become very useful in programming some crazy JavaScript, all without writing any actual JavaScript. How very Zen.

Comments (1)

An Example of RSpec

Intense

I've taken a real shining to Behaviour Driven Development since working on my last project. BDD is the next evolution of Test Driven Development, and basically you think about all the behaviours of your system first. Then you write them down (key step here!), and then make your application meet these define behaviours. On a couple of my side projects, I've started using RSpec to implement some BDD.

When I first started out, I noticed a great void of coherent examples on how to use RSpec. Partially because RSpec was changing which broke old tutorials and tests, and partially because the people writing stuff down were so far out on the edge, that what they had didn't help me out in the slightest. I'm throwing this out there, so that you can see how easy it can be to add a specification test to your rails application.

You'll need to have rails, and rspec installed for this to work. Roll out a vanilla rails application and install the rspec plugins:


# rails myapp
# cd myapp
# ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_1_0_0/rspec
# ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_1_0_0/rspec_on_rails

Add the -x flag to the install command if your code is already in subversion so that svn:extern is used and you can pick up RSpec updates. Now, we need to bootstrap rspec, and create the user object we are going to be writing specifications for:


# ruby script/generate rspec
# ruby script/generate rspec_model user

Now that all the monkey work is done, we can get down to business, I quickly modified the db/migrate/001_create_user.rb to read:


class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column "identity", :string
    end
  end

  def self.down
    drop_table :users
  end
end

Boring, eh? Well I'm trying to keep it that way. These examples can really get out of hand, if you aren't careful. What we have is the simplest user model ever, all it's got is an identity column. Within my application, I would like it to be impossible for a user to exist without an identity. Simple enough, so I modify /spec/models/user_spec.rb to be:


require File.dirname(__FILE__) + '/../spec_helper'

describe User, " in General" do
  before(:each) do
    @users = User.new
    #Identity is a required field
    @users.identity = "http://whatever.com"
  end

  it "should be valid" do
     @users.should be_valid
  end

  it "should be invalid without an identity" do
    @users.identity = nil
    @users.should_not be_valid
  end
end

The first specification is so trivial it appears silly. It is a valid (and it is already in the generated *_spec.rb file) specification, and will blow up if people change what it means for your model to be valid without updating the specification. The rest almost reads itself. You can see we are describing a User, " in General" and that we have two specification, it should be valid, and it should be invalid without and identity. Pretty simple, eh? Well, let's keep it that way. You can now run the specification test:


# rake spec

Uh, oh! FAILURES! Written in red (on colour TTY) is 'User in General should be invalid without an identity' FAILED. Well, of course, we haven't written any code to invalidate a user without a identity; up to this point, we've only been writing specifications. To make our application meet the meager specifications I changed the app/models/user.rb to be:


class User < ActiveRecord::Base
  validates_presence_of :identity, :message => "Identity is required"
end

Now if I run the rake spec again, I can see that my application is up to specification. That's it. Done, Kaput, and still useless. But, at least we have a starting point. The next steps would be to write down more specifications, and then make the application pass those specifications tests. Repeat, until you have an application that meets all your specifications. Some other nifty things that rspec can bring to the table are rake spec:doc which writes out some bullet point specification (Great to give to the pointy haired manager when he comes askin' what specification to which you were building the application. rake spec:rcov will give you a coverage report of your specification tests (in case you cheated and went ahead writing code without specifying it's behaviour). Already my application has a pile of specifications written for it, and it's weird how you just write better code when you stop to think what you really want it to do first.

Comments

An Ugly New Website

Lauren is a Drama Queen

I've been thinking about getting away from Typo for a little while now. It's a decent little engine, and was my first real run with Ruby on Rails, but there was a couple of things that were always under my skin.

After some looking on the net, I found that all the cool kids are using Mephisto for their web logging needs. I grabbed a copy and took it for a spin on my test site, and was quite impressed. I decided I would share with you all how I made the transition, although I think I was the last person using Typo.

The first thing was to make sure I didn't mangle my current, production database so I created a new one through the web panel and then:

mysqldump -u olduser -p oldpassword -h mysql.existinghost.com existing_database \
| mysql -u testuser -p testpassword -h mysql.testhost.com testing_database

I now had a testing_database that I could wreck havoc with. Next I needed the bleeding edge of Typo, so that I could migrate my database to the current version. I couldn't do this with my current version because I wasn't ready to upgrade my production site to the newest Typo. Therefore I:

svn checkout http://svn.typosphere.org/typo/trunk typo_delete
svn checkout http://svn.techno-weenie.net/projects/mephisto/trunk .

Now I had the lastest version of Mephiso installed in my testing webroot, and typo where I could use it. I then updated the database.yml files for both applications so that typo pointed to the testing database, and Mephisto had production set to a brand new database, as well as the typo entry set to the testing database. Once that was done, I started the migration

cd typo_delete
rake db:migrate VERSION=56

You needed to specify the version to stop at, because the bleeding edge of Typo breaks the Mephisto conversion script. Then we are ready to bootstrap the new database, and migrate the data over

cd ../mephisto
rake db:bootstrap
script/runner "Mephisto.convert_from :typo" -e production

At this point I could not log into the admin application with my old Typo user. The problem was that my user appeared to be missing a validation time, so I logged into the database and:

update users set activated_at = now(), admin = 1 where login = 'wdevauld';

Now I was able to get into the admin applet and check out my articles.

The final conversion I made before switching over was to pre-populate all my tags. I believe that tagging is the future of the internet, and quite a few sites I use (Del.icio.us and Flickr are at the top) implement tagging rather well. I don't want to get caught up in sets, categories or sections so from now on it's all Tags all the time. Most of my Typo articles were categorized to some extent, and the conversion pulled them over into sections. I wanted to pre-populate all my tags with my sections (previously my categories) so I dove into the rails production console and

Article.find(:all).collect do |article|
   Tagging.set_on(article, article.sections.collect{|i|i.name}.reject{|i|i=="Home"}.join(', '))
 end

There in three easy lines of code I had all my articles tagged with their section names. It was a shame I had to pull the Home section out, but under mephisto everything is in 'Home' and I didn't need a 'Home' tag with every old article in it. The last bit of meat to get me happy enough to switch production over was to facilitate the old permanent links, so I edited config/environment.rb to include:

Mephisto::Routing.redirect 'articles/*' => '$1'

and I was ready enough to go. The old RSS feed doesn't work anymore, because Mephisto facilitates Atom feeds, and that's the way it's going to be. Those that are tech savvy enough to use a feed reader for my blog, are surely not going to be stumped enabling the Atom feed.

This is the first post under the new regime, and I've been compiling a list of what I still need to do in order to be happy again:

  • Fill the sidebar up again:
    • Recent Del.icio.us tags
    • Monthly archives
    • Flickr stream
    • Blog-roll
    • Perhaps some feedburner love
  • Code formatting
  • Automagic pings to all my favorite sites
  • Way more Style!

I was hoping to participate in the CSS Reboot this spring, but I want to have the new look in place before them. Although with how busy I've been lately, I think May 1 is a reasonable deadline.

Comments

Setting up the Mac

Me again

As I sit here, my Visine drenched eyes trying to keep focused on my LCD monitor, I'm finishing up a night of installation and configuration. Jerry and I have decided to undertake a side project, with what's left of my...

Who am I kidding? I don't have any free time.

Anyways, tonight I needed to get my Mac developer ready. The clean shiny toys that I installed when I first recieved my little dynamo were not enough for the industrial use I would need out of them.

I started off with my favorite code editor, and the required configuration file to make it the way I wanted. I know a lot of the heavyweights in the rails community have been caught using TextMate, but I wasn't about learning a new editor, as well as forking out close to forty euro for a fancy text editor. The installation was as easy as I've grown to expect from my new Apple toy. I grabbed a dmg file from the web, dragged it's contents into the Application folder, and all was done.

Next was getting to the meat of Rails, and I diligently followed these instructions to get it done. It was the first time I compiled anything on my Mac, and from the instructions I don't think the Mac community is as technically experimental as the Linux folks. All the old conforting commands like sudo and source were available right out of the box, and truth be told the most difficult part was waiting for the downloads to complete. Downloading Xcode and MySQL took the longest, Xcode because it's huge, and MySQL because I chose a terrible mirror.

After that adventure, I had to get subversion installed, as well as a snazzy front end for it.

I was just getting ready to get started and I decided to flip to my dashboard to see what time it was, and low and behold, there was only enough time left in the day to write this stuff down and head straight to bed.

Comments

Rails: Easy Multiple Selects

Fish Creek Park

Large version here
Again, the geek content is high in here. If you don't give a rat's ass about web developement frameworks, or don't know what one is, you should just finish this visit with a look at the pretty picture.

First, and foremost, Rails is sweet. If you've ever spent any time dealing with a web development framework, as soon as you dig into Rails you will understand these people know the way that is it supposed to be. I remarked to a co-worker that the reason I like Rails is that the database, if done correctly, is transparent. You only need to deal with the logic of the objects, and their persistant storage is secondary.

Jerry and I have been embarking on a black hat operation to sneak more Rails into our application. Recently, we've been looking at an alarming alert mechanism. Within the workings of this particular application I came across some implementation details regarding Rails that I thought I'd share with those that found me through google, just in case I've delt with this problem before you have.

Implementation details have been changed to protect the innocent.

Let's pretend we need to keep track of rubber duckies and the bathtubs that they are found within. Being that I'm coming from a DBA background I'd like to make the database objects first

create table rubber_duckies (id int not null auto_increment, name char(64), primary key (id));
create table bathtubs (id int not null auto_increment, name char(64), primary key (id));

Rails has an amazing amount of magic to deal with references. We want a rubber ducky to be able to go to multiple bathtubs (You never know when Ernie will visit), and a bathtub should be able to house more than a single ducky. This n-to-m relationship can only be encapsulated in a reference table, so create the table:

create table bathtubs_rubber_duckies (bathtub_id int not null, rubber_ducky_id int not null);

You need to put the references in alpha-numeric order for this work. So because we are dealing with bathtubs and rubber duckies we need to respect that bathtubs are first in alphanumeric order. Of note: You can't put an ID column on a reference table, or Rails will puke.

Now we need to tell the Rails models that we have a relationship between the database objects. So after you've generated the scaffold you can edit app/model/bathtub.rb to read:

class Bathtub < ActiveRecord::Base
        has_and_belongs_to_many :rubber_duckies
end

and app/model/rubber_ducky.rb to be:

class RubberDucky < ActiveRecord::Base
        has_and_belongs_to_many :bathtubs
end

Now that we've established that a ducky can be found in multiple bath tubs and that a bathtub can have multiple duckies, we're finally down to the meat. Most of the hard work is already done because of the strict naming we used in our tables. Firing up the console at this point will show you that a rubber_ducky object will have a collection of bathtubs attached, and vice versa.

From the front end, we want to be able to assign a Rubber Ducky to any number of bathtubs through a multiple select box, so we modify app/views/rubber_duckies/_form.rhtml and add:

<p><b>Bathtubs</b></p>
<% selected = @rubber_ducky.bathtubs.collect {|bt| bt.id.to_i } %>
<select name="rubber_ducky[bathtub_ids][]" size="3", mutliple="multiple">
<%= options_from_collection_for_select Bathtub.find_all, 'id', 'name', selected%>
</select>

The naming of your multiple select with tell rails that the information you are sending back is a list of bathtub identifiers for your ducky that you are editing or creating.

Now, we have support for a multiple select that allows for a subset of a collection to be saved to an object. I've dealt with other frameworks, and with most it's not this nice.

Comments