bitfluent

Kamal Fariz Mahyuddin on Ruby, Rails, Apple and being a Dad.

Reach me by email.
May
2nd
Fri
permalink

An update in a really long while

Woah, one month with no updates.

I started contracting for a startup in Seattle, WA since beginning of April and have been neglecting to update stuff. It’s pretty nice here plus I get to access things that people here take for granted like attending Seattle.rb hack nights, Pandora, Hulu and this weekend, Barcamp Portland!

See you guys in a bit.

Apr
1st
Tue
permalink
Mar
30th
Sun
permalink

Updating Counter Cache in Migrations

I’m nearly complete with an app I’ve been working on, so I thought I’d dedicate some time for optimization. One of the first things I did to my models was to add counter caches so that performing counts on my association were fast.

First try,

class AddCommentCounterCacheOnTopics < ActiveRecord::Migration
  def self.up
    add_column :topics, :comments_count, :integer, :default => 0
  end

  def self.down
    remove_column :topics, :comments_count
  end
end

Very standard. However, it initializes the column to 0, effectively “losing” all my comments (they’re there in the DB, but Rails adds an additional optimization whereby it won’t fetch the association if the counter cache is 0). Looks like I need to update the comments_count column to whatever it was at the time of migration.

Second try,

class AddCommentCounterCacheOnTopics < ActiveRecord::Migration
  def self.up
    add_column :topics, :comments_count, :integer, :default => 0

    Topic.find(:all).each do |t|
      t.comments_count = t.comments.count
      t.save!
    end
  end

  def self.down
    remove_column :topics, :comments_count
  end
end

Two things to note: First, I’m using the #count method in t.comments.count because calling #size will use the value in t.comment_count which is 0. #count on the other hand will perform a SQL count(). Second, this won’t work! Why? Because counter cache columns are set to attr_readonly.

To work around this, a little hack. Final result,

class AddCommentCounterCacheOnTopics < ActiveRecord::Migration
  def self.up
    add_column :topics, :comments_count, :integer, :default => 0

    def Topic.readonly_attributes; nil end # A little evil hack

    Topic.find(:all).each do |t|
      t.comments_count = t.comments.count
      t.save!
    end
  end

  def self.down
    remove_column :topics, :comments_count
  end
end
Mar
24th
Mon
permalink
Excellent panel on scaling customer support. Definitely something to take into account whenever you think of launching a new product out there. Are you too caught up in the idea and the development, forgetting the most important ingredient of your success: the users?
Mar
20th
Thu
permalink

My First Podcast Mention!

It’s the first time I’ve ever gotten mentioned in a podcast! It’s on Episode 023 of the Rails Envy Podcast for my blog post on using the Fire Eagle gem with Rails.

Download the episode and fast forward to 11:24 :)

Mar
18th
Tue
permalink

Google Gears for Safari Soon?

Adds support for offline storage for Web applications in SQL databases link ยป
- from About the Safari 3.1 Update via sharedcopy.com

permalink
Jaws drop How it recovered from the guy kicking it and later slipping on ice was freaking amazing! via SvN
Mar
17th
Mon
permalink

#ruby-lang

  • ddfreyne: Somebody decided that the best way to let all tests pass... is to comment out the tests that fail
  • ddfreyne: ...
  • kamal_fariz: i'd go one further and mock rspec's expectation matcher to return true
  • ddfreyne: heh
Mar
14th
Fri
permalink
Mar
11th
Tue
permalink

Interfacing a Rails App to Fire Eagle Pt 1: Exploration via IRB

Here are my notes as I explore interfacing a Rails app to Fire Eagle. Tokens have been masked to protect the innocent :)

  1. Create a new application.
  2. Get the fireeagle gem.

    MacBook-Pro:src kamal$ git clone git://github.com/kamal/fireeagle.git fireeagle
    Initialized empty Git repository in /Users/kamal/src/fireeagle/.git/
    remote: Generating pack...
    remote: Done counting 377 objects.
    remote: Deltifying 377 objects...
    remote:  100% (377/377) done
    remote: Total 377 (delta 192), reused 0 (delta 0)
    Receiving objects: 100% (377/377), 75.91 KiB | 9 KiB/s, done.
    Resolving deltas: 100% (192/192), done.
    MacBook-Pro:src kamal$ cd fireeagle
    MacBook-Pro:fireeagle(master) kamal$ rake install_gem
    (in /Users/kamal/src/fireeagle)
    sudo gem install --local pkg/*.gem
    Successfully installed fireeagle-0.6.1
    1 gem installed
    Installing ri documentation for fireeagle-0.6.1...
    Installing RDoc documentation for fireeagle-0.6.1...
    
  3. Fire up irb and instantiate a client with the Consumer Key and Secret provided by Step 1.

    MacBook-Pro:~ kamal$ irb
    >> require 'fireeagle'
    => true
    >> client = FireEagle::Client.new(
      :consumer_key    => 'AAAAAAAAAAAA',
      :consumer_secret => 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB')
    => #<FireEagle::Client:0x1bef504 ...>
    
  4. Generate a Request Token and associate it with the user because we’ll need to locate who authenticated our app.

    >> client.get_request_token
    => #<OAuth::Token:0x1bddb24 @secret="emfPurZAbYlRqNL7fSxhXOkxCJRZ2T1r", @token="mGxmGcGPNyjr">
    >> current_user.update_attributes!(
      :request_token        => client.request_token.token,
      :request_token_secret => client.request_token.secret)
    
  5. Redirect the user to the Authorization URL.

    >> client.authorization_url
    => "https://fireeagle.yahoo.net/oauth/authorize?oauth_token=mGxmGcGPNyjr"
    
  6. After the user grants us permission, Fire Eagle will redirect the user’s browser to our Callback URL (defined in Step 1) with the same oauth_token params.

    http://yourapp.example.com/your_call_back_path?oauth_token=mGxmGcGPNyjr
    
  7. Find the User by the oauth_token and instantiate a new client (client2 in our IRB session) with the request token and secret.

    >> user = User.find_by_request_token(params[:oauth_token])
    >> client2 = FireEagle::Client.new(
      :consumer_key         => 'AAAAAAAAAAAA',
      :consumer_secret      => 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB',
      :request_token        => user.request_token,
      :request_token_secret => user.request_token_secret)
    
  8. Convert the Request Token to a long-lived Access Token and save it. Optionally, you can nil out the Request Token because you are not going to need them anymore.

    >> client2.convert_to_access_token
    => #<OAuth::Token:0x23c4824 @token="CCCCCCCCCCCC", @secret="DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD">
    >> user.update_attributes!(
      :access_token         => client2.access_token.token,
      :access_token_secret  => client2.access_token.secret
      :request_token        => nil
      :request_token_secret => nil)
    
  9. Go to town! From here on out, you can directly instantiate a client using the user’s Access Token to read or update their location.

    >> client3 = FireEagle::Client.new(
      :consumer_key        => 'AAAAAAAAAAAA',
      :consumer_secret     => 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB',
      :access_token        => user.access_token,
      :access_token_secret => user.access_token_secret)
    >> client3.update(:q => 'petaling jaya')
    => #<FireEagle::Response:0x2549a8c ...>
    >> client.user.best_guess.name
    => "Petaling Jaya, Malaysia"
    

A lot of the stuff above can be extracted into a Rails plugin. First dibs on acts_as_hatchling!