Handling Money in Rails – Part II

This is Part II in a series of posts that will address the problems and solutions that I have encountered while building an eCommerce application. Part I

If you’re following our series, we’ve built a Payment class that has methods to set and get amount which is a Money type. Now we’ve reached the point of needing to persist data.

To review, our current implementation of Payment is:


# app/models/payment.rb

class Payment
  attr_reader :amount_cents, :amount_currency

  def initialize
    @amount_cents = 0
    @amount_currency = 'USD'
  end

  def amount=(amount_value)
    raise ArgumentError unless amount_value.respond_to? :to_money
    money = amount_value.to_money
    @amount_cents = money.cents
    @amount_currency = money.currency_as_string
    money
  end

  def amount
    Money.new amount_cents, amount_currency
  end
end

A Migration

The first thing we’ll need is a quick migration to actually be able to persist our payments.


class CreatePayments < ActiveRecord::Migration
  def up
    create_table :payments do |t|
      t.integer :amount_cents,    null: false, default: 0
      t.string  :amount_currency, null: false

      t.timestamps
    end
  end

  def down
    drop_table :payments
  end
end

Composition with composed_of

Ok, now we just need to inherit from ActiveRecord::Base and get to saving and retriving this data from our database. We’re also going to make use of composed_of to eliminate our setters and getters. the full documentation is also available at api.rubyonrails.org.

In essense the method is passed 2 parameters:

  1. The name of the attribute, as a symbol
  2. An options hash

The attribute param should be obvious in our case (:amount), and we’ll look at the options in a little more detail.

The first option we’ll look at is the :class_name, which again should fairly obviously be “Money” since we always want our amount to be an instance of Money.


# app/models/payment.rb

composed_of :amount, class_name: 'Money'

Mapping the Attributes

Next, we’ll look at the :mapping option. This one is a little less apparent from the start, but makes a bit of sense as we examine it more closely.

The :mapping key takes an array of arrays. Each array is a one-to-one mapping with the first item being the attribute of the model class, and the second item being the attribute of the target class, in our case: Money.

The 2 attributes we need to set on our Payment class are amount_cents and amount_currency. These match-up with the cents and currency_as_string attributes on the Money class, respectively.

Attribute in Payment Attribute in Money
amount_cents cents
amount_currency currency_as_string

So, about halfway through, our method call should look like this:


# app/models/payment.rb

composed_of :amount,
  class_name: 'Money',
  mapping: [ %w(amount_cents cents), %w(amount_currency currency_as_string) ]

Getter = Constructor

The next option that we need to set is, :constructor. This key takes either a method name or a Proc that is called when getting our amount. The method or Proc is passed arguments for each of the attributes set in our :mapping arrays (amount_cents, and amount_currency).

Notice that the constructor is essentially the same implementation as the original amount method definition.


# app/models/payment.rb

composed_of :amount,
  class_name: 'Money',
  mapping: [ %w(amount_cents cents), %w(amount_currency currency_as_string) ],
  constructor: Proc.new { |cents, currency| Money.new(cents, currency) }

Setter = Converter

The final option that we need to set is, :converter. This key takes either the name of a method on the class that was set in our earlier :class_name option, or a Proc that is called when setting the amount attribute. This Proc will be called with one argument (the new value to assign to amount), and only if the argument passed is not an instance of :class_name (Money).

Ok, we’re finally ready to finish off our method call and have a fully monetized amount attribute for Payment. Again, notice that this is the same implementation as our original amount= method.


# app/models/payment.rb

composed_of :amount,
  class_name: 'Money',
  mapping: [ %w(amount_cents cents), %w(amount_currency currency_as_string) ],
  constructor: Proc.new { |cents, currency| Money.new(cents, currency) },
  converter: Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : raise(
    ArgumentError, "Can't convert #{value.class} to `Money`"
  ) }

Success! We have a Payment class that knows how to represent an amount as actual money, but can save to any database that only knows about integers and strings.

All of the tests from our earlier suite should still be green, and we’ve pushed all of the converting to ActiveRecord.

Hopefully, you’ve learned a little about the not commonly discussed composed_of method, and are already envisioning ways to compose your models of multiple different types of classes without worrying about serialzing data into the database.

While this is can come in very handily, it can also be cumbersome to use in multiple models. For example, we would most definitely be violating DRY if we had classes representing Payment, Product, LineItem, Order that all had essentially the same composed_of method call. This doesn’t even mention the possibility of having multiple attributes on a model that need to monetized.

So, next time we’ll look into creating our own monetize class method to take care of all of this for us.

Handling Money in Rails – Part I

This is Part I in a series of posts that will address benefits of using an actual Money type in your Ruby or Rails projects.

