Byte Friendly

Thoughts on programming, scalability and other stuff.

Move Capistrano’s Log Directory

| Comments

So you want to move your log directory? To a separate volume, maybe? But you are afraid that it will break too many things, like all those libraries with #{rails_root}/log paths hardcoded in them? Fear not, there’s a simple workaround.

As you probably know, capistrano aliases your rails log dir to a shared dir outside of app folder.

1
ln -s /deploy_path/shared/log /deploy_path/current/log

This means that, in fact, all your logs turn up at /deploy_path/shared/log/. Now all we need is to symlink that dir as well!

1
2
3
4
5
6
7
8
9
sudo mkdir /var/log/yourapp

# make it writable to the app
sudo chown -R youruser:yourgroup /var/log/yourapp/

# deal with existing log dir (I back it up, but you can remove it, or copy the files to 
#   the new location)
mv /deploy_path/shared/log /deploy_path/shared/log_
ln -s /var/log/yourapp /deploy_path/shared/log

Hope this helps :)

Cap Deploy From an Arbitrary Branch

| Comments

As you know, capistrano can deploy your app from a specific branch in the repo. You just have to specify it

1
set :branch, 'develop'

If you develop in a single branch, that should do it for you. All commits go to one branch and all deploys are likely served by the same branch. This is a simple model, good enough for small projects.

However, if you adopt git flow, it encourages you (not to say “forces”) to have separate branch for every feature, release or hotfix that you’re working on. And, naturally, you want to deploy to dev/staging server right from a feature branch, not having to merge it first to develop/master. This is trivial to accomplish, just change the branch name:

1
set :branch, 'feature/my-test-feature'

Now you have a pending file change. What do you do with it? Commit to source control? That increases file churn rate for absolutely no reason. git checkout that file? This is an extra action, of which you will be tired quite soon (not to mention chance to accidentally commit the file).

Solution? Externalize the setting. When deploying, look in the environment for a branch to use and fallback to default name.

1
set :branch, ENV['BRANCH'] || "develop"

Then you do this

1
BRANCH=feature/my-test-feature cap deploy

Or, if you have multistage enabled

1
BRANCH=feature/my-test-feature cap staging deploy

Of course, this requires a bit more typing on each deploy, but at the same time it decreases chances of deploying from a wrong branch.

Happy deploying!

Sorting Humanized Numbers in tablesorter.js

| Comments

Tablesorter is a jQuery plugin for sorting tables. It appeared first in a couple of “X jquery table sorting plugins” blog posts, so I went with it when I needed to sort a table a couple of days ago.

It works pretty well out of the box and it even lets you customize some bits. One of those bits is column parser: if you have special data in a column, you need a special way to sort it.

Here’s an official example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// add parser through the tablesorter addParser method 
$.tablesorter.addParser({
    // set a unique id 
    id: 'grades',
    is: function(s) {
        // return false so this parser is not auto detected 
        return false;
    },
    format: function(s) {
        // format your data for normalization 
        return s.toLowerCase().replace(/good/,2).replace(/medium/,1).replace(/bad/,0);
    },
    // set type, either numeric or text 
    type: 'numeric'
});

$(function() {
    $("table").tablesorter({
        headers: {
            6: {
                sorter:'grades'
            }
        }
    });
});

However, it leaves much to be desired. Like what to do when I want my parser to be auto-detected. Turns out that this is function is a predicate. It accepts a string and decides whether it can be handled by this parser. Then the format function will make conversion to a normalized textual/numeric form.

In my case I have a table with a lot of numbers. And those numbers are in humanized form. That is, instead of 121738 I output 121.74k. This format is not properly sortable as either number or text, so I wrote a parser.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$.tablesorter.addParser({
  id: 'humannum',
  is: function(s) {
    return /^[\d\.]+[Mk]?$/.test(s);
  },
  format: function(s) {
    var is_m = s.indexOf('M') !== -1;
    var is_k = s.indexOf('k') !== -1;
    var fl = parseFloat(s);
    var res = fl;
    if (is_m) {
      res = fl * 1000000;
    } else if (is_k) {
      res = fl * 1000;
    }
    return res.toString();
  },
  type: 'numeric'
});

