object.io

All code has costs. Only some code has value.

Upgrading ruby hash syntax

Consistency makes code easier to read. Long-lived projects can be a wildly inconsistent in their ruby hash syntax, so here is a quick way of upgrading all symbol-based hash-keys to the new leaner format. Simply put: perform the below operation to convert :thing => 'value' to thing: 'value' in one easy step.

Please remember to commit everything first so you have a clean slate to start out from, and undo is easy.

In Sublime Text, open Find | Find in Files... and enter:

1
2
Search:  "([^:]):([^:\s=]+)\b\s*=>\s*"
Replace: "\1\2: "

Note: do not include the " in the search/replace values, it is included only for readability. Ensure the Regular Expression mode is active (the button looks like: .*). Now is a good time to use the File | Save All action.

Remember to double-check everything looks just right. I recommend using git add --patch . for this, because that way you can easily skip any changes you might not agree with.

This is just a minor detail in the overall readability and consistency of a project, but even minor details matter when it comes to long-term ease of maintainance.

Flash of Convenience

Flash messages in Rails applications need a bit of love. A lot of them tend to be identical within a given app, and it’s a bit annoying to manage defaults and fallback for all these calls to I18n.t(). The following is a small helper-method and a simple structure to keep it all neatly organized.

For this to work, add the text you want shown to your config/locale/en.yml (by default):

1
2
3
4
5
6
7
8
9
en:
  posts:
    create:
      flash:
        created: "Your post will now be reviewed before publishing"

  flash:
    created: "Successfully created"
    updated: "Successfully updated"

Here the posts#create action has a custom flash for the :created flash-message. All other controllers and actions in the application where a flash-message of :created is used the default message of "Successfully created" will be shown.

Next, add this to your ApplicationController (this is all of the magic):

1
2
3
4
5
6
  protected
  def flash_message cause, args = {}
    primary_key = "#{controller_name}.#{action_name}.flash.#{cause}"
    default_key = "flash.#{cause}".to_sym
    I18n.t primary_key, args.merge(:default => default_key)
  end

Finally, this is how you use it within an action:

1
2
3
  flash[:notice] = flash_message(:created)
  # and
  redirect_to :root, :notice => flash_message(:created)

Try to keep the actions somewhat generic like :created, :deleted, :updated, etc. This makes the default values easier to manage.

What just happened?

The I18n system has some neat convenience shortcuts we’re using here. First of all we build the primary flash lookup-key based on the current controller and action.

Second bit of functionality used here is the fallback message. Fallback messages can be given as an explicit string, or a symbol. When a symbol is used it will be used to lookup a new message in the I18n backend. We use that for the :default argument where the fallback is set to be a global flash message like flash.created.

Now you can customize all the flashes in your app without touching the code, you have all these small bits of user interface in a single file for easy overview. You can also easily vary the messages between languages, customize text where it is necessary, and have a convenient global fallback message.

Getting to know Ruby debugger

This is a guest-post by Cameron Dykes, about getting started with the ruby debugger.

A key step to debugging any program is replicating the environment to ensure you can consistently produce the bug. In my early Ruby days, to inspect the environment, I used a primitive method: placing puts lines in my code to print values to the console (let’s call them “inspection puts”).

It may have looked something like this:

1
2
3
4
5
6
7
8
9
class Buggy
  # assuming perform_operation and special_options exist...
  def buggy_method(param=nil, options={})
    puts "\n\n\nDEBUG"
    puts "param: #{param}"
    puts "options: #{options}"
    @instance_var = perform_operation(special_options(param, options))
  end
end

The case for ruby-debug

This method very easily gives me the information I need, but it has some downsides:

  1. To inspect the return value of special_options I have to add another inspection puts.
  2. Every addition of a new puts requires that I restart the application to inspect the results.
  3. To inspect how special_options and perform_operation are handling the data, I have to add inspection puts inside of them.
  4. I must remember to remove all of the inspection puts before I push the code.

If only there was a better way to do this.

ruby-debug to the rescue! By putting a breakpoint in our code, we have the ability to inspect environment state, check the return value of any methods, and step through our code one line at a time. This is much more versatile than inspection puts because the full environment is available to us. The “I wonder what the value of this is” problem is gone since we can inspect whatever we want. The step functionality the debugger gives us is useful as well, allowing us to step inside of a called method while maintaining the interactive environment.

Setting up ruby-debug

To get set up with the debugger, we’ll need to install the gem:

1
2
3
4
5
# Using MRI-1.9.2
gem install ruby-debug19

# Using MRI-1.8.7 or Ruby Enterprise Edition
gem install ruby-debug

ruby-debug in action

Let’s update the example from above using a debugger breakpoint instead of inspection puts:

1
2
3
4
5
6
7
class Buggy
  # assuming perform_operation and special_options exist...
  def buggy_method(param=nil, options={})
    require 'ruby-debug'; debugger
    @instance_var = perform_operation(special_options(param, options))
  end
end

The next time the method is called, the debugger will stop and give us an interactive shell. We can inspect the values of each variable with the eval command:

1
2
eval param
eval options

We can also see what the return value is of the invoked methods:

1
2
eval special_options(param, options)
eval perform_operation(special_options(param, options))

We can even use the step command to enter inside of special_options and perform_operation to see what they do.

Here are the various debugger commands that I most commonly use:

  • list, l - show the code for the current breakpoint
  • eval, e - evaluate expression and print the value
  • step, s - next line of code, moving within methods
  • continue, c - continue in the program until the program ends or reaches another breakpoint
  • quit, q - abort the program

Many more commands are available, which can be seen by entering help in the debugger.

Better debugging ftw!

With the ruby-debug gem, we have a better tool for diving in to our code than inspection puts. Using a debugger breakpoint, we can interactively step through our code with less cleanup.

