Thursday, January 31, 2008

IE 6 Minimum Height

IE is such a wonderful browser... *cough* Anyways, I found a slight display problem with divs in IE 6. Apparently, the div will only shrink to a certain height before it stops shrinking. From experimenting, I found the height to be around 20px.

Anything larger than 20px, IE 6 will display properly. However, anything below 20px, such as 15px, IE 6 will continue to display as 20px. Frustrating huh?

I found a hack to get around this, which is to set the div to:

overflow: hidden;

This will allow the div to shrink to any height, and hide the content inside the div that goes over the specified height.

Annoying.


W

12 Hour Time

RoR comes with many fantastic helpers for the view. However, there is one helper that is definitely lacking some features. Perhaps it is purposely missing features because of its complexity and range of customization. What I'm referring to is the DateHelper.

Firstly, when selecting a date, having selects as inputs just outright sucks! It's very hard for the user to visualize such a layout. We are used to seeing calendars. In what real world situation do you ever look at dates in a select box format... none that I can think of. But calendars, that's another story. Calendars are so normal to users that without them, we're lost! Unfortunately, RoR does not come prepackaged with a calendar format for dates. But luckily, RoR is extendable through plugins! We're currently testing out which one works best for us, and we'll let you know when we've come to a decision.

The second problem with the prepackaged helper is the fact that when selecting times, we're forced to take the inputs based on a 24 hour clock... EWWW!!! No body works on a 24 hour clock (except if you're in the military...) The usual person just isn't used to seeing a 24 hour clock. That's where plugins come in again!

I found a plugin that worked very well and required very little modifications:

12_hour_time

This plugin allows you to add an option to your current time selects to make them based on a 12 hour clock:

:twelve_hour => true

The rest (frontend and backend) are automatically handled for you!

Brilliant!


W

Tuesday, January 29, 2008

Lighbox modification

A few posts ago, I mentioned modal pop-ups and using the Lightbox Gone Wild implementation. Everything was going well, until I tested it out in IE6 for compatibility.

Well... to no surprise, there was a slight problem. Every so often when leaving the page, a JavaScript error would appear, complaining that some 'handler' was null or not an object. However, in Firefox, I was not having any problems.

I finally narrowed it down to a line with this call:

Event.observe(window, 'unload', Event.unloadCache, false);

I quickly checked the Prototype API and found out that this call was no longer necessary in version 1.6. I removed this line, and everything is now working fine once again (even on IE6!)


W

Saturday, January 26, 2008

Pagination

When displaying a list of information to the user, it's always handy to separate it into several pages if your list gets too long. This is a very common task, but also a very redundant and mundane task.

The logic to figure out how to display the page links is rather repetitive:

<Prev 1 2 ... 99 100 Next>

Instead of wasting time writing and rewriting this, wouldn't it be a better use of time working on something else? Well, there's a plugin that makes your life easy! It's called will_paginate.

will_paginate gives a wrapper around the find functions, allowing you to give the page number you want, and it returns to you just those objects that will be displayed on the page. Furthermore, with one simple function call, the nice page links will be displayed for you properly! Check out the will_paginate page for a quick example.

The only down-side I found to this plugin was that it didn't support AJAX. I hope that in one of the future releases they'll add AJAX support. In the meantime, I added one myself called will_remote_paginate. It merges will_paginate and the usual remote calls together like so:

<%= will_remote paginate @products, :url => list_products_path, :method => :get %>

If your interested in seeing what I did, give me a shout and I'll post it up. It's not the best solution in the world, but at least it works for me!


W

Thursday, January 24, 2008

Modal Pop-ups

Ever seen those really neat pages where you click on a link, and instead of an actual pop-up in another window, you get a "pop-up" that sits on top of your existing page. And even better, it's modal and anything not in the "pop-up" becomes darker.

Well, I wanted to implement something like that. My first initial thought was to create a new div, center it, make it transparent (slightly), then lay another div on top of this. The first div would block everything underneath it, while the second div would display what I needed. I saw two immediate problems. The first was that IE 6 doesn't support transparent png files very well (of course... considering IE 6 doesn't support many things very well), and the second was that IE 6 (again!) doesn't honour the z-index on selects. Wow.... how did IE 6 ever get so popular... (M$). Anyways... long story short, it requires a number of hacks to get everything working it seems.

