Rss Feed Tweeter button Facebook button Technorati button Reddit button Linkedin button Webonews button Delicious button Digg button Flickr button Stumbleupon button Newsvine button

A Waage Blog

Ruby, Rails, Life

Ruby floats, BigDecimals and money (currency)

without comments

Fellow Ruby-ers, please be warned!!! DO NOT use Ruby floats when performing arithmetic calculations involving money!

My calculations work in IRB, so I was really confused when I ran into this weird situation where (what I thought was) a simple arithmetic calculation led to strange results in my unit tests (I cannot stress the importance of good unit testing!).

My backend calculation was basically this (simplified):

# arbitrary amounts for these two variables
percentage = 12
total_in_cents = 400

discount = percentage.to_f / 100.0
total_in_float = total_in_cents.to_f * 100.0
new_price = (total_in_float * discount ).round / 100

Now, it’s pretty obvious that 12% of (400 cents) $4.00 should just be $0.48 (48 cents)
However, my barrage of unit tests kept producing strange results where a simple calculation was returning incorrect results. Doing some research, I discovered a series of articles worth reading including:

Also, check out the Money gem – I’ve never used it personally, but people have said good things about it.

Heeding the advice I found online, I re-wrote all my money-related calculations using BigDecimals instead of Floats.

percentage = 12
total_in_cents = 400
discount = BigDecimal(percentage.to_s) / 100
total_in_float = BigDecimal(total_in_cents.to_s) * 100
new_price = (total_in_float * discount ).to_i / 100

After switching over from Floats to BigDecimals, my unit tests all passed!
Lesson learned and hope this heads-up helps you guys too.

Summary:
Use BigDecimals for money calculations and remember to write good UNIT TESTS!!

Written by Andrew Waage

November 9th, 2011 at 5:29 pm

Git – Push a branch to remote repository

without comments

So you just created a new branch locally. You put in 200 hours into this branch, and then realize that the only copy of all these changes is on your MacBook! Don’t get scared, just push the branch to your remote server.

It’s easy:

$> git push -u <remote-name> <branch-name>

Or, usually:

$> git push -u origin branch-name

Written by Andrew Waage

August 26th, 2011 at 1:23 pm

Posted in Git and SVN

Tagged with , , ,

Rails 3 RSpec Request Spec – Testing Subdomains

without comments

How do you test sub-domains in RSpec Request specs (integration tests) ???

# Pass it into the GET request!
get '/programs/100', nil, {'HTTP_HOST' => 'sub.domain.com'}

The 3rd parameter to the get method is a hash of HTTP headers.
See the Rails API documentation for details.

Note:
Depending on the type of test you are working with (support / controller / request / integration etc.) you pass in the sub domain differently.

Here’s some good reference posts on Stack Overflow for setting subdomains in controller specs:
1. Rails RSpec Set Subdomain

# Set the @request.host in a before block
before(:each) do
  @request.host = "#{mock_subdomain}.example.com"
end

2. Subdomains in RSpec Controller Tests

 # I haven't tried this, and not sure you would need to mock out the current_subdomain method.
  @subdomain = 'sub.domain.com'
  controller.expects(:current_subdomain).returns(@subdomain)
  @request.host = "#{@subdomain}.test.host"

Written by Andrew Waage

July 11th, 2011 at 3:43 pm

RSpec – Running One Single Test at a Time

without comments

In the old days I would pass a regular expression to run a particular unit test or group of similarly named unit tests by name.

Here’s the easy way to run one test in RSpec… by line number!
Look at the line-number of any RSpec block (it, describe, etc), and simply run the rspec command, passing in the [filename]:[line number]:

$ rspec models/user_spec.rb:27

Happy testing!

Written by Andrew Waage

July 11th, 2011 at 3:03 pm

Rails Rotating Log Files with logrotate

with one comment

I know there’s a way to specify Rails log rotation parameters directly in the app. This works for some people:

# Can place this in environment.rb
# 2nd argument - number of log files to keep
# 3rd argument - size (bytes) that log files are allowed to reach before rotation
config.logger = Logger.new(config.log_path, 8, 1024)

However…. I like the customizability of using logrotate better!
Here’s my logrotate config file that handles weekly log rotation, delayed compression and uses the copy-truncate method:

I place this config in the /etc/logrotate.d folder (ubuntu)
(ie. /etc/logrotate.d/<rails_app_name>)

/var/www/rails//shared/log/production.log {
  weekly
  missingok
  rotate 8
  compress
  delaycompress
  notifempty
  copytruncate
}

This config will rotate my production.log file weekly, keeping at most 8 log files. It delays compression until next rotation (extra precaution, simply to make sure the log file is not in use), and uses the ‘copytruncate’ method which basically copies the current log file, and then truncates this log file, so the Rails app maintains file pointer for continued writing.

Written by Andrew Waage

June 15th, 2011 at 11:56 am