Happy debugging!

Debugging references

Who am I?

Deploy a private Github repository with whiskey_disk

whiskey_disk makes it very easy to quickly deploy a new version of your site to one or more servers. It’s very efficient, as files are copied directly from your git repository to the server. This means you can very quickly make large deployments even when you are on a slow internet connection. You do need a secure way for your server to access the git repository while the deploy is going on, read on to see how this is easily done.

For this example, I’m going to deploy a small static site from a private Github repository to a server via ssh using whiskey_disk. There are a few steps in getting this all setup, but once you have the ssh infrastructure playing nicely, a complete deployment of a small static site takes on the order of 2-5 seconds. Stay with me, and I’ll show how all the pieces fit together.

To begin with, I’ll assume you have a site or application already pushed to a private Github reposity that you now want to do a deploy of. You also have ssh access to the server you are deploying to for this to work. For this example, I’m deploying the master branch of the following private git repository:

1
git@github.com:rud/whiskey_disk-demo.git

Adding the configuration file

Make sure you have your appropriate code pushed to Github, then we’ll add the whiskey_disk config file config/deploy.yml to the project:

1
2
3
4
demo:
  domain:     "deploy_user@example.com"
  deploy_to:  "/home/deploy_user/domains/useful_site.com"
  repository: "git@github.com:rud/whiskey_disk-demo.git"

This is a simple YAML file (indentation matters), you will need to adjust the domain and paths as appropriate. I use the name demo for the environment, other common examples would be staging, production, etc. A lot more options are available, see the official whiskey_disk readme, but this all we need to get started.

Running whiskey_disk setup

whiskey_disk has two commands: a setup command, for doing the initial repository clone, and the deploy command used for all subsequent deploys.

With the config/deploy.yml file in place inside the project, we’re going to just try and run the setup command from the local computer, seeing what happens. For now, this errors out:

1
2
3
4
5
6
7
$ wd setup -t demo
Initialized empty Git repository in /home/deploy_user/domains/useful_site.com/.git/
Host key verification failed.
fatal: The remote end hung up unexpectedly
fetch-pack from 'git@github.com:rud/whiskey_disk-demo.git' failed.
error: pathspec 'origin/master' did not match any file(s) known to git.
Did you forget to 'git add'?

Okay, we’re not quite ready to do deployments just yet.

Resolving all ssh issues

Github hangs up on us - what’s happening? Testing from my local machine as described in the github ssh guide:

1
2
$ ssh -T git@github.com
Hi rud! You've successfully authenticated, but GitHub does not provide shell access.

That’s the kind of success we’re looking for on the server as well. Ssh to the server as the deploy user, and try the same command there:

1
2
3
4
5
6
$ ssh -T git@github.com
The authenticity of host 'github.com (207.97.227.239)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,207.97.227.239' (RSA) to the list of known hosts.
Permission denied (publickey).

Try the command again, now that the host key is trusted:

1
2
$ ssh -T git@github.com
Permission denied (publickey).

Notice how we get a new error later in the connection process, a Permission denied (publickey) instead of Host key verification failed.

For the sake of completeness, this is what this situation looks like from whiskey_disk:

1
2
3
4
5
6
$ wd setup -t demo
Initialized empty Git repository in /home/deploy_user/domains/useful_site.com/.git/
Permission denied (publickey).
fatal: The remote end hung up unexpectedly
fetch-pack from 'git@github.com:rud/whiskey_disk-demo.git' failed.
error: pathspec 'origin/master' did not match any file(s) known to git.

ssh-agent to the rescue

The root cause for this difference is Github knows my public key and has access to the corresponding private key, but this key is not available to my server, so it cannot authenticate against Github. The solution is fortunately simple. Do this on your local machine:

1
$ ssh-add

This prompts you for the password for your private key, then keeps the private key unlocked in memory for automated access. You need to do this after each time you restart before you can do new deployments, as the unlocked key is only stored in memory. We also need to enable ssh-agent support for the connection to the server - as ssh-agent gives a server the possibility to use our private key without further prompting, you need to trust the administrator of servers you deploy to. With that caveat out of the way, open your ~/.ssh/config file, and add a config block like this:

1
2
Host example.com
    ForwardAgent yes  # This enables ssh-agent functionality, making whiskey_disk happy

Adjust the hostname to taste, obviously. You can also add port-number here, if your admin moved ssh access from the default port. While you have that file open, also check out these extremely handy ssh productivity tips for a lot of extra ssh goodness.

Close the previous connection to your server, open a new one (so ssh-agent can do its magic), then re-run the test:

1
2
$ ssh -T git@github.com
Hi rud! You've successfully authenticated, but GitHub does not provide shell access.

We have ssh-win!

Deploying

Time to re-try the deployment setup from the local machine:

1
2
3
4
$ wd setup -t demo 
Results:
deploy_user@example.com => succeeded.
Total: 1 deployment, 1 success, 0 failures.

This means the repository was successfully cloned into place. We only have to do this once, from now on deployments are just a matter of:

1
2
3
4
$ wd deploy -t demo 
Results:
deploy_user@example.com => succeeded.
Total: 1 deployment, 1 success, 0 failures.

I’m seeing a total time taken for a deployment in the area of 2-5 seconds. Remember to push to Github before running your deployment, as it is from Github your server is pulling versions.

This method for deployment is extremely awesome, and the amount of awesome grows with the size of your deployments, as server-server connections are most likely a lot faster than your own internet connection.

Next steps

There is a lot more to whiskey_disk that I’ve shown here. In particular I’d like to highlight the “Configuration Repository” section of the official whiskey_disk readme. Editing per-application configuration files on individual servers is effectively a thing of the past.