<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><description>Kamal Fariz Mahyuddin on Ruby, Rails, Apple and being a Dad</description><title>bitfluent</title><generator>Tumblr (3.0; @bitfluent)</generator><link>http://blog.bitfluent.com/</link><geo:lat>3.121319</geo:lat><geo:long>101.412048</geo:long><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/bitfluent" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item><title>Creating a Custom dpkg Search Index for Chef 0.7.x</title><description>&lt;p&gt;I wanted to search across all my nodes that they have the latest Chef client package installed. The default indexer doesn’t index installed debian packages, so I whipped out a custom dpkg indexer based on the &lt;a href="http://wiki.opscode.com/display/chef/Search+Indexes"&gt;sample code from the wiki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here it is:&lt;/p&gt;

&lt;script src="http://gist.github.com/225092.js"&gt;&lt;/script&gt;&lt;p&gt;It’s a lot longer than the sample because I had to do the whole client authentication dance (most of it was copied directly from &lt;code&gt;lib/chef/client.rb&lt;/code&gt;). Good news is that authentication will change dramatically in the upcoming Chef 0.8.x release which should simplify creating custom clients.&lt;/p&gt;

&lt;p&gt;Once you run the indexer, your search screen on chef-server will now see a new query form for dpkg. Here’s an example of querying for packages that match the name attribute “chef” and returning only the “version” and “status” attributes.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://img.skitch.com/20091103-f1cccxw1etpt28s224cs2hm3ji.png" alt="Chef Server Search Screen"/&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=5v5OLiI4PjY:rx0QXNYZ8Ts:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=5v5OLiI4PjY:rx0QXNYZ8Ts:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/5v5OLiI4PjY" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/5v5OLiI4PjY/231907871</link><guid isPermaLink="false">http://blog.bitfluent.com/post/231907871</guid><pubDate>Tue, 03 Nov 2009 23:14:40 +0800</pubDate><category>chef</category><category>search</category><category>index</category><category>indexer</category><category>dpkg</category><category>apt</category><feedburner:origLink>http://blog.bitfluent.com/post/231907871</feedburner:origLink></item><item><title>Railscasts Theme for RubyMine</title><description>&lt;p&gt;I ported the &lt;a href="http://twitter.com/rbates"&gt;Ryan Bates’&lt;/a&gt; &lt;a href="http://media.railscasts.com/resources/textmate_theme.zip"&gt;Railscasts TextMate theme&lt;/a&gt; to &lt;a href="http://www.jetbrains.com/ruby/index.html"&gt;RubyMine&lt;/a&gt; because I love it so damn much. You’ll notice some differences due to how RubyMine’s lexer works, but it’s mostly serviceable.&lt;/p&gt;

&lt;p&gt;My wishlist for RubyMine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add more scopes for finer-grained highlighting&lt;/li&gt;
&lt;li&gt;support setting opacity&lt;/li&gt;
&lt;li&gt;use the Mac’s native color picker&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="http://github.com/kamal/RubyMine-colors"&gt;Get the RubyMine Railscasts theme&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Screenshots comparing the theme on TextMate vs. RubyMine.&lt;/p&gt;

&lt;h3&gt;Ruby&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://img19.yfrog.com/img19/3545/zd7.gif" alt="ruby"/&gt;&lt;/p&gt;

&lt;h3&gt;ERB&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://img16.yfrog.com/img16/699/ofc.gif" alt="erb"/&gt;&lt;/p&gt;

&lt;h3&gt;Javascript&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://img23.yfrog.com/img23/9274/q3c.gif" alt="javascript"/&gt;&lt;/p&gt;

&lt;h3&gt;CSS&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://img23.yfrog.com/img23/2687/cuk.gif" alt="css"/&gt;&lt;/p&gt;

&lt;h3&gt;YAML&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://img197.yfrog.com/img197/3164/z8x.gif" alt="yaml"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://github.com/kamal/RubyMine-colors"&gt;Get the RubyMine Railscasts theme&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=uxLPxugjas4:B4gcfZypPI8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=uxLPxugjas4:B4gcfZypPI8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/uxLPxugjas4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/uxLPxugjas4/198076049</link><guid isPermaLink="false">http://blog.bitfluent.com/post/198076049</guid><pubDate>Sun, 27 Sep 2009 14:07:22 +0800</pubDate><category>textmate</category><category>theme</category><category>rubymine</category><category>railscasts</category><category>ide</category><category>ruby</category><category>rails</category><category>editor</category><feedburner:origLink>http://blog.bitfluent.com/post/198076049</feedburner:origLink></item><item><title>Using Chef Server Indexes as a Simple DNS</title><description>&lt;p&gt;I’ve been setting up a small cluster of nodes for a client on Amazon EC2. For each node that is brought up, an internal hostname is attached to it to make it easier to address, for example, &lt;code&gt;db0&lt;/code&gt;, &lt;code&gt;db1&lt;/code&gt;, &lt;code&gt;admin&lt;/code&gt; and so forth. To manage all this, I had been manually editing &lt;code&gt;/etc/hosts&lt;/code&gt; on each node whenever new nodes and/or roles are added to the mix. As you can imagine with EC2, it is all too easy to spin up new instances when the need arises, so I needed a more sustainable solution.&lt;/p&gt;

&lt;p&gt;The common approach is to run a private internal DNS server. As these machines are configured with &lt;a href="http://www.opscode.com/chef"&gt;Chef&lt;/a&gt;, &lt;a href="http://github.com/37signals/37s_cookbooks/tree/master/cookbooks/djbdns/recipes/"&gt;37signal’s &lt;code&gt;djbdns::autozone&lt;/code&gt; recipe&lt;/a&gt; looked like a good starting point. It uses &lt;a href="http://wiki.opscode.com/display/chef/Search+Indexes"&gt;Chef Server Indexes&lt;/a&gt; to seed &lt;a href="http://cr.yp.to/djbdns.html"&gt;djbdns&lt;/a&gt; by querying all nodes and extracting the IP Address, FQDN and a custom DNS Aliases attribute. Nodes running &lt;code&gt;chef-client&lt;/code&gt; keeps the Chef Server Indexes up to date everytime it connects to the server on a predefined interval. This makes it possible for djbdns to be automatically maintained as new nodes are spun up and added to Chef Server.&lt;/p&gt;

