I'm a Ruby on Rails / jQuery web developer. Follow me at @sikachu

Attack of the Droids

December 25th, 2010 Posted in Gadgets, Garbage | No Comments »

(อ้างถึง http://www.isriya.com/node/3426/attack-of-the-droids)

TL;DR You bought an Android phone from store today, it might be outdated by next week. Also, you have to do a lot of comparison as you’re buying a PC.

ผมมองว่าการที่โทรศัพท์มือถือ Android ออกรุ่นใหม่ๆ เยอะมาก จากหลายๆ ค่าย มันทำให้เรารู้สึกว่าเครื่องเราดูตกรุ่นเร็วขึ้น อย่างเช่นสมมุติคุณซื้อ HTC Hero มา ผ่านไปสามสี่เดือนมีเครื่องรุ่นใหม่ออกมาซะแล้ว ทำอะไรได้มากกว่า และอาจจะรองรับ ROM รุ่นใหม่กว่าได้ ถ้าคุณยังต้องการที่จะได้รับประสบการณ์ที่ดีที่สุดจากการใช้ Android คุณก็คงต้องเปลี่ยนเครื่องใหม่

กลับกัน ถ้าโทรศัพท์ที่ใช้ Android เปลี่ยนใหม่ ออกเป็นรายปี หรือรายครึ่งปี คุณซื้อโทรศัพท์เครื่องหนึ่งคุณก็จะมั่นใจว่ามันจะเป็นเครื่องที่ดีที่สุดไปอีกซัก 6 เดือน – 1 ปี ก่อนที่คุณจะเห็นเครื่องรุ่นใหม่ที่น่าสนใจจะซื้อ

คิดว่า Android คงอยากจะเดินไปทางเดียวกับ Symbian ในการที่เพิ่มจำนวนของเครื่องที่ใช้ platform นี้ให้มากที่สุด หารู้ไม่ว่ามันกลับเพิ่มความสับสนให้ผู้ใช้เป็นอย่างมากในการซื้อเครื่อง เพราะคุณจะต้องมานั่งเทียบ spec แบบซื้อคอมพิวเตอร์เครื่องหนึ่งเลยทีเดียว แทนที่จะไม่ต้องคิดมากแล้วซื้อแค่ “รุ่นที่ใหม่ที่สุด” ไปเลย

Hoptoad ❤ Heroku

December 15th, 2010 Posted in My Project, Ruby, Ruby on Rails | No Comments »

Recently, I’ve started to migrate my application to Heroku. While most of the functionalities are there, one of the thing I was missing is Hoptoad‘s deploy tracking, as you cannot add a deploy hook to call Rake task as you normally do.

After I found that Heroku actually supports HTTP Post hook, I spend 2 hours reading API and hacking into hoptoad_notifier code. And now, I would like to presents you Hoptoad ❤ Heroku.

What is it?

It’s a middleware application that will converts parameters from Heroku’s HTTP Post hook to parameters that Hoptoad would understand.

How to use it?

You just add the HTTP Post deploy hook to your Heroku project. It should look something like this:

heroku addons:add deployhooks:http url=http://hoptoad-loves.heroku.com/YOUR_HOPTOAD_API_KEY/YOUR_RACK_ENV

You’d then substitute YOUR_HOPTOAD_API_KEY and YOUR_RACK_ENV to match your configuration.

You can find more information on GitHub or the site itself: http://hoptoad-loves.heroku.com/

(Please note that Hoptoad deploy tracking feature is only available in their paid plans.)


I hope you found it useful for your project. All of the code is hosted on GitHub. Contributions and feedbacks are always welcome.

My Devise custom routes

December 13th, 2010 Posted in My Idea, Programming, Ruby, Ruby on Rails | 6 Comments »

Since the release of Rails 3, I’ve been using Devise as my authentication gem. While it delivers everything I need, its default route doesn’t look really good when I have only one authentication scope, such as User. The default route when you’re using devise_for :users will look like this:

         new_user_session GET    /users/sign_in(.:format)          {:action=>"new", :controller=>"devise/sessions"}
             user_session POST   /users/sign_in(.:format)          {:action=>"create", :controller=>"devise/sessions"}
     destroy_user_session GET    /users/sign_out(.:format)         {:action=>"destroy", :controller=>"devise/sessions"}
            user_password POST   /users/password(.:format)         {:action=>"create", :controller=>"devise/passwords"}
        new_user_password GET    /users/password/new(.:format)     {:action=>"new", :controller=>"devise/passwords"}
       edit_user_password GET    /users/password/edit(.:format)    {:action=>"edit", :controller=>"devise/passwords"}
                          PUT    /users/password(.:format)         {:action=>"update", :controller=>"devise/passwords"}
 cancel_user_registration GET    /users/cancel(.:format)           {:action=>"cancel", :controller=>"devise/registrations"}
        user_registration POST   /users(.:format)                  {:action=>"create", :controller=>"devise/registrations"}
    new_user_registration GET    /users/sign_up(.:format)          {:action=>"new", :controller=>"devise/registrations"}
   edit_user_registration GET    /users/edit(.:format)             {:action=>"edit", :controller=>"devise/registrations"}
                          PUT    /users(.:format)                  {:action=>"update", :controller=>"devise/registrations"}
                          DELETE /users(.:format)                  {:action=>"destroy", :controller=>"devise/registrations"}
        user_confirmation POST   /users/confirmation(.:format)     {:action=>"create", :controller=>"devise/confirmations"}
    new_user_confirmation GET    /users/confirmation/new(.:format) {:action=>"new", :controller=>"devise/confirmations"}
                          GET    /users/confirmation(.:format)     {:action=>"show", :controller=>"devise/confirmations"}

As you can see, all of the routes are defined under /users path. So, I’ve modified my devise route block to this:

devise_for :users, :skip => [:registrations, :sessions] do
  # devise/registrations
  get 'signup' => 'devise/registrations#new', :as => :new_user_registration
  post 'signup' => 'devise/registrations#create', :as => :user_registration
  get 'users/cancel' => 'devise/registrations#cancel', :as => :cancel_user_registration
  get 'users/edit' => 'devise/registrations#edit', :as => :edit_user_registration
  put 'users' => 'devise/registrations#update'
  delete 'users/cancel' => 'devise/registrations#destroy'
 
  # devise/sessions
  get 'signin' => 'devise/sessions#new', :as => :new_user_session
  post 'signin' => 'devise/sessions#create', :as => :user_session
  get 'signout' => 'devise/sessions#destroy', :as => :destroy_user_session
end

Which will yield these routes:

    new_user_registration GET    /signup(.:format)                 {:action=>"new", :controller=>"devise/registrations"}
        user_registration POST   /signup(.:format)                 {:action=>"create", :controller=>"devise/registrations"}
 cancel_user_registration GET    /users/cancel(.:format)           {:controller=>"devise/registrations", :action=>"cancel"}
   edit_user_registration GET    /users/edit(.:format)             {:controller=>"devise/registrations", :action=>"edit"}
                    users PUT    /users(.:format)                  {:action=>"update", :controller=>"devise/registrations"}
             users_cancel DELETE /users/cancel(.:format)           {:controller=>"devise/registrations", :action=>"destroy"}
         new_user_session GET    /signin(.:format)                 {:action=>"new", :controller=>"devise/sessions"}
             user_session POST   /signin(.:format)                 {:action=>"create", :controller=>"devise/sessions"}
     destroy_user_session GET    /signout(.:format)                {:action=>"destroy", :controller=>"devise/sessions"}
            user_password POST   /users/password(.:format)         {:action=>"create", :controller=>"devise/passwords"}
        new_user_password GET    /users/password/new(.:format)     {:action=>"new", :controller=>"devise/passwords"}
       edit_user_password GET    /users/password/edit(.:format)    {:action=>"edit", :controller=>"devise/passwords"}
                          PUT    /users/password(.:format)         {:action=>"update", :controller=>"devise/passwords"}
        user_confirmation POST   /users/confirmation(.:format)     {:action=>"create", :controller=>"devise/confirmations"}
    new_user_confirmation GET    /users/confirmation/new(.:format) {:action=>"new", :controller=>"devise/confirmations"}
                          GET    /users/confirmation(.:format)     {:action=>"show", :controller=>"devise/confirmations"}

I think my new routes is much better. For example, I think user would prefer a path to sign in page to be /signin than /users/sign_in, as it more memorable.

I hope this post would inspire you more about customize Devise’s routes to fit your need.

`method_missing` call stack in Ruby

November 23rd, 2010 Posted in Programming, Ruby | 2 Comments »

Just found out something cool from reading “Don’t Know Metaprogramming In Ruby?” from RubyLearning blog. Consider this code snippet:

class Person
end
 
class Student < Person
end
 
class HighSchoolStudent < Student
end

If you’re calling some method that’s undefined, let’s say we’re calling HighSchoolStudent#name Ruby will try to call the method in this order:

  1. HighSchoolStudent#name
  2. Student#name
  3. Person#name
  4. Object#name
  5. Kernel#name
  6. BasicObject#name
  7. HighSchoolStudent#method_missing
  8. Student#method_missing
  9. Person#method_missing
  10. Object#method_missing
  11. Kernel#method_missing
  12. BasicObject#method_missing

From this example, you can see that not explicitly define method and capture it in method_missing might not be good in terms of the performance. So, what should you do?

I think the best way to do it, if you know that this method will get called again, would be define the ‘real’ method after it gets called. That will reduce the call stack starting from the second run, resulting in faster code.

# Let say we're defining method for #*_with_id here
def method_missing(name, *args)
  super if name !~ /_with_id$/
 
  define_method "#{name}_with_id" do
    instance_variable_get(:id) + "-" + instance_variable_get(:name)
  end
end

Hope this will make your code run faster, while still DRY.

Gems — Follow up from my talk at #BarcampBKK4

October 29th, 2010 Posted in Programming, Ruby, Ruby on Rails | No Comments »

I think I should noted down two gems that I mentioned in my talk.

  • Omniauth — a middleware that help you authenticate user to various SSO sites, such as Facebook Connect, Twitter, Google Account
  • Devise — fully-functional authentication system. If you write an app that need authentication, this should be the solution for you. Writing your own authentication is discourage now. More on this later.

I feel like I was saying 3 of them, but I can’t remember the other one. Anyway, don’t forget to dig into RubyGems to find out some great gem to use in your project.

10 Things you should know about Ruby — #BarcampBKK4

October 24th, 2010 Posted in Programming, Ruby | 2 Comments »

I’ve done a talk about Ruby in Barcamp Bangkok 4 on Oct 24. Here is the slide in case someone want it.

You can download it if you want. This slide is CC-BY-NC-SA (as some photo I use require share-alike license.)

One good question actually was the difference between module and class. Actually, it just that simple — you cannot create object from a module.

Module exists in Ruby to help you DRY up your code.

When you write several classes which have the same functionality, you better take that portion of the code out and put it in a module and include them back into your classes. That’s actually something Ruby coder loves to do — DRY up your code, and make it more maintainable.


If you have any more question, feel free to post them in the comment section. I actually fumbled on my word a lot, because I didn’t get enough sleep last night. If you also want me to clearify something up, please let me know. Anyway, I hope you enjoyed it.

My predictions on Apple’s ‘Back to Mac’ event

October 20th, 2010 Posted in Apple | No Comments »

Well, after all the rumors, I can’t helped but predict something that Mr. Jobs will reveal today:

Macbook Air Refresh

I think this should definitely come, after several leaks of the machine. I think Apple will still offer the machine in both 11″ and 13″, but several parts in it would be proprietary and not user-replaceable. I also predicting that the 11″ version will have the same resolution as the current 13″ Macbook pro — delivers more resolution than it predecessor.

Macbook Pro Refresh

It makes sense as the current Macbook Pro are reaching the end of cycle. I expecting just a speed bump from the previous version — making the top of the line to be 2.8 GHz Core i7-640, which can be turbo-boosted to 3.46 GHz. I really saw this one coming from reading Engadget’s post a while back.

Mac OS X 10.7 “Lion”

I think we’ll able to see the “sneak-peak” version of the OS which introduce several improvements from the current Snow Leopard. I’m expecting a new finder interface, with the new iChat that can talk to iPhone via FaceTime.

iLife ’11

This one will be announced for sure, and I think we might seeing a new application added to the suite. I think this will be available right after the keynote.

White iPhone 4

I put it here just for Christ’s sake — it has been too long. Anyway, I think Apple could use a little moment to talk about it, even the event’s name is “Back to Mac”. I don’t know what would be a good time to talk about it if they’re going to launch the phone before Christmas.


That’s it for my prediction. Be prepared to tuned in to Apple’s streaming at midnight tonight! (GMT+7)

RailsCamp (Thailand) 2010

October 20th, 2010 Posted in News, Programming, Ruby, Ruby on Rails | 1 Comment »

เพิ่งทราบข่าวมาวันนี้ว่าที่ประเทศไทย จะมีคนจัดงาน RailsCamp แล้ว!

RailsCamp (Thailand) 2010

ดีใจ + ตกใจนิดหน่อย เพราะเป็นโปรเจคที่คิดไว้ตั้งแต่เมื่อ 2-3 ปีที่แล้ว ตั้งแต่เมื่อตอนที่เขียน Rails ใหม่ๆ ว่าอยากให้มีงานที่เป็น Ruby on Rails Community ของคนไทย … ในเมื่อมีคนทำแล้ว ผมก็ขอสนับสนุนเต็มทีครับ ผมคงจะเตรียมเรื่องไปพูดนิดหน่อย ไม่แน่ใจเหมือนกันว่าเรื่องอะไร แต่ถ้าดูจาก Schedule ปัจจุบัน อาจจะได้พูดเรื่อง Behavior Driven Development using Cucumber หรือไม่ก็ Devise เพราะว่าเรื่องใหญ่ๆ อย่าง Rails 3 กับ Ruby 1.9 ได้อาจารย์ @rawitat กับคุณ @neokain พูดไปแล้ว :D

ไปเจอกันโลด วันเสาร์ที่ 13 พฤศจิกายน 2553 เวลา 9:30-17:00 น. ณ Opendream

ปล. ได้เวลาปัดฝุ่นโปรเจคลับแล้วสินะ ;)

