Configuring Capistrano and Mongrel

For this article I used the chapter Setting Up A Development Environment in Agile Web Development with Rails, version 2.0, plus these two articles.

Capistrano working together with Mongrel allows you to deploy and restart Mongrel clusters quite nicely.

Configure Mongrel Cluster

First you need to configure a Mongrel cluster. Here is an example that creates three Mongrel instances starting at port 8000, listening on the local interface, 127.0.0.1.

$  mongrel_rails cluster::configure -N 3 -p 8000 -e production -a 127.0.0.1 \
   -c /usr/local/rails/production/current \
   -C /usr/local/rail/production/current/config/mongrel_cluster.yml

Here is how the created file looks like.

---
cwd: /usr/local/rails/production/current
log_file: log/mongrel.log
port: "8000"
environment: production
address: 127.0.0.1
pid_file: tmp/pids/mongrel.pid
servers: 3

Note that for testing purposes you should comment out the address line.

#address: 127.0.0.1

We specified listening only on the local interface for security purposes but for testing the cluster we need to access it directly via its remote IP address.

Setup Capistrano

Next you create a stub Capfile and config/deploy.rb.

$ capify .

Now you can get a list of all the tasks that are available and there quite a few.

$ cap -T

Next modify config/deploy.rb like in this example.

require 'mongrel_cluster/recipes'

set :application, "foobook"
set :repository, "http://example.com/svn/foo/trunk/foobook"

role :web, "192.168.3.17"
role :app, "192.168.3.17"
role :db,  "192.168.3.117", :primary => true

set :deploy_to, "/usr/local/rails/production"
set :mongrel_conf, "#{current_path}/config/mongrel_cluster.yml"
set :svn_user, "fkim"
set :svn_password, "fkim"
#set :user, "root"            # defaults to the currently logged in user
set :scm, :subversion         # defaults to :subversion
set :svn, "/usr/bin/svn"      # defaults to searching the PATH

Deploy using Capistrano

Now run setup which will create the directories remotely.

$ cap deploy:setup

Check dependencies.

$ cap -q deploy:check

Deploy for the first time.

$ cap deploy:cold

Everytime after you can deploy like this.

$ cap deploy

Starting and Stopping Mongrel using Capistrano

If you want to just start the mongrel cluster.

$ cap mongrel:cluster:start

If you want to just stop the mongrel cluster.

$ cap mongrel:cluster:stop

And that’s it.

Not that bad at all. Starting and stopping mongrel was the biggest issue for me. I realized that I did not need to create the spin script as suggested in the Using Capistrano with Rails article. If the mongrel configuration is correct then Capistrano will correctly start and stop it and you can use the above cap commands to test it.

Listing All Tables

With MySQL listing all the tables in a database is easy enough.

mysql> show tables;

With Oracle it’s a little less straight-forward.

> select table_name from user_tables;

OR

> select * from user_objects where object_type = 'TABLE'; 

OR

> select table_name from tabs;

OR

> select table_name from all_all_tables;

Installing Ruby on Rails on Red Hat Enterprise Linux 4

This post is based on instructions from these articles.

Determine what Ruby RPMs are installed:

$ rpm -qa | egrep '(ruby)|(irb)'
ruby-libs-1.8.1-7.EL4.2

Uninstall Ruby RPMs:

$ sudo rpm -e ruby-libs-1.8.1-7.EL4.2

Install Ruby from source:

$ wget ftp://ftp.ruby-lang.org/pub/ruby/ruby-1.8.6-p114.tar.gz$ tar xvfz ruby-1.8.6-p114.tar.gz
$ cd ruby-1.8.6-p114
$ ./configure --prefix=/usr (the default prefix is /usr/local)
$ make
$ make test
$ sudo make intall

Install Ruby Gems:

$ wget http://rubyforge.org/frs/download.php/29548/rubygems-1.0.1.tgz
$ tar xvfz rubygems-1.0.1.tgz
$ cd rubygems-1.0.1
$ sudo ruby setup.rb

Install Rails:

$ sudo gem update
$ sudo gem update --system
$ sudo rm `gem env gempath`/source_cache
$ rm -f ~/.gem/source_cache
$ sudo gem update
$ sudo gem install rails -v 1.1.6
$ sudo gem install rails -v 1.2.6
$ sudo gem install rails (if you want the latest version, 2.0.2)

Install Capistrano:

$ sudo gem install capistrano

