Email personalities

I see a lot of people complaining on Twitter about the massive amount of email they get and how hard it is to keep up.

If you really have this problem, you have my sympathy. But to be honest, I find it hard to believe that everybody is drowning in email.

The 10 second reply

How long do you spend replying to each email? For short mails, I find that I can reply in as little as 10 seconds:

  • 5 seconds to read a few lines of text
  • 5 seconds to reply with a single line

I use Gmail, and while the keyboard shortcuts could still be better, they’re just fine for 95% of time you’re reading and writing emails. So no waste of time there, just reading and writing. In peak periods I have literally responded to 5-6 emails per minute so I’m not just making this up.

So how much email did you say you get every day…?

I can’t reply in 5 seconds!

Right, some mails take a lot longer to reply to. But still, very few emails can not be responded to in a few minutes.

Keep your replies brief. If this is not possible, seriously consider if your work is right for you, or if you’re having a tedious discussion over email which could be settled in a 20 minute Skype meeting.

I felt this myself while working at VMware. I did get several hundred mails per day, and just reading through them was obviously time consuming. Some email threads would get extremely long and tangled, and I would spend 30 minutes replying to a single email.

Ultimately, this was part of what led to my decision to move on in my career.

Email receiver personalities

The Average Joe

You’re a regular guy, just like me. You get at most 10-20 mails per day (don’t count all those mailing lists and notifications).

Respond to them immediately or schedule one or two time slots for it per day. Keep your replies as brief as possible without causing confusion that may spur followup questions from the sender.

Come up with some workflow to ensure that you only read each email once. For example, in Gmail you can “Archive” mails to get them out of your inbox while still keeping them available and searchable.

The Hotshot

If people constantly hammer you with business propositions and speaker invitations, great! You’re doing good!

And you can afford a Virtual Assistant to take care of your email. If you prefer to reply personally, start each email with a sentence (automatically inserted of course) explaining that you’re very busy so you’ll have to keep your reply brief. People will understand and be grateful that you took the time to reply at all.

The Superstar

You get hundreds of emails per day asking you for all kinds of crap. Additionally, you get lots of relevant business email.

Autoreply or don’t reply at all to the irrelevant stuff. People won’t be surprised and it may even enhance your aura of arrogance :-)

Hire a Virtual Assistant to pick out the mails that are truly relevant to you.

The President

You get tons of email and much of it is important. Hire a staff to deal with it.

Objects on Rails on Kindle

I am really enjoying Avdi Grimm’s “Objects on Rails”, but I wanted to be able to read it on my Kindle. The book is an ongoing project, and Avdi sends out each new version as a Zip file containing HTML, images and a source folder.

This turned out to be really easy, and in this post I’m going to walk you through the steps necessary to get it on your Kindle. Note that it probably only looks good on the DX because of all the code samples and illustrations in the book.

The Kindle uses the (supposedly awful) MOBI format, so we need a way to convert from HTML to that. On OSX we can use Calibre for this. It’s open source and pretty useful, except for the UI which is unintuitive to the point of being downright bizarre.

Luckily it comes with a handful of comand line tools which are installed from the GUI. So

  • Fire up Calibre
  • Open the Preferences (Cmd-,)
  • Select Miscellaneous
  • Press “Install command line tools”

You can close the GUI again now.

To convert an HTML book with accompanying images and other assets, Calibre accepts a Zip file as input. Unfortunately, we can’t convert Avdi’s file directly because of the source folder it contains.

So we need to get rid of this first and then use the ebook-convert command line utility from Calibre.

Here’s a quick and dirty shell script to automate this process – be sure to run it from a tmp dir.

#!/bin/sh
mkdir oor
cd oor
unzip ~/Downloads/objects-on-rails.zip
rm -rf src
zip ../oor.zip *
cd ..
ebook-convert oor.zip oor.mobi
rm oor.zip
rm -rf oor

If this completes without errors, it will leave you with the MOBI book as oor.mobi, ready to be copied to your Kindle.

