One of the things I enjoy working at RSB is the 10% personal project time given to each team member to pursue their own interest (sure, we’ve all heard about Google’s 20% time and we hope reach that as we grow). Anyway, that works out to 4 hours or one afternoon’s worth of time. I choose to focus this time on building the Malaysia.rb community.
Ever since Malaysia.rb started in May 2007, we’ve had regular monthly meetups every third Thursday of the month at various locations in the Klang Valley. And, with the first meetup of 2008 coming upon us this week, it is my duty to send out the event announcement:
Malaysia.rb is kicking off 2008 with a bang! The January 2008 Meetup is happening this Thursday and it’s gonna seriously rock.
Four reasons why you shouldn’t miss this meetup:
- Meeting room with AV facilities and FOOD - MDEC has graciously stepped up to sponsor this month’s location.
- Special guest, Colin Wong, a Malaysian ex-Googler turned investor. Look out for his talk details below.
- Free pre-meetup PeepCode screening of “Rails From Scratch Part I” for early birds
- Two free PeepCode episodes to be won
When:
Thursday, January 17th 2008, 8:00PM
Agenda:
7:00 - 8:00pm Pre-meetup PeepCode screening
8:00 - 8:15pm Opening by the Organizer
8:15 - 8:45pm "From Batu Gajah to Mountain View and Beyond" - Colin Wong
8:45 - 9:15pm "Rails Fragment Caching with Interlock" - Kamal Fariz
9:15 - 9:35pm Open Lightning Talks
9:35 - 9:45pm PeepCode episode lucky draw and close
Cost:
Free. No registration required, just come right in, have a seat, and join the crowd. Invite your friends.
Contact:
kamal.fariz@gmail.com
+60123099143
Where:
PJ Hilton
No 2 Jalan Barat
46200 Petaling Jaya
Malaysia
Location
Directions:
Take the Kelana Jaya LRT line to Asia Jaya. It’s across the Federal Highway from there.
Hope to see you there!
UPDATE The meetup location is now confirmed to be at PJ Hilton. Please RSVP directly to me if you plan on attending so that I can estimate the number of people coming (for food, mostly).
Prerequisite
Install
gitsudo apt-get install git-core
Initializing gitosis
Grab
gitosiskamal@dev:~$ git clone git://eagain.net/gitosis.git Initialized empty Git repository in /home/kamal/gitosis/.git/ remote: Generating pack... remote: Done counting 549 objects. remote: Deltifying 549 objects... remote: 100% (549/549) done Indexing 549 objects... remote: Total 549 (delta 379), reused 136 (delta 95) 100% (549/549) done Resolving 379 deltas... 100% (379/379) doneInstall
gitosiskamal@dev:~/gitosis$ cd gitosis && sudo python setup.py install running install running bdist_egg running egg_info creating gitosis.egg-info ... Installing gitosis-init script to /usr/bin Installing gitosis-run-hook script to /usr/bin Installing gitosis-serve script to /usr/bin Installed /usr/lib/python2.4/site-packages/gitosis-0.2-py2.4.egg Processing dependencies for gitosis==0.2 Finished processing dependencies for gitosis==0.2Create the
gituser on the serverkamal@dev:~$ sudo adduser \ > --system \ > --shell /bin/sh \ > --gecos 'git version control' \ > --group \ > --disabled-password \ > --home /var/local/git \ > git Adding system user `git' (UID 106) ... Adding new group `git' (GID 109) ... Adding new user `git' (UID 106) with group `git' ... Creating home directory `/var/local/git' ...Back on your local machine,
scpyour SSH public key to the serverMacBook-Pro:~ kamal$ scp ~/.ssh/id_dsa.pub dev.ror.com.my:/tmp id_dsa.pub 100% 613 0.6KB/s 00:00Back on the server, initialize
gitosiskamal@dev:~/gitosis$ sudo -H -u git gitosis-init < /tmp/id_dsa.pub Initialized empty Git repository in ./ Initialized empty Git repository in ./Back on your local machine, clone the automatically created
gitosis-adminprojectMacBook-Pro:~ kamal$ git clone git@dev.ror.com.my:gitosis-admin.git Initialized empty Git repository in /Users/kamal/gitosis-admin/.git/ remote: Generating pack... remote: Done counting 5 objects. remote: Deltifying 5 objects... remote: 100% (5/5) done remote: Total 5 (delta 1), reused 5 (delta 1) Indexing 5 objects... 100% (5/5) done Resolving 1 deltas... 100% (1/1) doneCheck out the contents of
gitosis-adminMacBook-Pro:~ kamal$ cd gitosis-admin/ MacBook-Pro:gitosis-admin kamal$ ls gitosis.conf keydir MacBook-Pro:gitosis-admin kamal$ more gitosis.conf [gitosis] [group gitosis-admin] writable = gitosis-admin members = kamal@MacBook-Pro.local MacBook-Pro:gitosis-admin kamal$ ls keydir/ kamal@MacBook-Pro.local.pubNote: The main configuration file lives in
gitosis.confand the users are identified by their SSH public keys which are inkeydir
Adding Users
Adding Aizat as a gitosis-admin member is easy
MacBook-Pro:gitosis-admin kamal$ cp /tmp/aizat-id_dsa.pub keydir/aizat@Zatto.local.pubNote: The filename in the keydir has to match the last bit in the public key file plus
.pubEdit gitosis.conf
[gitosis] [group gitosis-admin] writable = gitosis-admin members = kamal@MacBook-Pro.local aizat@Zatto.localAdd the new file to git
MacBook-Pro:gitosis-admin kamal$ git add keydir/aizat\@Zatto.local.pubCommit the changes
MacBook-Pro:gitosis-admin kamal$ git-commit -a -m "Added Aizat as a gitosis admin" Created commit 37c0966: Added Aizat as a gitosis admin 2 files changed, 2 insertions(+), 1 deletions(-) create mode 100644 keydir/aizat@Zatto.local.pubAnd push to the server
MacBook-Pro:gitosis-admin kamal$ git push updating 'refs/heads/master' from 3b5462b027a02a5b29bf43f4ca878350ccc8b913 to 37c0966f285715cc38bd801a3955bb6f319ccdf4 Also local refs/remotes/origin/master Generating pack... Done counting 8 objects. Result has 5 objects. Deltifying 5 objects... 100% (5/5) done Writing 5 objects... 100% (5/5) done Total 5 (delta 1), reused 0 (delta 0) refs/heads/master: 3b5462b027a02a5b29bf43f4ca878350ccc8b913 -> 37c0966f285715cc38bd801a3955bb6f319ccdf4To add non-admin users, the process is the roughly the same. You only need to create a new group in
gitosis.conf.
Adding Projects
Add a new group for other users and set a new test project as the writable project
[gitosis] [group gitosis-admin] writable = gitosis-admin members = kamal@MacBook-Pro.local aizat@Zatto.local [group rsb] writable = test_project members = @gitosis-admin kegan@MacBook.local saimer@MacBook.local seanx2@Dell.localCommit and push
MacBook-Pro:gitosis-admin kamal$ git-commit -a -m "Added a new group for RSB and set write perms on a new test project" MacBook-Pro:gitosis-admin kamal$ git push updating 'refs/heads/master' from 37c0966f285715cc38bd801a3955bb6f319ccdf4 to 2fc5d9c394218fb757ce6fe468b9ea8096e99dcf Also local refs/remotes/origin/master Generating pack... Done counting 8 objects. Result has 6 objects. Deltifying 6 objects... 100% (6/6) done Writing 6 objects... 100% (6/6) done Total 6 (delta 1), reused 0 (delta 0) refs/heads/master: 37c0966f285715cc38bd801a3955bb6f319ccdf4 -> 2fc5d9c394218fb757ce6fe468b9ea8096e99dcfCreate the test project
MacBook-Pro:~ kamal$ mkdir test_project MacBook-Pro:~ kamal$ cd test_project/ MacBook-Pro:test_project kamal$ git init Initialized empty Git repository in .git/ MacBook-Pro:test_project kamal$ git remote add origin git@dev.ror.com.my:test_project.git MacBook-Pro:test_project kamal$ echo "This is an test project hosted in git" > README MacBook-Pro:test_project kamal$ git add README MacBook-Pro:test_project kamal$ git commit -a -m "Added a README file" Created initial commit f70c2f7: Added a README file 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 READMENote: It is important that you have files to add, otherwise the next command will fail. I think this has to do with
gitnot tracking empty directories?Push the test_project to the server
MacBook-Pro:test_project kamal$ git push origin master:refs/heads/master updating 'refs/heads/master' from 0000000000000000000000000000000000000000 to f70c2f74d71fbbb1edd0f5da2554fd208f75051b Also local refs/remotes/origin/master Generating pack... Done counting 3 objects. Deltifying 3 objects... 100% (3/3) done Writing 3 objects... 100% (3/3) done Total 3 (delta 0), reused 0 (delta 0) refs/heads/master: 0000000000000000000000000000000000000000 -> f70c2f74d71fbbb1edd0f5da2554fd208f75051bTest out by cloning the project
MacBook-Pro:~ kamal$ git clone git@dev.ror.com.my:test_project.git test_project2 Initialized empty Git repository in /Users/kamal/test_project2/.git/ remote: Generating pack... remote: Done counting 3 objects. remote: Deltifying 3 objects... remote: 100% (3/3) done remote: Total 3 (delta 0), reused 0 (delta 0) Indexing 3 objects... 100% (3/3) done
And you’re done!
In the next article, I will explore migrating existing Subversion repositories to git. Stay tuned!
That’s not a typo! It’s supposedly a programmer’s joke… something about recursion (no, of course the CSS dev wasn’t the one who came up with that, geez :P) :x
Big kudos to Moo who planned our first outing–dinner and a movie! ^^;
Kegan and Lee Ching
Kamal and Mai
Moo and Wendy
Sean
The food had excellent reviews (as seen by everyone’s big, happy, cheeky grins right after dessert):
Group picture! :)
Unfortunately, I am Legend had mixed reviews: the guys enjoyed it while I found the start to be quite a huge bore and the ending somewhat lackluster. Think literal crosseyed confusion as to what the heck just happened at the end…
For more pictures from the RSB Super Barty event (more yummy pictures of the food and shots of people taking pictures of other people), check out our Time Together entry on Facebook :)
Aizat missed it, but was there with us in spirit! We’re so hocking Ninja Jones when he comes back from Chile, eh? ;)