&lt;p&gt;I also came across a much simpler solution in &lt;a href="http://twitter.com/dysinger"&gt;Tim Dysinger’s&lt;/a&gt; &lt;a href="http://dysinger.net/2008/10/13/using-amazon-ec2-metadata-as-a-simple-dns/"&gt;Using Amazon EC2 Metadata as a Simple DNS&lt;/a&gt;. In this approach, he crons a query to Amazon EC2’s metadata to rebuild &lt;code&gt;/etc/hosts&lt;/code&gt; every hour. I like the simplicity of it (very similar to what I’ve already been doing by hand) and not having to run yet another daemon. However, it required the EC2 secret and key which my client may or may not be willing to share.&lt;/p&gt;

&lt;p&gt;The solution I came to is a hybrid of the two. Instead of querying EC2’s metadata, I queried the Chef Server Indexes. Instead of cron, I relied on chef-client’s interval. Instead of djbdns, I used the &lt;code&gt;hosts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Here’s how it looks like:&lt;/p&gt;

&lt;h4&gt;cookbooks/hosts/recipes/default.rb&lt;/h4&gt;

&lt;pre class="sh_ruby"&gt;
#
# Cookbook Name:: hosts
# Recipe:: default
#
# Copyright 2009, Bitfluent
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     &lt;a href="http://www.apache.org/licenses/LICENSE-2.0"&gt;http://www.apache.org/licenses/LICENSE-2.0&lt;/a&gt;
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

hosts     = {}
localhost = nil

search(:node, "*", %w(ipaddress fqdn dns_aliases)) do |n|
  # node own's record, store in localhost
  if n["ipaddress"] == node[:ipaddress]
    localhost = n
  else
    hosts[n["ipaddress"]] = n
  end
end

template "/etc/hosts" do
  source "hosts.erb"
  mode 0644
  variables(:localhost =&gt; localhost, :hosts =&gt; hosts)
end
&lt;/pre&gt;

&lt;h4&gt;cookbooks/hosts/templates/default/hosts.erb&lt;/h4&gt;

&lt;pre class="sh_ruby"&gt;
# Auto-generated by chef for &lt;%= @node[:fqdn] %&gt;
#

127.0.0.1 localhost &lt;%= @localhost["fqdn"] %&gt; &lt;%= (@localhost["dns_aliases"] || []).sort.join(" ") %&gt;
&lt;% @hosts.keys.sort.each do |ip| %&gt;
&lt;%= ip %&gt; &lt;%= @hosts[ip]["fqdn"] %&gt; &lt;%= (@hosts[ip]["dns_aliases"] || []).sort.join(" ") %&gt;
&lt;% end %&gt;
&lt;/pre&gt;

&lt;p&gt;A few things to note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I use a &lt;code&gt;hosts&lt;/code&gt; hash because for some reason, the server indexes were returning duplicate nodes&lt;/li&gt;
&lt;li&gt;I output sorted IPs and dns aliases to keep &lt;code&gt;/etc/hosts&lt;/code&gt; stable. Hashes are themselves not sorted. I didn’t want Chef to replace the files if no new nodes or aliases were added.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Happy cooking!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=tKxsmBLLTys:V7VLXbr6CHw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=tKxsmBLLTys:V7VLXbr6CHw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/tKxsmBLLTys" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/tKxsmBLLTys/196658820</link><guid isPermaLink="false">http://blog.bitfluent.com/post/196658820</guid><pubDate>Fri, 25 Sep 2009 23:11:00 +0800</pubDate><category>chef</category><category>ec2</category><category>dns</category><category>hosts</category><feedburner:origLink>http://blog.bitfluent.com/post/196658820</feedburner:origLink></item><item><title>Installing CouchDB 0.9.0 Ubuntu Karmic Package on Jaunty</title><description>&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add Karmic into &lt;code&gt;/etc/apt/sources.list&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;deb-src &lt;a href="http://us.archive.ubuntu.com/ubuntu"&gt;http://us.archive.ubuntu.com/ubuntu&lt;/a&gt; karmic main restricted universe multiverse
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run &lt;code&gt;apt-get update&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo apt-get update
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Because Karmic is so bleeding edge, the 0.9.0 package has been superseeded by 0.10 snapshots. You can locate the binary builds for &lt;code&gt;0.9.0-2ubuntu5&lt;/code&gt; at &lt;a href="https://launchpad.net/ubuntu/+source/couchdb/0.9.0-2ubuntu5"&gt;https://launchpad.net/ubuntu/+source/couchdb/0.9.0-2ubuntu5&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Download the package.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ wget &lt;a href="http://launchpadlibrarian.net/29926161/couchdb_0.9.0-2ubuntu5_i386.deb"&gt;http://launchpadlibrarian.net/29926161/couchdb_0.9.0-2ubuntu5_i386.deb&lt;/a&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;0.9.0 requires libicu40. Jaunty only ships with licicu38. So we need to build and install libicu40 from Karmic’s source.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo apt-get build-dep libicu40
$ sudo apt-get -b source libicu40
$ sudo dpkg -i libicu40_4.0.1-2_i386.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Karmic’s couchdb package requires Erlang R13B1. Jaunty ships with R12B5.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo apt-get build-dep erlang-base-hipe=1:13.b.1-dfsg-2
$ sudo apt-get -b source erlang-base-hipe=1:13.b.1-dfsg-2
$ sudo dpkg -i erlang-base-hipe_13.b.1-dfsg-2_i386.deb erlang-crypto_13.b.1-dfsg-2_i386.deb erlang-mnesia_13.b.1-dfsg-2_i386.deb erlang-public-key_13.b.1-dfsg-2_i386.deb erlang-runtime-tools_13.b.1-dfsg-2_i386.deb erlang-ssl_13.b.1-dfsg-2_i386.deb erlang-inets_13.b.1-dfsg-2_i386.deb erlang-xmerl_13.b.1-dfsg-2_i386.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the last few extra packages needed. Thankfully, Jaunty has them at the required versions.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo apt-get install dictionaries-common hunspell-en-us libhunspell-1.2-0 libjs-jquery libnspr4-0d libnss3-1d libpython2.6 libstartup-notification0 xulrunner-1.9
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, install the couchdb package.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo dpkg -i couchdb_0.9.0-2ubuntu5_i386.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=kU9nY_ydGvw:9m0EoUmJHo4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=kU9nY_ydGvw:9m0EoUmJHo4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/kU9nY_ydGvw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/kU9nY_ydGvw/179451993</link><guid isPermaLink="false">http://blog.bitfluent.com/post/179451993</guid><pubDate>Fri, 04 Sep 2009 14:53:00 +0800</pubDate><category>couchdb</category><category>jaunty</category><category>ubuntu</category><category>karmic</category><category>apt-get</category><category>erlang</category><feedburner:origLink>http://blog.bitfluent.com/post/179451993</feedburner:origLink></item><item><title>Git Tip: Ignoring Icon\r in .gitignore</title><description>&lt;p&gt;If you have a git repository at the root level of a &lt;strong&gt;writable disk image&lt;/strong&gt;, you may notice that Mac OS X litters it with various junk.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;MBP:foo(master) kamal$ git status
# On branch master
# Untracked files:
#   (use "git add &lt;file&gt;..." to include in what will be committed)
#
#   .DS_Store
#   .VolumeIcon.icns
#   .fseventsd/
#   "Icon\r"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Three out of four of the files above is an easy &lt;code&gt;.gitignore&lt;/code&gt; entry away. The last one, &lt;code&gt;"Icon\r"&lt;/code&gt;, on the other hand, is quite a bitch. I tried various permutations of it in .gitignore with no luck. &lt;code&gt;Icon&lt;/code&gt;, &lt;code&gt;"Icon"&lt;/code&gt;, &lt;code&gt;"Icon\r"&lt;/code&gt;, nothing worked.&lt;/p&gt;