คาดการณ์ราคา iPhone 4 ในไทย

September 16th, 2010 Posted in Apple, Gadgets | 2 Comments »

สั้นๆ ได้ใจความ: ผมว่าราคา iPhone 4 16GB อยู่ที่ ฿21,500 และ 32GB อยู่ที่ ฿25,000

เหตุผล

ช่วงนี้ข่าวคราวในเรื่องของการจำหน่าย iPhone 4 ในประเทศไทยนั้นเริ่มหนาแน่นขึ้น เนื่องจากถึงเวลาที่ Apple จะเริ่มจำหน่าย iPhone 4 ในประเทศที่เป็นกลุ่มสุดท้ายของเป้าหมาย ซึ่งข่าวลือนั้นมีตั้งแต่การที่ค่ายยักษ์ใหญ่อย่าง AIS จะมีการจำหน่าย iPhone 4 (ซึ่งก็จริง!) และการที่จะเปิดให้จองก่อน แล้วได้รับเครื่องสิ้นเดือน ก็ดูท่าจะจริงอีกเช่นกัน

ถึงแม้ข่าวลือต่างๆ จะมีออกมาเยอะเท่าไรก็ตาม แต่สิ่งหนึ่งที่หลายๆ คนถามถึงแต่ไม่มีข่าวออกมาเลยนั่นคือเรื่องของราคาครับ ส่วนใหญ่แล้วจะคิดว่าราคาคงจะเท่ากว่ารุ่นเดิม หรือว่าน้อยกว่ารุ่นเดิม ตามวิธีการตั้งราคาของ Apple ประเทศไทย … ผมก็อดไม่ได้ครับ อยากลองคำนวณดูบ้างว่าถ้า iPhone 4 เข้าประเทศไทยมาแล้ว ราคาควรจะอยู่ที่เท่าไร

