❯~/pixelastic

Working on Wednesday #4: Rails for Zombies

(I finally managed to update Ubuntu to the 10.10 version, configure Unity to make it more usable, and I'm not ready to start the RoR tutorial I wanted to start last week.)

I used vim this week (instead of nano) for very simple file editing. I will force myself to use only vim on wednesday to really learn it. I plan to completly drop Komodo Edit once I'll be familar enough with vim to gain more time than I lose.

Rails for Zombies

Anyway. I finally get to start the Rails for Zombies tutorial. And here is what I learned.

There are two distinct things I'm going to learn here: Ruby and Rails. I discovered that I was already familiar with a lot of basic concepts of Rails, thanks to cakePHP. Model and table convention, model relationships (belongs to, has many, etc) were exactly the same.

Ruby on the other hand was new. But also very interesting. No need to add () after a method to call it when no args are passed, possibility to chain method of the same object (much like jQuery) and the use of dynamic (yet strongly typed) vars.

The first part of the tutorial (basic CRUD) was easy. I didn't find how to make a complex SQL query like this (cake) one:

$Zombie->find('all', array(
'order' => array('Zombie.graveyard' => 'ASC', 'Zombie.name' => 'DESC'
));

I guess it's just a matter of time before I learn how to to do in Rails anyway. I saw that there was more "advanced" method (much like the cake find method) that would surely allow such finding.

Part 2: Validation and Relationships

The second part of the tutorial was more about models. Validation rules were pretty much the same as those of cakePHP. But the Ruby syntax makes it more concise.

I couldn't find how to retrieve the validation errors when saving an object using the create method (no problem when manually calling save, though).

I enjoyed being able to create binded instances by simply adding the binded element to the model. Not very clear isn't it? Here some code to explain it better. The last two lines have exactly the same effect:

class Zombie < ActiveRecord::Base
end
class Weapon < ActiveRecord::Base
belongs_to :zombie
end
Weapon.create(:name => "Rocket Launcher", :zombie_id => 2)
Weapon.create(:name => "Rocket Launcher", :zombie => Zombie.find(2))

Later on, I had to select all weapons binded to a specific Zombie. I was able to find the a specific zombie using any of the two following commands:

z = Zombie.where(:name => "Ash").first
z = Zombie.find(:first, :conditions => { :name => "Ash" })

Then, I wanted to find the weapons binded to this zombie. First I tried the following command:

Weapon.where(:zombie_id => z.id)

Obviously, this worked. But I wanted to test some more of the Rails magic. So I tried something along the lines of what I did in the previous chapter and tried to use my z var directly instead of z.id.

Weapon.where(:zombie => z)

This didn't worked. Well, I guess I'll also learn why later on. But one even more weird thing is that the following command did work even I'm not really sur why?

Weapon.where(:zombie_id => z)

That's all I did today (well I also did some more Mercurial and Dingux testing, but that wasn't really work)

Alternate content with SWFObject

SWFObject allow a web developer to add an alternate content in place of the swf file is the user doesn't have flash installed.

In the case of a web app this is great to provide your user with a link to the Adobe website. You then tell your users Flash is needed and provide a link for them to install it.

Unfortunatly, if for one reason or another the swf file you try to load is unreachable (maybe your host is down ?), SWFObject will display the same alternate content. Meaning that a user with a perfectly correct Flash version will see an error message telling him that he does not have Flash when in fact the error is on your side.

That's not very user friendly and makes you look bad.

So, to fix this, I added two error messages as alternate content:

<div id="alternateContent" class="alternateContent">
  <div class="noFlash message">It seems that you do not have the Flash player installed. Please install it, by <a href="http://www.adobe.com/go/getflashplayer" target="_parent">following this link</a>.</div>
  <div class="error404 message">Sorry, we were unable to load the game. Please try again in a few moments.</div>
</div>

Then, in CSS I decided to hide them both:

.alternateContent .message { display:none; }
.alternateContent.noFlash .noFlash { display:block; }
.alternateContent.error404 .error404 { display:block; }

And finally, in Javascript I checked for the current Flash version. If it's equal to zero, it means that Flash is not installed, so I display the noFlash error message, otherwise, I guess it's a 404 error and display the other message.

var flashVersion = swfobject.getFlashPlayerVersion(),
  alternateContent = $('#alternateContent')
;
// Displaying one message or the other
if (flashVersion.minor=='0') {
  alternateContent.addClass('noFlash');
} else {
  alternateContent.addClass('error404');
}

This is not bulletproof: I only test for two cases. And a better solution would also have been to put the error message dynamically using Javascript instead of polluting the HTML markup with contradictory text.

SWFObject must be included in the <head>

We just discovered a strange behavior on one of our test VM (one without Flash installed). I don't know how we've missed it before.

When embedding the main flash file of our application, the alternate content wasn't displayed to players. In fact it seemed to be displayed very briefly then disappear.

I had correctly embeded it with the static publishing method and added a call to swfobject.registerObject to soften the edges. I remembered it working correctly in my tests, so I quickly made a small test case.

It occurs that the alternate content was corretcly displayed if I removed the swfobject.registerObject call and only used the static publishing.

I also found that everything worked fine if I called the swfobject.js file in the head, but started to bug when I moved it at the end of the page.

As I didn't want to add a call to a .js file in my head, I google a bit and I finally found that one have to call swfobject.switchOffAutoHideShow(); right before the swfobject.registerObject call. This seems to fix things by not hiding the alternate text

Edit

I spent some more hours trying to debug the last corner cases. We had a user using Firefox 3.5.3 with Flash 10.0.42.34. Using SWFObject, we required at least version 10.1. Unfortunatly, the app was still loading, but was buggy because of features not present in the user flash version.

After some tests, I discovered that the swfobject.js code MUST be included in the <head> of the document (well, before any .swf in the page, so better put it in the <head>).

Doing so, you won't even need the previous fix (swfobject.switchOffAutoHideShow()).

I would have loved putting it at the end of the page, with the others JS files, but I guess I won't have a choice here.

Working on Wednesday #3: The long path to get it done

This morning I started reading a lot of online blog post, framework description and other webdev material online I had in my bookmarks. After reading them, I saved them on my pinboard account to find them easily later.

Lot of good JS and CSS framework/libraries in there, I hope to be able to test them soon.

I also check my mailbox and respond to mails from my normal job. I shouldn't. I'm on a day off, I have to force myself to completly cut out those external calls.

Now, let's the work begin

Anyway, I finally finished the vimtutor. I still have a some difficulties to remember the mapping of the basic hjkl movement keys (I always want use k to move down instead of j). However, I feel confident enough in it to try writing my new code in it.

So, I headed to the Rails for Zombies tutorial. I thought it was free but the "win $5 worth of online teaching" / "next course costs $5" make me doubt it a little. Well, we'll see.

After registering to it, I wanted to save my login and password to KeePass as I always do.

Unfortunatly, KeePass on my Ubuntu machine works by using mono, and the keyboard interaction weren't that good (caret is slightly misplaced, inputs lags before getting displayed, text selection is weird). So I thought "Hey, let's see if there is a new version". Big mistake.

It appears that there was indeed a new version, a real one, with apt-get and stuff while mine was some hackish install I manually made a few month ago.

But wait, this means manually adding a ppa directory. No big deal, I trust the author. What? Still doesn't work? Hmm, I see. I need mono 2.6, and I only have 2.4.

What? 2.4 is the latest available version on Ubuntu 10.10? You mean I must upgrade to 11.04? Hmm. Why not. I'll have to do it eventually, so let's go.

So I ran all my updates, to have the most up to date system. Unfortunatly, language-pack-gnome-fr doesn't want to upgrade so the OS upgrade isn't displayed.

Wait, what am I doing?

And that's when I remember that my initial wish was to learn Ruby on Rails and that I was now on the road to upgrading my whole operating system. That is not exactly speaking procrastination, but it makes me do so much other things that my initial goal that I really looks like it.

I finally managed to purge my packets, upgrade to 10.10 and then to 11.04. But this took me 4 hours, and now it's 11PM and I guess I won't start this rails tutorial today, after all.

cakePHP: Fatal error: Class 'Controller' not found

Last Friday, just before turning off my computer and going home, the cake project I'm working on was displaying a scary Fatal error: Class 'Controller' not found in /var/www/project/app/app_controller.php on line 5

Today, I'm getting back to work and here's what happened. After a morning of trial and error, I finally reverted back to a previous svn commit, then re- updated to the latest one and it seemed to solve the issue.

But later on the day, after another update, it started bugging again and the error message wasn't very helpful.

After much googling, this [bug report](http://cakephp.lighthouseapp.com/projects/43067/tickets/26-class- controller-not-found) gave me a hint: I had new models but didn't create the corresponding tables. Strangely, creating the missing logs table for my Log class solved the issue.

Hope this blog post will be usefull for anyone else struggling with this error message.