&lt;h3&gt;Solution&lt;/h3&gt;

&lt;p&gt;The trick is to use the literal &lt;code&gt;^M&lt;/code&gt; control character in the name. Yup, Apple decided that the file name should contain an actual &lt;code&gt;CRLF&lt;/code&gt;! But there’s a trick to this trick: you need &lt;strong&gt;two&lt;/strong&gt; &lt;code&gt;^M&lt;/code&gt; characters. Credits go to &lt;a href="https://kerneltrap.org/mailarchive/git/2008/12/3/4301904"&gt;this post&lt;/a&gt; for the solution.&lt;/p&gt;

&lt;p&gt;Here’s how your .gitignore should look like in &lt;code&gt;vi&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;.DS_Store
.VolumeIcon.icns
.fseventsd
Icon^M^M
~
~
~
~
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A quick check,&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;MBP:foo(master) kamal$ git status
# On branch master
nothing to commit (working directory clean)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All clean!&lt;/p&gt;

&lt;h3&gt;Those Are Not Caret-Ms&lt;/h3&gt;

&lt;p&gt;It took me a while to figure out how to &lt;em&gt;create&lt;/em&gt; a control character. Everything I searched on Google was concerned with &lt;em&gt;removing&lt;/em&gt; them. I even booted up Windows XP in &lt;a href="http://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt; to create a text file in Notepad in the hopes of copy-and-pasting it.&lt;/p&gt;

&lt;p&gt;Finally, in a fit of desperation and being the respectable Rubyist that I am, I fired up &lt;code&gt;irb&lt;/code&gt; and wrote that mofo to the .gitignore file directly.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&gt;&gt; f = File.open(".gitignore", "a+") # append
=&gt; #&lt;File:.gitignore&gt;
&gt;&gt; f.write("Icon\r\r")
=&gt; 8
&gt;&gt; f.close
=&gt; nil
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There! Done! Err, maybe someone can tell me the easy way to do it. What’s the keystroke in &lt;code&gt;vi&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Anyway, if you can’t be arsed, let me save you the trouble. Here’s a &lt;a href="http://dl.getdropbox.com/u/238893/gitignore"&gt;copy of my &lt;code&gt;.gitignore&lt;/code&gt;&lt;/a&gt;. Download, rename and stick it in your repo.&lt;/p&gt;

&lt;h3&gt;Make It Global&lt;/h3&gt;

&lt;p&gt;Here’s a little tip: create the &lt;code&gt;.gitignore&lt;/code&gt; in &lt;code&gt;~&lt;/code&gt; to make it apply across all repos. It’s additive — &lt;code&gt;git status&lt;/code&gt; will use the contents of both the local &lt;code&gt;.gitignore&lt;/code&gt; and &lt;code&gt;~/.gitignore&lt;/code&gt;. My strategy is to ignore project-specific junk like &lt;code&gt;*.log&lt;/code&gt; and &lt;code&gt;database.yml&lt;/code&gt; in the repo itself and general annoyances like above in &lt;code&gt;~/.gitignore&lt;/code&gt;. In a later post, I’ll put up examples of each and explain them line by line.&lt;/p&gt;

&lt;h3&gt;What’s With the Disk Image in the First Place?&lt;/h3&gt;

&lt;p&gt;Being paranoid, I’ve begun to store client work on encrypted disk images. I can’t stress this enough: &lt;strong&gt;if you’re a developer, it’s your responsibility to secure your client’s source code in the event of theft!&lt;/strong&gt; Can you imagine the shit storm and liability you’ll face if your laptop got stolen?&lt;/p&gt;

&lt;p&gt;To manage the disk images, I’m currently trialing &lt;a href="http://www.knoxformac.com/"&gt;Knox&lt;/a&gt;. It does cost some money even though it uses the same backend you get for free via FileVault and Disk Utility. However, the difference with Knox-created images is the disk images grow on demand and it makes it really easy to switch between images. FileVault, on the other hand, applies it to the entire home directory while Disk Utility images take up the entire space at creation time.&lt;/p&gt;