An improved Emacs window setup

One thing that has been annoying me for a while is the amount of time I spend managing my Emacs windows. Often I’ll split in the wrong direction, delete the wrong window or (especially) open too many windows and waste time navigating around them and getting confused.

So I was inspired by the recent Emacs screencast by the venerable Gary Bernhardt. Gary now uses Vim but was previously an Emacs user. Although he doesn’t really go into it, it looks from the screencast like he used a fixed window layout in those days.

Examining his dotfiles confirms this.

So I borrowed some of his setup and augmented it a bit to come up with this:

http://fullofsta.rs/wp-content/uploads/2012/01/wpid-emacs-windows2.png

Window roles

The top-right window is used for all temporary output, for example ack results, man pages, ido completions etc.

The bottom-right window is where I interact with magit.

The two leftmost panes are general purpose (I tend to load a spec in the middle and the associated production code to the left)

Although not enforced by Emacs, the idea is that this is a fixed window layout. And as it turned out, I soon got used to navigating between these windows very quickly. Also, nothing prevents me from splitting one of the tall windows vertically if I temporarily need an extra window.

Code

The elisp to divide the frame into these windows is perhaps not so interesting:

(defun jpt-setup-windows ()
  (interactive)
  (switch-to-buffer "*scratch*")
  (delete-other-windows)
  (split-window-horizontally)
  (split-window-horizontally)
  (balance-windows)
  (other-window 2)
  (split-window)
  (other-window -1)
  (setq grb-temporary-window (nth 1 (window-list)))
  (setq jpt-magit-window (nth 2 (window-list))))

However, here is the code to enforce that temporary and magit output goes into the correct buffers:

(setq special-display-regexps
 '("^\\*Completions\\*$latex "
   "^\\*Ido Completions\\*$"
   "^\\*magit"
   "^\\*ack\\*$latex "
   "^\\*Help\\*$"
   "^\\*grep\\*$latex "
   "^\\*Apropos\\*$"
   "^\\*elisp macroexpansion\\*$latex "
   "^\\*local variables\\*$"
   "^\\*Compile-Log\\*$latex "
   "^\\*Quail Completions\\*$"
   "^\\*Occur\\*$latex "
   "^\\*frequencies\\*$"
   "^\\*compilation\\*$latex "
   "^\\*Locate\\*$"
   "^\\*Colors\\*$latex "
   "^\\*tumme-display-image\\*$"
   "^\\*SLIME Description\\*$latex "
   "^\\*.* output\\*$"                  ; tex compilation buffer
   "^\\*TeX Help\\*$latex "
   "^\\*Shell Command Output\\*$"
   "^\\*Async Shell Command\\*$latex "
   "^\\*Backtrace\\*$"))

(defun jpt-try-window (window)
  (if (window-live-p window)
        window
      (first (window-list))))

(defun jpt-window-for-buffer (buffer)
  (if (string-match "\\*magit" (buffer-name buffer))
      (jpt-try-window jpt-magit-window)
    (jpt-try-window grb-temporary-window)))

(defun grb-special-display (buffer &optional data)
  (let ((window (jpt-window-for-buffer buffer)))
    (with-selected-window window
      (switch-to-buffer buffer)
      window)))