ลองดูจากตารางอันนี้นะครับ:

Screen shot 2553-09-16 at 18.15.57.png

จะเห็นได้ว่าผมลองเอาราคาของผลิตภัณฑ์ที่เพิ่งออกใหม่ในอเมริกาบวกกับภาษี มาเทียบกับราคาขายปลีกในประเทศไทย เพื่อหาว่า Apple ใช้อัตราใดในการตั้งราคาสินค้า ซึ่งก็ได้ออกมาประมาณ 32.77 บาทต่อ 1 ดอลล่าร์สหรัฐ (ในขณะที่อัตราแลกเปลี่ยนตอนนั้นอยู่ที่ประมาณ 31 บาทต่อ 1 ดอลล่าร์สหรัฐ)

ปกติแล้วหลักการตั้งราคาของ Apple ผมเข้าใจว่าเป็นอัตราแลกเปลี่ยน ณ ขณะนั้น บวกไปอีกประมาณ 1-2 บาท เพื่อเป็นการประกันความเสี่ยงของอัตราแลกเปลี่ยน แล้วบวกค่าขนส่งของแต่ละชิ้น … ซึ่งตอนนี้ถึงแม้อัตราแลกเปลี่ยนจะลงมาอยู่ประมาณ 29~30 บาทต่อ 1 ดอลลาร์สหรัฐแล้วก็ตาม ผมคิดว่าอัตราที่คำนวณได้นี่น่าจะใกล้เคียงอัตราที่ Apple จะใช้จริงไม่มากก็น้อยครับ