&lt;p&gt;Hope this entry helps someone out there.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=wrgZUoHjxJo:xqHBlCOUfMw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=wrgZUoHjxJo:xqHBlCOUfMw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/wrgZUoHjxJo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/wrgZUoHjxJo/173740409</link><guid isPermaLink="false">http://blog.bitfluent.com/post/173740409</guid><pubDate>Fri, 28 Aug 2009 17:34:28 +0800</pubDate><category>git</category><category>gitignore</category><category>knox</category><category>disk image</category><category>icon</category><category>icon\r</category><feedburner:origLink>http://blog.bitfluent.com/post/173740409</feedburner:origLink></item><item><title>Intro to Ruby on Rails Charity Tutorial Survey</title><description>&lt;p&gt;&lt;img src="http://img.skitch.com/20090825-f24tj11dpecuri76pxi8e7s3bx.png" alt="Tweet" title="My tweet that kicked it off"/&gt;&lt;/p&gt;

&lt;p&gt;Here’s the plan: I’m going to run a 1-day Introduction to &lt;a href="http://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; tutorial. I won’t charge a single cent for this tutorial. Instead, you will be asked for a minimum donation to be given to a charity. I haven’t worked out the details yet, but most likely I’ll hold this in October in conjunction with the &lt;a href="http://foss.my/"&gt;FOSS.my 2009 Conference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Would you be interested in this? Help me out by &lt;a href="http://bit.ly/rails-charity"&gt;filling in this short survey&lt;/a&gt; so that I can plan and customize the tutorial for you. Much thanks!&lt;/p&gt;

&lt;p&gt;&lt;a href="http://twitter.com/home?status=RT%20@kamal:%20The%20Rails%20charity%20tutorial%20survey%20is%20up!%20Please%20take%202%20mins%20to%20fill%20it%20out.%20http://bit.ly/rails-charity%20RTs%20are%20appreciated."&gt;Retweet this&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=15a5uv3cQFE:aEbc5nRcOcM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=15a5uv3cQFE:aEbc5nRcOcM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/15a5uv3cQFE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/15a5uv3cQFE/171055039</link><guid isPermaLink="false">http://blog.bitfluent.com/post/171055039</guid><pubDate>Tue, 25 Aug 2009 13:00:33 +0800</pubDate><category>ruby</category><category>rails</category><category>tutorial</category><category>charity</category><category>foss.my</category><category>donation</category><category>training</category><feedburner:origLink>http://blog.bitfluent.com/post/171055039</feedburner:origLink></item><item><title>Tethering over Celcom 3G on the iPhone 3.0</title><description>&lt;p&gt;Open this link in MobileSafari - &lt;a href="http://files.getdropbox.com/u/238893/my_celcom.mobileconfig"&gt;my_celcom.mobileconfig&lt;/a&gt; and install the profile. Alternatively, you can download this file and mail it to yourself and open it up in the iPhone Mail.app.&lt;/p&gt;

&lt;p&gt;Credits go to &lt;a href="http://help.benm.at/help.php"&gt;&lt;a href="http://help.benm.at/help.php"&gt;http://help.benm.at/help.php&lt;/a&gt;&lt;/a&gt;. I downloaded the settings for SG telcos from that site to see which parts were customizable and pretty much found you just need to change the APN to use &lt;strong&gt;celcom3g&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Also, for you people on DiGi: &lt;a href="http://www.mediafire.com/download.php?mnlhztjoywr"&gt;DiGi.mobileconfig&lt;/a&gt; courtesy of &lt;a href="http://forum.lowyat.net/user/Snuffykl"&gt;Snuffykl&lt;/a&gt; from &lt;a href="http://forum.lowyat.net/"&gt;LYN&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT:&lt;/strong&gt; I’m on the RM68/month unlimited data plan. Is it the cheapest unlimited 3G plan around?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT:&lt;/strong&gt; I can confirm that tethering and MMS does not work on the iPhone 2G (1st-gen) &lt;em&gt;right now&lt;/em&gt;. It will probably require jailbreaking which should be out on Friday, 19th June.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT:&lt;/strong&gt; “Does not work” means the MMS and tether options are nowhere to be found in Settings.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=d11CM3xcrSQ:HunVW2I3AWE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=d11CM3xcrSQ:HunVW2I3AWE:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/d11CM3xcrSQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/d11CM3xcrSQ/125749139</link><guid isPermaLink="false">http://blog.bitfluent.com/post/125749139</guid><pubDate>Thu, 18 Jun 2009 17:42:00 +0800</pubDate><feedburner:origLink>http://blog.bitfluent.com/post/125749139</feedburner:origLink></item><item><title>Are You Coming To #barcampkl?</title><description>&lt;p&gt;&lt;a href="http://search.twitter.com/search?q=%23barcampkl"&gt;#barcampkl&lt;/a&gt; is happening this weekend, 4-5th April 2009, at &lt;a href="http://barcamp.org/barcampklvenue"&gt;Inti College Subang&lt;/a&gt;. Some pretty &lt;a href="http://barcamp.org/barcampklsessions"&gt;interesting talks have been scheduled&lt;/a&gt; ahead of time although the organizers have promised to implement a “real” unconference where the talks are voted at the opening of each day.&lt;/p&gt;

&lt;p&gt;So, are you coming? &lt;a href="http://barcamp.org/BarcampKLregister"&gt;Register here&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=kqBDiCXs8LI:_pPvgURxAuQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=kqBDiCXs8LI:_pPvgURxAuQ:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/kqBDiCXs8LI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/kqBDiCXs8LI/91503795</link><guid isPermaLink="false">http://blog.bitfluent.com/post/91503795</guid><pubDate>Tue, 31 Mar 2009 16:26:11 +0800</pubDate><feedburner:origLink>http://blog.bitfluent.com/post/91503795</feedburner:origLink></item><item><title>Secure and Signed AuthSub Requests in Ruby</title><description>&lt;p&gt;I pulled the trigger in my last blog post about &lt;a href="http://blog.bitfluent.com/post/87311646/solving-this-website-has-not-registered-with-google-to"&gt;solving Google AuthSub’s warning&lt;/a&gt;. It solved only half the problem - the not-showing-the-warning half. I was too quick to assume it would work since Google returned a token. Actually performing &lt;code&gt;GET&lt;/code&gt; on the contacts feed would yield me a &lt;code&gt;401 Unauthorized&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, what did I get wrong? A few things.&lt;/p&gt;

