require 'rubygems' require 'postgres' module ServerSide module SQL COMMA_SEPARATOR = ', '.freeze module Schema COLUMN_DEF = '%s %s'.freeze UNIQUE = ' UNIQUE'.freeze NOT_NULL = ' NOT NULL'.freeze DEFAULT = ' DEFAULT %s'.freeze TYPES = Hash.new {|h, k| k} TYPES[:double] = 'double precision' def column_definition(column) c = COLUMN_DEF % [column[:name], TYPES[column[:type]]] c << UNIQUE if column[:unique] c << NOT_NULL if column[:null] == false c << DEFAULT % PGconn.quote(column[:default]) if column[:default] c end def create_table_column_list(columns) columns.map {|c| column_definition(c)}.join(COMMA_SEPARATOR) end CREATE_INDEX = 'CREATE INDEX %s ON %s (%s);'.freeze CREATE_UNIQUE_INDEX = 'CREATE UNIQUE INDEX %s ON %s (%s);'.freeze INDEX_NAME = '%s_%s_index'.freeze UNDERSCORE = '_'.freeze def index_definition(table_name, index) fields = index[:columns].join(COMMA_SEPARATOR) index_name = index[:name] || INDEX_NAME % [table_name, index[:columns].join(UNDERSCORE)] (index[:unique] ? CREATE_UNIQUE_INDEX : CREATE_INDEX) % [index_name, table_name, fields] end def create_indexes_sql(table_name, indexes) indexes.map {|i| index_definition(table_name, i)}.join end CREATE_TABLE = "CREATE TABLE %s (%s);".freeze def create_table_sql(name, columns, indexes = nil) sql = CREATE_TABLE % [name, create_table_column_list(columns)] sql << create_indexes_sql(name, indexes) if indexes && !indexes.empty? sql end DROP_TABLE = "DROP TABLE %s;".freeze def delete_table_sql(name) DROP_TABLE % name end class Generator :attr_reader :table_name, :primary_key def initialize(table_name, &block) @table_name = table_name @primary_key = {:name => :id, :type => :serial} @columns = [] @indexes = [] instance_eval(&block) end def primary_key(name, type, opts) @primary_key = {:name => name, :type => type}.merge(opts) end def column(name, type, opts) @columns << {:name => name, :type => type}.merge(opts) end def has_column?(name) @columns.each {|c| return true if c[:name] == name} false end def index(columns, opts) columns = [columns] unless columns.is_a?(Array) @indexes << {:columns => columns}.merge(opts) end def to_s if @primary_key && !has_column?(@primary_key[:name]) @columns.unshift(@primary_key) end create_table_sql(@table_name, @columns, @indexes) end end end end end columns = [ {:name => :id, :type => :serial}, {:name => :name, :type => :text, :null => false}, {:name => :path, :type => :text, :null => false}, {:name => :kind, :type => :integer}, {:name => :parent_id, :type => :integer}, {:name => :producer_id, :type => :integer}, {:name => :state_quality, :type => :smallint, :null => false, :default => 0}, {:name => :state_stamp, :type => :double}, {:name => :state_value, :type => :text}, {:name => :state_datatype, :type => :smallint} ] indexes = [ {:columns => [:path], :unique => true}, {:columns => [:parent_id]}, {:columns => [:kind]} ] include ServerSide::SQL::Schema puts create_table_sql(:nodes, columns, indexes) __END__ class Node < ServerSide::Model schema :nodes do # this also calls set_table_name with the table name primary_key :id # implied if no such declaration exists, and calls set_primary_key column :name, :text, :null => false column :path, :text, :null => false column :kind, :integer column :parent_id, :integer column :producer_id, :integer column :state_quality, :smallint, :null => false, :default => 0 column :state_stamp, :double column :state_value, :text column :state_datatype, :smallint index :path, :unique => true index :parent_id index :kind end end DB = Postgres::Database.new columns = [ {:name => :name, :type => :text, :null => false}, {:name => :path, :type => :text, :null => false}, {:name => :kind, :type => :integer}, {:name => :parent_id, :type => integer}, {:name => :producer_id, :type => :integer}, {:name => :state_quality, :type => :smallint, :null => false, :default => 0}, {:name => :state_stamp, :type => :double}, {:name => :state_value, :type => :text}, {:name => :state_datatype, :type => :smallint} ] indexes = [ {:columns => [:path], :unique => true}, {:columns => [:parent_id]}, {:columns => [:kind]} ] DB.create_table(:nodes, columns, indexes) DB.delete_table(:nodes) DB.table?(:nodes)