What a great event! … the MDeC InnoTech.my 2007, held on Friday, 7 Dec. Congratulations to MDeC TeDD team for pulling this off.
This year’s forum was themed “Connecting Innovation, Capital and Technopreneurs” where various VCs from different parts of the world (namely US, India and China) spoke.
A particular VCs that we Malaysian can probably better relate to, is Colin Wong. Colin was born in Batu Gajah, Perak, an ex-Googler, the founder & president of Prosperati, and now the CEO of ZoeCity.
Colin started off by presenting the VC landscape in the US–the top three investment areas were software (USD$1.11B, Q3 2007), biotechnology (USD$1.1B, Q3 2007), and clean technology (USD$844m, Q3 2007). It was also noted that there was an investment trend shift towards internet/consumer media, consumer hardware, and material science. Regardless, Silicon Valley remains as the top US innovation ecosystem, where research (universities) meets venture capitalism and entrepreneurs.
The gist of Colin’s talk was about risks and failures. Both the VCs and entrepreneurs in Malaysia shouldn’t be afraid of taking risks in new ventures that may not have proven business models, management teams or technologies.
Fail fast, fail often to find success. It’s all about risk.
(And yes, Colin, it would be awesome if MDeC were to set up office in Silicon Valley ;))
All in all a great event, except there was no vegetarian food for lunch (ouch!).
* Picture courtesy of my friend Azrul

