Claasloader

My IT blog

Error Pages in Rails: You’re Still Doing It Wrong

Error pages are important. Well, so important, there are even books written about it.

For your shiny new Rails app, you most often want to customize the error pages (404 and 500). Rails’ standard pages are not appropriate for most use cases (how could they).

Usually, you want to include your page logo image, some common page headers, and at least one link back into your application. The layout of the error page should be at least similar to the rest of the application. Now there’s a problem: By default, error pages are static html files. This makes totally sense, since in case of serious server problems, your Rails app might not be able to create the error page dynamically anymore. On the other hand, you cannot reference your standard CSS resources in a static html file since you don’t know the Asset Pipeline ids in advance.

When you google this problem, people recommend pretty complicated strategies like:

Instead, I recommend a much simpler solution:

  • Add controller actions for 404 and 500
  • Use ERB or Haml to style your error pages nicely
  • Right after deployment, call your 404 and 500 actions and save them as static HTML files

It would require only a few lines in Capistrano (thanks to my colleague Ali for figuring out the details):

./lib/capistrano/tasks/error_pages.rake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace :error_pages do
  desc 'Generate static error pages and save in public folder'
  task :create do
    on roles(:web) do |host|
      public_500_html = File.join(release_path, "public/500.html")
      execute :curl, '-s', "https://www.example.com/500_page", "> #{public_500_html}"

      public_404_html = File.join(release_path, "public/404.html")
      execute :curl, '-s', "https://www.example.com/404_page", "> #{public_404_html}"
    end
  end
end

after 'deploy:published', 'error_pages:create'

Integrate a Time Attendance System

Some weeks ago we got the requirement to integrate our punch in/out door device with our main business system. When our people come to work, they would hold their ID cards in front of the machine, and the machine would create records who arrived to work and at what time. Now the task is to read out data from the door machine and display it in the main system.

Sounds easy? Well, in theory, it is.

In practice, the first problem is to choose an appropriate time attendance model. Given the requirement that we want to read out the data by our own software, the ideal model would be one which has some kind of web service (HTTP) API. We could then regularly call this API to import the door punches into our system.

Unfortunately, we were not able to find any single product supporting a HTTP API. All of the products we checked come with their own kind of time management software, typically some standalone application running on Windows, where you can create your users, setup working shifts, holidays etc. This software can connect to the device and calculate the working times.

When it comes down to APIs, about half of the products we’ve checked would be kicked out directly. They just didn’t offer any. The only way to read data out would be to used the software shipped with the device. Those which do offer an API would all ship with a SDK for C#/.NET.

Although we are a Ruby and not a .NET shop, we decided for a Fingertec Timeline 100. They have a fairly comprehensive website, offer a lot of different software to use with their devices, and claim their SDK being “well documented”. When you check their support sites and their documentation, you’ll find that quality is not that high. However, compared with the other manufacturers we examined, they looked best.

Checking Fingertec’s SDK and API documentation, it looked like using it would be days of pain. Fortunately, they offer some software which we could use instead of the SDK. The FTDP (Fingertec Data Processor) can be installed as a Windows service, reading the punch data regularly and writing it to a database (SQL Server or Oracle). Our software could then connect to that database, and we’re done.

So we setup FTDP and SQL Server as described in their guide. Sadly, the Windows service always failed to connect to the database. We’ve checked this problem with Fingertec’s support (they were really nice and responsive) and they confirmed that it’s a bug in the current FTDP version (1.7).

Finally, we came up with a hacky solution: Running FTDP as a Windows service is totally fine when using the local Access database (which is the default setup). You can then access the data by converting the Access .mdb file into CSV. We copy the FTDP.mdb file to our Linux server and use mdb-tools for conversion. mdb-tools provides the mdb-export command, so you could dump the data like

1
$ mdb-export /path/to/file.mdb Transaction -D "%Y-%m-%d %H:%M:%S"

To copy the file, we setup the FTDP data directory as a network share and mount it on the Linux server via Samba. (Update: In the meantime, we use AWS S3 to transfer the data.)

Install Stash 2 on Ubuntu 12.04