Install Mongrel:

$ sudo gem install mongrel
$ sudo gem install mongrel_cluster

Install Memcache Client:

$ sudo gem install memcache-client

You Used Ruby to Write WHAT?!

The brilliant Zed Shaw, author of Mongrel and many other open source projects, wrote a fantastic article called You Used Ruby to Write WHAT?! which is a part of series of acticles called You Used THAT Programming Language to Write WHAT?!

Some interesting quotes from the Ruby article.

Web programming, sometimes. Ruby on Rails (RoR) is potentially a huge cost-saving framework for developing Web applications. Complex applications are measured in thousands of lines of code rather than 10- or 100-thousands of lines.

The caveat on Ruby for Web programming is that Rails is better suited for building Web applications. I’ve seen many projects attempt to create a WebDAV server or a content management system (CMS) with Rails and fail miserably. While you can do a CMS in Rails, there are much more efficient technologies for the task, such as Drupal and Django. (In fact, I’d say if you’re looking at a Java Portal development effort, you should evaluate Drupal and Django for the task instead.)

Large data crunching. It’s sad to say, but Ruby (all versions worth using in production, and even Ruby 1.9) suffer from huge problems with large-scale garbage collection, I/O processing and thread operations. Ask anyone running a Rails application that accepts large file uploads; they’ll tell you it chews their CPU just to process the MIME boundaries in the request body. Ruby has had (and still has) frequent bugs and misfeatures in its garbage collector (GC) implementation that makes handling large chunks of data difficult. Yes, you can do it; and yes, being clever helps; but why bother, when you can just use a language without such problems?

Image manipulation. I don’t really think many languages are great at image manipulation, but Ruby is particularly bad at it. The primary libraries available are based on Image Magick, which is slow, bloated and takes forever to install on many systems. Ruby talks to Image Magick through RMagick, which suffers from memory leaks, spawns external processes silently for many operations and is difficult to install (unless your computer is nearly exactly the same as the author’s). Other libraries are no better. Either they don’t support many operations, or they also require a myriad of dependencies, build tools and other requirements just to do limited image manipulation.

Server protocols. Ruby’s problems with I/O processing means that writing truly scalable servers is a waste of time. Most of the people I know doing server work in Ruby eventually give up and either use a mix of Ruby and C or they switch to another language entirely.

With Ruby’s garbage collection problems, you’ll have issues with long-running processes, especially if they have to process large data streams and you’re not careful about how you deal with them. With Ruby’s Threads you’ll find many problems when you try to scale your application beyond the 1,024 open files Ruby can handle. Ruby’s Thread contains several technical flaws that make it unsuitable for really huge-scale operations. Ruby can handle large network traffic, but it takes more careful planning and programming than with many other languages.

Enterprise deployments. The “enterprise” hates Ruby, and this is mostly a social problem. The majority of corporate systems are based on either Java or C#, with no room for a rogue language like Ruby.

In fact, take a good hard look at many of the companies running large-scale systems. You’ll see Python at Google, PHP at Yahoo, Perl at Amazon, Java at eBay—but very little Ruby. This will change as Ruby on Rails becomes popular.

I discovered that most of the college students either were working with Ruby or planned to adopt it. Either that, or they used Java because their university required it. Very few were interested in languages like Python, Perl, Java or C, except when it was required or could get them a job. This has probably been the most overlooked “feature” of Ruby by most people advocating it: Young kids actually want to work for you if you build software in Ruby or Python, Erlang, Haskell or Lisp.

Puzzle Interview Questions

  1. With a pencil and paper:
    1. Draw a large square
    2. Insrcribe a circle inside the large square
    3. Inscribe a small square inside the circle
    4. What is the ratio of the area of the large square to the area of the small square?

      Assume the width of the large square is d. The diameter of the circle inside the square is d and its radius is r = d/2.
      The small circle’s diagonal measures 2r. Therefore its area is 2r2. The large square’s area is d2 or 4r2.
      Therefore the ratio of the areas is 2:1.

  2. You wake up one morning to find an intact Boeing 747 in your front yard. You need to rent a crane to remove it. You need to determine the weight of the airplane in order to rent the correct crane. How do you figure out the weight of the airplane?

    I’d look it up on Google.
    But assuming I don’t have Google I’d guess like this:
    Plane can carry 500 passengers. Each passenger weighs 200 lbs plus about 100 lbs for a chair and 100 lbs for luggage and food. Let’s round up to 1000 lbs. That comes out to 500,000 lbs.
    Let’s assume the plane should be about 10 times heavier to carry such a load which is about the ratio between a car and a full passenger load.
    Therefore I will guess a Boeing 747 weighs between 5 and 10 million lbs.

  3. On a traditional analog clock, how many degrees separate the hour and minute hands at 3:15?

    Minute hand is at 90 degrees, where 3 is located.
    The hour hand is at the 3 plus 25% of the way to 4. There is 360/12 degrees between each numeral.
    Therefore the difference between the hour and minute 360/12/4 which is 7.5 degrees.