&lt;p&gt;Firstly, I didn’t have to set the &lt;code&gt;sig&lt;/code&gt; in the request authorization URL. A simple &lt;code&gt;secure=1&lt;/code&gt; would have been sufficient. I confused this concept with other request schemes like Facebook’s which signs the request and appends the sig to the URL parameters.&lt;/p&gt;

&lt;p&gt;Secondly, for secure AuthSub requests, you need to set &lt;a href="http://code.google.com/apis/accounts/docs/AuthSub.html#signingrequests"&gt;special signed headers&lt;/a&gt;. Regular non-secure AuthSub requests only need a short header.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Authorization: AuthSub token="COy2q9qdGRDWm7iMAm"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;However, if you want secure requests, you need to sign the request. It’ll look something like this.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Authorization: AuthSub token="COy2q9qdGRDWm7iMAm" sigalg="rsa-sha1" data="GET &lt;a href="http://www.google.com/m8/feeds/contacts/default/thin?max-results=200"&gt;http://www.google.com/m8/feeds/contacts/default/thin?max-results=200&lt;/a&gt; 1237370831 18094396511823580603" sig="XOQNfKpQ8VPCN2Yp+Zt="
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I thought I nailed it but it still returned me &lt;code&gt;401 Unauthorized&lt;/code&gt;. I downloaded the &lt;a href="http://code.google.com/p/gdata-python-client/"&gt;Python GData client&lt;/a&gt; to compare the sig values and indeed the sig I generated was off.&lt;/p&gt;

&lt;p&gt;So the final piece of the puzzle is how do I correctly generate the signature. &lt;a href="http://github.com/stuart/google-authsub"&gt;google-authsub&lt;/a&gt; (and &lt;a href="http://github.com/kamal/contacts/commit/2ceaadbcc8e18cd94759399c357ef0db051ba513"&gt;my contacts commit&lt;/a&gt;) got it wrong. This is what it was doing:&lt;/p&gt;

&lt;pre class="sh_ruby"&gt;
digest = OpenSSL::Digest::SHA1.new(data).hexdigest
sig = [@@pkey.private_encrypt(digest)].pack("m")  #Base64 encode
&lt;/pre&gt;

&lt;p&gt;After much googling, I found the answer. Immad Akhund posted &lt;a href="http://groups.google.com/group/google-help-dataapi/tree/browse_frm/thread/5893816ff68a689e/f8689d7a9bf611bc?rnum=1&amp;_done=%2Fgroup%2Fgoogle-help-dataapi%2Fbrowse_frm%2Fthread%2F5893816ff68a689e%3Ftvc%3D1%26#doc_2cea553921272a53"&gt;the solution on the Google Data Protocol mailing list&lt;/a&gt; in June 2008. The correct way to sign &lt;code&gt;data&lt;/code&gt; is to use the &lt;code&gt;OpenSSL::PKey::RSA#sign&lt;/code&gt; method and remove the newlines after Base64 encoding.&lt;/p&gt;

&lt;pre class="sh_ruby"&gt;
sig = @@pkey.sign(OpenSSL::Digest::SHA1.new, data)
sig = [sig].pack("m").gsub(/\n/, "") #Base64 encode
&lt;/pre&gt;

&lt;p&gt;So, there you have it. I’ve pushed a &lt;a href="http://github.com/kamal/contacts/commit/b3e9585fe1688f946eeb5fb8fe028bd2f63e374b"&gt;new commit&lt;/a&gt; to the contacts library with all the fixes. I’ll be forking google-authsub and pushing fixes there too in hopes that another person wouldn’t need to spend a day figuring it out like I did.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=UFI4klm5OsY:Dx-MDRsbgJE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=UFI4klm5OsY:Dx-MDRsbgJE:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/UFI4klm5OsY" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/UFI4klm5OsY/87588119</link><guid isPermaLink="false">http://blog.bitfluent.com/post/87588119</guid><pubDate>Wed, 18 Mar 2009 23:21:00 +0800</pubDate><category>authsub</category><category>signed</category><category>gdata</category><category>contacts</category><category>api</category><category>ruby</category><category>openssl</category><feedburner:origLink>http://blog.bitfluent.com/post/87588119</feedburner:origLink></item><item><title>Solving "This website has not registered with Google to establish a secure connection for authorization requests"</title><description>&lt;p&gt;I recently ran into an ominous warning on the Google Contacts Access Request landing page.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://img.skitch.com/20090317-daidwk7cmj89eg4d54wfbksstf.png"&gt;&lt;img src="http://img.skitch.com/20090317-d86gb6ij7r6k1d3f51i4k4i332.png" alt="This website has not registered with Google to establish a secure connection for authorization requests."/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After some googling, the solution is to &lt;a href="https://www.google.com/accounts/ManageDomains"&gt;register the requesting domain&lt;/a&gt; and &lt;a href="http://code.google.com/apis/gdata/authsub.html#Registered"&gt;upload a self-signed X.509 certificate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once completed, I retried the request and got a slightly less threatening warning.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://img.skitch.com/20090317-1ybbgbjxi2gbcxsutf59pbfqq7.png"&gt;&lt;img src="http://img.skitch.com/20090317-hhy3wq68s4b9gxadxgb39u6kp.png" alt="This website is registered with Google to make authorization requests, but has not been configured to send requests securely."/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The solution was to add two additional parameters to the request: &lt;code&gt;secure=1&lt;/code&gt; and a signature. Fortunately, I found how to generate the signature in the &lt;a href="http://github.com/stuart/google-authsub"&gt;google-authsub gem&lt;/a&gt;. A few minutes later, I added support for signing AuthSub requests to &lt;a href="http://github.com/mislav/contacts"&gt;Mislav’s contacts library&lt;/a&gt; and got the results I wanted. &lt;a href="http://github.com/kamal/contacts/commit/2ceaadbcc8e18cd94759399c357ef0db051ba513"&gt;Commit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://img.skitch.com/20090317-fxneu9qf5rqfww8ce7b53gdug.png"&gt;&lt;img src="http://img.skitch.com/20090317-emmmiy9nxdgk4jqefkxyibxf9x.png" alt="Yay!"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: I got it wrong about appending the signature to the parameter. I posted a follow up on &lt;a href="http://blog.bitfluent.com/post/87588119/secure-and-signed-authsub-requests-in-ruby"&gt;how to correctly perform signed AuthSub requests in Ruby&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=jiP7T3m4w5Q:z-gAmIKJjQ0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=jiP7T3m4w5Q:z-gAmIKJjQ0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/jiP7T3m4w5Q" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/jiP7T3m4w5Q/87311646</link><guid isPermaLink="false">http://blog.bitfluent.com/post/87311646</guid><pubDate>Wed, 18 Mar 2009 01:47:00 +0800</pubDate><category>ruby</category><category>contacts</category><category>authsub</category><category>gmail</category><category>google</category><category>api</category><category>authorization</category><feedburner:origLink>http://blog.bitfluent.com/post/87311646</feedburner:origLink></item><item><title>#rubinius</title><description>evan: i'm curious, how do you know the JVM is deopt'ing in certain cases?&lt;br /&gt;&#xD;
headius: it tells us&lt;br /&gt;&#xD;
evan: twitter?&lt;br /&gt;&#xD;
evan: us post?&lt;br /&gt;&#xD;
evan: collect call?&lt;br /&gt;&#xD;
headius: reaches out of the screen and slaps us sideways&lt;br /&gt;&#xD;
evan: hah&lt;br /&gt;&#xD;
headius: there's a bunch of debug options for hotspot&lt;br /&gt;&#xD;
evan: NOT SO FAST BUCKO&lt;br /&gt;&#xD;
headius: some in the released jdk, some require a debug build&lt;br /&gt;&#xD;
headius: LogCompilation, PrintInlining, PrintAssembly&lt;br /&gt;&#xD;
evan: -XXSlapRatio=1persecond&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=F_kW1wrKhV4:MtArllWubf0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=F_kW1wrKhV4:MtArllWubf0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/F_kW1wrKhV4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/F_kW1wrKhV4/72282131</link><guid isPermaLink="false">http://blog.bitfluent.com/post/72282131</guid><pubDate>Thu, 22 Jan 2009 17:28:00 +0800</pubDate><feedburner:origLink>http://blog.bitfluent.com/post/72282131</feedburner:origLink></item><item><title>Sharing Contracts</title><description>&lt;p&gt;I love it when companies that provide professional services share the contracts they use when dealing with clients. Many people consider contracts to be part of their secret sauce and competitive advantage over their competition so it’s understandable that these documents are not discussed much (plus they also cost money having to go through legal counsel).&lt;/p&gt;