If you need an inhouse git server and you have a small team only, then Stash from Atlassian might be something for you. We use it for several months now and I can say we are pretty happy with it.

Here’s how to install it on a Ubuntu 12.04 box:

Prerequisites

Stash is running on Java, so you need to install a JRE. OpenJDK is supported.

1
~$ sudo aptitude install openjdk-7-jre-headless

Git is also needed, of course:

1
~$ sudo aptitude install git

You also need MySQL in case you want to run the database on the same server:

1
/srv$ sudo aptitude install mysql-server-5.5

Prepare User and Directories

Stash needs its own user, whose home directory is used as a data store. I therefore chosed to locate this directory under /srv/:

1
2
3
/srv$ sudo mkdir stash
/srv$ sudo /usr/sbin/useradd --create-home --home-dir /srv/stash --shell /bin/bash stash
/srv$ sudo chown stash:stash stash/

Install Files

I chosed to host installation in /opt/:

1
2
/opt$ sudo cp -a /home/claas/install/atlassian-stash-2.11.3
/opt$ sudo chown -R stash:stash atlassian-stash-2.11.3/

Set STASH_HOME

You need to tell Stash where its home directory is located. Add following line to /opt/atlassian-stash-2.11.3/bin/setenv.sh

/opt/atlassian-stash-2.11.3/bin/setenv.sh
1
set STASH_HOME=/srv/stash

IPv4

Stash now already starts, but I noticed it listens on IPv6 ports only. To enable the regular IPv4 ports, open `/opt/atlassian-stash-2.11.3/bin/setenv.sh again and add the following parameter to the appropriate Java call:

/opt/atlassian-stash-2.11.3/bin/setenv.sh
1
-Djava.net.preferIPv4Stack=true

Try Stash the First Time

Now, Stash is ready for a first try. Start it by

1
/opt/atlassian-stash-2.11.3/bin$ sudo -u stash -i /opt/atlassian-stash-2.11.3/bin/start-stash.sh

And open http://<your-server>:7990/setup to see if it’s working.

You can stop it by

1
/opt/atlassian-stash-2.11.3/bin$ sudo -u stash -i /opt/atlassian-stash-2.11.3/bin/stop-stash.sh

Automatic Startup

We want to have Stash being started automatically once the server is rebooted.

First, create a generic name for the application directory (this will upgrading to a newer version easier later on):

1
/opt$ sudo ln -s atlassian-stash-2.11.3 atlassian-stash-latest

Download the startup script provided by Atlassian and save it as /etc/init.d/stash. Adjust following lines to match our installation:

/etc/init.d/stash
1
2
3
4
5
6
...
# Required-Start:    $remote_fs $syslog $mysql
...
STASH_INSTALLDIR="/opt/atlassian-stash-latest"
...
STASH_HOME="/srv/stash"

And enable it via:

1
2
/etc/init.d$ sudo update-rc.d stash defaults
/etc/init.d$ sudo chmod +x /etc/init.d/stash

Finally, we need to set the JAVA_HOME environment variable. Add this line to /etc/environment:

/etc/environment
1
JAVA_HOME="/usr/lib/jvm/java-7-openjdk-amd64"

Install JDBC Driver

Stash doesn’t come with a JDBC driver, so you have to download this separately from the MySQL home page. Then, copy it to the stash installation directory:

1
2
/opt/atlassian-stash-latest/lib$ sudo cp /path/to/mysql-connector-java-5.1.29/mysql-connector-java-5.1.29-bin.jar .
/opt/atlassian-stash-latest/lib$ sudo chown stash:stash mysql-connector-java-5.1.29-bin.jar

Prepare Database

Finally, you need to create a database for Stash:

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> SET GLOBAL storage_engine = 'InnoDB';
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE DATABASE stash CHARACTER SET utf8 COLLATE utf8_bin;
Query OK, 1 row affected (0.02 sec)

mysql> GRANT ALL PRIVILEGES ON stash.* TO 'stash'@'localhost' IDENTIFIED BY 'secret';
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql> quit

End

These steps were enough to setup a basic Stash server for our needs. Check the manual for more installation options and details.