<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Samuel Wong]]></title>
  <link href="http://samuelhwong.com/atom.xml" rel="self"/>
  <link href="http://samuelhwong.com/"/>
  <updated>2012-01-22T01:43:28-05:00</updated>
  <id>http://samuelhwong.com/</id>
  <author>
    <name><![CDATA[Samuel Wong]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Simplicity]]></title>
    <link href="http://samuelhwong.com/blog/2012/01/22/simplicity/"/>
    <updated>2012-01-22T00:44:00-05:00</updated>
    <id>http://samuelhwong.com/blog/2012/01/22/simplicity</id>
    <content type="html"><![CDATA[<p>In my undergraduate engineering design course, we were taught
to &#8220;KISS and MISS&#8221;, which stands for &#8220;Keep It Super Simple&#8221;
and &#8220;Make It Super Simple&#8221;. (Or if you prefer, &#8220;Keep It Simple, Stupid&#8221;)
I find this to be true in my work. Too often,
I get sucked into building complex, flashy code at the expense of
achieving the basics within a smaller timeframe. I think to myself:
&#8220;wouldn&#8217;t it be nice to have an <a href="http://twitter.github.com/bootstrap/javascript.html">AJAX popup</a>
 and a
<a href="http://37signals.com/svn/archives/000558.php">yellow-fade technique</a>
right there when the user clicks the button?&#8221; And I get excited
about using my newly-learned <a href="http://documentcloud.github.com/backbone/">Backbone.js</a>
 knowledge&#8230; Oh, it goes on from there!</p>

<p>Two weeks later, I still haven&#8217;t pushed out that &#8220;easy&#8221; feature I promised
I&#8217;d get done &#8220;in a few days.&#8221;</p>

<p>So I&#8217;m learning, again, how to keep things simple. It helps to
know that software is always evolving and there are many opportunities
to go back and refine code. Software development is a process and
you gotta grow hair before thinking about your hair-dos. It also helps
to know that humans are amazingly versatile and capable of
tolerating inconveniences (even solving them in creative ways).
There&#8217;s less pressure to deliver &#8220;perfect&#8221; software because
crashes are &#8220;okay.&#8221;</p>

<p>It&#8217;s all part and parcel of this journey called development.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Thoughts on SOPA]]></title>
    <link href="http://samuelhwong.com/blog/2012/01/19/thoughts-on-sopa/"/>
    <updated>2012-01-19T00:31:00-05:00</updated>
    <id>http://samuelhwong.com/blog/2012/01/19/thoughts-on-sopa</id>
    <content type="html"><![CDATA[<p>There have been a huge uproar over SOPA lately. I thought I would
weigh in on it because the Internet is so integral to my life
and livelihood.</p>

<p>I am against SOPA because it breaks a foundational aspect of
the Internet &#8211; reliability and security. In geekspeak, SOPA
breaks the domain name system (DNS).</p>

<!-- more -->


<h3>What is DNS and how does it work?</h3>

<p>A DNS server is a computer that converts a human-readable web address
(like <a href="http://www.google.com">google.com</a>)
to a string of numbers
&#8211; the IP address &#8211; that corresponds to a web server
(in this case, <a href="http://74.125.226.52">74.125.226.52</a>).
There are millions of these DNS servers all over the world, and working
together, they make the Internet accessible to everyone.
The accuracy guaranteed by DNS makes the Internet a great
place to express oneself and do business. By construction,
DNS guarantees that wherever you are in the world, at any time,
when you type in www.samuelhwong.com, you are connected to my web
server, which &#8220;serves&#8221; you my blog. It&#8217;s guaranteed that you won&#8217;t
be seeing something you didn&#8217;t ask for and it protects my identity
on the web.</p>

<p>The reliability of DNS was built into the system. DNS servers all over
the world replicate and cache DNS entries from each other.
Only one server has the authority to make changes to a particular
DNS record; the rest of the Internet <em>must</em> replicate and cache it.
In general, DNS records have a Time To Live (TTL) of 24 hours, which
means that if a domain name is modified (i.e. I want to point it to
another IP address), the change will propagate worldwide within 24
hours. This is amazing stuff and makes for a very efficient,
accurate, and redundant system.</p>

<h3>How does SOPA change DNS?</h3>

<p>SOPA changes this behaviour. A provision in SOPA would allow the
Attorney General to order ISPs to block access to infringing
websites. For example, if my website was
blacklisted and you are a Rogers customer, samuelhwong.com would
point to an FBI site (say, 111.111.111.111) instead of 208.94.116.79.</p>

<p>What&#8217;s so bad about this, you say? This is bad news because
Rogers&#8217; DNS record is effectively overriding mine (the authority).
Within 24 hours, other DNS servers will come calling to do an &#8220;update&#8221;
and they will encounter a conflict. In one case, other DNS servers
will propagate 111.111.111.111, which amounts to DNS poisoning.
In another case, the other DNS servers do not to trust DNS records from
Rogers&#8217; servers and don&#8217;t propagate records from them -
which is equally bad because it begs the questions: whose
servers can we trust? which ones are &#8220;right&#8221;?</p>

<h3>Don&#8217;t touch my DNS!</h3>

<p>You might think that&#8217;s it&#8217;s not such a bad idea to
block infriging websites. But the manner in which we are doing
it breaks a fundamental part of how the
Internet works: only one server in the world has the authority to make
changes to a particular DNS record and every other DNS <em>must</em>
replicate and cache it. If any non-authoritative
DNS server is given the ability
to change DNS records at will, it severely compromises Internet security.
If you type in paypal.com, you never know whether you&#8217;re seeing
the website that PayPal Inc. intended for you to see. For all you
know, it could be a scammer pretending to be PayPal and there&#8217;s goes
your credit card information. Only PayPal should have control
over its domain name &#8211; not the ISPs and not the government.</p>

<p>DNS is such a fundamental and important part of the Internet
that efforts have been made to make it even more secure. DNSSEC
adds security to the domain name system by digitally signing
records to protect against DNS poisoning. The digital signature
ensures the DNS record came from an authoritative source before
it is used. Again, it upholds the basic principle that only
one server in the world can be the
authority for a particular DNS record. SOPA unravels the
very purpose of DNSSEC.</p>

<p>You would think all this fiddling with DNS would stop piracy
but it doesn&#8217;t. You can easily bypass DNS by typing in the IP
address directly. If ISPs start blocking IP address, then
we&#8217;re in really deep trouble. IP blocking amounts to firewalling
and unnecessary censorship because many (innocent)
websites could be hosted behind a single IP address.</p>

<p>The DNS issue is only one aspect of SOPA, which from my perspective,
is a big one. (Yes, it&#8217;s a decidedly geek issue.) If we want to take
down the pirates, there are other ways to do it, like seizing
computers and freezing their revenue sources. Barring advertisers
from advertising on infringing sites is a provision in SOPA
that I can agree with.</p>

<p>Just don&#8217;t mess with my DNS.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Amusement of the day]]></title>
    <link href="http://samuelhwong.com/blog/2012/01/16/amusement-of-the-day/"/>
    <updated>2012-01-16T11:32:00-05:00</updated>
    <id>http://samuelhwong.com/blog/2012/01/16/amusement-of-the-day</id>
    <content type="html"><![CDATA[<p>I was playing around with <a href="https://github.com/github/gollum">Gollum</a> and when I booted
it up, I was greeted with a great log message:</p>

<pre><code>== Sinatra/1.2.6 has taken the stage on 4567 for development with backup from Thin
&gt;&gt; Thin web server (v1.3.1 codename Triple Espresso)
&gt;&gt; Maximum connections set to 1024
&gt;&gt; Listening on 0.0.0.0:4567, CTRL+C to stop
^C&gt;&gt; Stopping ...

== Sinatra has ended his set (crowd applauds)
</code></pre>

<p>Wait! That&#8217;s Triple Espresso! I better update my Thin &#8211; it&#8217;s still on v.1.2.11 codename Bat-Shit Crazy.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Compiling the Bootstrap framework]]></title>
    <link href="http://samuelhwong.com/blog/2012/01/15/compiling-the-bootstrap-framework/"/>
    <updated>2012-01-15T16:48:00-05:00</updated>
    <id>http://samuelhwong.com/blog/2012/01/15/compiling-the-bootstrap-framework</id>
    <content type="html"><![CDATA[<p>I recently migrated <a href="http://www.unstash.com">unstash</a> to the next version of
<a href="http://twitter.github.com/bootstrap">Bootstrap</a>, v.2. Version 2 hasn&#8217;t been released yet,
which means the CSS file isn&#8217;t available - I had to compile it myself. To do
this, I had to install a bunch of software packages. Here&#8217;s how I did it.</p>

<!-- more -->


<h3>Install Node</h3>

<p>I had git installed via HomeBrew from before, so I jumped right into grabbing
the source for Node:</p>

<pre><code>$ git clone https://github.com/joyent/node.git
</code></pre>

<p>I wanted the latest stable version, and it was</p>

<pre><code>$ git checkout v0.6.7
</code></pre>

<p>I created a <code>local</code> folder in my <code>$HOME</code> and copmiled Node into it:</p>

<pre><code>$ mkdir ~/local
$ ./configure --prefix=$HOME/local/node
$ make
$ make install
</code></pre>

<p>I added Node to my <code>$PATH</code> and I was done:</p>

<pre><code>$ vi ~/.bash_profile
$ export PATH=$HOME/local/node/bin:$PATH
$ node -v
&gt; 0.6.7
</code></pre>

<h3>Install NPM</h3>

<p>This one was easy:</p>

<pre><code>$ curl http://npmjs.org/install.sh | sh
</code></pre>

<h3>Install LESS</h3>

<p>Using NPM, I installed LESS:</p>

<pre><code>$ npm install less -g
</code></pre>

<h3>Finally, compile Bootstrap</h3>

<p>I grabbed the Bootstrap repository:</p>

<pre><code>$ git clone https://github.com/twitter/bootstrap.git
</code></pre>

<p>And compiled it:</p>

<pre><code>$ make build
</code></pre>

<p>My reward?</p>

<pre><code>&gt; Bootstrap successfully built! - Sun 15 Jan 2012 17:32:20 EST
</code></pre>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Freelancer's guide to productivity tools]]></title>
    <link href="http://samuelhwong.com/blog/2012/01/07/freelancers-guide-to-productivity-tools/"/>
    <updated>2012-01-07T13:42:00-05:00</updated>
    <id>http://samuelhwong.com/blog/2012/01/07/freelancers-guide-to-productivity-tools</id>
    <content type="html"><![CDATA[<p><img class="center" src="https://s3.amazonaws.com/samuelhwong.com/2012-01-07/toolbox.jpg"></p>

<p>If you are a freelancer or entrepreneur, you&#8217;re a creator and you&#8217;re joining
the ranks of Leonardo da Vinci, Michelangelo, and Vitruvius. (Don&#8217;t know Mr. da Vinci?
<a href="http://www.ontariosciencecentre.ca/davinci/default.asp">Go to the science center</a>, dude.)
Okay, maybe you&#8217;re not <em>that</em> good and no one is commissioning you to paint the Sistine
Chapel. But you and &#8216;Mike&#8217; are cut from the same cloth &#8211; you&#8217;re both making
something beautiful from nothing at all.</p>

<p>To do this hard work, it&#8217;s advisable to get the right tools. Sure, you can survive without
them. In fact, <a href="http://kenrockwell.com/tech/notcamera.htm">it&#8217;s not the camera that takes good photos</a>,
it&#8217;s the person behind the camera.
But once you get going and know the field better, having good tools will make life easier
and allow you to concentrate on the real work of imagining and problem-solving.</p>

<p>Yes, this week&#8217;s episode on freelancing is about productivity <em>tools</em>.</p>

<!-- more -->


<h3>The Journal</h3>

<p>Leonardo da Vinci is famous for his journal and you should have one too. Your journal
is the place where you jot down everything and I do mean <em>everything</em> &#8211; half-baked ideas,
problems you encounter, solutions that worked and attemps that failed, dreams and plans for
the future, ah-ha moments, procedures for doing things, and important contacts.</p>

<p>I&#8217;ve played with many solutions over the years, so there&#8217;s definitely isn&#8217;t one-size-fits-all
method here. You&#8217;ll just have to try and be persistant in finding the tool that fits your
work style.</p>

<p>Some recommendations:</p>

<ul>
<li><a href="http://mediawiki.org">MediaWiki</a> - Great for linking between pages, naturally multi-player, has an expressive
and simple syntax, and you can insert jpgs within your notes.</li>
<li><a href="http://dokuwiki.org">DokuWiki</a> - Another wiki. DokuWiki stores pages in plain text files - no database needed.
This makes backup easy and you can easily write scripts to generate documentation.</li>
<li><a href="http://tiddlywiki.com">TiddlyWiki</a> - A personal notework wiki. It&#8217;s rather unique. Go play with it to see what I mean.</li>
<li><a href="http://evernote.com">Evernote</a> - Great for syncing across devices, but the free plan is capped.</li>
<li>Pen, paper and scanner - The old-fashioned way with a modern twist. Write and draw as you would, and scan them to
keep a backup and for sharing. <a href="http://www.moleskine.com/">Moleskine</a> notebooks are all the rage but dollar store notepads
also work (You can get three pocket-sized, spiral-bound notepads for $1).</li>
<li><a href="http://docs.google.com">Google Docs</a> - Word processing, spreadsheets, presentations. All in the cloud, searchable,
and you can see what others are doing in real-time.</li>
</ul>


<h3>Project management tools</h3>

<p>If you on a team, you&#8217;ll want to know who is doing what. If you&#8217;re working solo, you&#8217;ll
want to know what you were working on last - especially for complicated projects
with multiple milestones and components.</p>

<ul>
<li><a href="http://www.apptrajectory.com">AppTrajectory</a> - Brought to you by the guys at ThoughtBot. There&#8217;s a 14-day free trial.</li>
<li><a href="http://www.pivotaltracker.com">PivotalTracker</a></li>
<li><a href="http://www.checkvist.com">Checkvist</a> - No frills to-do list. There are keyboard-shortcuts for just about everything,
which makes it appeal to the command-line guy within me. Lists can be shared and the layout looks great on mobile.</li>
<li><a href="http://www.rememberthemilk.com">Remember the Milk</a> - The classic to-do list that&#8217;s feature-rich and mobile apps are available.</li>
<li><a href="http://www.trello.com">Trello</a> - Brought to you by the guys at Fog Creek. At it&#8217;s core, it&#8217;s a to-do list, but you can
create multiple lists and drag to-dos between them. Great for projects that have definable processes/stages.</li>
</ul>


<h3>Communication tools</h3>

<p>Teamwork means communication. When I&#8217;m evaluating communication tools, I look for how easy
it is to send something to someone: text, image files, code snippets, screenshots, links, videos.
Search and chat history is a must. Notifications (pinging someone when they are not in the room)
is also a necessity.</p>

<ul>
<li><a href="http://www.hipchat.com">Hipchat</a> - HipChat makes it simple to create your own chatrooms. You can easily
attach images and files - they get uploaded to Amazon S3 and your team can click a link to download it.
There are mobile apps for iOS and Android, and you can communicate via text message as well.</li>
<li><a href="http://www.gmail.com">Google Apps</a> - The built-in chat client in Gmail is nice, but we&#8217;ve had some issues
with it. Manually inviting your team into the chatroom each time gets tiresome, and it gets worse if
when they get automatically booted out of the room due to inactivity. (Maybe Google fixed this by now?)
The upside is you get Google+ Hangouts and screen-sharing, all for free.</li>
</ul>


<h3>File sharing</h3>

<p>Some documents need to be shared, such as PDFs of legal documents and invoices.</p>

<ul>
<li><a href="http://www.dropbox.com">Dropbox</a> - Everyone knows Dropbox, right?</li>
<li>SSH-enabled server - This is easy to set up. This guarantees your information is not being
given to a 3rd-party and you can do it for cheap/next to nothing if you already have a web server.</li>
</ul>


<p>I&#8217;m sure there are lots of other services and tools out there. If you&#8217;ve fallen in love
with your tools, rave about it in the comments! I&#8217;d love to increase my productivity!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[How to test ActiveModel callbacks with RSpec]]></title>
    <link href="http://samuelhwong.com/blog/2011/12/29/how-to-test-activemodel-callbacks-with-rspec/"/>
    <updated>2011-12-29T09:50:00-05:00</updated>
    <id>http://samuelhwong.com/blog/2011/12/29/how-to-test-activemodel-callbacks-with-rspec</id>
    <content type="html"><![CDATA[<p>In <a href="http://unstash.com">unstash</a>, every time a person (say, the owner of the item) makes a
change to a loan request (i.e. he approves it), the other person involved
in the loan (in this case, the borrower) is notified.</p>

<p>This is easy in Rails. In my <code>Loans</code> model, I have an <code>after_save</code>
callback that runs <code>LoanNotification.create_from_loan(self)</code>. I&#8217;ve purposely
delegated the task of creating the actual notification and determining the recipient
away from <code>Loan</code>. This is because <code>Loans</code> shouldn&#8217;t be responsible for how
<code>LoanNotifications</code> are created (<a href="http://en.wikipedia.org/wiki/Single_responsibility_principle">SRP</a>).</p>

<p>Here&#8217;s how I implemented this in RSpec.</p>

<!-- more -->


<p>My first attempt:</p>

<pre><code>describe Loan do
  describe 'after_save', :focus do
    it "creates a loan notification" do
      @loan = FactoryGirl.build(:loan)
      LoanNotification.should_receive(:create_from_loan)
      @loan.save!
    end
  end
end
</code></pre>

<p>But this is brittle because I might one day decide to rename <code>create_from_loan</code>
and cause the test to break. What I really want to test is the effect of <code>after_save</code>,
which is saving a <code>LoanNotification</code> object to the database.</p>

<p>The better way:</p>

<pre><code>describe Loan do
  describe 'after_save', :focus do
    it "creates a loan notification" do
      expect {
        @loan = FactoryGirl.build(:loan)
        @loan.save!
      }.to change{ LoanNotification.all.count }.by(1)
    end
  end
end
</code></pre>

<p>This gets at the core of what I want to test and clearly tells us
that <code>Loan</code> is not responsible for <em>how</em> the <code>LoanNotification</code> is created,
but only that it <em>is</em> created.</p>

<p>In my <code>LoanNotification</code> spec, I can test that the recipient is correctly
identified and any data is saved correctly.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Octopress is way sexy]]></title>
    <link href="http://samuelhwong.com/blog/2011/12/23/octopress-is-way-sexy/"/>
    <updated>2011-12-23T23:35:00-05:00</updated>
    <id>http://samuelhwong.com/blog/2011/12/23/octopress-is-way-sexy</id>
    <content type="html"><![CDATA[<p>Wordpress is the undisputed king of the blog engines, but he&#8217;s getting
fat and bloated, sitting on that throne of his. I recently created an e-commerce
website out of a Wordpress installation &#8211; honestly, is there anything Wordpress
can&#8217;t do with the right plugin?</p>

<p>But let&#8217;s face it: using Wordpress is hard work.</p>

<!-- more -->


<p>Migrating from one URL to another is a pain and requires digging into
the database itself. Updates come regularly but backups are more trouble than
it&#8217;s worth. Yes, there&#8217;s probably a plugin for that, but I expect backups
to be a standard feature, not an add-on. On top of this, you have to be online
to write and I&#8217;m not always in range of wifi. Granted, it doesn&#8217;t take a
rocket scientist to do these things, but this particular rocket scientist has
better things to do with his time, like actually write a blog post.</p>

<p>Boy was I ecstatic when I found out about Octopress.</p>

<ul>
<li>It&#8217;s deadbeat simple.</li>
<li>I get to use my favourite tools: the terminal and vi.</li>
<li>It uses markdown so I don&#8217;t have to think about formatting.</li>
<li>It has responsive CSS so it looks beautiful on my phone (Android, of course).</li>
<li>It backs itself up onto github with a cronjob.</li>
<li>It looks way sexy in black.</li>
</ul>


<p>If you&#8217;re not afraid of the command line, go give Octopress a try. If you&#8217;re
99% of the population that can&#8217;t understand geek-speak, stick with Wordpress.
In the meantime, I&#8217;ll go ahead and brag about how this self-hosted blog costs
me $0.01 per month to run.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Freelancer's guide to workspaces in suburban Toronto/GTA]]></title>
    <link href="http://samuelhwong.com/blog/2011/12/20/freelancers-guide-to-workspaces-in-suburban-toronto-slash-gta/"/>
    <updated>2011-12-20T14:57:00-05:00</updated>
    <id>http://samuelhwong.com/blog/2011/12/20/freelancers-guide-to-workspaces-in-suburban-toronto-slash-gta</id>
    <content type="html"><![CDATA[<p>So you&#8217;re a freelancer, entrepreneur, or part of a team at a startup company &#8211; congrats for taking the plunge. You&#8217;re joining thousands of like-minded, driven individuals who have decided to make their dreams a reality instead of just thinking about it. The perks are great: you work on what you like, how you like, when you like, and the commute time is the 5 seconds it takes to walk from your bed to the desk. Life is rosy!</p>

<p>But you&#8217;ll quickly realize there are some hidden battles to fight, and the most challenging ones are those that come from within. Over the next few weeks, I hope to write about some of these battles and how I&#8217;ve fared against them. This is mostly for my own benefit as I reflect on a year of doing full-time startup work. This week is about the workspace battle.</p>

<!-- more -->


<p>If you work from home, you&#8217;ll know this battle well. Your foes are distractions and temptations like the TV, the fridge, the sofa, and even your very own room. There is something about working alone and being too comfortable that makes it difficult to focus on what needs to be done. Maybe there is a psychological effect as well. As the work-home line becomes increasingly blurred, the roles and routines we have also become blurred: &#8220;Am I at work? Am I at home?&#8221; We may act differently at home than when we&#8217;re in the workplace and the blurry line makes for great distractions and odd moments of indecisions. &#8220;Should I be dressing better even when no one is around? Should I be raking the leaves or calling that client?&#8221;</p>

<p>The solution I&#8217;ve found is to work outside of the home once in a while. But you don&#8217;t need to shell out the big bucks for an office or co-working space, though. Below is a list of free and nearly-free workspaces I&#8217;ve worked out of:</p>

<h3>Public libraries (Toronto and Markham)</h3>

<p><em>Wifi</em>: yes<br/>
<em>Outlet</em>: generally, yes but not always<br/>
<em>Noise level</em>: quiet<br/>
<em>Price</em>: free<br/>
<em>Notes</em>: Toronto public libraries don&#8217;t allow SSH connections. My favourites are Fairview (TPL), North York Centre (TPL), Milliken (MPL), and Angus Glen (MPL).</p>

<h3>Second Cup and Starbucks</h3>

<p><em>Wifi</em>: yes<br/>
<em>Outlet</em>: yes, at certain tables<br/>
<em>Noise level</em>: low<br/>
<em>Price</em>: a cup of coffee</p>

<h3>Ikea (Sheppard Ave. &amp; Leslie St.)</h3>

<p><em>Wifi</em>: no<br/>
<em>Outlet</em>: yes, at certain tables<br/>
<em>Noise level</em>: low-medium<br/>
<em>Price</em>: if you get there at 9:30am, get breakfast for $1 and free coffee.<br/>
<em>Notes</em>: the Swedish meatballs are excellent</p>

<h3>Food courts in shopping malls</h3>

<p><em>Wifi</em>: generally, no<br/>
<em>Outlet</em>: depends. Markville Mall has some outlets near the middle of the food court<br/>
<em>Noise level</em>: high<br/>
<em>Price</em>: free</p>

<h3>Centennial Community Centre (McCowan Rd. and Bullock Dr.)</h3>

<p><img class="left" src="https://s3.amazonaws.com/samuelhwong.com/2011-12-20/IMG_20111220_135616-thumb.jpg"></p>

<p><em>Wifi</em>: yes<br/>
<em>Outlet</em>: not at the table, but there are some close by<br/>
<em>Noise level</em>: dead quiet<br/>
<em>Price</em>: free</p>

<div style="clear:both"></div>


<h3>Bayview Village (Bayview Ave. &amp; Sheppard Ave.)</h3>

<p><em>Wifi</em>: no<br/>
<em>Outlet</em>: at certain tables and at the bar table<br/>
<em>Noise level</em>: medium<br/>
<em>Price</em>: free<br/>
<em>Notes</em>: the seating area is located near the Chapters entrance on the inside of the mall</p>

<h3>McDonalds</h3>

<p><em>Wifi</em>: yes<br/>
<em>Outlet</em>: generally, no<br/>
<em>Noise level</em>: medium<br/>
<em>Price</em>: a cup of coffee, or Chicken McNuggets if you prefer</p>

<h3>Pizza Pizza (Warden Ave. &amp; Hwy. 7)</h3>

<p><em>Wifi</em>: yes<br/>
<em>Outlet</em>: yes<br/>
<em>Noise level</em>: low-medium<br/>
<em>Price</em>: a slice of pizza</p>

<p><strong>What&#8217;s your favourite workspace?</strong></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Notes on AndroidTO conference]]></title>
    <link href="http://samuelhwong.com/blog/2011/11/10/notes-on-androidto-conference/"/>
    <updated>2011-11-10T21:29:00-05:00</updated>
    <id>http://samuelhwong.com/blog/2011/11/10/notes-on-androidto-conference</id>
    <content type="html"><![CDATA[<p>Just some of my personal notes from the AndroidTO conference in Toronto. Not all
the slides/code from the speakers have been posted to the androidto.com site&#8230;
boo-urns!</p>

<p>What&#8217;s covered:</p>

<ul>
<li>Just Because It&#8217;s Mobile Doesn&#8217;t Mean It has to be Native</li>
<li>Android tips and tricks</li>
<li>Near Field Communication (NFC)</li>
<li>Rogers Catalyst</li>
<li>Kernels 101</li>
<li>Custom Views</li>
<li>Google AppInventor</li>
</ul>


<!-- more -->


<h3>Just Because It&#8217;s Mobile Doesn&#8217;t Mean It <em>has</em> to be Native</h3>

<p>This is an ongoing discussion everywhere. Unfortunately I only caught the tail end of this talk since I was manning the registration booths.</p>

<p>Some HTML5 keywords that I caught:</p>

<ul>
<li>Cache manifest</li>
<li>.appcache file</li>
<li>Html manifest: Use Javascript to detect when manifest has updated</li>
<li>window.localStorage</li>
<li>The basecamp web app is a good example.</li>
</ul>


<p>Some tools that were helpful in building a mobile-conscious webapp:</p>

<ul>
<li><a href="batmanjs.org">Batman.js</a>, a javascript mvc framework that uses data attributes to render interacrive views</li>
<li><a href="getskeleton.com">Skeleton CSS</a>, a responsive CSS framework. Responsiveness is all the rage now. The css will detect the mobile agent and present a different layout.</li>
<li>Use the <a href="http://developer.apple.com/library/IOs/#documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html">meta tag</a> to set viewport size.</li>
</ul>


<p><a href="https://github.com/csaunders">Chris&#8217;s Github</a> | <a href="http://christophersaunders.ca/">Chris&#8217;s blog</a></p>

<h3>Android tips and tricks</h3>

<ul>
<li>Use Content providers</li>
<li>Register your intent filters; registering will launch your native app whenever you encounter your URL</li>
<li>Use fragments, reusable!</li>
<li>Error tracking and other reporting with <a href="http://www.flurry.com/">Flurry</a> and <a href="http://www.bugsense.com/features/acra">Acra</a></li>
<li>Dev tools: Layoutopt, Hierarchyviewer, Monkey (for automated testing), Strict mode</li>
</ul>


<p>Tips</p>

<ul>
<li>Leave the ui thread alone</li>
<li>Develop and test on worst case devices</li>
<li>Design for orientation switching</li>
<li>Keep in mind dpad</li>
<li>Design for offline whenever possible</li>
<li>Beta test on the market</li>
<li>Be stingy with permissions</li>
<li>Register company package: com.unstash.Android</li>
<li>Use strings.xml and translate</li>
<li>Use public apis, calendar content providers in ICS</li>
<li>Always assume your tasks can be interrupted</li>
<li>Battery life considerations</li>
<li>How to jump the learning curve: watch google io videos</li>
<li>How to quickly implement expected UI gestures like pull-down-to-refresh? It is a custom widget, you might find on Github.</li>
</ul>


<h3>NFC</h3>

<p>This was the best talk, honestly. They had demos of NFC working and also dived into the source code. Essentially, NFC can be used to launch Intents and transfer information from one device to another, or from a NFC tag to the phone.</p>

<p><a href="http://www.codinggreenrobots.com/downloads/open-source-projects/androidto-2011/">Code</a></p>

<h3>Rogers Catalyst</h3>

<p>Ah, the requisite sponsor speaker. Rogers offer interesting things to developers, like network-based location (coarse), access to Customer Telephone Number (CTN), SMS delivery and receive-handling (shortcodes), and carrier billing. Of course, they&#8217;ll charge you for use of these services and it&#8217;s only available for Rogers customers. The speaker demo&#8217;ed an app where he could tweet to his phone and the phone would reply with its location, via Twitter DM.</p>

<p>Question from the audience: why would you use Rogers location API over Google&#8217;s location API? Turns out, Google uses Rogers location API underneath its own API&#8230; so it might be faster to use Rogers directly? Or they just want your money.</p>

<p><a href="http://androidto.com/wp-content/uploads/2011/10/Rogers-Catalyst-For-Android-Developers.pdf">Slides</a></p>

<h3>Kernels 101</h3>

<p>This went over my head, partly because I was on the side of the stage and partly because his font size was so small I could hardly follow what he was doing. But in essence, he compiled the Android kernel with some familiar tools like fastboot and codesorcery.</p>

<h3>Custom views</h3>

<p>Sometimes you just got customize some behaviour not offered by the APIs. To do this, dive into the android source code. Grepcode.com is a good tool for code diving. Check out kik.com developers blog.</p>

<p><a href="http://www.youtube.com/watch?v=fLw4Wn_qlWA">Video</a> <a href="http://kik.com/dev/androidto/BetterLiving.rar">Code</a></p>

<h3>Google AppInventor</h3>

<p>It&#8217;s a &#8220;gateway drug&#8221; for programmers just starting out on the Android journey. It&#8217;s also great for prototyping. Speaker claims to get an app done in under 1 hour. Notable features:</p>

<ul>
<li>All online</li>
<li>Fusion tables , store my SQL table online</li>
<li>REST-capable</li>
<li>Drag and drop GUI</li>
<li>No version control</li>
<li>Lots of changes, very rapid development and adding of new features</li>
</ul>


<p>Official resources: <a href="http://androidto.com/speakers">http://androidto.com/speakers</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[FUSE for OS X and MacFusion]]></title>
    <link href="http://samuelhwong.com/blog/2011/11/02/fuse-for-os-x-and-macfusion/"/>
    <updated>2011-11-02T21:28:00-04:00</updated>
    <id>http://samuelhwong.com/blog/2011/11/02/fuse-for-os-x-and-macfusion</id>
    <content type="html"><![CDATA[<p><img class="center" src="https://s3.amazonaws.com/samuelhwong.com/2011-11-02/Mac-Mini-2011.jpg"></p>

<p>My new Mac Mini doesn&#8217;t have an optical drive. It&#8217;s a bit of a pain, but I definitely think it is the way forward. Soon, everything will be cloud-based and accessible over the Internet. But I still have old DVDs. What to do? Apple has something called <a href="http://support.apple.com/kb/HT1777?viewlocale=en_US">Remote Disc</a>. It allows other computers to share their optical drive over the network. I&#8217;ve tried it and works painlessly. Unfortunately, Remote Disc does not support Linux. What&#8217;s a Ubuntu lover to do? Go hacking with <code>sshfs</code>, of course.</p>

<!--more-->


<p>SSHFS allows you to mount a filesystem over SSH. On my Ubuntu laptop, I installed openssh-server:</p>

<pre><code>sudo apt-get install openssh-server
</code></pre>

<p>On the Mac, I installed <a href="http://osxfuse.github.com/">Fuse for OS X</a> and <a href="http://macfusionapp.org/">MacFusion</a>. Be sure to install the MacFUSE compatibility layer. Otherwise, MacFusion will complain that the session was unexpectedly terminated. MacFusion includes ftpfs and sshfs. The sshfs binary can be added to the PATH using a symlink:</p>

<pre><code>sudo ln -s /Applications/Macfusion.app/Contents/PlugIns/sshfs.mfplugin/Contents/MacOS/sshfs /usr/local/bin/sshfs
</code></pre>

<p>To mount the filesystem, I used MacFusion&#8217;s simple UI.</p>

<p><img class="center" src="https://s3.amazonaws.com/samuelhwong.com/2011-11-02/Screen-Shot-2011-11-02-at-12.30.38-PM.png"></p>

<p>Or in Terminal:</p>

<pre><code>mkdir /media/cdromdrive
sshfs samuel@192.168.1.171:/media/mountdir /media/cdromdrive
</code></pre>

<p>where 192.168.1.171 is the IP address of my Ubuntu box, <code>/media/mountdir</code> is the location of the CD&#8217;s mount point, and <code>/media/cdromdrive</code> is where it will be mounted on the Mac.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Ruby on Rails development platform on VirtualBox]]></title>
    <link href="http://samuelhwong.com/blog/2011/10/29/ruby-on-rails-development-platform-on-virtualbox/"/>
    <updated>2011-10-29T21:25:00-04:00</updated>
    <id>http://samuelhwong.com/blog/2011/10/29/ruby-on-rails-development-platform-on-virtualbox</id>
    <content type="html"><![CDATA[<p>Our dedicated development box is too loud. So I decided to take my super quiet Mac Mini and build a virtual Linux server so our Unstash dev team can work on it. This is a walkthrough installation of a Ruby on Rails development platform for a small team of 4 developers. I hope it helps someone out there looking to do something similar!</p>

<p><strong>Hardware:</strong> Mac Mini mid-2011 with 8GB RAM</p>

<p><strong>Goal:</strong> Run a virtualized ubuntu server and give each developer their own account.</p>

<!--more-->


<p><strong>Step 1: Install the Ubuntu 11.10 server through VirtualBox</strong></p>

<p><a href="http://virtualbox.org">VirtualBox</a> is a GPL-licensed virtualization product by Oracle. Installing VirtualBox is very straightforward &#8211; just download the DMG file for OS X and it installs like any other app.</p>

<p><a href="http://ubuntu.com">Ubuntu</a> 11.10 server edition is available in a 64-bit flavour. It says AMD in the filename, but don&#8217;t worry about it. It still works on my Intel-based Mac. Download the ISO but don&#8217;t burn to disc because we will be mounting it onto a virtual drive.</p>

<p>In VirtualBox, hit &#8220;New&#8221; to create a new Virtual Machine (VM). In the wizard:</p>

<ul>
<li>Name: ubuntu server 11.10 (it automatically selects ubuntu as the operating system)</li>
<li>Memory: 3072 MB</li>
<li>Network: bridged Ethernet (this is done so that the VM will appear like any other client on my LAN)</li>
<li>Virtual hard disk:

<ul>
<li>[X] Start-up Disk</li>
<li>[X] Create new hard disk</li>
<li>[X] VDI (VirtualBox Disk Image)</li>
<li>[X] Dynamically allocated</li>
<li>Size: 10.00 GB</li>
</ul>
</li>
</ul>


<p><img class="center" src="https://s3.amazonaws.com/samuelhwong.com/2011-10-29/Screen-Shot-2011-10-29-at-1.39.14-PM.png"></p>

<p>On first power-up of a new VM, VirtualBox will prompt you for boot media. Select the Ubuntu ISO file. It will boot directly into the Ubuntu server installation process. For components, I installed the OpenSSH server, PostgreSQL server, and LAMP server.</p>

<p><strong>Step 2: Install rvm for multiple users</strong></p>

<p>Before installing rvm, I installed git, curl, and other tools via apt-get.</p>

<pre><code>$ sudo apt-get install git curl build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake libtool bison subversion
$ sudo apt-get install libpq-dev libpq5
$ sudo apt-get install imagemagick --fix-missing -y
</code></pre>

<p>I followed <a href="http://beginrescueend.com/rvm/install/">http://beginrescueend.com/rvm/install/</a> using the Multi-User set of instructions. I installed the latest release version. I restarted my session and got a successful install of rvm. I then installed Ruby 1.9.2 and some gems.</p>

<pre><code>$ rvmsudo rvm install 1.9.2
$ rvm use 1.9.2 --default
$ rvmsudo gem install bundler rails heroku taps pg ruby-debug19 --no-rdoc --no-ri
</code></pre>

<p><strong>Step 3: Clone repository.</strong></p>

<p>I host my code on bitbucket. It used to be on Github, but bitbucket is free and does what I need &#8211; a private, offsite backup of my code.</p>

<pre><code>$ git clone path/to/git/repo
</code></pre>

<p><strong>Step 4: Run bundle to install gems listed in the Gemfile.</strong></p>

<pre><code>$ cd unstash
$ bundle
</code></pre>

<p>During the bundle install process, I encountered some problems:</p>

<ul>
<li><strong>Can&#8217;t install RMagick 2.3.0. Can&#8217;t find Magick-config.</strong> Solution: <tt>sudo apt-get install libmagick9-dev</tt></li>
</ul>


<p><strong>Step 5: Add other users.</strong></p>

<p>I like to use <a href="http://wiki.samuelhwong.com/index.php/How_to_add/remove/list_users">adduser</a>. For rvm to work with all users, they must be added to the <code>rvm</code> group. I also want to have a single group that all developers are members of.</p>

<pre><code>$ sudo addgroup unstash
$ sudo usermod -g unstash samuel   # this changes samuel's primary group to unstash
$ sudo usermod -a -G rvm samuel    # this adds rvm to samuels' secondary groups
$ rvmsudo rvm use 1.9.2 --default  # this makes everyone use ruby 1.9.2
</code></pre>

<p><strong>Step 6: Configure postgresql database tables.</strong></p>

<p>Since I chose to install PostgreSQL during the OS installation, all I need to do now is set the password for the <code>postgres</code> user. ALl the developers will be using this user to access the databases.</p>

<pre><code>$ sudo -u postgres psql postgres
postgres=# \password postgres
$ sudo -u postgres createdb samuel_development
</code></pre>

<p>To simplify the procedure of creating multiple databases for multiple users, I wrote a simple script at <a href="https://gist.github.com/1326557">https://gist.github.com/1326557</a></p>

<p><strong>Step 7: Configure database.yml</strong></p>

<p>This one is self-explanatory with one gotcha. The host parameter must be set as the loopback address, 127.0.0.1.</p>

<pre><code>adapter: postgresql
host: 127.0.0.1
database: &lt;app_name&gt;_development
username: postgres
password: &lt;your_password_here&gt;
pool: 5
timeout: 5000
</code></pre>

<p><strong>Step 8: Set environment variables</strong></p>

<p>If your app depends on environment variables, such as for API keys, set them in <code>~/.bashrc</code>.</p>

<pre><code>export FACEBOOK_SECRET='abc123abc123'
export FACEBOOK_KEY='def456def456'
</code></pre>

<p><strong>Step 9: Create aliases for starting up the server</strong></p>

<p>To keep developers from setting on each other, everyone has their own assigned port to run Thin on.</p>

<pre><code>$ bundle exec thin start -p 4000
</code></pre>

<p>The above can be placed in an alias in <code>~/.bashrc</code> or <code>~/.bash_aliases</code></p>

<pre><code>alias go='bundle exec thin start -p 4000'
</code></pre>

<p><strong>Step 10: Get down to work</strong></p>

<p>There are a few other things I haven&#8217;t explored yet, such as using <a href="http://devcenter.heroku.com/articles/procfile">foreman</a> to manage multiple processes and <a href="http://railscasts.com/episodes/285-spork">spork</a> to preload the test environment. These processes open up ports that may or may not conflict with other developers on the system.</p>

<p>If you have any ideas, comments, or another configuration you found to be useful, drop me a line.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Server for a small team]]></title>
    <link href="http://samuelhwong.com/blog/2011/10/29/server-for-a-small-team/"/>
    <updated>2011-10-29T21:24:00-04:00</updated>
    <id>http://samuelhwong.com/blog/2011/10/29/server-for-a-small-team</id>
    <content type="html"><![CDATA[<p>Working with the Unstash team of volunteers has been absolutely fantastic. But it hasn&#8217;t been without its technical challenges.</p>

<p>In the early days, we were having Wednesday night meetups at E&#8217;s house. We would all bring our laptops and hack code together. We hosted our site at <a href="http://site5.com">Site5</a> and we fell squarely in the &#8216;power users&#8217; category. A typical evening would see 3 or 4 of us all SSHing into the Site5 server. We were using the Symfony framework, which involved running the symfony generator scripts quite frequently. We would crank out code, run those PHP scripts, and &#8216;svn commit&#8217; and &#8216;svn up&#8217; all through the night. We had separate directories for each developer and we used checkouts of the SVN repo for our staging and production versions of the code.</p>

<p>In theory, this arrangement works well - each developer is totally isolated from the others. We each had our own copy of the code, our own MySQL database, and our own URL via different subdomains. In practice, it wasn&#8217;t so dandy.</p>

<!--more-->


<p>Using a shared hosting server as a development platform for 3 to 4 developers just doesn&#8217;t work. The problems were in how Site5 set up their server:</p>

<ul>
<li><strong>Long-running processes were killed:</strong> this was an issue with Site5&#8217;s firewall that detected long-running processes and automatically killed them. It&#8217;s understandable from their perspective, but it was frustrating for us. Even if it was just emacs, the process was killed. Talking to support helped; they put our frequently used programs on their whitelist. But every time they migrated our account to another server, we had to go through the whitelist process all over again.</li>
<li><strong>Lack of available memory:</strong> running symfony&#8217;s PHP scripts would spontaneously halt when it hit the memory limit. Having no access to php.ini, we had to resort to other means to increase the memory size. We had some success &#8211; again, Site5&#8217;s support was super &#8211; but it was a pain.</li>
<li><strong>Crazy Terminal word-wrapping:</strong> this was a huge pet peeve of mine. For whatever reason, the terminal display had a set maximum width (around 180 characters) that would automatically word-wrap and <span style="text-decoration: underline;">there was nothing you could do about it</span>. It didn&#8217;t matter that you stretched your Terminal window full screen. It would still wrap at 180 characters. Worse, if your Terminal window was larger than 180 characters, it would improperly wrap and actually start at the beginning of the same line and overwrite existing characters. The crazy thing was, if you viewed code in emacs, the code would fill the available width as you&#8217;d expect.</li>
<li><strong>Only one SSH user:</strong> this wasn&#8217;t a critical issue, but it was a pain. We had a number of people who were interested in joining us, and I would have to give them our one-and-only SSH username and password, which also happens to be the same credentials used to access CPanel. Not that I don&#8217;t trust them, I just didn&#8217;t feel very safe handing out our master key. And when these folks had to drop out of the team for whatever reason, I had to reset the password and let the rest of the team know about the new password. In practice, I just didn&#8217;t remember or have time to do a mundane thing like that.</li>
</ul>


<p>We briefly considered other solutions like a VPS, spinning up an Amazon instance, or find another shared host that would be more forgiving to us. Unfortunately, nothing really popped up as a great solution.</p>

<p>In January 2011, I committed myself to working on Unstash full-time. We ditched Symfony and moved to Rails. It was the best decision we&#8217;ve ever made.</p>

<p>From January to April, we worked off my Ubuntu laptop. I would open user accounts for each person and each person would have their own database table, home folder, and copy of the code. It was better, but not without its troubles:</p>

<ul>
<li><strong>My laptop was slow:</strong> a certain someone decided to play a practical joke on me and succeeded in DOSing my machine with ping flooding. Over the same wifi network. That&#8217;s how slow it is.</li>
<li><strong>My laptop had a measly 2.5GB of RAM:</strong> we ran out pretty quickly when we had a full house of 4 developers all running their own Mongrel instance.</li>
<li><strong>Availability:</strong> It didn&#8217;t allow the other folks to work on the project unless I had my laptop on.</li>
</ul>


<p>In anticipation of a summer intern, we bought a cheap AMD box and installed Ubuntu on it. We put everything we had on there and it was heaven. We had complete control and it was everything we wanted it to be. My only peeve now was that it was <em>as noisy as a jet engine</em>. You really do get what you pay for. In LW&#8217;s wood-paneled and hardwood-floored home office, the noise just bounces off everything.</p>

<p>Now I&#8217;m looking at another solution: virtualization. I recently purchased a Mac Mini (mid-2011) for myself and upgraded its RAM to 8GB. It&#8217;s more than enough to run a virtualized Ubuntu server on it. Best of all, the Mini is dead quiet. That is, unless I&#8217;m playing Starcraft 2 and that&#8217;s when the fan kicks in real good.</p>

<p>I&#8217;m hoping this solution will save some electricity (I&#8217;m on my Mac anyways) and give me some peace and quiet. I&#8217;ll write another post about how it all goes down.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Gamification]]></title>
    <link href="http://samuelhwong.com/blog/2011/10/28/gamification/"/>
    <updated>2011-10-28T21:22:00-04:00</updated>
    <id>http://samuelhwong.com/blog/2011/10/28/gamification</id>
    <content type="html"><![CDATA[<p>At first, I didn&#8217;t believe it would work on me. But I was wrong. I&#8217;m totally into this whole <a href="http://en.wikipedia.org/wiki/Gamification">gamification</a> thing.</p>

<p>What is it? Gamification is when sites encourage participation by awarding badges, points, or awards. FourSquare is a great example. To unlock badges, you need to meet a certain requirements, such as checking into a gym a certain number of times to get the &#8220;Gym&#8221; badge. My first thought was: &#8220;who cares how many badges you get?&#8221; I&#8217;m not a bragging type of person and I&#8217;m only ever competitive when I&#8217;m already near the top of a leaderboard (which is almost never). Naturally, I thought gamification would never work on me.</p>

<p>In general, this is very true. I play Starcraft 2 and I don&#8217;t care for unlocking the avatars, badges and achievements. I would never use FourSquare for the badges. I&#8217;m on Steam and I don&#8217;t care for the achievements there either. However, I&#8217;ve discovered that gamification does work on me if it is implemented with privileges as rewards, and that these privileges helps me Get Stuff Done or Get Stuff Done Better. I&#8217;m a productivity and efficiency freak, and it is little wonder that this method is so effective for me.</p>

<!--more-->


<p><a href="http://stackoverflow.com">StackOverflow</a> and its sister sites are a prime example of gamification with privileges. StackOverflow is a Q&amp;A website, which encourages good questions and good answers through a reputation system. When you are a first-time user, you have 1 reputation point and the only thing you can do is ask and answer questions. You can&#8217;t upvote or downvote, you can&#8217;t make edits or comments on existing questions and answers, you can&#8217;t put photos in your posts, and you can&#8217;t have more than 2 hyperlinks in your posts. As other users upvote your questions/answers, you gain reputation and with great reputation comes great privileges! Other useful abilities like flagging, commenting, editing, and bounties are opened up as you go along. You also gain badges for certain actions, like your first upvote gives you the Supporter badge. But I don&#8217;t care for badges. I really don&#8217;t.</p>

<p>Ultimately, for gamification to work well, the system of rewards must appeal to a wide variety of audience types. This means offering a variety of different rewards: badges, points, and filling up trophy cases work well for some, but not all, users. When linking a point system with escalating privileges, a fine balance must be struck between earning privileges and having enough privileges at the low-end of the ladder to keep the site is useful and engaging. We&#8217;re definitely looking to implement a gamification system for <a href="http://unstash.com">Unstash</a>. Look for it soon!</p>

<p>While you&#8217;re eagerly awaiting our launch, you can <a href="http://stackexchange.com/users/360925?tab=accounts">view my StackExchange reputation</a>. Not that I&#8217;m bragging, of course.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Mac: Home and End in the Terminal]]></title>
    <link href="http://samuelhwong.com/blog/2011/10/09/mac-home-and-end-in-the-terminal/"/>
    <updated>2011-10-09T21:19:00-04:00</updated>
    <id>http://samuelhwong.com/blog/2011/10/09/mac-home-and-end-in-the-terminal</id>
    <content type="html"><![CDATA[<p>In the Terminal preferences: Settings &gt; Keyboard tab. Select HOME and for the string to send, press ctrl+a. It should show up as \001. For END, press ctrl+e which shows up as \005.</p>

<p><img class="center" src="https://s3.amazonaws.com/samuelhwong.com/2011-10-09/terminal-home-end.png"></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Quick tip: ASCII codes and link_to]]></title>
    <link href="http://samuelhwong.com/blog/2011/09/29/quick-tip-ascii-codes-and-link-to/"/>
    <updated>2011-09-29T19:39:00-04:00</updated>
    <id>http://samuelhwong.com/blog/2011/09/29/quick-tip-ascii-codes-and-link-to</id>
    <content type="html"><![CDATA[<p>Problem: I wanted an ASCII character, <code>»</code>, to appear inside the body of a link. By default, Rails 3 escapes HTML for safety so I get this:</p>

<p><img class="center" src="https://s3.amazonaws.com/samuelhwong.com/2011-09-29/Screen-Shot-2011-09-29-at-1.24.52-PM.png"></p>

<p>Solution: Append <code>.html_safe</code> at the end of the body parameter.</p>

<pre><code>&lt;%= link_to 'Learn more about collaborative consumption &amp;raquo;'.html_safe,
'http://blog.unstash.com',
:class =&gt; 'btn primary large' %&gt;
</code></pre>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Ebook: Rails 3 in Action]]></title>
    <link href="http://samuelhwong.com/blog/2011/09/23/ebook-rails-3-in-action/"/>
    <updated>2011-09-23T19:28:00-04:00</updated>
    <id>http://samuelhwong.com/blog/2011/09/23/ebook-rails-3-in-action</id>
    <content type="html"><![CDATA[<p><a href="https://s3.amazonaws.com/samuelhwong.com/2011-09-23/katz_cover150.jpg"><img class="left" src="https://s3.amazonaws.com/samuelhwong.com/2011-09-23/katz_cover150.jpg"></a></p>

<p>While I wouldn&#8217;t say this is a great book, it is a good book. There aren&#8217;t many review of this book out yet, so I thought I would put in my two cents, as a beginner Rails developer (9 months).</p>

<!-- more -->


<p><strong>The Fantastic: It&#8217;s up to date.</strong></p>

<p>It&#8217;s Rails 3.1-ready. It uses the popular gems everyone else uses to get stuff done: Devise, Paperclip. It talks about building an API. Everyone is building APIs these days, and I&#8217;m glad the authors included a great walkthrough, complete with tests. That particular chapter was my main motivation for buying this book.</p>

<p><strong>The Good: It&#8217;s tutorial-like.</strong></p>

<p>It reminds me of the Depot app in the PragProg Rails book. The authors take the time to hold your hand as you progress through the building out the features. It&#8217;s very test-oriented, which is fantastic. There&#8217;s no better way to learn than by example.</p>

<p><strong>The Bad: The source code doesn&#8217;t compile out of the box.</strong></p>

<p>I git-clone and expected a fully running app after &#8216;bundle install&#8217;, but alas, I fell into dependency hell. Apparently I needed to get &#8216;forem&#8217;, which I did, but it threw some more errors about needing a specific version of another gem, and it goes from there. I spent a good 15 minutes trying to fix and I gave up. Not particularly newbie-friendly, given the tone of the book.</p>

<p><strong>The Ugly: An obvious mistake.</strong></p>

<p>An obvious mistake is when you have a mistake in your test and it causes the test to fail. I&#8217;m not sure how this got by the authors since it is in the provided source code, too.</p>

<p><span style="text-decoration: underline;">Listing 13.3  spec/api/v1/projects_spec.rb</span></p>

<p><code>get "#{url}.json"</code></p>

<p>should read</p>

<p><code>get "#{url}.json", :token =&gt; token</code></p>

<p>It took me a whole day to find this bug because I trusted the book too much (which I should have known better because no one&#8217;s perfect).</p>

<p><strong>Overall: Good book.</strong></p>

<p>My only disappointment is missing out on those 50%-off discount codes.</p>

<p>Photo credit: <a href="http://www.manning.com/katz/">Manning Publications</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Tossing out old hard drives]]></title>
    <link href="http://samuelhwong.com/blog/2011/09/15/tossing-out-old-hard-drives/"/>
    <updated>2011-09-15T19:26:00-04:00</updated>
    <id>http://samuelhwong.com/blog/2011/09/15/tossing-out-old-hard-drives</id>
    <content type="html"><![CDATA[<iframe width="560" height="315" src="http://www.youtube.com/embed/Y8Es9tWXZgw" frameborder="0" allowfullscreen></iframe>


<p>Ever since watching the YouTube videos promoting Toronto&#8217;s e-waste program, I&#8217;ve been scrounging my house for obsolete electronics to recycle. I&#8217;m glad they have curb-side pick-up now &#8211; I don&#8217;t have the time to make a special trip to transfer station. I&#8217;ve cleared out quite a bit: old monitors, a TV, a printer, a scanner and several hard drives.</p>

<p>Being the security-conscious person that I am, I want to thoroughly erase all data on these drives before setting it out on the curb. Deleting won&#8217;t do. Emptying the recycle bin won&#8217;t do. Formatting won&#8217;t do. I would bash the drives with a sledgehammer if I had one. So what&#8217;s a tech geek to do? Use Linux. Here&#8217;s my special recipe for utterly destroying data and making it near impossible to recover.</p>

<!-- more -->


<ol>
<li>Boot from a live CD or from other hard drive into Linux.</li>
<li><p>Use the &#8216;fdisk&#8217; command to check the name of the partitions of the hard drive. It&#8217;s usually <code>/dev/sda1</code> or <code>/dev/sdb1</code>, etc.</p>

<pre><code>sudo fdisk -l
</code></pre></li>
<li><p>Use the &#8216;wipe&#8217; command to securely erase data. Wipe makes multiple passes on the drive, writing different data patterns to it so that all parts of the drive are overwritten with data. This takes a long time, so I use quick mode. Do this for each partition.</p>

<pre><code>sudo wipe -q /dev/sda1
</code></pre></li>
<li><p>Use <code>dd</code> to destroy the partition table. This takes random data (<code>/dev/urandom</code>) and writes it to the drive.</p>

<pre><code>sudo dd if=/dev/urandom of=/dev/sda bs=4096
</code></pre></li>
</ol>


<p>And that&#8217;s how it&#8217;s done.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Workaround: Launchy doesn't launch]]></title>
    <link href="http://samuelhwong.com/blog/2011/09/07/workaround-launchy-doesnt-launch/"/>
    <updated>2011-09-07T19:24:00-04:00</updated>
    <id>http://samuelhwong.com/blog/2011/09/07/workaround-launchy-doesnt-launch</id>
    <content type="html"><![CDATA[<p>While doing BDD, I encountered a problem with Cucumber&#8217;s &#8220;Then show me the page&#8221; step. It may have been due to moving to a Mac or something else, but in any case, Firefox doesn&#8217;t launch and the error displayed is:</p>

<pre><code>Failure in opening /Users/samuel/unstash/unstash/tmp/capybara/capybara-201109071505526995598430.html with options {}: No application found to handle scheme ''. Known schemes: file, ftp, http, https
</code></pre>

<p>There&#8217;s an open issue on launchy&#8217;s github page, but even if it does get fixed in the next release, I would be wary of updating because of all the other gem dependencies&#8230; I don&#8217;t want to fix something that works 99% of the time.</p>

<p>My workaround is a simple bash script (I called it &#8220;launchff&#8221;) that pulls out the file path in the error message, and launches firefox.</p>

<!-- more -->


<pre><code>#!/bin/bash
bundle exec cucumber $1 --require features/ 2&amp;gt; error.log
SAVEDFILE=`cat error.log | awk '{print $4}'`
# print out error log
echo "================"
echo "ERROR LOG"
echo "================"
cat error.log
rm error.log
open -a Firefox.app $SAVEDFILE[/bash]
</code></pre>

<p>To execute: <code>$ launchff features/users/view-loans.feature</code></p>

<p>The result: the saved capybara page is opened in firefox in a separate tab.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Raising RecordNotFound exceptions]]></title>
    <link href="http://samuelhwong.com/blog/2011/09/02/raising-recordnotfound-exceptions/"/>
    <updated>2011-09-02T19:21:00-04:00</updated>
    <id>http://samuelhwong.com/blog/2011/09/02/raising-recordnotfound-exceptions</id>
    <content type="html"><![CDATA[<p>In our app, bringing up a user&#8217;s profile, items, or messages is done through the user&#8217;s nickname (a.k.a. username). It makes for very beautiful URLs and encourages users to be early-adopters so they can claim their name. For example, <a href="http://unstash.com/samuelwong">http://unstash.com/samuelwong</a> will bring you to my profile page.</p>

<p>ActiveRecord in Rails makes it easy to grab the records associated with an id or attribute (in this case, a nickname):</p>

<pre><code>@item = Item.find(params[:id])
@user = User.find_by_nickname(params[:nickname])
</code></pre>

<p>However, when you use any &#8216;find_by_*&#8217; methods, RecordNotFound exceptions are not raised for you. A simple trick solves this problem: use the &#8216;bang&#8217; version of the method.</p>

<!-- more -->


<pre><code>@user = User.find_by_nickname!(params[:nickname])
</code></pre>

<p>Sometimes it is useful to have custom error pages depending on what was not found. For example, http://unstash.com/puffythemagicdragon is not a valid profile, a search box should appear to help the user find the correct profile page. At the same time, http://unstash.com/items/3254 should result in a search box to help the user find the correct item.</p>

<p>This can be accomplished on a per-method basis in the controller:</p>

<pre><code>begin
  @user = User.find_by_nickname(params[:nickname])
rescue ActiveRecord::RecordNotFound
  render ...
end
</code></pre>

<p>Or over the entire controller:</p>

<pre><code>rescue_from ActiveRecord::RecordNotFound, :with =&gt; :throw_404
def throw_404
  render ...
  true
end
</code></pre>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Remapping keyboard and mouse in Mac OS X Lion]]></title>
    <link href="http://samuelhwong.com/blog/2011/08/29/remapping-keyboard-and-mouse-in-mac-os-x-lion/"/>
    <updated>2011-08-29T18:39:00-04:00</updated>
    <id>http://samuelhwong.com/blog/2011/08/29/remapping-keyboard-and-mouse-in-mac-os-x-lion</id>
    <content type="html"><![CDATA[<p>I recently got a Mac Mini (mid-2011) and I&#8217;m back to fighting with my keyboard
and mouse to make it the most productive and comfortable experience it can be.
I spend too much time in front of the screen, and spending even an hour to get
everything Just Right is worth it. In a nutshell, I want my Logitech wave
keyboard (K350) and mouse (M510) to be exactly the same as with my
<a href="http://samuelhwong.com/blog/2011/05/23/remapping-keyboard-and-mouse-buttons-in-ubuntu-11-04/" title="">Ubuntu setup</a>.</p>

<p>My final setup is as follows:</p>

<ul>
<li>CapsLock behaves as Ctrl.</li>
<li>Windows key behaves as Command</li>
<li>Left and right scroll wheel tilt behaves as back and forward buttons in browser (Firefox, Chrome) and Finder, respectively</li>
<li>Tracking speed is Just The Way I Like It.</li>
<li>Wheel button launches Mission Control.</li>
<li>Thumb button launches links in new tab. (Command-click)</li>
<li>Home and end keys jump the cursor to before the first and after the last characters in the current line, respectively.</li>
</ul>


<p>Here&#8217;s how I did it.</p>

<!-- more -->


<p>Mac OS X Lion (10.7) has difficulty detecting the keyboard layout when I
first plugged it in. But after following the instructions (i.e. press the
key to the right of Left Shift, etc.) it correctly identifies the layout.</p>

<p>In System Preferences, Lion has a wonderful keyboard panel:</p>

<p><a href="https://s3.amazonaws.com/samuelhwong.com/2011-08-29/Screen-Shot-2011-08-29-at-9.31.44-AM.png"><img class="center" src="https://s3.amazonaws.com/samuelhwong.com/2011-08-29/Screen-Shot-2011-08-29-at-9.31.44-AM-thumb.png"></a></p>

<p>Next, go to Modifier Keys&#8230;</p>

<p><a href="https://s3.amazonaws.com/samuelhwong.com/2011-08-29/Screen-Shot-2011-08-29-at-9.35.04-AM.png"><img class="center" src="https://s3.amazonaws.com/samuelhwong.com/2011-08-29/Screen-Shot-2011-08-29-at-9.35.04-AM-thumb.png"></a></p>

<p>Bingo, now CapsLock behaves as Control. To configure the mouse behaviour, you&#8217;ll need the infamous <a href="http://www.logitech.com/en-us/584/3129">Logitech Control Center</a> (LCC). It installs an LCC panel in System Preferences. The mouse config shows up just fine, but the keyboard is nowhere to be found. The keyboard is actually half-supported: if you &#8220;Open Unifying Software&#8221; &gt; &#8220;Advanced&#8230;&#8221;, you should see the keyboard happily living there. You just can&#8217;t configure it beyond the factory settings.</p>

<p><a href="https://s3.amazonaws.com/samuelhwong.com/2011-08-29/Screen-Shot-2011-08-29-at-9.37.45-AM.png"><img class="center" src="https://s3.amazonaws.com/samuelhwong.com/2011-08-29/Screen-Shot-2011-08-29-at-9.37.45-AM-thumb.png"></a></p>

<p><a href="https://s3.amazonaws.com/samuelhwong.com/2011-08-29/Screen-Shot-2011-08-29-at-9.39.39-AM.png"><img class="center" src="https://s3.amazonaws.com/samuelhwong.com/2011-08-29/Screen-Shot-2011-08-29-at-9.39.39-AM-thumb.png"></a></p>

<p>Go ahead the click the mouse photo and choose &#8220;Configure&#8230;&#8221;. Here are my settings:</p>

<p><a href="https://s3.amazonaws.com/samuelhwong.com/2011-08-29/Screen-Shot-2011-08-29-at-9.46.47-AM.png"><img class="center" src="https://s3.amazonaws.com/samuelhwong.com/2011-08-29/Screen-Shot-2011-08-29-at-9.46.47-AM-thumb.png"></a></p>

<p>The thumb button is an &#8220;Advanced Click&#8221;, and the way it is set up, it performs a single click of the left mouse button (Button number 1) with the Command key. When you thumb-click on a link, this has the effect of opening it in a new tab (Chrome and Firefox).</p>

<p>The other major problem I had after installing LCC was that the keyboard
remapping I did earlier no longer worked. Go back to the Keyboard preference
panel, and for each entry in the &#8220;Select Keyboard&#8221; drop-down, remap CapsLock
to Control. There are 13 Logitech entries &#8211; they are labeled Unifying
Receiver, Unifying Receiver 1, Unifying Receiver 1 2, etc. I have no idea
what this means, so I remapped them all. And it works!</p>

<p>I found the Tracking Multiplier in the LCC to be very useful when Lion&#8217;s
Tracking Speed wasn&#8217;t good enough. Watch out though, it does get a little
wobbly if you set it too high. I disabled the &#8220;Move content in direction
of finger movement when scrolling or navigating&#8221; because rolling the mouse
towards you should always mean you&#8217;re going down the page. ;)</p>

<p>Home and End keys: I used this hack courtesy of
<a href="http://lifehacker.com/225873/mac-switchers-tip--remap-the-home-and-end-keys">Lifehacker</a>
to get my Home/End behaviour back.</p>

<p>Edit the default keybindings file, <code>~/Library/KeyBindings/DefaultKeyBinding.dict</code>.
Create the directory and/or the file if they&#8217;re not already there, and make it
look like this:</p>

<pre><code>{
  /* Remap Home / End to be correct */
  "\UF729" = "moveToBeginningOfLine:"; /* Home */
  "\UF72B" = "moveToEndOfLine:"; /* End */
  "$\UF729" = "moveToBeginningOfLineAndModifySelection:"; /* Shift + Home */
  "$\UF72B" = "moveToEndOfLineAndModifySelection:"; /* Shift + End */
}
</code></pre>

<p>If there are already entries in <code>DefaultKeyBinding.dict</code>, just add the 4 new
mappings above to the main section of your file. A reboot may be needed to
get it to take effect.</p>

<p>Whew! Fight over, and I still like this Mac. Don&#8217;t worry Linux, you still have
a place in my heart. I&#8217;ll be installing Ubuntu as a VM soon enough.</p>
]]></content>
  </entry>
  
</feed>

