A collection of computer systems and programming tips that you may find useful.
 
Brought to you by Craic Computing LLC, a bioinformatics consulting company.

Friday, November 30, 2007

Running a Rails Applications in a Subdirectory

Part 1: Running a Rails Applications in a Subdirectory

I'm running a Rails application and a separate static web site on the same host.

I want to refer to the static content with a url like http://myserver/public/index.html etc.

I want to refer to the Rails app with a url like http://myserver/db

By default Rails expects an application to reside at the top level of a site, i.e. http://myserver, so I need a way to change this default path. There are two simple steps involved:

1: In config/environment.rb add a line like this at the bottom of the file:

ActionController::AbstractRequest.relative_url_root = "/db"

...where '/db' is the subdirectory where you want your application to appear

This will add that prefix to every URL generated in your Rails app pages and will cause the Rails dispatcher to respond to that path.

2: Adding that prefix will cause your links to stylesheets and javascripts to break. Fix that by adding a symbolic link in your Rails app /public directory

% ln -s . db

... where 'db' is the prefix with no leading slash

Restart your server and try it out.

Part 2: Serving Static Content from Apache and your Rails app from Mongrel

Currently (late 2007) the preferred Web server solution for Rails applications is to use Apache2 as the front end and Mongrel as the Rails application server. All requests come into Apache. Those for a static web site are handled directly, as Apache is great at that job. Requests for the Rails app are proxied to an instance of the Mongrel web server running on a different port. You can find details of this configuration here:
http://mongrel.rubyforge.org/docs/apache.html

These are the steps needed to set up this configuration.

1: Set up the Rails app under a subdirectory as shown in Part 1.

2: Run Mongrel on a port other than 80 as described in the Apache link above. For example, you might run it on port 8000 using this command in your app directory:

% mongrel_rails start -d -p 8000 -e production -P /my/path/railsapp/log/mongrel.pid

3: Configure Apache2 to proxy Rails requests to Mongrel using a VirtualHost block similar to this:

<VirtualHost>
ServerName yourserver.com
ProxyPass /db http://localhost:8000/db
ProxyPassReverse /db http://localhost:8000/db
ProxyPreserveHost on

DocumentRoot /yourpath/static/html
<Directory>
Options Indexes FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
</Directory>

</VirtualHost>


The VirtualHost blocks sets up a host on the standard port 80.

The ProxyPass directives tell Apache to pass any requests with URLs that start with '/db' over to the web server running on the same machine (localhost) at port 8000. It also modifies the responses from that server so that they appear to come from Apache instead of Mongrel. So from the user's perspective the Mongrel server is invisible.

Any other requests are handled by Apache directly and will fetch static content from the directory '/yourpath/static/html', which is where you would put your static web site.

For this to work, your Apache server must load the mod_proxy module, and probably some others. See the Mongrel/Apache link given above for details on that.

Look at the various Apache Proxy options if you need finer control over what gets proxied and what gets served directly. You can set up multiple different Rails apps on the same host in this way and/or multiple Mongrel instances that can serve an application under high load. Again, see the Mongrel/Apache link for details.

Tuesday, July 31, 2007

Installing Instiki on Mac OS X

Instiki is a Wiki clone written in Ruby and Rails. In the spectrum of wiki software, it is relatively simple with a correspondingly clean interface, unlike the default mediawiki pages. It is not perfect but if you need to get a wiki up and running quickly it may be just what you need.

The instiki pages suggest that installation is trivial, but that is a little misleading. Here are the steps needed to get it up and running on a Mac OS X 10.4.

It assumes that you have Ruby and MySQL already installed and that you have at least a passing familiarity with Rails applications.

1: Download the instiki package from: http://rubyforge.org/frs/?group_id=186&release_id=10014
The version current at the time of writing is instiki-0.11.pl1.tgz

2: Unpack the .tgz file
Use the default 'Archive Utility' or use tar on the command line. Be aware of this weird gotcha that I encountered with StuffitExpander.

3: Create the database in MySQL
The database name is not critical.
# mysqladmin -u root -p create instiki_production

4: Edit config/database.yml in the instiki directory
Replace the existing 'production' database definition with this:
production:
adapter: mysql
database: instiki_production
username: root
password: xxxxx
host: localhost
# socket: /path/to/your/mysql.sock


Add your own password, and don't worry about the socket line unless you are on Linux.