(setq special-display-function #'grb-special-display)

Compared to the code I lifted from Gary’s dotfile, here is what I added:

  • The special handling of the magit status buffer
  • Gracefully handles if the designated window is not available (falls back to first available window, see jpt-try-window)

Conclusion

I really like this setup. The first few days I kept splitting and (worse) merging windows as before, but I have now gotten used to the fixed layout. I generally don’t use the small windows on the right for anything besides their designated purposes.

In the rare case where I need more than two working buffers open, I just do a split like before – the main difference is that I now remember to close the new window because it feels a little wierd to deviate from the fixed layout.

Reading list

As we enter 2012, I am reading more books than ever. Since I got my beloved Kindle, I find it much easier to focus on whatever I’m reading because there are no distractions. Exactly like a physical book but without the inconvenience.

But that’s a topic for another post, today I want to present my current reading list. These are all books I own, and I have read a few chapters of many of them. So probably the closest I’ll get to a new year’s resolution this year (besides 1920×1200 and scheduling my running) is to actually finish these books, one at a time.

Programming books

Other books

  • God is not Great – always trying to get a better understanding of why I’m an atheist
  • Anomaly – I recently read and liked the Wool Kindle Single series. This is said to be similar.
  • The Old Man and the Wasteland – I have no idea what this is, but it’s in my Kindle Library
  • The Enemy – a Kindle Single by Christopher Hitchens about Osama bin Laden

The ‘backup’ gem needs a HOME

TL;DR

The backup gem contains a bug that causes it to crash when the HOME environment variable is not set. Notably, this is the case when jobs are started by ‘god’.

I have patched this and will soon send a pull request to the authors of the gem.

Read on to get a few hints on how to track down issues like this one.

The story of a painful bug hunt

0. Background

On several projects I’m using god to start and monitor daemon processes, especially resque workers. On one particular project, this just stopped working recently and as a workaround I had to start the resque workers manually under tmux.

Today I finally took the time to figure out what was going on.

1. Symptoms

In my god.conf I have a ‘resque’ watch configured which starts five workers. Here is the relevant snippet from god.conf:

num_workers = rails_env == 'production' ? 5 : 2
num_workers.times do |num|
  God.watch do |w|
    w.name     = "resque-#{num}"
    w.group    = 'resque'
    w.interval = 60.seconds
    w.env      = {"QUEUE"=>"*", "RAILS_ENV"=>rails_env}
    w.start    = "bundle exec bootup_rake -f #{rails_root}/Rakefile environment resque:work"
    w.dir      = "#{rails_root}"

When doing a “god start resque”, god tries to start up the workers but they crash almost immediately. If I do “ps aux | grep resque” within a few seconds I see the processes but then they just disappear.

2. What is god saying?

Finding the scroll that collects the utterings of god is easy:

/var/log/god.log

However, it quicly becomes apparent that god has nothing interesting to say:

I [2011-11-15 16:24:57]  INFO: Using pid file directory: /var/run/god
I [2011-11-15 16:30:55]  INFO: Syslog enabled.
I [2011-11-15 16:30:55]  INFO: Using pid file directory: /var/run/god
I [2011-11-15 16:39:30]  INFO: Syslog enabled.
I [2011-11-15 16:39:30]  INFO: Using pid file directory: /var/run/god
...

A bit of googling suggests that I try to run god in the foreground to see what is going on.

3. Non-daemonized god

So let’s see how god is being invoked:

root@foo:~# ps aux | grep god
root     27956  4.5  0.2 179304 19132 pts/1    Sl   10:12
   0:00 /usr/local/rvm/rubies/ruby-1.9.2-p290/bin/ruby
   /usr/local/rvm/gems/ruby-1.9.2-p290/bin/god -P /var/run/god.pid -l /var/log/god.log

We need to make a few tweaks:

  • don’t send output to a log file (remove -l)
  • load the config file with -c <config>
  • keep it in the foreground with -D

So our command becomes:

root@foo:~# /usr/local/rvm/rubies/ruby-1.9.2-p290/bin/ruby
   /usr/local/rvm/gems/ruby-1.9.2-p290/bin/god
   -P /var/run/god.pid -c /etc/god.conf -D

This yields slightly more interesting output. Here are the lines for the worker ‘resque-1′:

I [2011-11-16 10:17:13]  INFO: resque-1 move 'unmonitored' to 'init'
I [2011-11-16 10:17:13]  INFO: resque-1 moved 'unmonitored' to 'init'
I [2011-11-16 10:17:13]  INFO: resque-1 [trigger] process is not running (ProcessRunning)
I [2011-11-16 10:17:13]  INFO: resque-1 move 'init' to 'start'
I [2011-11-16 10:17:13]  INFO: resque-1 start: bundle exec bootup_rake
   -f /var/www/foo/current/Rakefile environment resque:work
I [2011-11-16 10:17:13]  INFO: resque-1 moved 'init' to 'start'
I [2011-11-16 10:17:13]  INFO: resque-1 [trigger] process is running (ProcessRunning)
I [2011-11-16 10:17:13]  INFO: resque-1 move 'start' to 'up'
I [2011-11-16 10:17:13]  INFO: resque-1 moved 'start' to 'up'
I [2011-11-16 10:17:13]  INFO: resque-1 [ok] memory within bounds [8952kb] (MemoryUsage)
I [2011-11-16 10:17:14]  INFO: resque-1 [ok] process is running (ProcessRunning)

OK, maybe that’s not so interesting after all. God starts the process and is satisfied with that.

What IS interesting is the fact that this actually works! The resque workers keep running and start processing jobs.

4 WTF?

So when I start god non-daemonized, it works. When god is started with ‘service god start’, it does not.

I need a way to see the log output from god when it is running in daemonized mode. This turns out to be as simple as:

god log resque

By default this gives me the output from the first worker in the group. I won’t repeat it here, but it is exactly the same as when running god in the foreground.

It seems to be safe to assume that god is behaving the same way whether started by me or via /etc/init.d/god. To be sure I also tried starting it by executing the exact two lines from this file. Same result.

5. Inspecting the environment

I have suspected for a while that this issue has to do with the environment in which the rake command is being run. This seems to be the only major source of difference between the working and the non-working experiments.

So I need to know the environment in which the failing rake invocations are being run. I know that for all running processes, the environment is available in the file /proc/<pid>/environ. But how can I get to this file when the process dies within seconds?

My solution is to run the following command immediately after god tries to start the workers:

less -f /proc/`ps aux | grep resque | head -1 | cut -d ' ' -f 4`/environ

What this does is

  • ps aux | grep resque finds the process info for the about-to-die resque workers
  • head -1 grabs the first one
  • cut -d ‘ ‘ -f 4 cuts out the PID (fourth field when using a space as the delimiter)
  • This PID is then used as part of the path passed to the outer less command

The environ file is in some kind of binary format (which is why I need -f on the less command) the details of which I haven’t bothered to look up.

I don’t really need to, because after running the above command I see this in my iTerm:

GEM_HOME=/usr/local/rvm/gems/ruby-1.9.2-p290^@TERM=xterm^@IRBRC=/usr/local/rvm/r
ubies/ruby-1.9.2-p290/.irbrc^@MY_RUBY_HOME=/usr/local/rvm/rubies/ruby-1.9.2-p290
^@QUEUE=*^@rvm_path=/usr/local/rvm^@PATH=/usr/local/rvm/gems/ruby-1.9.2-p290/bin
:/usr/local/rvm/gems/ruby-1.9.2-p290@global/bin:/usr/local/rvm/rubies/ruby-1.9.2
-p290/bin:/usr/local/rvm/bin:/var/www/foo/shared/bundle/ruby/1.9.1/bin:/usr/loca
l/rvm/gems/ruby-1.9.2-p290/bin:/usr/local/rvm/gems/ruby-1.9.2-p290@global/bin:/u
sr/local/rvm/rubies/ruby-1.9.2-p290/bin:/usr/local/rvm/bin:/usr/local/sbin:/usr/
local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games^@PWD=/var/www/foo/releases/20
111115153743^@LANG=en_US.UTF-8^@SHLVL=1^@BUNDLE_GEMFILE=/var/www/foo/releases/20
111115153743/Gemfile^@RAILS_ENV=production^@rvm_ruby_string=ruby-1.9.2-p290^@GEM
_PATH=/usr/local/rvm/gems/ruby-1.9.2-p290:/usr/local/rvm/gems/ruby-1.9.2-p290@gl
obal^@RUBYOPT=-I/usr/local/rvm/gems/ruby-1.9.2-p290/gems/bundler-1.0.18/lib -rbu
ndler/setup^@BUNDLE_BIN_PATH=/usr/local/rvm/gems/ruby-1.9.2-p290/gems/bundler-1.
0.18/bin/bundle^@RUBY_VERSION=ruby-1.9.2-p290^@

Notice that the variables are separated by some byte that less renders as “^@”. Good!

6. Reproducing the bug

Off to Emacs to transform this into something useful. I won’t bore you with the details, but a quick keyboard macro transforms the above into:

export GEM_HOME=/usr/local/rvm/gems/ruby-1.9.2-p290
export TERM=xterm
export IRBRC=/usr/local/rvm/rubies/ruby-1.9.2-p290/.irbrc
export MY_RUBY_HOME=/usr/local/rvm/rubies/ruby-1.9.2-p290
export QUEUE=*
export rvm_path=/usr/local/rvm
export PATH=/usr/local/rvm/gems/ruby-1.9.2-p290/bin:/usr/local/rvm/gems/ruby-1.9.2-p290@global/bin:
   /usr/local/rvm/rubies/ruby-1.9.2-p290/bin:/usr/local/rvm/bin:/var/www/foo/shared
export PWD=/var/www/foo/releases/20111115153743
export LANG=en_US.UTF-8
export SHLVL=1
export BUNDLE_GEMFILE=/var/www/foo/releases/20111115153743/Gemfile
export RAILS_ENV=production
export rvm_ruby_string=ruby-1.9.2-p290
export GEM_PATH=/usr/local/rvm/gems/ruby-1.9.2-p290:/usr/local/rvm/gems/ruby-1.9.2-p290@global
export RUBYOPT=-I/usr/local/rvm/gems/ruby-1.9.2-p290/gems/bundler-1.0.18/lib -rbundler/setup
export BUNDLE_BIN_PATH=/usr/local/rvm/gems/ruby-1.9.2-p290/gems/bundler-1.0.18/bin/bundle
export RUBY_VERSION=ruby-1.9.2-p2

Back on the server, I clear all existing environment vars with a snippet I found here:

unset  $ ( /usr/bin/env | egrep '^(w+)=(.*)$' |
  egrep -vw 'PWD|USER|LANG' | /usr/bin/cut -d= -f1);

Next, I paste all the exports above into iTerm and finally I am ready to try starting the worker again.

Voila!! It crashes:

rake aborted!
can't convert nil into String
/var/www/foo/shared/bundle/ruby/1.9.1/gems/backup-3.0.16/lib/backup.rb:40:in `join'
/var/www/foo/shared/bundle/ruby/1.9.1/gems/backup-3.0.16/lib/backup.rb:40:in `<module:Backup>'
/var/www/foo/shared/bundle/ruby/1.9.1/gems/backup-3.0.16/lib/backup.rb:8:in `<top (required)>'
...

Here are the lines around line 40 of backup.rb:

##
# Backup's Environment paths
PATH               = File.join(ENV['HOME'], 'Backup')
DATA_PATH          = File.join(ENV['HOME'], 'Backup', 'data')

Ah, so the backup gem assumes that the HOME environment variable is set. If not, it crashes.

7. Done

Fortunately the source for “backup” is on Github, so I could easily fork the project and apply the very simple patch that was needed.

Scheduled jobs for your Rails app

If you need to run scheduled jobs in your Rails app (!= background jobs as in Resque), you should use the excellent whenever gem.

However, one detail that is really easy to miss is which shell environment the cron job actually runs under.

bundle: command not found

And an easy way to confuse yourself even further is by trying to debug issues in this area by:

  1. Logging into your  server as the deployment user
  2. Doing crontab -l to see which entries whenever installed
  3. Testing an entry by cut’n'pasting it to the shell prompt
The outcome will not be reliable, because you’re running it from a login shell and cron is not. So for example, your rbenv shims may be in the path while testing, but cron does not load your .bashrc or .bash_profile files.

On a project I work on, the staging and production machines are currently quite different. In production we use RVM (globally installed) while staging has rbenv locally installed for the deployment user.

So in staging I need to assume that no binaries are available and use the full path to bundler, in my case /home/passenger/.rbenv/versions/1.9.2-p290/bin/bundle.

(I know, symlinking this to eg. /usr/local/bin/bundle would probably be more elegant, but I actually like the reminder of where my ruby is actually installed. I don’t spend much time on the servers, so I tend to forget.)

Running stuff only on certain stages

Another issue I struggled with for a while is how to schedule jobs only in production. For example, I don’t want to run S3 backups from staging.

I use capistrano/multistage, so I needed to inspect the value of the current “stage” from schedule.rb. It turns out that by including this:

require 'capistrano/ext/multistage'

set :whenever_environment, defer { stage }
require &quot;whenever/capistrano&quot;

…the stage is available in schedule.rb as @environment. Very simple, but it took some googling to find.

Example schedule.rb

Below is my anonymized schedule.rb. It should be pretty self explanatory, post questions in the comments if not.

def production?
 @environment == 'production'
end

set :bundler, production? ? &quot;gp_bundle&quot; : &quot;/home/passenger/.rbenv/versions/1.9.2-p290/bin/bundle&quot;

job_type :gp_rake, &quot;cd :path &amp;&amp; RAILS_ENV=:environment :bundler exec rake :task --silent :o utput&quot;
job_type :gp_runner, &quot;cd :path &amp;&amp; :bundler exec rails runner -e :environment ':task' :o utput&quot;
job_type :gp_bundle_exec, &quot;cd :path &amp;&amp; RAILS_ENV=:environment :bundler exec :task&quot;

every 5.minutes do
 gp_rake &quot;thinking_sphinx:index&quot;
end

every 30.minutes do
 gp_runner &quot;FooBar.update&quot;
end

every 1.day, :at =&gt; &quot;4:00am&quot; do
 gp_runner &quot;Baz.purge_inactive&quot;
end

if production?
 every 1.day, :at =&gt; &quot;4:00am&quot; do
   gp_bundle_exec 'backup perform --trigger my_backup --config-file config/backup.rb --log-path log'
 end
end

EDIT: Jakob Skjerning posted this snippet in the comments:

job_type :rake, “cd /var/www/appname/#{environment}/current &amp;&amp; /home/appname/.rvm/gems/ree-1.8.7-2010.02@appname-#{environment}/bin/bundle exec /home/appname/.rvm/wrappers/ree-1.8.7-2010.02@appname-#{environment}/rake –silent RAILS_ENV=#{environment} :task :o utput”

Making the active tab in Chrome stand out

I’ve long found it annoying that the active tab in Google Chrome has almost the same color as the inactive ones.

This can be easily fixed by installing a custom theme such as this one. The active tab in this theme is dark and the inactive ones light. The contrast makes it possible for me to see in my peripheral vision where I am when switching between tabs.

Don’t know why I didn’t think of this before…

Firing up a new project in 5 secs

I’m running Gitosis on my home Linux server. I like to keep everything under source control, and this setup allows me to have dozens of small projects with very little overhead.

This is really convenient, except every time I start a new project I have to

  • Add it to gitosis-admin, commit and push
  • Set up the initial Git repo for the project
  • Touch, add and commit a README
  • Setup the remote
  • Do the inital push with “-u”

Obviously, this gets pretty tedious, so I automated it with the following script.

#!/bin/bash

PROJECT=$1
THIS_SCRIPT=`readlink $0`
GITOSIS_ADMIN_DIR=${THIS_SCRIPT%/*/*}
GIT_SERVER=chopmo.dk

echo "Project:" $PROJECT
echo "Gitosis admin is at $GITOSIS_ADMIN_DIR"
echo "Adding project to gitosis-admin"
cd $GITOSIS_ADMIN_DIR
sed -i "" "/just-me/,$s/writable.*/& $PROJECT/" gitosis.conf
git add gitosis.conf
git ci -m "Adding new project $PROJECT"
git push

echo "Setting up local repo"
mkdir $PROJECT
cd $PROJECT
git init
touch README
git add README
git ci -m "initial commit"
git remote add origin git@$GIT_SERVER:$PROJECT.git
git push -u origin master

echo "Done."

It is stored under “bin” in my gitosis-admin repo and symlinked to ~/bin/new_project (which is why the readlink at the top is important).

The “sed” call appends the new project name to the list of projects which are writable by only me. The just-me,$ addresses are necessary because the structure of my gitosis.conf is:

[gitosis]

[group gitosis-admin]
writable = gitosis-admin
members = ...

[group just-me]
writable = foo bar baz ...

members = ...

So without the limiting addresses, I would append the new project to the writable projects in the gitosis-admin group as well.

My bash skills are pretty weak, so feedback on the script would be very welcome. But this works, and it means that the amount of time from I get an idea for a new project until I can start hacking on it is brought down to just a few seconds.

 

Xoom review

For some reason, I have completely neglected to write about my favourite gadget of the year: The Motorola Xoom.

I just love this device. It is perfect for casual browsing, checking email, checking Twitter, Facebook, newssites and all that waste of time. And Amazon has created an excellent tablet-optimized Kindle app, which is probably what I’ve spent the most hours using (until now that I’ve bought a real Kindle – more about that in an upcoming post).

It has more storage space that I’m going to need anytime soon (32GB I think), it has Wifi of course, excellent battery life, nice and responsive screen. In short, the hardware is great and the build quality feels good.

This is the device that Google uses for developing Honeycomb, meaning that it is always up to date and contains no wierd-ware. This is just stock Android 3.x, and everything works together as it should.

App highlights: Kindle, Google Maps, the builtin browser, BeyondPod HD, Gmail, IMDB, the list goes on. We’re really starting to see some great tablet-optimized apps, even if most of them are from Google so far.

BeyondPod really stands out, they have done an excellent job of adapting their phone version (which I like a lot) to the larger screen. It offers a much better blog reading experience than eg. Google Reader in the browser.

Well, it can’t be all good, can it? On to the bad stuff.

Most obvious flaw: The reflective screen. This sucks exactly as much as it does on a laptop and makes it impossible to use the device outside except at night. I ended up buying a screen protector which has solved this problem at the cost of a little screen clarity. But you get used to that, and the screen protector also eliminates ugly fingerprints on the screen. It even makes it possible to use the tablet even if your fingers aren’t completely dry which was a welcome surprise. It is really a bitch to apply it, but well worth the effort.

Second slight drawback: It’s a bit heavy for extended reading sessions. However, you quickly get used to resting it on something.

Conclusion: If you like Android and want a tablet, I highly recommend this device. It’s more geeky than the iPad, but if you’re a geek like me, you’ll appreciate this.

~/bin/remind

Here is a very simple helper script I came up with today:

#!/bin/sh
grep $1 ~/.bash_history | uniq

I’m currently using Maven on a client project and I keep forgetting the various invocations of mvn. So this helps remind me.

Example:

[ ~/wb/foo ] $ remind mvn
mvn
which mvn
mvn -v
mvn -cpu hpi:create
mvn package
mvn --help
mvn -o package
mvn install
mvn -DdownloadSources=true -DdownloadJavadocs=true -DoutputDirectory=target/eclipse-classes eclipse:eclipse
mvn hpi:run

This is handy for those cases where I want an overview of all ways in which I’ve recently used a command.

If I just want to recall a specific command, I use bash’s Ctrl-R and whichever few letters of the command I remember:

ctrl-R ec

This will recall the long second-to-last command in the example above.