… namely the UI Developer: me.
I’m not even going into the Rails file structure. However, just know that if you want to make your Rails programmer’s life much, much easier (and to make sure they won’t pee in your shampoo, ever) put your files in the public folder. Needless to say, images go in the public/images folder and stylesheets in public/stylesheets. Simple, yes?
Okay, fluff aside and since sharing is caring, today I’ll be sharing the first three sexy lines of Rails code I picked up!
THE BASIC
What’s a UI dev without his/her stylesheet? So first thing’s first: call that stylesheet!
<%= stylesheet_link_tag 'style' %>
Rails is intelligent enough to know that your stylesheets are indeed stylesheets and they’re housed in the public/stylehseets folder without you actually having to specify! How nifty is that? :)
THE SIMPLE BITS
How linking works in Rails:
<%= link_to('RSB, holla!', 'http://blog.ror.com.my') %>
which is actually your
<a href="http://blog.ror.com.my">RSB, holla!</a>
FUN STUFF
Making those images prettier with image_tag
Because Rails is emazing at compacting code, your generic <img src="images/spiffy.png" alt="spiffy!" class="wrapper" /> now becomes:
<%= image_tag 'spiffy.png', :class => 'wrapper', :alt => 'spiffy!' %>
Alrighties, so we’ve gotten the easy part out of the way :) Next week we’ll dive a little into how partials and conditionals work! :) And no, it’s not rocket science, it’s Ruby magic!
p/s: It is secretly fun harassing Kamal for code logic! :P
I was at MIMOS Technology Forum 2007 yesterday. Met a couple of friends from Cradle, there was Dato’ Abdul Wahab Abdullah (MIMOS Preseident and CEO), Dato’ Kong Cho Ha (Deputy Minister, MOSTI), and some really smart researchers.
What I find most interesting is that the amount of R&D MIMOS is cooking up in their kitchen.
In particular, MIMOS has a project called Wi-Wi. This was presented by Dr. Mazlan Abbas (MIMOS’s Head of Wireless Comm Cluster). Wi-Wi is a MIMOS patented wireless technology, featuring peer-to-peer WiMax-Wifi combo that promise to bring wireless Internet access to the most remote part of our country. The device is small enough to be mounted on lamp post, and …. well, you get the idea.
Now here is something for MIMOS to think about. Instead of having one (or a few) monopolistic company deploying Wi-Wi, make Wi-Wi device easy enough for anyone that want to deploy it. And that person get to share the revenue generated from his Wi-Wi device. This is not dissimilar to FON.
So Wi-Wi, … something to watch out for!
Unfortunately I did not manage to stay for the whole event, as I have to run for a workshop (which is awesome by the way, but that’s for another post).
Rails Routing and Facebook’s POSTs
If you have done any amount of Facebook development with Rails, you will quickly encounter the number one thing that has pissed off every Rails developer - Everything Is A POST!
With Facebooker’s pseudo-resource route generator, things are not so bad. It allows us declarations such as
facebook_resources :entries
facebook_resources :photos
in config/routes.rb which is nice and concise. However, the paths generated are so old school!
GET /entries # index
GET /entries/new # new
POST /entries/create # create
GET /entries/1/show # show
GET /entries/1/edit # edit
POST /entries/1/update # update
POST /entries/1/destroy # destroy
Yuck!
Rails taught us to love HTTP verbs, did it not? The above could be simplified if we took into account the HTTP request method, like so,
GET /entries # index
GET /entries/new # new
POST /entries # create
GET /entries/1 # show
GET /entries/1/edit # edit
PUT /entries/1 # update
DELETE /entries/1 # destroy
Unfortunately, you are stuck with just POST, buddy. Don’t even dream about your PUT and DELETE. Is there anything we can do? Fortunately, yes!
We Can Have Our RESTful Routes Back!
So, how do you solve this with Facebook? Facebook very recently added an additional request parameter to disambiguate between GET and POST via fb_sig_request_method. The actual requests from Facebook are still POSTs but we can make use of fb_sig_request_method as an added condition in our routes.
This is how you do it. Stick the following piece of code in lib and require it in a config/initializers file, or if you are using Facebooker as a plugin, modify vendor/plugins/facebooker/lib/facebooker/rails/routing.rb
def facebook_resources(name_sym)
name = name_sym.to_s
with_options :controller => name, :conditions => { :method => :post } do |map|
map.with_options :conditions => { :fb_sig_request_method => 'GET' } do |get|
get.named_route("new_#{name.singularize}", "#{name}/new", :action => 'new')
get.named_route(name, name, :action => 'index')
get.named_route(name.singularize, "#{name}/:id", :action => 'show', :id => /\d+/)
get.named_route("edit_#{name.singularize}", "#{name}/:id/edit", :action => 'edit', :id => /\d+/)
end
map.with_options :conditions => { :fb_sig_request_method => 'POST' } do |post|
post.named_route("create_#{name.singularize}", name, :action => 'create')
post.named_route("update_#{name.singularize}", "#{name}/:id", :action => 'update', :id => /\d+/)
post.named_route("destroy_#{name.singularize}", "#{name}/:id/destroy", :action => 'destroy', :id => /\d+/)
end
end
end
Notice the new :condition => { :fb_sig_request_method => 'GET'} and :condition => { :fb_sig_request_method => 'POST'}? That is the key we need to be able to overload a path like /entries/1 to call #show or #update depending on the condition. As a result, our routes now generate much nicer paths. Check it:
GET /entries # index
GET /entries/new # new
POST /entries # create
GET /entries/1 # show
GET /entries/1/edit # edit
POST /entries/1 # update
POST /entries/1/destroy # destroy
A patch has been submitted to Facebooker. Hopefully the maintainers will see it fit to merge this in. As an added bonus, the patch also includes singleton resources via
facebook_resource :entry
to generate the following routes:
GET /entry # show
GET /entry/new # new
POST /entry # create
GET /entry/edit # edit
POST /entry/update # update
POST /entry/destroy # destroy
Note that this is still very much a workaround until Facebook moves all the fb_sig metadata into the request headers and use real GETs and POSTs. But until then, happy programming!
Just a quick note that the blog will be down for maintenance at 1PM MYT. Barring no issues, it should be a quick 5 minute downtime.
As a startup, we have the opportunity to seed the company culture and process. This affects what tools we use to manage these processes. We are still experimenting with various tools, and I foresee there will still be a lot of changes in the near future. Nevertheless, I have listed down all the tools we are using to keep this ship sailing.
Tools we evaluated, but didn’t make the cut:
- Retrospectiva for keeping track of our projects’ tasks and change sets. We stop using Retrospectiva because it does not have enough features to support our version of agile software development.
- Acunote for tracking projects’ tasks. Acunote is an excellent tool. But we decided to switch to Rally due to more complete feature sets (bug tracking, estimate vs actual time comparison, etc). Free for teams of 5 and below.
Tools we are using now:
- TextMate for Rails development.
- SVN for versioning our source code.
- Mac OS X for a rock solid platform. The ability to run Windows using Parallels or Fusion for cross-browser testing is a nice bonus.
- Basecamp for tracking our administrative and non-coding tasks.
- Apple iCal with WebDAV for sharing calendars internally.
- StikiPad for our internal wiki.
- Google Apps for email hosting.
- Google Alerts for keeping tabs on what the web says about us and our products.
- WordPress for all our blogging needs.
- Amazon S3 for offsite backup and asset hosting.
Tools we have our eyes on:
- Rally for managing our projects. Community Edition is free for teams of 10 and below.
- Campaign Monitor for email lists management and marketing.
Most tools listed above are hosted and come with affordable pay-as-you-go or free plans. This frees the company from a lot of fixed costs and full-time maintenance effort so we can focus on building the next big thing.
What tools you use to run your startup?
- Malaysia.rb January 2008 Meetup
- Migrating to Git #1: Setting Up git and gitosis
- The first RSB Super Barty
- MDeC InnoTech.my 2007
- apps.facebook.com is down
- Ruby 101 for the Rails-challenged
- MIMOS Technology Forum 2007
- Breaking News! Differentiate GET and POST Requests In Facebook
- Down For Maintenance
- The Software Tools We Use