ว่าแต่วันเปิดตัว มีใครจะไปต่อแถวซื้อบ้างไหมครับ? ถ้าไปแล้วเจอกันทักทายกันได้นะครับ :D

UPDATE

วันนี้ตกใจมาก เพราะไปเจอว่า Geeknone ได้อัพเอาไว้ในราคาเดียวกันเป๊ะกับผมเลย แถมอัพไว้ตั้งแต่ปลายเดือนสิงหาแล้วด้วย … ยืนยันครับว่าผมไม่ได้รู้เรื่องเลยว่ามีคนกะประมาณราคาเอาไว้แล้ว + อัพบล็อกไว้ด้วยแล้ว ผมไม่ได้ลอกมานะครับ -_-”

แต่เอ .. คำนวณออกมาแล้วราคาเท่ากันกับที่เขาคาดเอาไว้อย่างนี้ แปลว่าความเป็นไปได้นี่มีสูงเลยสิเนี่ย 555+

Possible pitfall on ActiveRecord::Base#create

August 21st, 2010 Posted in Ruby, Ruby on Rails | 3 Comments »

I came across this a while ago when I was trying to clean up my code. Just write it down so you won’t follow me :)

Consider that Post having :title attribute and

class Post < ActiveRecord::Base
  validates_presence_of :title
end

Well, what do you think would be the result of the following expression?

if Post.create(:title => nil)
  puts "Saved!"
else
  puts "Validation error ..."
end

Somehow, I expected "Validation error ..." message from it, but I got "Saved!" Why?

Return value of #create

You need to remember that #create method always return the object itself, which have the id as nil. This, evaluates to true and make our code fail. The solution for this problem would be using #new to create object, and then using #save:

post = Post.new(:title => nil)
if post.save
  puts "Saved!"
else
  puts "Validation error ..."
end

For you one-liner, I think you can do this:

if post = Post.new(:title => nil) && post.save

or (as suggested by shr)

if Post.new(:title => nil).save