&lt;p&gt;However, &lt;a href="http://blog.obiefernandez.com/"&gt;Obie Fernandez&lt;/a&gt; of &lt;a href="http://hashrocket.com/"&gt;HashRocket&lt;/a&gt; and &lt;a href="http://www.stuffandnonsense.co.uk/"&gt;Andy Clarke of Stuff and Nonsense&lt;/a&gt; have been awesome by sharing the contracts they use in their daily business:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://blog.obiefernandez.com/content/2008/10/msa-series-2-cooperation-and-reliance.html"&gt;Master Services Agreement Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.obiefernandez.com/content/2008/09/master-services-agreement-part-1.html"&gt;Master Services Agreement Part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://24ways.org/2008/contract-killer"&gt;Contract Killer&lt;/a&gt; (via &lt;a href="http://twitter.com/cheeaun"&gt;@cheeaun&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://blog.flavert.com/2008/12/23/fast-reply-to-sharing-contracts-by-kamal-fariz/"&gt;Flavert Media Lab’s Contract&lt;/a&gt; (thanks &lt;a href="http://twitter.com/tekong"&gt;@tekong&lt;/a&gt;!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’d love to collect more of these kinds of write-ups. Do you have some? &lt;a href="http://twitter.com/kamal_fariz"&gt;Twitter me!&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=pxJJxKmxd9M:PgbnWttbBqk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=pxJJxKmxd9M:PgbnWttbBqk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/pxJJxKmxd9M" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/pxJJxKmxd9M/66347173</link><guid isPermaLink="false">http://blog.bitfluent.com/post/66347173</guid><pubDate>Tue, 23 Dec 2008 12:41:00 +0800</pubDate><category>contract</category><category>client</category><category>msa</category><category>agreement</category><category>freelance</category><feedburner:origLink>http://blog.bitfluent.com/post/66347173</feedburner:origLink></item><item><title>Test autoposting from Posterous</title><description>&lt;p&gt;I never knew posterous could autopost to other services as well. That’s pretty rad.&lt;p style="font-size: 10px;"&gt;  &lt;a href="http://posterous.com"&gt;Posted via email&lt;/a&gt;   from &lt;a href="http://kamal.posterous.com/test-autoposting-from-posterou"&gt;kamal’s posterous&lt;/a&gt; | &lt;a href="http://kamal.posterous.com/test-autoposting-from-posterou#comment"&gt;&lt;span style="font-size: 11px"&gt;Comment »&lt;/span&gt;&lt;/a&gt;  &lt;/p&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=t63NWM7PoS0:MKWgZdDMt7o:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=t63NWM7PoS0:MKWgZdDMt7o:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/t63NWM7PoS0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/t63NWM7PoS0/65875090</link><guid isPermaLink="false">http://blog.bitfluent.com/post/65875090</guid><pubDate>Sat, 20 Dec 2008 15:14:31 +0800</pubDate><feedburner:origLink>http://blog.bitfluent.com/post/65875090</feedburner:origLink></item><item><title>Full List of 1,339 Rails Contributors</title><description>&lt;p&gt;One of the biggest plus points of using git for open source projects is the preservation of the original author of the patch. Here’s how it looks like when displayed in &lt;a href="http://github.com"&gt;GitHub&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://img.skitch.com/20081031-mjqbkapmx62bbkt35di6wj9ywa.png" alt="Committer sign-off"/&gt;&lt;/p&gt;

&lt;p&gt;In the Subversion days, patches into Rails were attributed by adding an arbitrary combination of the author’s name/email/nick at the end of the commit messages. Makes it kind of hard to keep track of the number of distinct contributors over the lifetime of the project.&lt;/p&gt;

