Using QtRuby with Rails ActiveRecord based Qt::AbstractTableModels
I do Ruby Rails development as part of my day job, and one of the nice parts of Rails is the ActiveRecord Object-Relational Mapping framework. Today I've been playing with QtRuby using a Qt::AbstractTableModel based on ActiveRecord, and it's really simple to implement and works really well.
To use ActiveRecord without Rails you create a database.yml file with the database details like this for instance:
development: adapter: postgresql database: boat_development username: rdale password: XXXXXI googled a bit for how to connect to the database and found this code. You read in the yaml and connect to the database (I needed to pass a '-I/usr/share/rails/activerecord/lib' to ruby to find the active record lib):
def setupDatabase @dbs = YAML::load(ERB.new(IO.read("database.yml")).result)# to slurp records into production db, change this line to production. curr_db = @dbs["development"] # these parameters are mysql-specific, figure out how to improve ActiveRecord::Base.establish_connection(:adapter => curr_db["adapter"], :database => curr_db["database"], :host => curr_db["host"], :username => curr_db["username"], :password => curr_db["password"])
end
I have a simple table called 'boats' for this example, and I set it up with a Rails Migration:
class BoatsTable < ActiveRecord::Migration def self.up create_table :boats do |t| t.column :name, :string, :limit => 20 t.column :capacity, :integer t.column :state, :integer # 0: funciona, 1: no funciona end garajonay = Boat.create(:name=> "Garajonay", :capacity => 268, :state => 0) orone = Boat.create(:name=> "Orone", :capacity => 268, :state => 0) enddef self.down drop_table :boats end end
So you need to run a 'rake migrate' command to create the table from the migration. Then you can access it with a simple model like this:
class Boat < ActiveRecord::Base endapp = Qt::Application.new(ARGV) setupDatabase() boats = Boat.find(:all) model = BoatTableModel.new(boats) table = Qt::TableView.new table.model = model table.show app.exec
Defining a custom Qt::AbstractTableModel is very simple. I based this on some code from Hans Fugal who has written a QtRuby based app to maintain a recipe database.
class BoatTableModel < Qt::AbstractTableModel def initialize(boats) super() @boats = boats enddef rowCount(parent) @boats.size end def columnCount(parent) 3 end def data(index, role=Qt::DisplayRole) invalid = Qt::Variant.new return invalid unless role == Qt::DisplayRole or role == Qt::EditRole boat = @boats[index.row] return invalid if boat.nil? v = case index.column when 0 boat.name when 1 boat.capacity when 2 boat.state else raise "invalid column #{index.column}" end || "" return Qt::Variant.new(v) end def headerData(section, orientation, role=Qt::DisplayRole) invalid = Qt::Variant.new return invalid unless role == Qt::DisplayRole v = case orientation when Qt::Horizontal ["Name","Capacity","State"][section] else "" end return Qt::Variant.new(v) end def flags(index) return Qt::ItemIsEditable | super(index) end def setData(index, variant, role=Qt::EditRole) if index.valid? and role == Qt::EditRole s = variant.toString boat = @boats[index.row] case index.column when 0 boat.name = s when 1 boat.capacity = s.to_i when 2 boat.state = s.to_i else raise "invalid column #{index.column}" end boat.save emit dataChanged(index, index) return true else return false end end
end
And that's all there is to it. You can edit values in the table and the database is updated. And it should be very easy to create general purpose tools for doing CRUD stuff that wouldn't need to know anything about the database table, as they can get the column names and numbers of columns from ActiveRecord.