Archive for November, 2010

Fixing the “You have to install development tools first” error with Nokogiri.

Friday, November 26th, 2010

After wracking my brains around a strange issue installing Nokogiri, wmoxam in #rubyonrails and I figured out how to get it to install.

Problem: You go to install nokogiri, either with bundler or with a straight “gem install nokogiri”, and you get an error like the following:

In your wisdom, you decide to jump over to Aaron Paterson’s Installing Nokogiri page, where he tells you to install the libs, libxml2 and libxslt, manually and configure the gem install to those libs with flags … but it still doesn’t work.

Solution: You are probably using RVM, right? Yeah, we’ll when you installed ruby 1.9.* with it, it was probably old. Update your rvm install and re-install ruby with the new RVM version, and you will be green.

1
2
3
4
5
6
7
8
rvm update head
rvm --force install 1.9.2
gem install bundle

## in your rails app
bundle
Installing nokogiri (1.4.2) with native extensions
 ...

Boom!

Tags: , ,
Posted in Information | 12 Comments »

ReadOnly needs to be set to true or false, not :true or :false

Thursday, November 18th, 2010

Just taking some time to let you in on another gotcha. Hopefully, this saves people some aggravation some day.

While working on Kludge, we took a RESTful approach and built our controller like so:

1
2
3
4
5
class ProjectsController < ApplicationController
  def update
    @project = Project.find(params[:id]).update_attributes(params[:project])
  end
end

While this works, it doesn’t offer you any protection that evil users may try to update projects that don’t belong to them. A better approach is to scope the projects to that belong to the account. So we updated the action to the following:

1
2
3
4
5
class ProjectsController < ApplicationController
  def update
    @project = current_account.projects.find(params[:id]).update_attributes(params[:project])
  end
end

…and we got an error!

1
2
ActiveRecord::ReadOnlyRecord (ActiveRecord::ReadOnlyRecord):
  app/controllers/projects_controller.rb:45:in `update'

This error arises because Rails makes all associations read-only. Since projects is now an association to account, we need to force readonly to false in the account model:

1
2
3
4
class Account < ActiveRecord::Base
  has_many :clients
  has_many :projects, :through => :clients, :readonly => :false
end

However, this will still throw the same error message! After googling, IRC-ing, and crying … I finally got the answer through brute-force trial and error. Turns out, :readonly does not accept the symbol :false, and only the literal false. Changing the following will make your test green:

1
2
3
4
class Account < ActiveRecord::Base
  has_many :clients
  has_many :projects, :through => :clients, :readonly => false # <-- no longer a symbol
end

Tags: ,
Posted in Code | 2 Comments »

Before Type Cast

Saturday, November 13th, 2010

While working on Kludge, I ran into a bit of a gotcha that I thought I’d share with everyone.

In our invoicing system, we have a Invoice model that has many LineItems. These line items, have a quantity attribute, and a quantity_type. This way, your line item can say “10 hours”, or “5 products”. When storing a quantity of hours, we wanted to actually store minutes (like 150), and then do some work on the reader when accessing that value (like 1.5) only when the quantity_type was :hour.

The Code

We thought it would be best to run a simple before_validation that checks whether or not the quantity being saved was to be saved in hours, and if so, convert it to minutes. Something like this:

1
2
3
4
5
6
class LineItemTest < ActiveSupport::TestCase
  def test_quantities_change_based_on_quantity_type_when_read
    @li = LineItem.create(:quantity => 0.5, :quantity_type => :hour)
    assert_equal(30, @li.read_attribute(:quantity))
  end
end

Our before validation looks like this:

1
2
3
4
5
6
7
8
9
10
11
class LineItem < ActiveRecord::Base

  before_validation :convert_quantity


private
  def convert_quantity
    logger.warn("-----#{quantity}")
    self.quantity = (quantity_type.to_sym == :hour) ? (quantity * 60).to_i : quantity
  end
end

This code was failing, and looking at our logs showed something strange:

1
2
-----0
  SQL (0.4ms)  INSERT INTO "line_items" ("created_at", "description", "invoice_id", "price_in_cents", "quantity", "quantity_type", "updated_at") VALUES ('2010-11-13 19:04:25.813989', NULL, NULL, NULL, 0, 'hour', '2010-11-13 19:04:25.813989')

Note the “—–0″: that zero is the value we are getting for quantity, before we even run any code. So what’s wrong?

Typecasting and *_before_type_cast

As it turns out, ActiveRecord will typecast your data before you get to it, and since our quantity column is an integer, the value of 0.5 was cast into 0. To correct this error, you can use the dynamic method *_before_type_cast to get the value …. well …. before type casting.

Updating the validation to:

1
2
3
4
private
  def convert_quantity
    self.quantity = (quantity_type.to_sym == :hour) ? (quantity_before_type_cast * 60).to_i : quantity
  end

… gave us our passing results. Hope this helps!

Tags:
Posted in Uncategorized | No Comments »