Clevic
RDoc | Rubygems Page | Rubyforge Project | gitweb | git clone git://semiosix.com/clevic
Clevic is a relational table GUI toolkit. It works with Qt or Java/Swing and Sequel.
An interesting side-effect of this will probably be that building form-style GUIs will be possible. Eventually. When the code has been written.
Lots more in the RDoc pages.
Screenshot of a fully defined UI with the foreign-key dropdown in place. Tabs contain the two tables.
See ModelBuilder for how to define views.

Code for minimal UI definition. Clevic will create a fairly sensible UI from the DB metadata.
require 'clevic.rb'
# see sql/accounts.sql for schema
# db connection
Sequel.connect( "postgres://#{host}/accounts_test?user=#{$options[:username] || 'accounts'}&password=#{$options[:password]}" )
# minimal definition to get combo boxes to show up
class Entry < Sequel::Model
include Clevic::Record
many_to_one :debit, :class_name => 'Account', :foreign_key => 'debit_id'
many_to_one :credit, :class_name => 'Account', :foreign_key => 'credit_id'
end
# minimal definition to get sensible values in combo boxes
class Account < Sequel::Model
include Clevic::Record
def to_s; name; end
end
Code for a full definition. The Entry model has some code to
update the credit and debit fields when the new item description
is found in the table.
require 'clevic.rb'
host = ENV['PGHOST'] || 'localhost'
$options ||= {}
constring =
if RUBY_PLATFORM == 'java'
"jdbc:postgresql"
else
"postgres"
end + "://#{host}/accounts_test?user=#{$options[:username] || 'accounts'}&password=#{$options[:password] || 'general'}"
db = Sequel.connect constring
db.test_connection
class Entry < Sequel::Model
many_to_one :debit, :class_name => 'Account', :key => :debit_id
many_to_one :credit, :class_name => 'Account', :key => :credit_id
include Clevic::Record
define_ui do
plain :date, :sample => '88-WWW-99'
distinct :supplier do |f|
#~ f.conditions "now() - date <= '1 year'"
f.dataset.filter( "now() - date <= '1 year'" )
f.sample( 'm' * 26 )
f.notify_data_changed = lambda do |entity_view, table_view, model_index|
if model_index.entity.credit.nil? && model_index.entity.debit.nil?
entity_view.update_from_description( model_index )
# move edit cursor to amount field
table_view.selection_model.clear
table_view.override_next_index( model_index.choppy( :column => :amount ) )
end
end
end
relational :debit, :display => 'name', :conditions => 'active = true', :order => 'lower(name)', :sample => 'Leilani Member Loan'
relational :credit, :display => 'name', :conditions => 'active = true', :order => 'lower(name)', :sample => 'Leilani Member Loan'
plain :amount, :sample => 999999.99
distinct :description
distinct :category
plain :cheque_number
plain :active, :sample => 'WW'
plain :vat, :label => 'VAT', :sample => 'WW', :tooltip => 'Does this include VAT?'
dataset.order( :date, :id )
end
# Copy the values for the credit and debit fields
# from the previous similar entry with a similar description
def self.update_from_description( current_index )
return if current_index.attribute_value.nil?
# most recent entry, ordered in reverse
similar = self. \
filter( current_index.attribute.to_sym => current_index.attribute_value ). \
order( :date.desc ). \
first
if similar != nil
# set the values
current_index.entity.debit = similar.debit
current_index.entity.credit = similar.credit
current_index.entity.category = similar.category
# emit signal to that whole row has changed
current_index.model.data_changed do |change|
change.top_left = current_index.choppy( :column => 0 )
change.bottom_right = current_index.choppy( :column => current_index.model.column_count - 1 )
end
end
end
end
class Account < Sequel::Model
one_to_many :debits, :class_name => 'Entry', :key => :debit_id, :order => :date
one_to_many :credits, :class_name => 'Entry', :key => :credit_id, :order => :date
include Clevic::Record
# define how fields are displayed
define_ui do
plain :name
restricted :vat, :label => 'VAT', :set => %w{ yes no all }
restricted :account_type, :set => %w{Account Asset Assets Expenses Income Liability Opening Balance Personal Tax VAT}
plain :pastel_number, :alignment => :right, :label => 'Pastel'
plain :fringe, :format => "%.1f"
plain :active
dataset.order( :name, :account_type )
end
end