It basically checks if a string consists of a number followed by an optional “M” or “k” and, if that’s the case, converts it to a “full” number.

I wish this was in the official doc.

Queue Prioritization in Sidekiq

| Comments

Unlike Resque, Sidekiq allows you to process several queues at the same time. You can even assign priorities. Here’s an example from default config:

1
2
3
4
:queues:
  - [often, 7]
  - [default, 5]
  - [seldom, 3]

If you want two queues have equal rights, just set the same priorities. However, if you omit priority parameter and define your queues like this:

1
2
3
4
:queues:
  - email
  - coffee
  - billing

Then queues will be checked in a serial manner. That is, Sidekiq will start making coffee only when all emails are sent. If a new email shows up, it will be processed before any other job. In other words, this is how Resque would handle the queues.

Choose whatever fits your task better. It’s nice to have some flexibility.

Using Sidekiq With Redis_failover Gem

| Comments

Sidekiq works pretty well with standard redis client. This is an example of configuring it (from official wiki):

1
2
3
4
5
6
7
8
Sidekiq.configure_server do |config|
  config.redis = { :url => 'redis://redis.example.com:7372/12', :namespace => 'mynamespace' }
end

# When in Unicorn, this block needs to go in unicorn's `after_fork` callback:
Sidekiq.configure_client do |config|
  config.redis = { :url => 'redis://redis.example.com:7372/12', :namespace => 'mynamespace' }
end

But, what if you need a special redis client that can’t be initialized with redis URL? No worries. Setter for Sidekiq#redis also accepts a ConnectionPool. Here we have full control over how we initialize our redis client. Here’s how you may initialize Sidekiq with a bunch of RedisFailover clients:

1
2
3
4
5
6
7
8
9
10
Sidekiq.configure_server do |config|
  pool = ConnectionPool.new(:size => 10, :timeout => 3) do
    zkservers = 'localhost:2181,localhost:2182,localhost:2183'
    RedisFailover::Client.new namespace: 'my_service', zkservers: zkservers
  end

  config.redis = pool
end

# do the same in Sidekiq.configure_client

You can even read Sidekiq’s concurrency setting and pass it to the connection pool initializer. Now your sidekiq farm doesn’t care about redis crashes :)

Happy sidekiqing :)

My (Un)success Story of Colemak

| Comments

I’ve been hearing a lot about Colemak recently. It’s a (relatively) new keyboard layout, specially designed for efficient touch typing in English. Any description of Colemak (including this very post) is accompanied by a story, how QWERTY layout was specifically designed to hinder fast typing, so that typewriter bars don’t get stuck. Ok, cool story, let’s try this silver bullet layout.

One of the advertised advantages of this layout is that “most” common QWERTY hotkeys are the same. Indeed, such hotkeys as select-all/cut/copy/paste didn’t move. Yes, this is an advantage for people who copy/paste text all day. But for us, coders, this is really a disadvantage, because some hotkeys are on the same keys, and others have moved. And worst of all, most hotkeys have changed their meaning.

Let me illustrate. Most (all?) browsers in Mac OSX understands the same hotkeys:

  • Cmd-T - open new tab
  • Cmd-F - find
  • Cmd-G - find next
  • Cmd-S - save

We’ve been using these hotkeys for years and now they are burned in our muscle memory. So, with Colemak, when you try to perform an action and you reach for the old key, you get completely different action instead.

  • You tried Cmd-T (qwerty), you hit Cmd-G (colemak), action - “find next”
  • Cmd-F, Cmd-E, “use selection for find” (chrome)
  • Cmd-G, Cmd-D, “add bookmark”
  • Cmd-S, Cmd-R, “reload”

