I’ve been playing around with Shoes (shoooes.net) lately as a way to put a cross-platform graphical user interface (GUI) on some of my small purpose-built command-line ruby scripts.
I find that it is quite easy to get started with, and lends a lot of flexibility to the way your program is structured and displayed. However, the structure feels a little bit alien compared to everyday ruby, and there are some gotcha’s you need to keep in mind while developing for Shoes.
I feel I must preface this article by saying that Shoes has excellent documentation, _why (the lucky stiff) turns documentation into its own art form. The manual, “Nobody Knows Shoes” reads a lot like a comic book, full of _why’s own original artwork and clippings from old-timey photos and art, and is complimented by the documentation at help.shoooes.net
I had a bit of trouble at first getting ActiveRecord to interface with a database from a straight port from one of my console apps because I glossed over the parts of the manual that detail the tricky behavior of the garbage collector reaping predefined classes after the app’s initial load.
The fix is pretty simple. Stick all your classes in an external file (or many) and load them using ‘require’.
Anyhow, here is a barebones example of a working implementation for using ActiveRecord in Shoes:
# in foo.rb class Foo < ActiveRecord::Base end #in app.rb Shoes.setup do gem 'activerecord' require 'active_record' ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :dbfile => 'foos_db.sqlite3' ) require 'foo' end Shoes.app do @foos = Foo.find(:all) para @foos end
Now, this example requires there is an existing sqlite database with a foos table, change out the establish_connection parameters to connect to any other database. The gem ‘activerecord’ statment tells shoes to install the activerecord gem into the shoes ruby library if it isn’t already there.
If you don’t already have a database, and just want to use a db to act as a storage layer for your app, then you might want to use ActiveRecord::Schema.define to create a database and setup the tables the same way you do for Rails migrations.
Here is a more complete example of an app to keep track of notes using ActiveRecord as the backend. I like the “base class that inherits from Shoes” pattern, so I’m using that here.
# in note.rb class Note < ActiveRecord::Base end # in app.rb Shoes.setup do gem 'activerecord' # install AR if not found require 'active_record' require 'fileutils' ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :dbfile => 'shoes_app.sqlite3' ) # create the db if not found unless File.exist?("shoes_app.sqlite3") ActiveRecord::Schema.define do create_table :notes do |t| t.column :message, :string end end end end class ShoesApp < Shoes require 'note' url '/', :index def index para 'Say something...' flow do @note = edit_line button 'OK' do Note.new(:message => @note.text).save @note.text = '' @result.replace get_notes end end @result = para get_notes end def get_notes messages =  notes = Note.find(:all, :select => 'message') notes.each do |foo| messages << foo.message end out = messages.join("n") end end Shoes.app :title => 'Notes', :width => 260, :height => 350
There you are; a cross-platform desktop app that doesn’t require a full-on build environment, and can be distributed with the source exposed for later improvements.
The first time this runs, it installs Activerecord, requires it, establishes a connection, creates the table unless one already exists. Then it shows a form to add notes followed by all the existing notes in the database. Adding a new note refreshes the notes shown.
This isn’t exactly a polished app with full CRUD, but should prove a good introduction to Shoes for someone used to working with ActiveRecord.