5: Install the database tables
This is a Rails command that creates all the right tables and sets up some other configuration settings:
# rake environment RAILS_ENV=production migrate

6: Start the Instiki Server

# ./instiki

This will fire up the Webrick web server built into Rails and bind the Instiki to port 2500 on the local machine. Point your browser (on that machine) to this URL:
http://localhost:2500

You should see a setup web page.

7: Configure the Wiki

In the setup page, enter the name of the wiki and the address (by which they mean the subdirectory on this site that will form the root of the wiki), and enter an administrator password.

It will return a home page with a text area into which you can enter the content of the page. Look at the hints on that page and on the Instiki project site for help on the formatting shortcuts.

You will notice the absence of any button or link that adds a new page. The way you do this in Instiki is to enter the name for that page in the parent page and surround it in double square brackets, like this:
[[Another Page]]
When you submit this page, you will see the text 'Another Page' with
a question mark next to it. Click on that to add content to the new page. That can be a little confusing until you get used to it.

With the Home Page in place, do some other setup by clicking 'Edit Web' on that page. Here you can fine tune some of the default styling, you can setup password protection for the entire site and, importantly, you can configure the site to publish a read-only version of itself in parallel to the one that you are editing. This feature can be very useful if you want to block changes to a public site or if you want to use Instiki as a simple Content Management System for a static web site.

If you opt for publishing the read-only version you can then access the two versions from similar but distinct URLs.

The URL for the regular home page looks like this (for a wiki called 'mysite')
http://localhost:2500/mysite/show/HomePage

The published (read-only) version can be found at
http://localhost:2500/craic/published/HomePage

Note you will get Rails errors if you try to access
http://localhost:2500/craic/published
or
http://localhost:2500/craic/published/show/HomePage

That had me confused for quite a while when I was testing if my installation was working.


8: Make your instiki available on the Internet

The default configuration for instiki runs on localhost at port 2500. If you want to run it as the sole public web server on your host you could change those two parameters in the installation (file script/server) or when you start instiki.
# ./instiki -b 192.168.0.0 -p 80

But a more likely scenario is that you want to add the wiki to an existing web site. Running instiki directly on a web server other than Webrick is non-trivial so your best bet is to configure your regular web server to proxy wiki requests that it receives to the instiki server.

This fairly straightforward but does involve some server configuration directives.

In this example, the main web site is called 'mysite.com' and the wiki name is 'mysite'. I want the wiki to accessed by urls like http://mysite.com/subdir/HomePage

I modified the following from this page at instiki.org
http://www.instiki.org/show/HowToUseInstikiAsWebSite

If your main web server is Apache then you want a configuration something like this, using the ProxyPass directives:
<virtualhost>
ServerName mysite.com
ServerAlias www.mysite.com
ProxyPass /subdir/ http://localhost:2500/mysite/published/
ProxyPassReverse /subdir/ http://localhost:2500/mysite/published/
</virtualhost>


If you are using lighttpd as your server, which is common in the Rails world, then things are a little cryptic and look like this:

$HTTP["host"] =~ "(^|\.)mysite.com$" {
server.document-root = basedir + "/web/mysite.com/html/"

# Rewrite the URL *before* entering the HTTP["url"] block
url.rewrite_once = ("^/subdir/(.*)$" => "/mysite/published/$1" )

# pass any /mysite/ urls to instiki on port 2500
$HTTP["url"] =~ "^/mysite/published/\S+" {
proxy.server = ( "" => (
( "host" => "127.0.0.1", "port" => 2500),
), )
}
}

What this does is to rewrite any input URL that include /subdir to point to /mysite/published and then to pass those on to the instiki web server.

Note that your must have mod_proxy added to your module list for this to work.
server.modules = ("mod_rewrite", "mod_fastcgi", "mod_accesslog", "mod_proxy")
Also note that you need to specify the proxy host as an IP address, not a hostname.

Restart the server and then http://mysite.com/subdir/HomePage will be passed to the instiki server as http://localhost:2500/mysite/published/HomePage

Be careful how you set up the regular expressions. Make sure that you can't mess with the URL and get the editable pages by mistake (unless you want to allow access to those). Also be aware that instiki will give cryptic Rails error dumps if you enter invalid or truncated URLs and that may confuse your users.

9: Set up instiki to run automatically
On Mac OS X you need to create a file under /Library/LaunchDaemons in Apple's plist format.
This should look something like this (here the instiki startup script is located at /Users/mysite/instiki/instiki).