Linked List Interview Questions

  1. Implement Insert and Delete for a Singly Linked List.
    public interface Link<E> {
    	public Link<E> getNext();
    	public void setNext(Link<E> link);
    
    	public Link<E> getPrev();
    	public void setPrev(Link<E> link);
    
    	public E getValue();
    	public void setValue(E value);
    }
    
    public class SingleLink<E> implements Link<E> {
    
    	public SingleLink(E value) { this.value = value; }
    
    	private Link<E> next;
    	private E value;
    
    	public Link<E> getNext() { return next; }
    	public void setNext(Link<E> next) { this.next = next;}
    
    	public Link<E> getPrev() { throw new UnsupportedOperationException(); }
    	public void setPrev(Link<E> prev) { throw new UnsupportedOperationException(); }
    
    	public E getValue() { return value; }
    	public void setValue(E value) { this.value = value;}
    
    	@Override
    	public boolean equals(Object obj) { return this.value.equals(obj); }
    
    	@Override
    	public String toString() { return getValue().toString(); }
    }
    
    public abstract class LinkedList<E> {
    
    	protected abstract Link<E> createHead(E value);
    	protected abstract Link<E> removeHead();
    	protected abstract Link<E> append(Link<E> tail, E value);
    	protected abstract void chain(Link<E> from, Link<E> to);
    	protected abstract void deleteAfterHead(E value);
    	protected abstract boolean isInsert(Link<E> link);
    	protected abstract boolean isEnd(Link<E> link);
    
    	public void insert(E value) {
    
    		if (head == null) {
    			createHead(value);
    			return;
    		}
    
    		Link<E> tmp = head;
    		while (!isInsert(tmp))
    			tmp = tmp.getNext();
    
    		append(tmp, value);
    	}
    
    	public void delete(E value) {
    
    		if (head == null)
    			return;
    
    		if (head.equals(value)) {
    			removeHead();
    			return;
    		}
    
    		deleteAfterHead(value);
    	}
    
    	Link<E> head;
    }
    
    public class SinglyLinkedList<E> extends LinkedList<E> {
    
    	@Override
    	protected Link<E> createHead(E value) {
    		if (value != null) {
    			head = new SingleLink<E>(value);
    		} else {
    			head = null;
    		}
    		return head;
    	}
    
    	@Override
    	protected Link<E> removeHead() {
    		if (isEnd(head.getNext())) {
    			head = null;
    			return null;
    		}
    
    		head = head.getNext();
    		return head;
    	}
    
    	@Override
    	protected Link<E> append(Link<E> tail, E value) {
    		Link<E> link = new SingleLink<E>(value);
    		tail.setNext(link);
    		return link;
    	}
    
    	@Override
    	protected void chain(Link<E> from, Link<E> to) {
    		from.setNext(to);
    	}
    
    	@Override
    	protected void deleteAfterHead(E value) {
    
    		Link<E> tmp = head.getNext();
    		Link<E> prev = head;
    		while (!isEnd(tmp)) {
    			if (tmp.equals(value)) {
    				chain(prev, tmp.getNext());
    				break;
    			}
    			prev = tmp;
    			tmp = tmp.getNext();
    		}
    	}
    
    	@Override
    	protected boolean isInsert(Link<E> link) {
    		return isEnd(link.getNext());
    	}
    
    	@Override
    	protected boolean isEnd(Link<E> link) {
    		return link == null;
    	}
    }
  2. Implement Insert and Delete for a Doubly Linked List.
    public class DoubleLink<E> extends SingleLink<E> {
    
    	public DoubleLink(E value) { super(value); }
    
    	private Link<E> prev;
    
    	@Override
    	public Link<E> getPrev() { return prev; }
    
    	@Override
    	public void setPrev(Link<E> prev) { this.prev = prev; }
    }
    
    public class DoublyLinkedList<E> extends SinglyLinkedList<E> {
    
    	@Override
    	protected Link<E> createHead(E value) {
    		if (value != null) {
    			head = new DoubleLink<E>(value);
    		} else {
    			head = null;
    		}
    		return head;
    	}
    
    	@Override
    	protected Link<E> removeHead() {
    		head = super.removeHead();
    		if (head != null) {
    			head.setPrev(null);
    		}
    
    		return head;
    	}
    
    	@Override
    	protected Link<E> append(Link<E> tail, E value) {
    		Link<E> link = new DoubleLink<E>(value);
    		tail.setNext(link);
    		link.setPrev(tail);
    		return link;
    	}
    
    	@Override
    	protected void chain(Link<E> from, Link<E> to) {
    		from.setNext(to);
    		if (to != null) {
    			to.setPrev(from);
    		}
    	}
    
    	@Override
    	protected void deleteAfterHead(E value) {
    
    		Link<E> tmp = head.getNext();
    		while (!isEnd(tmp)) {
    			if (tmp.equals(value)) {
    				chain(tmp.getPrev(), tmp.getNext());
    				break;
    			}
    			tmp = tmp.getNext();
    		}
    	}
    }
  3. Implement Insert and Delete for a Sorted Linked List.
    public class SortedLinkedList<E> extends SinglyLinkedList<E> {
    
    	@Override
    	public void insert(E value) {
    
    		if (head == null) {
    			createHead(value);
    			return;
    		}
    
    		Link<E> tmp = head;
    		Link<E> prev = null;
    		// XXX: yuck, dynamic casting to Comparable
    		while (!isEnd(tmp) &&
    	Comparable<E>) tmp.getValue()).compareTo(value) < 0) {
    			prev = tmp;
    			tmp = tmp.getNext();
    		}
    
    		Link<E> link = new SingleLink<E>(value);
    		if (prev == null) {
    			link.setNext(head);
    			head = link;
    		} else {
    			prev.setNext(link);
    			link.setNext(tmp);
    		}
    	}
    }
  4. Implement Insert and Delete for a Circular Linked List.
    public class CircularLinkedList<E> extends DoublyLinkedList<E> {
    
    	@Override
    	protected Link<E> createHead(E value) {
    		head = super.createHead(value);
    		if (head != null) {
    			head.setNext(head);
    			head.setPrev(head);
    		}
    		return head;
    	}
    	
    	@Override
    	protected Link<E> removeHead() {
    		if (head.getNext() == head) {
    			head = null;
    			return null;
    		}
    		
    		Link<E> prev = head.getPrev();
    		head = head.getNext();
    		head.setPrev(prev);
    		prev.setNext(head);
    		return head;
    	}		
    
    	@Override
    	protected Link<E> append(Link<E> tail, E value) {
    		Link<E> link = new DoubleLink<E>(value);
    		tail.setNext(link);
    		link.setPrev(tail);
    		link.setNext(head);
    		head.setPrev(link);
    		return link;
    	}
    	
    	@Override
    	protected boolean isEnd(Link link) {
    		return link == head;
    	}
    
    	@Override
    	protected boolean isInsert(Link link) {
    		return link.getNext() == head;
    	}
    }

