Sunday, April 09, 2006

Playing with Ruby on Rails

Recently I decided to play with Ruby on Rails and see what all the fuss was about. I've been around a while and taught myself a bunch of stuff along the way from Omnis7 to Java to T-SQL to .NET, and so Ruby on Rails shouldn't be too hard ... right?

Well, it wasn't, once I invested in Programming Ruby and Agile Web Development with Rails. Those two books helped me considerably, especially the follow-on tutorials in the Rails specific one. Without those I would have been left floundering.

However, there were two problems that neither book really helped me with (although the first one was properly shown in the Programming Ruby book, it just didn't bother to point out the weirdness of it).

Elsif

You might not have realised it, but that heading above is missing an "e" (and a space, but most programming languages ignore that). Unlike any other language I have learnt, Ruby's If ... Elseif ... End construct spells the "Elseif" without the second "e", as in "Elsif". Perhaps it was because Matz is Japanese, but this really threw me - I even ignored the way the Ruby book showed me it was.

Decimals

Rails' ActiveRecord is very nice for doing lots of hard work for you. Thank you very much David Heinemeier Hansson. Unfortunately it maps decimal database columns to float variables ... which if you have ever tried using floating numbers in financial calculations you would know means major problems!

Frankly this is a show-stopper for me. I can't imagine ever doing something real with Rails that would not involve dollars, and I certainly do not want nasty rounding errors creeping in because there are floats mapped to my decimal table columns. It turns out that the solution in Ruby is to use the BigDecimal data type. It took a little while to find the API documentation for this datatype, but eventually I did.

That didn't help me work out how to implement it. The Rails community is full of talk about modifying ActiveRecord to do the job properly - but it turns out that ends up involving a lot of database-specific code, and whilst ProgresSQL and SQL Server have been done, good old MySQL is left wanting, and that is what I am using to play around with. There is a little blurb in the book about overriding the accessor/mutator attributes for a model (don't worry if that makes no sense to you, it didn't to me either until about 8 hours into the tutorial). The online API documentation for ActiveRecord seems to hint that the _before_type_cast accessor could help here.

After too much mucking about, I finally got an answer that is elegant and works very nicely. I even have some sample code for those other newbies following the Rails tutorials. At the beginning of our Product model we need to add a couple of lines:

# modified this to get the price as a decimal

require "bigdecimal"
require "bigdecimal/util.rb"

Then later, we can override the default price accessor/mutator with a simple call to BigDecimal:

def price
    # we want to make Price a BigDecimal, the format
    # will look weird if we don't do anything to it
    # but we are using sprintf() in our fmt_currency
    # method, which handles this nicely
    BigDecimal(price_before_type_cast, 2)
end

As I've noted, the main problem with BigDecimal is that the native display of it is ugly. A value of 20.00 is displayed as 0.2E2, which is nasty. Fortunately the formatting seems to work out in text fields OK, and we end up formatting price everywhere we show it as a label so that it shows with dollar signs. The formatting function sprintf() has no problem with BigDecimal so that works fine!

So far (and I have all of the cart code working) this does not seem to lead to any issues with the Depot tutorial, and it gives an elegant solution (provided you are willing to create a formatting method for displaying these numbers wherever they are outside form fields).

[EDIT: The year-old enhancement request relating to the decimal issue is an interesting read.]

Submit to Reddit

9 comments:

  1. Anonymous12:53 am

    elsif came from Perl.

    ReplyDelete
  2. Anonymous5:56 am

    Elsif threw you off? Perl has elsif, Python has elif..

    ReplyDelete
  3. Aargh ... must be showing my non-web background then. ;)

    FYI, the languages I DO know are:
    * VB
    * VB.Net
    * VBA
    * VB Script
    * LotusScript
    * Javascript
    * XSLT
    * Java
    * PHP
    * Omnis7
    * CSS
    * HTML/XHTML

    ReplyDelete
  4. Anonymous12:38 pm

    I think you are just showing your self-admitted spotty programming background. Ada also uses elsif and Perl has been and is very popular for other tasks before people started using it for web apps. Same with Python. It's bad form to rant about something that (a) you know little about and (b) is relatively common.

    ReplyDelete
  5. Well ... it wasn't really much of a rant, the point was to perhaps help some other Ruby newbie out.

    I felt that it would have been nice if the Ruby book had made it more obvious, but perhaps it seemed obvious to the author that Ruby learners would come from a Python/Perl/Ada background?

    ReplyDelete
  6. Islam is from Allah, The Creator and Sustainer of all that exists. As such, it is for Him Alone to command and direct His creation and He Alone is responsible for what He will accept and what He will not accept. It is also for Him Alone to decree what is good and what is bad and He must be the One to show the right path in all things.

    Everything in the life of the creations of Allah, especially for those who have choices to make (us), must be dictated and clarified by the One who Created everything in the fist place. We understand from this that Islam must be a complete and total way of living, explaining everything in the life from beginning to end and from birth until death.

    Islam is careful to remind us that it not a religion to be paid mere lip service; rather it is an all-encompassing way of life that must be practiced continuously for it to be Islam.

    There are other injunctions and commandments which concern virtually all facets of one's personal, family and civic life. These include such matters as diet, clothing, personal hygiene, interpersonal relations, business ethics, responsibilities towards parents, spouse and children, marriage, divorce and inheritance, civil and criminal law, fighting in defense of Islam, relations with non-Muslims, and so much more.

    In conclusion, we are forced to realize that ISLAM is a complete way of life for the human being. Everything is covered in Islam from birth to death to live in the complete submission to the Will of the One Who Created man in the first place.

    To get a better understanding of this beautiful way of life, please visit our blog Understanding Islam---> http://thejourney2islam-team.blogspot.com/

    We hope to see there, peace

    ReplyDelete
  7. "Admin", your screenname is a bit deceptive, however I appreciate that your doing what seems best to you.

    In the interest of tolerance I will leave the above comment as it stands, but perhaps you should have chosen a blog post where it was more relevant?

    You might also want to consider that like Christianity, Islam has many diverse interpretations. Even when you believe your current understanding to be the "only" way, I believe it is always best to let people know that you represent only one point of view.

    ReplyDelete
  8. I agree that, despite what our anonymous friends say, it wasn't much of a rant -- and I still backspace that 'e' (elseif) away all too often.

    Your posting helped get me started on today's quest for a solution to the 'price-as-float' issue, and I just wanted to point out some of the other potential workarounds I found. I detailed my search here, but the simple version is that I ended up going with the dollars_and_cents plugin.

    Thanks for the informative article, and welcome to rails!

    ReplyDelete
  9. TheBlatherskite,

    Thanks for the feedback, it's always nice to know you've helped someone out (kinda makes the hours spent hitting my head against a brick wall worthwhile!).

    I'll bear the dollars_and_cents plugin in mind when doing more Ruby coding (which I haven't for a while).

    ReplyDelete