<plist version="1.0">
<dict>
<key>Label</key>
<string>net.instiki</string>
<key>OnDemand</key>
<false>
<key>Program</key>
<string>/Users/mysite/instiki/instiki</string>
<key>ProgramArguments</key>
<array>
<string>/Users/mysite/instiki/instiki</string>
<string>--daemon</string>
</array>
</dict>
</plist>>


Call your file net.instiki.plist and then reboot your machine. Assuming your web server starts up in a similar fashion, you should be able to go to your main site URL and then to the wiki link, whereupon you'll see the published version of the wiki.

10: Outstanding issues
From this point you are on your own in terms of creating your content, pages and styles. Refer to the instiki.org site for help with that.

Currently the WEBrick web server is hardwired into the instiki code. This works fine but under heavy load this would not be acceptable. Being able to replace it with lighttpd or mongrel, like you do with regular Rails applications, would solve the problem but for now you'd have to hack instiki to get this.

Thanks and acknowledgements

Instiki was created by David Heinemeier Hansson and further developed by Alexey Verkhovsky, Matthias Tarasiewicz and Michal Wlodkowski. I thank them all for their work!

Monday, July 30, 2007

Serious Gotcha with Mac OS X / Stuffit Expander / Instiki .tgz file

After a lot of screwing around I've figured out the reason why my installation of the Instiki software was failing on my installation of Mac OS X.

Hopefully this is some very esoteric combination of factors but I want to put the story out there in case it helps someone else.

Background:
I want to install the Wiki software 'instiki' on my Mac (OS X 10.4.9). I downloaded this version:
instiki-0.11.pl1.tgz
from rubyforge
http://rubyforge.org/frs/?group_id=186&release_id=10014

I downloaded it using the Camino web browser v1.5

I copied it from the downloads folder to the target folder in the Finder.
I double-clicked the .tgz file to unpack the archive. Normally the Mac OS X Archive Utility takes care of that. In this case Stuffit Expander popped up and did the job (version 8.0.2).

To cut a long story short, for some reason Stuffit Expander made two copies of certain files (not all of them). For example app/models/web.rb appeared as web.rb and web.1.rb. The real problem was that web.rb was empty (0 bytes) whereas the real content was in web.1.rb. Instiki doesn't know anything about the .1.rb files and only sees the empty versions when you fire it up. Not surprisingly it craps out with a whole slew of odd messages.

The solution for me was to either unpack the archive manually
# tar xzvf instiki-0.11.pl1.tgz
or to remove the Stuffit application. Once you've done that then the default Archive Utility should handle the unpacking and the problem will go away.

Why Stuffit should do this I don't know... very, very strange behaviour and a real pain to troubleshoot...

Tuesday, July 24, 2007

Ignoring Files and Directories in Subversion (SVN)

Subversion allows you to remove specified directories and files from revision control, such as temporary files or logs that would quickly become a real pain if you had to commit and update them like regular source files.

There are various ways to do this with global settings for file types or on a per directory basis.

I have run into a problem several times, checking in projects that have been started outside of SVN. If I import the entire directory tree into SVN, then check out a copy, all the directories are already under revision control and I need a way to remove them from it. There are 3 ways to handle this.

1: Delete the specific directories before you import the project into SVN.
This might be fine for tmp or log directories where all the contents are 'disposable' but this may not work in other situations like Rails doc directories where you have added a custom Readme file, for example.
When you check out the project, create those directories by hand and then use svn:ignore to tell svn to ignore them.

# mkir tmp
# svn propset svn:ignore 'tmp' .
# svn commit -m 'Ignore tmp directory'


2: Use svn propset to ignore the contents of particular directories.
To ignore the contents of a tmp directory, but not the directory itself, you could do this in a checked out copy of the project.

# svn propset svn:ignore '*' tmp
# svn commit -m 'Setting svn:ignore on contents of tmp'
# svn update


Now when you modify a file in tmp and then run svn status you should see no changes.
You can also use this to ignore certain types of files, such as gzipped files in the current directory:

# svn propset svn:ignore '*.gz' .

Follow this with a commit and update.
But note that this will NOT ignore any .gz files that existed prior to issuing the svn:ignore as these were already under revision control. You can use svn rm to get rid of these from the repository.

3. Remove the directory from the repository then recreate with the svn:ignore property.