So... instead of tackling the problem myself, I naturally assumed that I wasn't the only one with this problem. Lo and behold, I found a very good implementation for this already that fits hand-in-hand with RoR:

Lightbox Gone Wild!

This wonderfully written code includes only 2 files. A javascript file and a CSS file. It is, however, based on Prototype and Script.aculo.us, which RoR already automatically includes in it's javascript folder. Calling it is even simpler. You create a link to the HTML file you want to have in your "pop-up" and label it with the class as "lbOn". That's it! You get the whole package deal with this simple modification! Don't believe me? Try it!

If you want it to call a controller to have dynamic information displayed (unlike the examples given on the website), instead of the link URL being something like "text.html", just change it to the path of a controller action. What will happen is an AJAX call to that controller action, whereby you can treat it as any other AJAX call, such as when you use link_to_remote or remote_form_for.

Simple as 1-2-3!


W

Wednesday, January 23, 2008

Mixins

In Ruby, there's something called mixins. What is this? Basically, they are mix of a class and modules. Modules can be thought of as relatively independent pieces of code that you may want to mix into your classes. Modules are used to keep the code modular and easier to maintain.

For example, if we wanted to have the ability to compare objects, we could create a Comparable module. This module would define the functions in order to compare objects. Some objects do not require this function however, so we do not need to add it to a base class. Instead, we can include this module to only those classes that require it. This is, of course, overly simplified, but it is only to illustrate the purposes of modules.

For a quick tutorial on mixins, visit this site:

http://www.juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/


W

Monday, January 21, 2008

DRYing Up YAML Fixtures (except for PostgreSQL!!!)

When creating YAML fixtures, there are many records that have many repeated values. For example:

user_1:
name: john
is_active: true

user_2:
name: billy
is_active: true

Here, the is_active field is repeated many times and has the same value true. This may be a default for many records. YAML allows you to set defaults:

defaults: &defaults
is_active: true

user_1:
name: john
<<: *defaults

user_2:
name: billy
<<: *defaults

Obviously, this simple example doesn't save us any time. However, if our defaults include many columns, this can save a lot of typing and headaches when default values need to be changed.

All this is wondeful... EXCEPT when your using PostgreSQL, and it's driving me NUTS!!! For some reason, this DRY method doesn't work. I haven't figured out why yet and my search for the answer has been rather futile. If any one has an answer, I'd like to know the reason.


W

Sunday, January 20, 2008

Customized Logger

Rails framework comes with a logger for you to log activities on your application, which is pretty handy. However, there's a problem. Your development.log or production.log (depends on which environment you are running) comes with many other informations which makes is very difficult to locate your own logs.

In order to solve this issue, we'll create a custom logger which will extend the original logger and save our own logs on a seperate file.