So, you have a working eCommerce application with customers, products, shopping carts, and line items, now you need to start collecting and managing money in droves and droves.

No problem, right? Just add a Payment class with an amount, and start solving the next problem! But what type of attribute should #amount be?

There are many ways to handle representing money in your system: Fixnum, Float, String, but which will work best?

Most tutorials and examples that I see regarding handling money suggest that you use a Fixnum integer to represent the amount in cents of a product price, a payment amount, or any other object that may need to represent money.

It’s also been suggested to use a Float so we get a closer representation of what money typically looks like. However, anytime this approach is used you’ll see extra code needed to handle precision, and to correct rounding errors.

In almost all cases, when displaying this information to a user, the Rails view helper number_to_currency is used. This should be our first hint that something is a bit off. We just asked Rails to display a number as currency… Shouldn’t there be a way to have a currency object that knows how it should display itself?

Also, when dealing with user input, we can expect to receive a wide range of values that all represent the same amount.

  • 15
  • $15
  • 15.00
  • $15.00

Instead of imposing strict input validations, and using something along the lines of validates_numericality_of our users should be able to enter any of the above and have it just work.

There just seem to be a mountain of issues and edge cases that come with using primitive Ruby constructs to represent a non-primitive object.

This is without even mentioning the possibility of needing to handle multiple currencies, or dealing with exchanging one currency for another.

Luckily, we do have access to Money objects in Ruby; there is a widely accepted library [Money][1]. Similar to how you’re already probably handling amounts, this object accepts an amount in cents, and an optional currency type.

A Failing Test

So, let’s get started by writing a quick test to describe how a future Payment class would handle money amounts. We’re going to test various forms of input that we listed earlier that may make their way through our application at some point.


# test/unit/payment_test.rb

require 'test_helper'

class PaymentTest < MiniTest::Unit::TestCase
  def setup
    @payment = Payment.new
    @fifteen_dollars = Money.new '1500', 'USD'
  end

  def test_string
    @payment.amount = '15'
    assert_equal @fifteen_dollars, @payment.amount
  end

  def test_string_with_symbol
    @payment.amount = '$15'
    assert_equal @fifteen_dollars, @payment.amount
  end

  def test_string_decimal
    @payment.amount = '15.00'
    assert_equal @fifteen_dollars, @payment.amount
  end

  def test_string_decimal_with_symbol
    @payment.amount = '$15.00'
    assert_equal @fifteen_dollars, @payment.amount
  end

  def test_integer
    @payment.amount = 15
    assert_equal @fifteen_dollars, @payment.amount
  end

  def test_decimal
    @payment.amount = 15.00
    assert_equal @fifteen_dollars, @payment.amount
  end
end

A fairly straight forward test that should most obviously fail. Our job is, just as obviously, to get it to pass.

Making It Pass

We're going to start with the most basic Ruby object to represent payments in our system and the behavior they need to encapsulate.


# app/models/payment.rb

class Payment attr_reader :amount_cents, :amount_currency

  def initialize
    @amount_cents = 0
    @amount_currency = 'USD'
  end

  def amount
  end

  def amount=(amount_value)
  end
end

This class should get us passed the first rule of TDD: make the test green or change the error message. We have changed the error message, but now it's time to make it green.

Our first step will be to make our application "money aware" by requiring the gem. As always, this is as simple as adding:


# Gemfile

gem 'money', '~> 5.1.0'

Now to implement #amount=. This needs to take an input value, and set the appropriate instance variables to be used when retrieving the amount.

Again the Money gem will handle this for us in most cases. They have very kindly provided #to_money to coerce any Numeric or String object into money. With this help, our method becomes trivial to implement.


# payment.rb

def amount=(amount_value)
  raise ArgumentError unless amount_value.respond_to? :to_money
  money = amount_value.to_money
  @amount_cents = money.cents
  @amount_currency = money.currency_as_string money
end

Since we also know that a Money object just needs an amount in cents and a currency type, again our amount method becomes simple to implement as well.


# payment.rb

def amount
  Money.new amount_cents, amount_currency
end

Just like that, we should have passing tests and an object that knows how to handle money!

That's it for Part I.

Patching Ruby 1.9.3 for Faster Rails Tests

I admit it, I haven’t fully gotten around to stubbing out all Rails classes in my tests. So, while I was writing tests for some older code, I decided to patch Ruby 1.9.3 to see how much I could gain just from that (I also modified a few env variables).

Results below:

13.907secs – 4.733secs = 400+% speed-up!

Not bad for 10mins of install time!

Note: I just added the env variables in my .rvmrc so they’re not loaded anytime I open the shell.

