Componentix logo

Componentix blog

Here we write some random thoughts and articles about software development: Java, Grails, Node.js, iPhone, iPad and more.

Read code with the speed of the light: Use the grep, Luke

How do you read code? Do you use your IDE to browse files? Likely you just waste time then. Main trick to read code fast is not read all the code.

There are 2 main ways I use to read less code.

First and most obvious way - search files for patterns. Plain old grep would do, but I highly prefer to use ag or ack as they are fine tuned for searching code.

Second way - use diffs in version control system (Git, Mercurial, Subversion, etc). We’ll get to it later as this deserves a blog post of it’s own.

So back to search. Let’s assume I’d like to explore use of some concept in code, e.g. notifications. I do simple search like this (-S stands for case-insensitive).

ag -S notification
26:- (void) enterBackgroundNotification: (NSNotification *) notification;

22:        [[NSNotificationCenter defaultCenter] addObserver: self
23:                                                 selector: @selector(enterBackgroundNotification:)
24:                                                     name: @"UIApplicationDidEnterBackgroundNotification"
32:    [[NSNotificationCenter defaultCenter] removeObserver:self];
35:- (void) enterBackgroundNotification: (NSNotification *) notification {

53:    [[NSNotificationCenter defaultCenter] addObserver: self
55:                                                 name: MPMoviePlayerPlaybackDidFinishNotification
60:    // TODO: Maybe remove notification here?
75:- (void) videoPlayerPlaybackDidFinish: (NSNotification *) notification {

Now I can easily notice that one subscribes to notifications using add Observer and removes subscription using removeObserver. So good idea to check if these calls match. Let’s highlight them.

ag "addObserver|removeObserver"

22:        [[NSNotificationCenter defaultCenter] addObserver: self
32:    [[NSNotificationCenter defaultCenter] removeObserver:self];

53:    [[NSNotificationCenter defaultCenter] addObserver: self

OOPS, looks like CXVideoViewController.m misses removeObserver call. Let’s show more context (-C 10 tells to show 10 lines around match, **/*Video* is used to restrict search to matching file list):

ag "addObserver|removeObserver" -C 10 **/*Video*
43-    // Movie playback is asynchronous, so this method returns immediately.
44-    [_player play];
47-- (void) viewWillAppear: (BOOL) animated {
48-    [super viewWillAppear: animated];
49-    self.navigationController.navigationBar.hidden = YES;
51-    [self playMovieAtURL: [_item viewVideoURL]];
53:    [[NSNotificationCenter defaultCenter] addObserver: self
54-                                             selector: @selector(videoPlayerPlaybackDidFinish:)
55-                                                 name: MPMoviePlayerPlaybackDidFinishNotification
56-                                               object: nil];
59-- (void) viewWillDisappear: (BOOL) animated {
60-    // TODO: Maybe remove notification here?
62-    [_player stop];

How sweet, we even have a TODO comment about notification.

And of course we can search for TODOs too:

9:// TODO: Better and configurable implementation, maybe use CocoaLumberjack

41:     // TODO: Item itself should either be copied or also synchronized

52:            // TODO: Maybe just update on timer, as it is additional problem to unregister callback

66:        // TODO: Why doesn't call super instead?

26:    // TODO: don't use Singleton
47:        // TODO: don't use Singleton
71:    /* TODO: Download All
101:/* TODO: Download All
192:// TODO: Refactor and use these, probably through some base table view controller

60:    // TODO: Maybe remove notification here?
69:    // TODO: Fix it

5:  s.homepage     = "http://TODO/WebToMobile"

Not that bad, I thought there would be more. ;)

Of course if your IDE is worth a dime, you’ll be able to do similar things inside IDE. Decent standalone code editors also usually provide similar functionality either built-in or as plugins.

However there are some nice features that IDEs usually don’t do:

  • search in code that isn’t configured as project in IDE (which is crucial as search is even more crucial for navigation in third-party code).
  • show context together with search results
  • easy filtering out unrelated files (using .gitignore or manually provided patterns)
  • ability to pipe search results further, e.g. save into file and work with them later

Some useful references:

  • grep - available almost on any system
  • ag - fast and takes .gitignore into account
  • ack - has easy filtering by programming language
  • git-grep - can travel in time if you use Git
  • ctags - pre-indexes symbols (useful for big projects)

Git aliases inspired by GUM, a better UI for Git

Some of the commands provided by Git are quite confusing and GUM project tries to address it.

However it looks like most useful stuff can also be achieved through use of Git aliases:

    stage = !sh -c '[[ -z "$@" ]] && git add -u || git add "$@" && git add -u "$@" ' -
    unstage = reset HEAD --
    rewind = ![[ -z "$@" ]] && git reset --hard HEAD || git checkout HEAD

These aliases provide such new commands:

  • git stage – stages all changes to already tracked files (including deletions)
  • git stage FILES – stages changes to given files only (including deletions and additions of new files)
  • git unstage – removes everything from staging area (working copy remains unchanged)
  • git unstage FILES – removes given files from staging area (working copy remains unchanged)
  • git rewind – resets all changes done to the working copy (including staging area)
  • git rewind FILES – resets all changes done to the given files (including staging area)

Code available as Gist:

Tags: bash git

Choose Grails version and configure GRAILS_HOME automatically, updated

It is quite a pain to set up GRAILS_HOME each time when you use different Grails versions and I already wrote a post about it with very simple bash script to help.

However script was oversimplified and so not working in many corner cases. After my post Yuri has come up with his version of script for Windows, using .BAT file.
Yuri’s version was more complicated and took corner cases into account.

When I have found some time to educate myself about bash, I wrote enhanced version of my script. Its usage follows additional functionality of Windows version.

Its usage is simple, there are such scenarios:

1. Run script in Grails project root directory. In such case it parses file and launches appropriate version of Grails.

Example: grails run-app

2. Run script anywhere with explicitly specified Grails version. It is needed for some tasks like grails upgrade or grails create-app. This just needs to pass version in parameter like -ver:1.3.1.

Example: grails create-app -ver:1.3.1

As in older version, to use the aforementioned script you need to set GRAILS_PREFIX variable to a value appropriate for your system. And of course your Grails distributions should be installed side-by-side in one common folder.
For example I have it set to /home/vg/tools/grails- on my Linux box.

The script itself:


# Check if GRAILS_PREFIX is set
if [ -z $GRAILS_PREFIX ]; then
    echo "You must define environment variable GRAILS_PREFIX before running Automatic Grails Version Selector"
    exit 1

# Define script params array
declare -a PARAMS

# Process script parameters
for PARAM in $*; do
    if [ ${PARAM:0:5} == "-ver:" ]; then
        # Extract version from -ver parameter
        # Add parameter to array, so that it is passed to original script
        PARAMS=( "${PARAMS[@]}" "$PARAM" )

# If version to use is not specified, try to detect it
if [ -z $GRAILS_VERSION ]; then
    # Check if file exists
    if [ -e ]; then
        # Get required Grails version
        # Note that CR characters are removed at first
        GRAILS_VERSION=`tr -d '\015' < | sed -n 's/app.grails.version=\(.*\)$/\1/p'`
        echo "Current directory doesn't represent existing Grails project, specify version as -ver:1.3.1"
        exit 1

# Set Grails home using configured prefix and determined version

# Check if GRAILS_HOME directory exists
if [ -d  $GRAILS_HOME ]; then
    # Run original Grails script
    $GRAILS_HOME/bin/grails ${PARAMS[@]}
    echo Grails home directory for this project does not exist: $GRAILS_HOME
    echo The current project might have updated to a newer Grails version.
    echo Make sure you have downloaded and installed the version of Grails required: $GRAILS_VERSION
    exit 1

Note that the script doesn’t have the default Grails version for the case when not launched in Grails project root. Adding such configuration parameter is left as an exercise to reader :)

Tags: grails java bash

Choose Grails version and configure GRAILS_HOME automatically

We are using Grails actively to develop web applications. So we ended up with different applications using different versions of framework.

Setting up GRAILS_HOME variable to point to different Grails release each time I switched to other application was really unpleasant hassle. So I thought about how to resolve this problem. Grails stores application version in file, I came out to the following Bash script:


# Get required Grails version
GRAILS_VERSION=`sed -n 's/app.grails.version=\(.*\)$/\1/p' <` 

# Set Grails home using configured prefix and determined version

# Run original Grails script
$GRAILS_HOME/bin/grails $@

It just parses the version from the file and appends it to GRAILS_PREFIX environment variable. The resulting value is exported into GRAILS_HOME variable.

To use the aforementioned script you need to set GRAILS_PREFIX variable to a value appropriate for your system. And of course your Grails distributions should be installed side-by-side in one common folder.
For example I have it set to /home/vg/tools/grails- on my Linux box.

You can grab/fork script easily as a Gist on GitHub.

UPD: The script was updated to take some corner cases into account. See updated version of the script.

UPD 2: There is also version for Windows.

Tags: grails java bash
Following e-mail is only for robots (never send anything to it, or you would be blacklisted):