Open environment.rb (the reason we put it there it's because you want it available as soon as your server boots up, so you can call it whenever you need it)

go to the very bottom and add the following lines:

class CustomLogger <>
end

YOUR_CUSTOM_LOG = CustomLogger.new("/path/to/your/log/file")

and you use it just like a regular logger:

YOUR_CUSTOM_LOG.info "GOT HERE"

something worth noting is that the variable that holds the CustomLogger object must be
a global variable, or else you won't be able to access it in your controllers or models.

another thing is that make sure you create the log file ahead of time, your server won't start if the log file you specified does not exist.

when everything is set, restart your server and test it out!


J

Saturday, January 19, 2008

Time Zones

When developing an application with an audience larger than your local neighbourhood in mind, you have to start thinking where your users are. One major point that comes into play is time. The world works on different time zones, and you must realize and honour that. When your application is based on time, time zones become important.

However, time zones are one of those things you just never want to touch. Why? Because usually it's a bloody mess! There are several factors to consider, like the time on your server and database (are they in local time or UTC?), local times for users using time zones, and daylight savings times. All this bundles together to make one messy problem.

RoR's built-in Date/DateTime/Time/TimeZone classes are poorly equipped to handle... well... anything to do with local times. However, because RoR is so extendable, there are always gems and plugins! In order to get everything working nicely, the problem of time zones requires 1 gem and 3 plugins! Wow! That's a lot, but trust me, it makes things much cleaner!

Firstly, there's the TZInfo gem. This little gem provides daylight savings aware transformations of time. This is useful because not everybody observes daylight savings.

Secondly, there's the TZInfo Timezone plugin. This little baby is where most of the magic occurs. This essentially replaces RoR's built-in TimeZone class. It does wonders.

Thirdly, there's the TzTime plugin. This is another little baby that does wonders. It basically replaces RoR's built-in Time class, and works in unison with TZInfo Timezone.

In short, all you do is set TzTime.zone to the current user's time zone, and then you can use things like TzTime.now, which will return you the current local time of the user. Clean and simple!

For an illustration of this, go to:

http://www.caboo.se/articles/2007/2/23/adding-timezone-to-your-rails-app

There was one modification which I needed to make in order for the example to work. Instead of TZInfo::Timezone, I needed to write TzinfoTimezone.

Good luck with time zones, and hopefully, this will keep your application a little bit cleaner (and minus the hair pulling!)


W

Sunday, January 13, 2008

ActiveForm

Suppose you're doing a search form. This search form can be using fields from many different ActiveRecord models and probably doesn't correspond to any table in your database.

What's nice about being an ActiveRecord model is that in the view, you can do wonderful things like a form_for helper or error_messages_for helper. Furthermore, ActiveRecord provides you a collection of wonderful validation functions.

However, without ActiveRecord, your solution would be to do it all by hand. How very sad T.T

This is where ActiveForm is a time saver! Basically, you create an ActiveForm model exactly the way you would an ActiveRecord model. It even supports validation! Furthermore, it is supported in the view, so all those helpers will still work! Just wonderful!

You can grab it as a plugin from here:

http://www.realityforge.org/svn/code/active-form/trunk

Enjoy!


W

Saturday, January 12, 2008

assert*

For testing, we have a collection of assertion functions that come with RoR. They're fundamental to any testing infrastructure. Without them, I can't imagine how testing could be done.

However, on the site that hosts the API, I cannot find the functions available. They list the functions for ActionController, but not for general testing.

Hence, I did a simple search and came up with this lovely site. Enjoy!

assert* functions


W

Auto Insertion Templates

I recently found a wonderful little tool in Aptana RadRails. The IDE provides a way for auto inserts using templates! This means you can type shortcut keywords, hit ctrl + space, and the corresponding templates will show up for you to choose from.

Window > Preferences > Ruby > Editor > Templates

is where you will find all the defined templates. This comes in handy when you want to customize the already installed set of templates, or feel like creating your own. It is very easy to create your own templates by looking at some of the examples already in the list.

This should allow you to develop your application much more quickly. Happy coding!


W

Friday, January 11, 2008

Locking

Usually in an application with more than 1 user, there are several resources that will be shared. And whenever there are shared resources, there are usually race conditions. Most web applications are not exceptions. Shared resources are a very old set of problems that have been solved many times in many different areas, not only for computers.

Luckily, in RoR, it provides a way to handle shared resources: locking. Locking comes in 2 flavours: optimistic and pessimistic.

Usually, optimistic locking would be used for critical sections where race conditions are not expected to happen very often (due to the abortion of the transaction if a conflict occurs), whereas pessimistic locking would be used for critical sections where race conditions are frequent.

There is, however, a huge advantage that optimistic locking has over pessimistic locking, and that is scalability. The fact that pessimistic locking for update locks the row so that no read/update/destroy can be performed on that row until it is released means that users may have to wait indefinitely. Especially when race conditions are frequent, a long queue could develop with arbitrarily long waits.

Unfortunately, with optimistic locking, there is the problem of abortion and requiring the user to redo what they just did. This is obviously not very user-friendly, as the user expects their changes to be made the first time. If the UI is thought out carefully though, the impact of this may be minimal.

RoR, being superly fantastic, supports both! And both are very simple to implement.

With optimistic locking, all you have to do is add a column to your table you want to optimistically lock. Using migrations, it would look like this:

t.column :lock_version, :integer, :null => false, :default => 0

It is important that the column name is lock_version, and the default value is 0. Now, whenever an update occurs, the lock_version is compared to the one in the database. If there are any differences, then a StaleObjectError is raised and must be handled in any fashion in which you deem appropriate. More information about RoR's optimistic locking can be found here.

With pessimistic locking, all you do is lock it down when you do a find:

Person.find(:all, :lock => true)

This will pessimistically lock any rows returned. If you already have an object, you can use the method object#lock!:

person = Person.find(1)
person.lock!

More information about RoR's pessimistic locking can be found here.

Well, hopefully that was interesting to you. One more tip about locking is to watch out for deadlocks! Good luck!


W

Thursday, January 10, 2008

Eager Loading

There are many instances where you need to find an object, or several objects, then require their associated objects afterwards. In the simple case, this would require (1 + n) SQL queries: one for the object(s) and n for the associated object(s) for each object. To solve this problem, RoR allows you to do eager loading of associations when performing a find. This means that the (1 + n) SQL queries can be squashed into 1 and RoR handles the placing of the data correctly. Examples of eager loading can be found here (under Eager loading of associations).

I thought I would take this one step further. Using the example of Post, I thought I would have something like:

class Post
has_many :comments, :include => :author, :order => "comments.date ASC, authors.name ASC"
end

This would eager load the authors of each comment in the post, ordered by the date created and then by alphabetical order of name.

This works great when doing:

@comments = post.comments

and later on using @comments in the view to display.

However, I started to have major problems when I attempted a find on the collection such as:

comment = post.comments.find_by_author_id(author.id)

When executing such a command, I got a database error. The error reported that the authors table was not included in the FROM clause of the SQL query (due to the order by author.name ASC). I checked the query in the output console, and sure enough, there was no join to the authors table. I tried looking around for explanations, but ended up with none.

I finally gave up and removed the :include and :order from the has_many relationship and ended up doing this instead when I required eager loading and ordering:

@comments = post.comments.find(:all, :include => :author, :order => "comments.date ASC, authors.name ASC")

Hopefully, if anyone knows the explanation as to why the previous did not work, I would love to hear it. Otherwise, I hope this solution works for you.


W

Wednesday, January 2, 2008

Radio Buttons

RoR is lovely when it comes to supplying frontend helpers. For example, if you want to create a form for a particular object, it's very simple:

<% form_for :user, :url => new_user_path do |f| %>
...
<% end %>

Inside "...", you can add all your fields, such as:

First name: <%= f.text_field :first_name %>
Last name: <%= f.text_field :last_name %>
...
<%= submit_tag 'Update' %>

This creates a form using the user object. It fills in the first and last names using the data found in the user object. If the user object is new, then the text fields are empty. If the user object is from database, then that information is displayed. Pressing the submit button submits the form to the specified url. It's very simple! There are helpers do to all sorts of things, such as text areas, selects, date selects, time selects, checkboxes, etc. Check the API for more helpers.

However, our topic for today is actually radio buttons. Many times, we want to use radio buttons to select some sort of true/false field. It is possible to do this with checkboxes, andRoR is wonderful with this. However, sometimes, 2 radio buttons are better. This is where I ran into some problems. Reading the API, I came up with 2 radio buttons as follows:

<%= f.radio_button :is_good, true %>
<%= f.radio_button :is_good, false %>

I thought that this would generate two radio buttons with values true/false respectively. However, it didn't! The first generated one that had a value of "true" and was checked. But the second generated a radio button with no value! I then changed the given values from booleans to strings:

<%= f.radio_button :is_good, 'true' %>
<%= f.radio_button :is_good, 'false' %>

Everything worked fine now, but given a model that held data, neither radio buttons were checked! This is due to the fact that the model holds boolean values, whereas the radio buttons use string literals. Saving was fine because we were using prepared statements, which automatically convert the string literals to boolean. I was completely stumped on the displaying of data though. I came up with a quick hack using virtual attributes, but it's ugly:

def is_good_temp=(is_good_temp)
@is_good_temp = is_good_temp
if @is_good_temp == 'true'
self.is_good = true
else
self.is_good = false
end
end

def is_good_temp
@is_good_temp.nil? ? 'true' : @is_good_temp
end

def is_good
@is_good.nil? ? true : @is_good
end

def is_good=(is_good)
@is_good = is_good
end

and changed the radio buttons to:

<%= f.radio_button :is_good_temp, 'true' %>
<%= f.radio_button :is_good_temp, 'false' %>

So basically what I did was create a virtual attribute to deal with the radio buttons using string literals, and have that convert the string literals to booleans and assign it to the actual variable. To be noted is that there is no nice function object#to_b to convert a string literal to boolean. There are nice functions like object#to_s and object#to_i, but not to boolean.

This is one disappointment from RoR. I have searched around and could find no solution to this. Perhaps I am doing this wrong, but for now, it works.

Any ideas would be appreciated, and I'll update if I find a better solution.


W