Introduction to deploying Rails Applications to the Desktop
Some things you’ll learn in this series of articles:
- How to distribute a Rails App to an Ubuntu Desktop
- How to Compile a custom Ruby installation to avoid conflicting with an existing Ruby install
- Get automatic updates to all users for free by virtue of being a Debian Package
- How to organize a build environment to make and distribute all Debian packages with a single command.
- How to build your own custom dumb browser in QT C++ (QT Creator now includes the ability to generate an HTML5 App, it didn’t when I started building Salor…)
This is not a step by step tutorial, it’s a broad discussion, though I plan to make an example video with a hello world style app at a later date.
Deploying Rails applications to the desktop, specifically unbuntu, though I plan to figure out how to do it with windows very soon, turns out to be much easier than I ever thought.
While deploying Rails apps can kind of suck in comparison to other types of web-applications, it’s hardly any more difficult than any other kind of executable deployment.
The reasons for using Rails for a desktop application are pretty obvious, ease of development, ease of maintenance, and Ruby can pretty much do anything you need it to do. For instance, in our application we use ruby to connect to a receipt printer and print out receipts, reports, labels and barcodes and more.
Before you deploy a Rails app, you might give a moments thought to whether or not you’ll be satisfied with have the application run in Firefox or not. We chose not, and bundled the application with it’s own webkit browser (Via QT Webkit), to make the app appear as seamless as possible.
One of the best selling points of Webkit is all the groovy .css features it has, like styling scrollbars, so our rails app is 100% uniform in appearance, including styled scrollbars. We also take advantage of QT’s fullscreen capabilities, so our application takes over the whole screen and looks like something completely custom.
The particular application is a Point of Sale software called Salor.
Here is an example of the main checkout screen, this is in fullscreen mode.
And here is one of the modal dialogs in the app that allows you to void an order.
Some things to notice: uniform style, the entire interface looks 100% custom, they don’t even know it’s running linux, you don’t see the Ubuntu app bars or any of that nonesense, so everything has this uniform native look, when in reality it’s all just running inside of a fullscreen Webkit widget.
Because of this small thing, and the fact that ubuntu doesn’t have any driver support, the app has to run as gksu, which was a bit of a pain to finagle at first, but now when the POS machine (A Zotac MAG with Touchscreen) starts up, it automatically runs the salor app which takes over the screen immediately so it’s a pretty seamless and consistent UX.
While it’s pretty simple to get this all running once you get the basics down, getting those basics were a complete monster, and I have my friend Michael to thank for finding out and fixing some of the more obscure things, like how to configure the rails application on install, getting the mysql username and password from the person installing. This actually wouldn’t be an issue if we were using sqlite3, which is still a possibilty, the only issue being Salor only doubles as a local installation, it is also a full blown website that is a Software as Service application. There is really very little difference between the two, a simple config variable switches Salor into Standalone mode.
The Debian Package Structure
The Deb docs wouldn’t tell you this, but it’s dead simple to make them, they are really just glorified mini file systems tarred up. The additional things are this idea of a control file, and then the scripts and templates for user input, which is where the headache comes in. What I ended up doing was creating a package called salor_manager, which essentialy is a big script that just receives cmdline arguments from dpkg and does fancy stuff, instead of trying to do them in BASH, which is a freakin nightmare.
Here is the template directory for Salor, the rails app get’s copied from a gitrepos into /opt/salor_pos at build time.
salor-src/ ├── DEBIAN │ ├── conffiles │ ├── config │ ├── control │ ├── postinst │ ├── postrm │ ├── preinst │ └── templates └── opt └── salor_pos └── salor
The postinst file looks like this:
#!/bin/sh . /usr/share/debconf/confmodule export RAILS_ENV=production echo "Reading configuration from Debconf database ..." db_get salor/enter_username username=$RET db_get salor/enter_password password=$RET db_get salor/enter_database database=$RET echo "Setting username and password for salor database ..." php /usr/bin/salor_manager salor-src install postinst $username $password $database db_go exit 0
The magic of getting some input from the user via db_get I’ll talk about a little later.
Of course, you can look at all of this in the salor_deb gitrepos.
For the little password getting db_get stuff, you need to have a templates file, like this, you can just more or less copy mine:
Template: salor/enter_database Type: string Default: salor_production Description: Please enter the name of the salor database. If you've already created a database for salor, enter the name here, otherwise leave the default. Description-de.UTF-8: Bitte geben Sie den Namen der salor Datenbank ein. Wenn Sie schon eine Datenbank für salor angelegt haben, geben Sie den Namen hier ein, ansonsten lassen Sie die Standardeinstellung. Template: salor/enter_username Type: string Default: root Description: Please enter the username for the database If you've created a dedicated user for the salor database, please enter the name here, otherwise leave the default value. Description-de.UTF-8: Bitte geben Sie den Benutzernamen für die salor Datenbank ein. Wenn Sie schon einen Datenbank-Benutzer für salor angelegt haben, geben Sie den Namen hier ein, ansonsten lassen Sie die Standardeinstellung. Template: salor/enter_password Type: password Description: Please enter the password for the database user that you've set in the previous step. Description-de.UTF-8: Bitte geben Sie das Passwort für den Datenbank-Benutzer ein, den Sie im letzten Schritt angegeben haben. Template: salor/done Type: note Description: Installation of salor is complete. If everything went alright, you should be able to browse to https://salor and see the login screen. Use Salor Pos from Applications/Office Use Chromium Browser to preview how salor displays on mobile devices. Login: su Password: su Description-de.UTF-8: Die Installation von salor ist vollständig. Wenn alles gut gegangen ist, sollten Sie auf https://salor browsen können und den Login-Bildschirm sehen. Verwenden Sie Mozilla Firefox für das Kassen-Hauptgerät. Verwenden Sie den Chromium Browser um zu sehen, wie salor auf mobilen Geräten angezeigt wird. Login: su Password: su
This also shows you how to make it multi-lingual, I can’t take credit for the above, that all goes to my friend and dev partner on the project Mike. Everything I know about deb packaging basically comes from him, so many thanks for all of his help.
Again, you can get most of this stuff if you checkout the gitrepos.
Deploying rails applications isn’t as easy as PHP, but it’s still not hard. We chose to go with Apache/Passenger configuration, but I didn’t want to conflict with existing ruby installs, and I didn’t want to deal with the headache of gem vs. apt-get, so I just bundle everything up in a salor-ruby package. In the repo you’ll see a compile.sh script that is used to compile it, you only really need to do this once, and from then on, you just use that ruby’s gem and bundle to install your depenencies, like passenger, which you’ll also have to compile the apache passenger module.
Accessing USB Printers with UDEV
In the gitrepos, you’ll see a udev rule file in the salor package, salor_deb / salor / etc / udev / rules.d , this is for using with my other project, also on github which is called printr,
printr is currently being used in several production deployments of this software to print to receipt printers, and to print labels and such. We are using the Metapace T-1 USB Thermal printer, I haven’t tested it with anything else, but I’d like too.
Salor Gui (C++ Frontend that is a dumb browser)
What with QT Creators ability to generate an HTML5 app pretty much the same why I am doing it, I won’t go into much detail. The only extra thing that is going on with salor_gui is that it is compiled against libdlo and libusb to write to mini usb screens, we use those instead of pole displays, which gives customers a full color, styled output of the current order total and item, as well as having room for advertisements, so you can upsell your products and get exposure for upcoming sales etc.
I will continue this part soon and talk about salor_customer_screen.cpp and replacing the QWebPage. Really what is going on is that there are two webkit widgets, one hidden that is accessing a special url to view a customer screen served by the rails app, so all of the visual appeal is 100% HTML/CSS, it just paints the webview to a file and passes the filename to a c module that prints the .bmp file directly to the USB device, mainly because there aren’t any linux drivers for the DisplayLink mini-usb screen.