Data Structures Interview Questions

  1. Find the largest int value in an int array (try to not use native functionality like “max()”)
    	public static int max(int [] array) {
    		int max = Integer.MIN_VALUE;
    		for (int ii = 0; ii < array.length; ii++) {
    			if (array[ii] > max)
    				max = array[ii];
    			if (max == Integer.MAX_VALUE)
    				break;
    		}
    		return max;
    	}
  2. What’s the difference between a stack and a queue?
     
    Stacks are LIFO, i.e. last in, first out. You push things on top of the stack and pop them off the stack.
    Queues are FIFO, i.e. first in, first out. You push things in one side, pop them off the other.
     
  3. Write a function to reverse a string (try to not use native functionality like strrev() or text[::-1])
             char * strrev(char * str) {
               int ii = 0, jj = strlen(str) - 1;
               char tmp;
               while (ii < jj) {
                 tmp = str[ii];
                 str[ii++] = str[jj];
                 str[jj--] = tmp;
               }
               return str;
             }
  4. Write a function that determines if two strings are anagrams. From wikipedia: an anagram is “the result of rearranging the letters of a word or phrase to produce a new word or phrase, using all the original letters exactly once.” (“abc” “bac” “cba” are anagrams, “abc” and “bba” are not)
  5. /**
     * Returns 1 if it's an anagram, 0 otherwise.
     */
    int anagram(char * str1, char * str2) {
    
      // quick check
      if (strlen(str1) != strlen(str2)) {
        return 0;
      }
    
      // copy str2 to our tmp
      char * tmp = malloc(strlen(str2) + 1);
      strcpy(tmp, str2);
    
      int ii = 0;
      while (str1[ii] != 0) {
        char * ch = strchr (tmp, str1[ii]);
    
        // if not found return -1 to indicate not an anagram
        if (ch == 0)
          return 0;
    
        // remove this found letter from second string
        ch[0] = 0;
        if (ch == tmp) {
          tmp = ch + 1;
        } else if (*(ch+1) != 0) {
          tmp = strcat(tmp, ch+1);
    	}
    
        // go to next character
        ii++;
      }
    
      return 1;
    }