# svn export tmp tmp1
# svn rm tmp
# svn commit -m 'remove tmp directory from repository'
# mv tmp1 tmp
# svn propset svn:ignore 'tmp' .
# svn commit -m 'ignore local directory tmp'



More information can be found in Chapter 7 of the SVN book

Wednesday, June 20, 2007

Apache 2.2 - Where are the modules ?

The Apache httpd server has played a critical role in most of my work over the past decade but these days I'm finding more and more reasons to look at alternatives. Its configuration syntax is not pretty, the distribution is bloated, and its performance with some applications (especially Rails) is almost unworkable. Here's another reason to grumble...

I recently compiled Apache 2.2.4 from source. I've done that several times in the past and just did the typical steps of:
# ./configure --prefix=/usr/local/apache2
# make
# make install


That works great and I can fire up the server just fine - until I want to serve an application that involves URL rewriting. I get this error:

[Wed Jun 20 17:00:07 2007] [alert] [client 192.168.2.26] /Users/jones/instiki/public/.htaccess: Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration

That tells me that the mod_rewrite module is not being loaded in the httpd.conf file. So I go into that file and search for the LoadModule section. In the past there would be a bunch of LoadModule statements, many of which were commented out. But in 2.2.4 there is only one. This is actually a good thing as it makes the httpd.conf file smaller and easier to look through.

I figured I just needed to add a line that loaded mod_rewrite. I checked my apache2/modules directory to get the correct file name - but there are no modules in there. I checked to see if it was compiled into the executable using this:

# /usr/local/apache2/bin/httpd -l

No, it's not in the list. So where are all the modules? Am I supposed to download them separately from apache.org? Looking on that site doesn't tell me what to do. What's the deal?

Turns out that you have to specify the modules you want at compile time. This is a really, really bad move on Apache's part. I'm sure it is done with the best of intentions but what a pain. In order to get my module I have to go and recompile the whole damn thing... great...

*WAIT* If you have already modified your current apache config files, htdocs directory, etc. copy them somewhere safe before you recompile and install over them!


Here's what I need to do to get mod_rewrite and mod_env compiled so that I can load them dynamically.

# ./configure --prefix=/usr/local/apache2 --enable-rewrite=shared --enable-env=shared

To see the full list of options (215 lines of them) do this:

# ./configure --help

You can get 'all' or 'most' (whatever that means) of the modules using these variants

# ./configure --prefix=/usr/local/apache2 --enable-mods-shared=all
# ./configure --prefix=/usr/local/apache2 --enable-mods-shared=most


I chose the latter when I recompiled and that gave the two that I need plus hopefully any others that I might need in the future. So my specific steps were:

# ./configure --prefix=/usr/local/apache2 --enable-mods-shared=most
# make
# make install


Then I went into the httpd.conf file, put back a couple of changes that I had made previously and went looking for the LoadModule lines. Well, I guess I got what I asked for... I asked it to compile most of the modules. It did that and it has set up the config to load all 43 of them!

All I want are two modules, for Pete's sake... so I reckon I want to delete the others, right? WRONG! Without the new configure directive, a bunch of the standard modules are compiled in the server. With the new directive, they are loaded dynamically. So if you delete these LoadModule lines then a load of other stuff will break.

With all this done, I can now get back to where I started with a functioning Apache installation. I hope this note helps you avoid the couple of hours of frustration that I just enjoyed.

Apache is a great piece of work, don't get me wrong, - but nonsense like this is really annoying.


