I’ve got some ActiveRecord in my Shoes 10

Posted by unixmonkey on November 18, 2008

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

Here’s a screenshot:
notes, the Shoes app

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.

Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. mileszs Wed, 19 Nov 2008 13:01:40 EDT

    Nice guide, man. Thanks!

    PS – I submitted it to RubyFlow. I hope you don’t mind.

  2. Nick Quaranto Thu, 20 Nov 2008 17:06:10 EDT

    Definitely impressed. Glad to see some Shoes action going on, keep it up! :)

  3. Fernando Sun, 21 Dec 2008 06:11:10 EDT

    I have tried to run this an I get a “no driver for sqlite3 found” error.
    I googled a bit and ended back here, so seem you are THE authority in the matter.
    Can you give me some clue?
    Is this common, or is it an issue of my installation?
    Thank you in advance

  4. Unixmonkey Sun, 21 Dec 2008 22:58:27 EDT

    @Fernando
    Likely you don’t already have the sqlite gem installed. In the Shoes.setup block add a line that says “gem ‘sqlite3-ruby’”. That should do it. I overlooked that dependency because I already had Rails and its dependencies installed.

  5. Hamza Khan-Cheema Tue, 03 Feb 2009 01:47:24 EDT

    Thanks for this post it has been really helpful.

    Just a note though, it took me some time to get this working on Mac OS X (Tiger) with the latest version of Shoes. It was complaining about not being able to find active_record. Even though I had it installed.

    It turns out that it did not like requiring active_record in the setup block. So I just changed the setup block so it had

    gem ‘activerecord’

    I then put both require statements beneath the setup block. After that I moved the database setup stuff into the ShoesApp class at the top and all was working.

    Hamza.

  6. Alfredo Mon, 06 Apr 2009 14:55:19 EDT

    @Hamza Could you post a gist? Trying to get it running on Leopard, not sure where you put the establish_connection part

  7. Alfredo Mon, 06 Apr 2009 15:04:52 EDT

    update: I got it running, but it gets stuck while trying to install the gem and eventually fails saying it could not find the gem, here’s the code: http://gist.github.com/90905

  8. madprog Thu, 03 Sep 2009 09:52:21 EDT

    Hello,

    First thank you for your post, I now know how to detect when a sqlite3 database was already created.
    But would you know how to detect if a mysql database was already initialized by a ActiveRecord::Schema.define ?

    I’m totally new to ruby and rails (I have just begun last night ^^), so excuse me if it is trivial…

  9. Unixmonkey Thu, 03 Sep 2009 10:19:46 EDT

    @madprog

    This should do the trick.

    unless ActiveRecord::Base.connection.tables.include?('table_name')
      ActiveRecord::Schema.define do
        # define table
      end
    end
  10. madprog Thu, 03 Sep 2009 11:17:31 EDT

    Very nice, thank you !

Comments