Computing Basics Interview Questions

  1. Use recursion to write a function that determines the length of a string
    int strlen(char * str) {
      if (str[0] == 0)
        return 0;
      return 1 + strlen(str + 1);
    }
  2. What’s the number after F in hexadecimal?0x10
  3. Bitwise: what is 7 | 1? 7 & 1? ~7?

    7 (0111). 1 (0001). -8 (1000).

  4. Write a script that prints the numbers from 1 to 100. But for multiples of four print “Four” instead of the number and for the multiples of seven print “Seven”. For numbers which are multiples of both four and seven print “FourSeven”.
    for (int ii = 1; ii <= 100; ii++) {
    	boolean isMultipleOfFour = (ii % 4 == 0);
    	boolean isMultipleOfSeven = (ii % 7 == 0);
    	if (isMultipleOfFour && isMultipleOfSeven)
    		System.out.println("FourSeven");
     	else if (isMultipleOfFour)
    		System.out.println("Four");
     	else if (isMultipleOfSeven)
     		System.out.println("Seven");
    	else
    		System.out.println(ii);
    }

ATG Consulting Interview

Today I had the most detailed but at same time most interesting ATG consulting interview yet. Here are the questions I was asked with answers when appropriate in italics.

  1. Which versions of ATG have you worked with?
  2. What parts of ATG’s stack have you worked with?
  3. How do you compare ATG with Ruby on Rails?
  4. What is Nucleus?
  5. What is the ATG Repository?
  6. When creating form handlers typically what ATG base class do you extend? GenericFormHandler.java
  7. When creating droplets what ATG base class do you extend? DynamoServlet.java
  8. What form handlers and methods do you use during checkout? ShoppingCartFormHandler, numerous handlers like handleMoveToConfirm, etc.
  9. What does a user typically see during checkout? Add to shopping cart, login, billing and shipping address, payment, confirm, confirmation, email confirmation, shipped email.
  10. How do you compare strings in Java? If String a = “hello” and String b= “hello” what is a == b? True, a and b both reference the same constant string.

In another interview I was asked these questions.

  1. What is HTTP? How does it work?
  2. If HTTP is stateless then how does a web application maintain state.

phpMyAdmin not starting

Today when I started up phpMyAdmin (version 2.10.0.2) by going to http://localhost/phpMyAdmin I saw this error screen.

phpMyAdmin – Error

I googled around but could not find any solutions.

So I went and tried to install the latest version of phpMyAdmin, 2.11.5. When I tried to run setup I saw this error screen.

phpMyAdmin – Error

Cannot start session without errors, please check errors given in your PHP and/or webserver log file and configure your PHP installation properly.

Now that I had an error message to work with I googled around and found this post, Xampp phpMyAdmin Problem, which helped me diagnose the problem.

It turns out during one of my cleaning sessions I wiped out the session directory in, “C:\DOCUME~1\fkim\LOCALS~1\Temp\php\session.” After restoring it I ran into my next problem.

phpMyAdmin – Error

Cannot load mysql extension. Please check your PHP configuration. – Documentation

I upgraded to PHP 5.2.5 from 5.2.1 but that did not help. I installed the mbstring module but that did not help.

Finally I added the PHP extension directory to the path (I had already added the PHP directory to the path) and it finally worked! My path now includes the following two directories, E:\Program Files\PHP and E:\Program Files\PHP\ext.