&lt;p&gt;Fortunately, &lt;a href="http://github.com/fxn"&gt;Xavier Noria&lt;/a&gt; whipped up a &lt;a href="http://pastie.org/304092"&gt;script to parse the legacy commit messages&lt;/a&gt; to extract the number of commits per author using these rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First extract authors from commit message&lt;/li&gt;
&lt;li&gt;If empty, check changelogs via &lt;code&gt;git show id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If empty, author is the committer&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The full list is can be found on the &lt;a href="http://contributors.rubyonrails.org/"&gt;Rails Contributors page&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=UtIr6a8_FHU:Aokz5F_nA3U:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=UtIr6a8_FHU:Aokz5F_nA3U:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/UtIr6a8_FHU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/UtIr6a8_FHU/57244160</link><guid isPermaLink="false">http://blog.bitfluent.com/post/57244160</guid><pubDate>Fri, 31 Oct 2008 15:42:00 +0800</pubDate><category>github</category><category>rails</category><category>git</category><category>contributor</category><category>patch</category><category>svn</category><category>patch</category><feedburner:origLink>http://blog.bitfluent.com/post/57244160</feedburner:origLink></item><item><title>Tumblr Client On The iPhone</title><description>&lt;p&gt;Would having a tumblr client on my iPhone see me posting more frequently? Obligatory test post. &lt;br/&gt;&lt;br/&gt;Posted with &lt;a href="http://lifecast.sleepydog.net"&gt;LifeCast&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=BPCrTa4_qH0:BH8-PBEVQCc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=BPCrTa4_qH0:BH8-PBEVQCc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/BPCrTa4_qH0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/BPCrTa4_qH0/45323921</link><guid isPermaLink="false">http://blog.bitfluent.com/post/45323921</guid><pubDate>Sat, 09 Aug 2008 23:43:54 +0800</pubDate><feedburner:origLink>http://blog.bitfluent.com/post/45323921</feedburner:origLink></item><item><title>On the Beauty of Rubinius' Design (I Wish I Had Rails.new)</title><description>&lt;p&gt;In &lt;a href="http://rubini.us/"&gt;Rubinius&lt;/a&gt;, you can spawn off a brand new complete &lt;a href="http://en.wikipedia.org/wiki/Virtual_machine"&gt;VM&lt;/a&gt; by simply calling &lt;code&gt;Rubinius.new&lt;/code&gt;. It’ll behave exactly as though it was invoked directly from an rbx binary sitting in your &lt;code&gt;$PATH&lt;/code&gt;, complete with STDIN/STDOUT (which you can override). I believe this is one of the basis of how Rubinius’ multi-VM architecture works.&lt;/p&gt;

&lt;p&gt;Anyway, I bring this up because I really, really wish Rails was architected in a similar fashion. I am building a &lt;a href="http://en.wikipedia.org/wiki/Content_management_system"&gt;CMS&lt;/a&gt; on top of Rails and would love to get my hands on a &lt;code&gt;Rails.new&lt;/code&gt; if there ever was one. Here’s why.&lt;/p&gt;

