class Miguel::Schema
Class for defining database schema.
Constants
- DEFAULT_TIME
String denoting default time.
- LOCK
Mutex protecting access to thread sensitive variables.
- ZERO_TIME
String denoting zero time in MySQL.
Attributes
Schema
options.
Public Class Methods
Get default schema options.
# File lib/miguel/schema.rb, line 637 def default_options sync{ @opts || {} } end
Set default schema options.
# File lib/miguel/schema.rb, line 642 def default_options=( opts ) sync{ @opts = opts.nil? ? nil : opts.dup } end
Define schema with provided block.
# File lib/miguel/schema.rb, line 648 def define( opts = {}, &block ) sync do set_schema( new( opts ).define( &block ) ) end end
Load schema from given file.
# File lib/miguel/schema.rb, line 655 def load( name, opts = {} ) name = File.expand_path( name ) sync do get_schema do with_options( opts ) do Kernel.load( name ) end end end end
Create new schema.
# File lib/miguel/schema.rb, line 420 def initialize( opts = {} ) @tables = {} @aliases = {} @defaults = {} @callbacks = {} @opts = self.class.default_options.merge(opts) end
Private Class Methods
Execute given block and return stored schema.
# File lib/miguel/schema.rb, line 682 def get_schema @schema = self yield @schema unless @schema == self ensure @schema = nil end
Store given schema for later if requested.
# File lib/miguel/schema.rb, line 676 def set_schema( schema ) @schema = schema if @schema == self schema end
Serialize access to thread sensitive variables.
# File lib/miguel/schema.rb, line 669 def sync( &block ) LOCK.synchronize &block rescue ThreadError yield end
Execute block with given options.
# File lib/miguel/schema.rb, line 691 def with_options( opts ) saved, @opts = @opts, default_options.merge(opts) yield ensure @opts = saved end
Public Instance Methods
Helper for creating join tables conveniently. It is equivalent to the following:
add_table name do foreign_key id_left, table_left foreign_key id_right, table_right primary_key [id_left, id_right] unique [id_right, id_left] end
In case a block is provided, it is used to further extend the table defined.
# File lib/miguel/schema.rb, line 462 def add_join_table( id_left, table_left, id_right, table_right, name = nil, &block ) name ||= [ table_left, table_right ].sort.join( '_' ) add_table name do foreign_key id_left, table_left foreign_key id_right, table_right primary_key [ id_left, id_right ] unique [ id_right, id_left ] instance_eval &block if block end end
Add table with given name, optionally defined with provided block.
# File lib/miguel/schema.rb, line 444 def add_table( name, &block ) name = name.to_sym fail( ArgumentError, "table #{name} is already defined" ) if @tables[ name ] @tables[ name ] = table = Table.new( self, name ) table.define( &block ) if block table end
Apply default options to given add_table
block statement. See set_defaults
for detailed explanation.
# File lib/miguel/schema.rb, line 592 def apply_defaults( table_name, name, *args ) opts = {} opts.merge!( get_defaults( :global ) ) if name[ -1 ] == '?' opts[ :null ] = true original_name = name name = name[ 0..-2 ].to_sym end opts.merge!( get_defaults( name ) ) opts.merge!( get_defaults( original_name ) ) if original_name if callback = @callbacks[ name ] callback.call( opts, args, table_name ) end opts.merge!( args.pop ) if args.last.is_a? Hash args << opts unless opts.empty? [ ( @aliases[ name ] || name ), *args ] end
Clear defaults and aliases for given statement.
# File lib/miguel/schema.rb, line 504 def clear_defaults( name ) @aliases.delete( name ) @defaults.delete( name ) @callbacks.delete( name ) self end
Define schema with the provided block.
# File lib/miguel/schema.rb, line 624 def define( &block ) fail( ArgumentError, "missing schema block" ) unless block set_standard_defaults unless opts[ :use_defaults ] == false instance_eval &block self end
Dump table definition to given output.
# File lib/miguel/schema.rb, line 616 def dump( out = Dumper.new ) for table in tables table.dump( out ) end out end
Get default options for given statement.
# File lib/miguel/schema.rb, line 512 def get_defaults( name ) @defaults[ name ] || {} end
Get tables with given names.
# File lib/miguel/schema.rb, line 439 def named_tables( names ) @tables.values_at( *names ) end
Set default options for given statement used in add_table
blocks. It uses the following arguments:
name
-
The name of the statement, like
:primary_key
or:String
. The special name:global
may be used to set default options for any statement. alias
-
Optional real statement to use instead of
name
, like:String
instead of:Text
. args
-
Hash containing the default options for
name
. block
-
Optional block which may further modify the options.
If a block is provided, it is invoked with the following arguments:
opts
-
The trailing options passed to given statement, to be modified as necessary.
args
-
Any leading arguments passed to given statement, as readonly context.
table
-
The name of the currently defined table, as readonly context.
The final options for each statement are created in the following order: :global
options, extended with :null
set to true
in case of ? syntax, merged with options for name
(without ?), modified by the optional block
callback, and merged with the original options used with the statement.
Also note that the defaults are applied in the instant the table
block is evaluated, so it is eventually possible (though not necessarily recommended) to change them in between.
# File lib/miguel/schema.rb, line 494 def set_defaults( name, *args, &block ) clear_defaults( name ) @aliases[ name ] = args.shift if args.first.is_a? Symbol @defaults[ name ] = args.pop if args.last.is_a? Hash @callbacks[ name ] = block fail( ArgumentError, "invalid defaults for #{name}" ) unless args.empty? self end
Set standard defaults and aliases for often used types.
The current set of defaults is as follows:
:global, :null => false :Bool, :TrueClass :True, :TrueClass, :default => true :False, :TrueClass, :default => false :Signed, :integer, :unsigned => false :Unsigned, :integer, :unsigned => true :Text, :String, :text => true :Time, :timestamp, :default => '0000-00-00 00:00:00' :Time?, :timestamp, :default => nil :unique, :index, :unique => true :Key, :integer, :unsigned => false :primary_key, :type => :integer, :unsigned => false :foreign_key, :key => :id, :type => :integer, :unsigned => false
If the unsigned_keys
option is set to true, the keys are set up to use unsigned integers instead.
# File lib/miguel/schema.rb, line 539 def set_standard_defaults( opts = self.opts ) # We set NOT NULL on everything by default, but note the ? # syntax (like Text?) which declares the column as NULL. set_defaults :global, :null => false # We also like our keys unsigned, so we allow setting that, too. # Unfortunately, :unsigned currently works only with :integer, # not the default :Integer, and :integer can't be specified for compound keys, # so we have to use the callback to set the type only at correct times. # Furthermore, Postgres's autoincrementing serials only work with Integer, # so we set the type only as long as the unsigned keys are requested. unsigned_keys = !! opts[ :unsigned_keys ] set_defaults :Key, :integer, :unsigned => unsigned_keys set_defaults :primary_key, :unsigned => unsigned_keys do |opts,args,table| opts[ :type ] ||= :integer unless args.first.is_a? Array or not opts[ :unsigned ] end set_defaults :foreign_key, :key => :id, :unsigned => unsigned_keys do |opts,args,table| opts[ :type ] ||= :integer unless args.first.is_a? Array or not opts[ :unsigned ] end # Save some typing for unique indexes. set_defaults :unique, :index, :unique => true # Type shortcuts we use frequently. set_defaults :Bool, :TrueClass set_defaults :True, :TrueClass, :default => true set_defaults :False, :TrueClass, :default => false set_defaults :Signed, :integer, :unsigned => false set_defaults :Unsigned, :integer, :unsigned => ! opts[ :signed_unsigned ] set_defaults :String, :text => false set_defaults :Text, :String, :text => true # We want times to be stored as 4 byte timestamps, however # we have to be careful to turn off the MySQL autoupdate behavior. # That's why we have to set defaults explicitly. default_time = opts[ :mysql_timestamps ] ? ZERO_TIME : DEFAULT_TIME set_defaults :Time, :timestamp, :default => default_time set_defaults :Time?, :timestamp, :default => nil self end
Get names of all tables.
# File lib/miguel/schema.rb, line 434 def table_names @tables.keys end
Get all tables.
# File lib/miguel/schema.rb, line 429 def tables @tables.values end