This is very, VERY frustrating to try search page content and keep adding bookmarks instead. Combine this with a fact that when you switch to a Russian layout, the hotkeys are the same as in QWERTY. So, essentially, you have to learn a whole new set of hotkeys for every program you use and always keep track of the current layout, or otherwise you’ll get unexpected results.

Now, is it really worth it? Given the fact that programmer’s productivity is not based on how fast and for how long he can type, I’d say “Not really”. I average 370-450 CPM on QWERTY. This is far more than sufficient for everyday coding needs.

Goodbye, Colemak. Hello again, QWERTY.

Talking Capistrano Is Your Friend

| Comments

I want to share with you a small tweak I came up with a couple of days ago. As some of you know, Mac OS X includes a ton of very handy utilities. One of them is say. The name is pretty descriptive, you pass it a string and it voices it, using text-to-speech engine, that is built in Mac OS. Try it now:

1
$ say Hello world!

You can use it anywhere where you can invoke shell commands. Here’s my capistrano after-hook, for example:

1
2
3
4
5
after 'deploy' do
  `say Sire, our unicorns have been deployed`
  # note the backticks. This string is roughly equivalent to
  # system "say Sire, our unicorns have been deployed"
end

Now I can launch my cap deploy (which can take minute or two) and go read Hacker News or whatever. I will be told when deploy is finished. I’m using this for a few days already and it still make me smile everytime. I should probably come up with more creative messages.

Happy tweaking!

Pretty Log in Git

| Comments

Here’s what default git log output looks like

While it is sure contains the information and is easily parsable by a machine, it’s not the best way to present it to a human. Luckily, the git log commands is quite customizable. Try this, for example:

git log --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset' --abbrev-commit --date=relative

You can even set make an alias for it:

git config --global --add alias.plog "log --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset' --abbrev-commit --date=relative"

And then you just call

git plog

Happy gitting!

Is TextMate 2 Ready for Everyday Coding? Let’s Find Out.

| Comments

Oh, the TextMate 2 we all waited for so long. Okay, I wasn’t really waiting, but I kept hearing about it. Never got to install the app.

So, tonight I wasn’t able to fall asleep. I opened my macbook and started reading news. One thing led to another and the next thing I know, I’m installing alpha version of TextMate and a bundle for Go. Really, Go? I hope I won’t be too surprised tomorrow :)

Okay, what’s new?

  • If you open a file with unknown extension, TextMate will offer to install a bundle for it, if there’s one.
  • “Right margin” is now called “View -> Show Wrap Column”
  • You can specify a default language for new files. 90% of my new files are Ruby scripts, so it is a timesaver for me.
  • New file browser. I’m not if I like it better or not.
  • When you click on items in the file browser, they don’t open automatically. You have to double-click.
  • Awesome new app icon! :)

I will update this post when I find something new.

Deferred Posting With Octopress

| Comments

One of the powerful things that Wordpress can do and Octopress can’t, is deferred posting (or scheduled blogging, whatever). I never really missed it when I was using Wordpress, so I switched to Octopress without hesitation. But now I could use that feature. In case you don’t know, deferred posting allows you to write a post beforehand and schedule its release to some moment in future.

This functionality is kinda trivial if you use dynamic request processing and a database (like wordpress does). But with static sites it’s not at all that easy. So before implementing something myself, I decided to google for other works on this topic. Here’s a good article that I’ve found: Synced and scheduled blogging with Octopress. It also contains some links to other articles.

In short, people write a daemon (or scheduled script) that checks your drafts from time to time. If a draft is due, it is copied to _posts directory and generate/deploy sequence is initiated.

This, obviously, requires a computer that is always on and has actual content for the site. I don’t really like the idea of a daemon, but, I guess, this is a price you pay for using “blog engine for hackers”.

Could this be implemented as an external web service? Is there an opportunity for a profitable startup? :)