&lt;p&gt;In a CMS setting, very little of what Rails offers out of the box is usable. You don’t have access to Rails routes so from the very beginning, you don’t have Rails automatically invoking the right controller, the right action and rendering the right view. This doesn’t make sense anyway - you don’t expect your CMS users to start writing controller code in your web editor, do you? (Unless you are &lt;a href="http://heroku.com/"&gt;Heroku&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;On a slight tangent, the assumption when writing a CMS is that when you ship, you would have written every conceivable controller and model there is (views don’t fall into this because users are generally familiar with the concept of customizable templates). One strategy to extend your “frozen code base” is via the use of widgets and third-party apps (like Facebook) so that you can create seemingly new pages served by custom controllers.&lt;/p&gt;

&lt;p&gt;So how do you design a CMS? I’ve been prototyping something for the past week and came up for a breather to check out how other people have solved it. I am delightfully surprised to find out that &lt;a href="http://radiantcms.org/"&gt;Radiant&lt;/a&gt; does it very close to what I have. In particular, Radiant has one single controller that accepts all requests (lets ignore the entire admin portion for the time being). Based on the path array (provided by the globbed route), it decides what to do / where to dispatch. It takes care of locating the page that corresponds to the URL (it uses a &lt;code&gt;Page&lt;/code&gt; model), rendering it and returning the result to the user. It is interesting to note that Radiant directly manipulates the &lt;code&gt;request&lt;/code&gt; and &lt;code&gt;response&lt;/code&gt; objects, something that Rails developers almost never had to reach for in a regular app. On the other hand, I am exploring the use of serializing the templates to disk and simply calling &lt;code&gt;render :template&lt;/code&gt; on it.&lt;/p&gt;

&lt;p&gt;Wait a minute. &lt;strong&gt;Holy cow, we just built (a simplified) Rails on top of Rails!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What I’d love to see here instead is a &lt;code&gt;Rails.new&lt;/code&gt; method just like Rubinius. Boom, a full blown MVC at your fingertips. Configure it right and there you have your very own CMS with minimal work. Or maybe there is. Lazyweb?&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=ITRrgXzZZSA:niLZSpb3Xr8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=ITRrgXzZZSA:niLZSpb3Xr8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/ITRrgXzZZSA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/ITRrgXzZZSA/36867302</link><guid isPermaLink="false">http://blog.bitfluent.com/post/36867302</guid><pubDate>Mon, 02 Jun 2008 17:13:00 +0800</pubDate><feedburner:origLink>http://blog.bitfluent.com/post/36867302</feedburner:origLink></item><item><title>An update in a really long while</title><description>&lt;p&gt;Woah, one month with no updates.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="http://www.rubyholic.com/groups/show/1"&gt;Seattle.rb hack nights&lt;/a&gt;, &lt;a href="http://www.pandora.com"&gt;Pandora&lt;/a&gt;, &lt;a href="http://www.hulu.com"&gt;Hulu&lt;/a&gt; and this weekend, &lt;a href="http://barcamp.org/BarCampPortland"&gt;Barcamp Portland&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;See you guys in a bit.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=ASFJ_qhUgUQ:AS7zliEdnyY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=ASFJ_qhUgUQ:AS7zliEdnyY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/ASFJ_qhUgUQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/ASFJ_qhUgUQ/33455744</link><guid isPermaLink="false">http://blog.bitfluent.com/post/33455744</guid><pubDate>Fri, 02 May 2008 03:53:52 +0800</pubDate><feedburner:origLink>http://blog.bitfluent.com/post/33455744</feedburner:origLink></item><item><title>http://dev.rubyonrails.org/ticket/11491</title><description>&lt;a href="http://dev.rubyonrails.org/ticket/11491"&gt;http://dev.rubyonrails.org/ticket/11491&lt;/a&gt;: &lt;p&gt;Finally, &lt;code&gt;render :partial =&gt; some_collection&lt;/code&gt; will pick the right template based on each object in the collection. I can now retire my workaround:&lt;/p&gt;

&lt;pre class="sh_ruby"&gt;
&lt;% some_collection.each do |item| %&gt;
  &lt;%= render :partial =&gt; item %&gt;
&lt;% end %&gt;
&lt;/pre&gt;

&lt;p&gt;Best part is that the &lt;code&gt;partial_counter&lt;/code&gt; is maintained across the different partials.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=yCmzONXMqAE:5z8-pdgWO4g:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=yCmzONXMqAE:5z8-pdgWO4g:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/yCmzONXMqAE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/yCmzONXMqAE/30422191</link><guid isPermaLink="false">http://blog.bitfluent.com/post/30422191</guid><pubDate>Tue, 01 Apr 2008 12:03:00 +0800</pubDate><feedburner:origLink>http://blog.bitfluent.com/post/30422191</feedburner:origLink></item><item><title>Updating Counter Cache in Migrations</title><description>&lt;p&gt;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 &lt;a href="http://api.rubyonrails.com/classes/ActiveRecord/Associations/ClassMethods.html#M001105"&gt;counter cache&lt;/a&gt;s so that performing counts on my association were fast.&lt;/p&gt;

&lt;p&gt;First try,&lt;/p&gt;

&lt;pre class="sh_ruby"&gt;
class AddCommentCounterCacheOnTopics &lt; ActiveRecord::Migration
  def self.up
    add_column :topics, :comments_count, :integer, :default =&gt; 0
  end

  def self.down
    remove_column :topics, :comments_count
  end
end
&lt;/pre&gt;

&lt;p&gt;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 &lt;code&gt;comments_count&lt;/code&gt; column to whatever it was at the time of migration.&lt;/p&gt;

&lt;p&gt;Second try,&lt;/p&gt;

&lt;pre class="sh_ruby"&gt;
class AddCommentCounterCacheOnTopics &lt; ActiveRecord::Migration
  def self.up
    add_column :topics, :comments_count, :integer, :default =&gt; 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
&lt;/pre&gt;

&lt;p&gt;Two things to note: First, I’m using the &lt;code&gt;#count&lt;/code&gt; method in &lt;code&gt;t.comments.count&lt;/code&gt; because calling &lt;code&gt;#size&lt;/code&gt; will use the value in &lt;code&gt;t.comment_count&lt;/code&gt; which is 0. &lt;code&gt;#count&lt;/code&gt; on the other hand will perform a SQL &lt;code&gt;count()&lt;/code&gt;. Second, &lt;strong&gt;this won’t work!&lt;/strong&gt; Why? Because counter cache columns are set to &lt;code&gt;attr_readonly&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To work around this, a little hack. Final result,&lt;/p&gt;

&lt;pre class="sh_ruby"&gt;
class AddCommentCounterCacheOnTopics &lt; ActiveRecord::Migration
  def self.up
    add_column :topics, :comments_count, :integer, :default =&gt; 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
&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=INPPIZC6qek:TrQYOJgBMYU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=INPPIZC6qek:TrQYOJgBMYU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/INPPIZC6qek" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/INPPIZC6qek/30194408</link><guid isPermaLink="false">http://blog.bitfluent.com/post/30194408</guid><pubDate>Sun, 30 Mar 2008 01:34:00 +0800</pubDate><feedburner:origLink>http://blog.bitfluent.com/post/30194408</feedburner:origLink></item><item><title>Excellent panel on scaling customer support. Definitely...</title><description>&lt;object type="application/x-shockwave-flash" data="http://blip.tv/scripts/flash/showplayer.swf?enablejs=true&amp;feedurl=http%3A%2F%2Fgetsatisfaction%2Eblip%2Etv%2Frss&amp;file=http%3A%2F%2Fblip%2Etv%2Frss%2Fflash%2F683327&amp;showplayerpath=http%3A%2F%2Fblip%2Etv%2Fscripts%2Fflash%2Fshowplayer%2Eswf" width="400" height="255" allowfullscreen="true" id="showplayer"&gt;&lt;param name="movie" value="http://blip.tv/scripts/flash/showplayer.swf?enablejs=true&amp;feedurl=http%3A%2F%2Fgetsatisfaction%2Eblip%2Etv%2Frss&amp;file=http%3A%2F%2Fblip%2Etv%2Frss%2Fflash%2F683327&amp;showplayerpath=http%3A%2F%2Fblip%2Etv%2Fscripts%2Fflash%2Fshowplayer%2Eswf" /&gt;&lt;param name="quality" value="best" /&gt;&lt;embed src="http://blip.tv/scripts/flash/showplayer.swf?enablejs=true&amp;feedurl=http%3A%2F%2Fgetsatisfaction%2Eblip%2Etv%2Frss&amp;file=http%3A%2F%2Fblip%2Etv%2Frss%2Fflash%2F683327&amp;showplayerpath=http%3A%2F%2Fblip%2Etv%2Fscripts%2Fflash%2Fshowplayer%2Eswf" quality="best" width="400" height="255" name="showplayer" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;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?&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=ddgTH4hQDHo:5cFzzskTcl4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bitfluent?a=ddgTH4hQDHo:5cFzzskTcl4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bitfluent?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bitfluent/~4/ddgTH4hQDHo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/bitfluent/~3/ddgTH4hQDHo/29670819</link><guid isPermaLink="false">http://blog.bitfluent.com/post/29670819</guid><pubDate>Mon, 24 Mar 2008 11:56:00 +0800</pubDate><feedburner:origLink>http://blog.bitfluent.com/post/29670819</feedburner:origLink></item></channel></rss>