Why I Am Learning to Love TDD

Wait, What?

I know that this might be ahead of my post progression (last time I was still using PHP), but I had this thought go through my head yesterday while working, and thought it would be better to get it out of my system.

Isn’t It Just Writing More?

Yeah, that’s what I used to think too. I thought it was a waste of time, and just one more thing to do. I write a piece of code, fire up the browser, it works or it doesn’t, and go from there.

I went along this path for quite some time, and I’m willing to bet that you already know what would CONSTANTLY happen.

  1. Make a change.
  2. Test it in the browser.
  3. Works 🙂
  4. Commit
  5. Deploy
  6. OH %$&$*, I FORGOT ABOUT THAT OTHER CLASS!
  7. Make a change….

I knew that this is why I was supposed to test, but I still didn’t really get it. I tried Cucumber, and it seemed like I was spending so much time writing features for trivial tasks like signing-in.

I didn’t know how to write a good test. It was just more work that was slowing me down, and it was actually easier to just deal with the headaches of unexpected consequences.

So, What Changed?

I started to finally grasp RSpec. I still wrote “bad” tests, but they did eliminate some of problems.

I started to see why this was important, and how it would help me avoid those errors where I just forgot that there a method was being used in multiple places.

The problem I still had were the “easy things.”

Do I really need to test adding a full_name method on a class? I’m just making a small change, and I know it’s fine, it’s no big deal to just change it.

95% of the time, this was probably true. I did know what I was doing with a small change, it it wouldn’t hurt anything to just do it. Was it worth going through the whole process just for that last 5%?

What turned me was another habit that this led into: too many times I found myself saying, “I can just change it on the server, and update my code locally later.”

I don’t have to tell you why this is a horrible idea.

Where I Am Today

I thought to write this post today, because yesterday I started to think that I’m starting to “get it,” with TDD. I was writing some simple methods that would send confirmation emails, and I was disciplined in writing a failing test, making it green, then moving forward.

Then came the time for the second method that just sent a different type of email… “Well, I’m awfully close to just copying the first… just write the method and you can test it later.”

“NOOO! Just take the 2 minutes and write a failing test first, you know this!”

And I did. And it took less than 2 minutes. And I felt more confident that everything worked.

I had my tests passing and was ready to deploy, when I had almost forgot to run the entire suite. OK, OK, I’ll run it first.

“Wait, a failing test? Why? Oh, it’s because I that other controller spec calls it too.”

I go in to update it and remember that I wrote the spec a long time ago and it’s very tightly coupled. This is going to need more than a quick change on 1 or 2 lines… “I know where it is, I can just deploy and refactor the spec tomorrow when I have more time.”

“NOOO!”

I think I am finally learning to love TDD

All nationalists have the power of not seeing resemblances between similar sets of facts. A British Tory will defend self-determination in Europe and oppose it in India with no feeling of inconsistency. Actions are held to be good or bad, not on their own merits, but according to who does them, and there is almost no kind of outrage — torture, the use of hostages, forced labour, mass deportations, imprisonment without trial, forgery, assassination, the bombing of civilians — which does not change its moral colour when it is committed by ‘our’ side. The nationalist not only does not disapprove of atrocities committed by his own side, but he has a remarkable capacity for not even hearing about them.

George Orwell

How I Started to Learn Ruby – Part II


<?php
  session_start();
?>

Where we left off… I had just started to learn PHP/MySQL to run my own online registration system for a couple of camps that I was working at.

I had just enough tools to do some real damage, and, it, was, awesome!

I really couldn’t believe that I used to spend my time building tables of data by hand! Reusable data, real templates, dynamic interaction; with a little time I could do it all.

Keep in mind, if I wanted to insert a row into a campers table in my database, I had to do this:


$query = sprintf("INSERT INTO campers (account_username, account_password, camper_email, camper_lastname, camper_firstname, overnightday, camper_address_street, camper_address_city, camper_address_state, camper_address_zip, camper_primaryphone, camper_secondaryphone, camper_mothername, camper_fathername, camper_highschool, camper_highschool_address_city, camper_highschool_address_state, camper_height, camper_weight, camper_age, camper_grade, camper_position, camper_insuranceco, camper_insuranceco_address, camper_insuranceco_policy, camper_insuranceco_group, camper_insuranceco_id, campfee, deposit1, deposit1date, totalpaid, totaldue, onlinecamper, shuttle, camper_canceled) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')",
        mysql_real_escape_string($account_username),
        $account_password,
        mysql_real_escape_string($camper_email),
        mysql_real_escape_string($camper_lastname),
        mysql_real_escape_string($camper_firstname),
        $overnightday,
        mysql_real_escape_string($camper_address_street),
        mysql_real_escape_string($camper_address_city),
        mysql_real_escape_string($camper_address_state),
        mysql_real_escape_string($camper_address_zip),
        mysql_real_escape_string($camper_primaryphone),
        mysql_real_escape_string($camper_secondaryphone),
        mysql_real_escape_string($camper_mothername),
        mysql_real_escape_string($camper_fathername),
        mysql_real_escape_string($camper_highschool),
        mysql_real_escape_string($camper_highschool_address_city),
        mysql_real_escape_string($camper_highschool_address_state),
        mysql_real_escape_string($camper_height),
        mysql_real_escape_string($camper_weight),
        mysql_real_escape_string($camper_age),
        mysql_real_escape_string($camper_grade),
        mysql_real_escape_string($camper_position),
        mysql_real_escape_string($camper_insuranceco),
        mysql_real_escape_string($camper_insuranceco_address),
        mysql_real_escape_string($camper_insuranceco_policy),
        mysql_real_escape_string($camper_insuranceco_group),
        mysql_real_escape_string($camper_insuranceco_id),
        $campfee,
        $deposit1,
        $deposit1date,
        $totalpaid,
        $totaldue,
        $onlinecamper,
        $shuttle,
        $camper_canceled);
$result = mysql_query($query);

As you can imagine, managing data like this without stored procedures, without an ORM can be incredibly time consuming, monotonous and error prone. Spread this among even a very small application, and I was right back ready to call it quits.

Again, this wasn’t fun any more. I went from solving real problems to pulling my hair out for an hour until I noticed that I misspelled “camper,” forgot a comma, or some stupid mistake.

A Light From Above

By this point, I had bought a second book trying to make sure I was processing payments properly. In the book there were a couple of pages mentioning “frameworks.” This sounded like JUST what I needed!

I had messed around with PEAR a little bit, but never found it to be much help. I didn’t really care for the packaging system you had to install, and to be honest really had no idea how to use it properly. These mystical “frameworks,” however, seemed like they could be a big help.

After reading up a little bit more, I had narrowed the choices down to 2: CakePHP and the Zend Framework.

I went through the basic tutorials for both to see which would work best for me, and it basically came down to 2 things:

  1. The basic CakePHP tutorials all seemed to be about building a blog. As I went through, it felt almost like doing a WordPress installation.
  2. Zend was heavily involved in the development of PHP.

If you couldn’t guess, I went with the Zend Framework. (By the way, I do now know that CakePHP is MUCH more than a blogging machine).

Adapters, Authorization, ACL, ORMS and….. MVC

I have to say, the Zend Framework really taught me a ton about the web. I started to REALLY learn about public directories, permissions, access control, mapping objects to database rows, and on, and on, and on.

Once again, I was able to shake myself from a rut by learning something new and becoming much more productive (you may notice a pattern here).

On I went, building multiple apps with the Zend Framework that were much more efficient, and I felt much more confident in their ability to perform tasks properly.

Along the way, I had also started to pick up this decades old patter, MVC. I still didn’t really “get” it, but it did help me separate my data, and have some form of organization in my projects.

Life was good.

And One Video Changed Me Forever

One odd day, I realized that I may have counted out CakePHP too soon, so I went to read a couple of other peoples’ reviews. Along the way, I saw that CakePHP was often being compared to something I hadn’t heard of before, Ruby on Rails.

Of course, I had to see this for myself, so off I went to their website. There I saw the original, Build a Blog Engine in 15 Minutes video.

After watching this video, I HAD NO IDEA WHAT WAS GOING ON.

Matrix Terminal

I saw this guy, DHH yell, “whoops” as he whipped together an application. I was watching text fly by like the Matrix, and felt completely overwhelmed.

That day I went back to my safe home of PHP and the very helpful Zend Framework. Work continued for some time, but I never forgot about that video…

Ford explained, “On that world, the people are people. The leaders are lizards. The people hate the lizards and the lizards rule the people.”

“Odd,” said Arthur, “I thought you said it was a democracy.”

“I did,” said ford. “It is.”

“So,” said Arthur, hoping he wasn’t sounding ridiculously obtuse, “why don’t the people get rid of the lizards?”

“It honestly doesn’t occur to them,” said Ford. “They’ve all got the vote, so they all pretty much assume that the government they’ve voted in more or less approximates to the government they want.”

“You mean they actually vote for the lizards?”

“Oh yes,” said Ford with a shrug, “of course.”

“But,” said Arthur, going for the big one again, “why?”

“Because if they didn’t vote for a lizard,” said Ford, “the wrong lizard might get in.

The Hitchhiker’s Guide to the Universe