[Note that this article is an experiment in hacking a Rails app in to static HTML off of a single root directory. This was done with routes.rb and some parsing of the URL within Rails. This is interesting in its own right, so we are leaving it up; however, a much better way is to simply go with the Tao of Rails. Specifically, for the artpage view below, simply use a rewrite on the URL: ^/art([0-9]+)\.html$ /sysadmin/artpage/$1 .]
We have a site that uses a database to store articles, headers, footers, and other information. We decided to port the site to Ruby on Rails. For more on installing Ruby and Rails on Mac OS X, see our recent article. The first step was to export the old database:
srv-8:mcjr usr4$ sqlite3 ~/sysadmintoolsold/mcj.rsd sqlite> .output db.txt sqlite> .dump sqlite> .quit |
We ended up doing this repeatedly to ensure that there were no name conflicts, so made a script we could run every time we erased mcjr (our app):
srv-8:rubyarts usr4$ cat m.sh rails --database=sqlite3 mcjr cd ~/mcjr rake db:create RAILS_ENV='development' cp ~/rubyarts/db.txt ~/mcjr/ srv-8:rubyarts usr4$ |
We need to create a controller:
srv-8:mcjr usr4$ ruby script/generate controller Sysadmin exists app/controllers/ exists app/helpers/ create app/views/sysadmin exists test/functional/ create test/unit/helpers/ create app/controllers/sysadmin_controller.rb create test/functional/sysadmin_controller_test.rb create app/helpers/sysadmin_helper.rb create test/unit/helpers/sysadmin_helper_test.rb srv-8:mcjr usr4$ |
This is what our database configuration looks like:
srv-8:app usr4$ cat config/database.yml # SQLite version 3.x # gem install sqlite3-ruby (not necessary on OS X Leopard) development: adapter: sqlite3 database: db/development.sqlite3 pool: 5 timeout: 5000 test: adapter: sqlite3 database: db/test.sqlite3 pool: 5 timeout: 5000 production: adapter: sqlite3 database: db/production.sqlite3 pool: 5 timeout: 5000 srv-8:app usr4$ |
The articles are on multiple lines, which makes it kind of nasty to import and export. The easiest, at least that we could come up with, was to hack on the dump with Perl:
srv-8:rubyarts usr4$ cat mig.sh perl -pi -e "s|cats|cats|g" ~/mcjr/db.txt perl -pi -e "s|tims|tims|g" ~/mcjr/db.txt perl -pi -e "s|\"tims\"|\"tims\"|g" ~/mcjr/db.txt perl -pi -e "s|\(cat text|\(cat text|g" ~/mcjr/db.txt perl -pi -e "s|\"journal\"|\"arts\"|g" ~/mcjr/db.txt perl -pi -e "s|journal\(|arts\(|g" ~/mcjr/db.txt perl -pi -e "s|types\(|classifications\(|g" ~/mcjr/db.txt perl -pi -e "s|VALUES\(|VALUES\(NULL,|g" ~/mcjr/db.txt perl -pi -e "s|people\(|peoples\(|g" ~/mcjr/db.txt perl -pi -e "s|\"people\"|\"peoples\"|g" ~/mcjr/db.txt perl -pi -e "s|times\(|tmes\(|g" ~/mcjr/db.txt perl -pi -e "s|atme time,|atme time,|g" ~/mcjr/db.txt perl -pi -e "s|time Text\)|tme Text\)|g" ~/mcjr/db.txt perl -pi -e "s|classification Text,|classification Text,|g" ~/mcjr/db.txt perl -pi -e "s|type Text\)|classification Text\)|ig" ~/mcjr/db.txt perl -pi -e "s|\"types\" VALUES|\"classifications\" VALUES|g" ~/mcjr/db.txt perl -pi -e 's|CREATE TABLE (.+)\((\w+) |CREATE TABLE $1\(id INTEGER PRIMARY KEY,$2 |g' ~/mcjr/db.txt perl -pi -e "s|images\(id|imgs\(id|g" ~/mcjr/db.txt perl -pi -e "s|\"images\" VALUES|\"imgs\" VALUES|g" ~/mcjr/db.txt perl -pi -e "s|urls\(id|uresources\(id|g" ~/mcjr/db.txt perl -pi -e "s|\"urls\" VALUES|\"uresources\" VALUES|g" ~/mcjr/db.txt srv-8:rubyarts usr4$ |
This makes the old schema of MCJ somewhat happy with Rails. Here is the schema we ended up with:
srv-8:mcjr usr4$ cat db/schema.rb ActiveRecord::Schema.define(:version => 0) do create_table "arts", :force => true do |t| t.text "title" t.text "classification" t.date "date" t.time "atme" t.text "entry" t.integer "artnum" t.text "realm" end create_table "cats", :force => true do |t| t.text "cat" t.text "u1" t.text "u2" t.text "u3" t.text "u4" t.text "u5" t.text "u6" t.integer "artnum" end create_table "classifications", :force => true do |t| t.text "name" t.text "longtitle" t.text "shortitle" t.text "pagetitle" t.text "realm" t.text "client" t.text "matter" t.text "description" t.text "project" end create_table "imgs", :force => true do |t| t.text "realm" t.integer "artnum" t.text "tag" t.binary "file" t.text "alt" t.text "label" t.text "classification" end create_table "peoples", :force => true do |t| t.text "realm" t.integer "artnum" t.text "person" end create_table "places", :force => true do |t| t.text "realm" t.integer "artnum" t.text "place" end create_table "realms", :force => true do |t| t.text "name" t.text "head" t.text "mid" t.text "foot" t.text "longtitle" t.text "shortitle" t.text "pagetitle" t.text "rectcode" t.text "prop" t.text "style" end create_table "settings", :force => true do |t| t.text "parameter" t.text "value" end create_table "things", :force => true do |t| t.text "realm" t.integer "artnum" t.text "thing" end create_table "tims", :force => true do |t| t.float "hours", :limit => 2 t.text "u1" t.text "u2" t.text "u3" t.text "u4" t.text "u5" t.text "u6" t.integer "artnum" end create_table "tmes", :force => true do |t| t.text "realm" t.integer "artnum" t.text "tme" end create_table "uresources", :force => true do |t| t.text "realm" t.integer "artnum" t.text "tag" t.binary "file" t.text "desc" t.text "label" t.text "classification" end end |
Generate the models (table objects) with:
srv-8:rubyarts usr4$ cat mod.sh ruby script/generate model uresource ruby script/generate model art ruby script/generate model setting ruby script/generate model people ruby script/generate model place ruby script/generate model build ruby script/generate model cat ruby script/generate model thing ruby script/generate model tme ruby script/generate model tim ruby script/generate model img ruby script/generate model realm ruby script/generate model classification srv-8:rubyarts usr4$ |
If there is a name conflict, an error like this will pop up:
The single-table inheritance mechanism failed to locate the subclass: 'early'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite Journ.inheritance_column to use another column for that information. |
To start the server with a different port number:
sudo ruby script/server -p 80 |
The first problem we had was that our routes were a bit difficult because all articles are in the root (just like on NetAdminTools). Here is how we set up config/routes.rb to distinguish these two:
map.connect ':id.:format', :requirements => {:id => /part\d+/}, :controller=>'Sysadmin', :action=>'printpage' map.connect ':id.:format', :requirements => {:id => /art\d+/}, :controller=>'Sysadmin', :action=>'artpage' |
Note how there are no slashes in the first map.connect parameter. The :requirements distinguish between print-nice articles and regular articles using a regular expression. Here is what the controller, Sysadmin looks like so far:
class SysadminController < ApplicationController def printpage @realmrow = Realm.find(:all, :conditions=> ["name='sys'"]) artNum = params[:id].gsub("part","") @artrow = Art.find(:all, :conditions=> ["artnum='" + artNum + "'"] ) end def artpage @realmrow = Realm.find(:all, :conditions=> ["name='sys'"]) artNum = params[:id].gsub("art","") @artrow = Art.find(:all, :conditions=> ["artnum='" + artNum + "'"] ) end end |
Here is a bit of the code that pulls out the entry from the DB in views\Sysadmin\artpage.html.erb:
<% for j in @artrow %> <%= j.entry %> <% end %> |
Pretty much we can put the whole page in here and pick off the dynamic or DB derived code as we migrate from a static (but DB generated) site to a somewhat dynamic site with rails. Note that if you add an h after the =, anything returned will be converted to proper HTML from text. We don’t want that in this case, but it is cool. Note that this was our first attempt at coding with Rails. Code these days is looking a lot better. This is just to illustrate the syntax highlighting:MacVim rocks:
There are tabs, even, so it means we can live without TextMate! We still have quite a bit to pull out of the database, but we are getting there. Very colorful along the way. Here is the output of the Mongrel server:
Processing SysadminController#artpage to html (for 127.0.0.1 at 2009-11-22 18:02:07) [GET] Parameters: {"id"=>"art1"} Realms Load (0.7ms) SELECT * FROM "realms" WHERE (name='sys') Journal Load (2.2ms) SELECT * FROM "arts" WHERE (artnum='1') Rendering sysadmin/artpage Completed in 13ms (View: 5, DB: 3) | 200 OK [http://www.sysadmintools.com/art1.html] |