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)
end
def 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
end
def 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.