If you are in the market for an alternative you might want to consider Nginx (http://nginx.net/), a relatively unknown http server that is getting a lot of good press in the Rails community.

Tuesday, June 19, 2007

Adding Hard Drives to a Linux System using fdisk

In recent releases of Fedora Linux (e.g. Fedora 6 and 7), the default filesystem configuration uses Logical Volumes, which are a flexible way to manage the hard drives on a machine. But this flexibility can come at a cost if and when the drives go bad.

The old school way of setting up drives is to use the fdisk command to set up specific, fixed partitions on each drive. This requires a bit more planning and a little more execution, but it has the advantage that of a machine fails for some reason, you can still move the drives to a new machine and mount them directly.

I wanted to do that on a system recently where I had 2 x 500GB drives set up as a logical volume and then wanted to add 2 more drives to use as live backup disks for other servers. I want backup disks to be as independent as possible and so adding them to the logical volume was not a good idea. Happily you can mix logical volumes and disks with traditional partitions with no problems.

Here are the steps needed to add two drives to the Fedora 7 system. The same information is available in a load of other places. I'm including it here largely for my own benefit next time I need to do this. All steps assume you are logged in as root.

1: See what physical disks you have on your system

# /sbin/fdisk -l

You'll get a load of output - look for lines that delimit each of the physical disks, such as:
Disk /dev/sda: 500.1 GB, 500107862016 bytes

Below these you will see the partitions on each drive. Note the distinction between a disk (/dev/sda) and a partition on that disk (/dev/sda1, /dev/sda2...)

On my system the disks are SATA drives and these appear under these names /dev/sda, /dev/sdb, /dev/sdc, /dev/sdd.

/dev/sda and /dev/sdb are used in the original Logical Volume that I set up. I'm going to set /dev/sdc and /dev/sdd as two separate disks, each with a single partition on each.


2: Use fdisk to create a single partition on each disk

# /sbin/fdisk /dev/sdc
Then in response to the various prompts give these answers:
n (create a new partition)
p (make it a primary partition)
1 (call this partition #1)
hit enter to start at the first sector
hit enter again to end at the last sector (in other words make the partition fill the entire drive)
w (write the partition to the disk and quit the program)

I repeated this for disk /dev/sdd

3: Create a filesystem on each drive

The standard Linux filesystem type at the moment is ext3, although alternate filesystems can be used. Each filesystem gets a label, which is typically the same as the mount point where you will access the drives. On my system I called the two filesystem /backup0 and /backup1. You create the filesystem with the appropriate variant of the mkfs command. Note that you now refer to the specific partition (/dev/sdc1) and not the disk itself (/dev/sdc).

# /sbin/mkfs.ext3 -L /backup0 /dev/sdc1
# /sbin/mkfs.ext3 -L /backup0 /dev/sdc1


Each command will produce quite a bit of output and may take some time as it creates blocks on the actual drives.

4: Create the mount points for each filesystem

A mount point is the Linux path/directory where this filesystem can be accessed under Linux. In my case the commands were:

# mkdir /backup0
# mkdir /backup1


5: Specify these mount points in the filesystem table file
Make a copy of the existing file - just in case
# cp /etc/fstab /etc/fstab.bak

Edit the current file (/etc/fstab) and add a line for each of the new filesystem.

LABEL=/backup0 /backup0 ext3 defaults 1 2
LABEL=/backup1 /backup1 ext3 defaults 1 2


The label is an alias for the actual partition. The next field is the mount point (same name as the label in this case). The next field is the type of filesystem (ext3) and the final 3 fields are just default options that you should include.

6: Reboot the system

# /sbin/shutdown -r now

You can re-mount the filesystems with 'mount -a' but a full reboot is a safe bet.

Check that the filesystems are mounted using 'df', 'cd' to the mount points and try writing to the disks. You will want to set the directory permissions to something appropriate using chmod and chown.

'Modern' ATX Motherboards have TWO Power Connectors

I just put together a new machine with a ASUS P5B Deluxe motherboard, Core 2 Duo CPU and a load of other good stuff. I built a similar system with the same motherboard a few months back and had no problems at all on the hardware end. I must have actually read the manual that time.

With this system I put everything together, double checked it and powered it on. The fans worked, the red and blue LEDs on the motherboard lit up as expected but there was no video output and no beep from the system... dead to the world.

After changing the video card, unplugging the drives, and swapping out the memory I was still getting nothing. The fact the there was not even a beep code from the motherboard had me thinking it was a bad board or a bad CPU. I needed a break so I took the dog for a walk...

Refreshed, I took another look at the system, checked the cables and... oh... OK...

Modern ATX form factor boards like this Asus P5B have TWO power connectors - the 'regular' 24 pin EATXPWR that has been around for a while, plus a 2x4 pin EATX12V connector.

If, like me, you forget about that one then the system will not boot... says so right there in the manual... in small print, on page 33...

On my board the socket for this is on the 'top left' corner by the keyboard, etc. sockets. You can use either a 4 pin plug or an 8 pin plug of the correct type. The board comes with a small plastic cover over 4 of the socket holes, so you need to remove this if you want to use the 8 pin plug.

So, a simple mistake to make... but one that took me well over an hour to figure out, dang it.

Archive of Tips