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

opts[R]

Schema options.

Public Class Methods

default_options() click to toggle source

Get default schema options.

# File lib/miguel/schema.rb, line 637
def default_options
  sync{ @opts || {} }
end
default_options=( opts ) click to toggle source

Set default schema options.

# File lib/miguel/schema.rb, line 642
def default_options=( opts )
  sync{ @opts = opts.nil? ? nil : opts.dup }
end
Also aliased as: set_default_options
define( opts = {}, &block ) click to toggle source

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( name, opts = {} ) click to toggle source

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
new( opts = {} ) click to toggle source

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
set_default_options( opts )
Alias for: default_options=

Private Class Methods

get_schema() { || ... } click to toggle source

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
set_schema( schema ) click to toggle source

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
sync( ) { || ... } click to toggle source

Serialize access to thread sensitive variables.

# File lib/miguel/schema.rb, line 669
def sync( &block )
  LOCK.synchronize &block
rescue ThreadError
  yield
end
with_options( opts ) { || ... } click to toggle source

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

add_join_table( id_left, table_left, id_right, table_right, name = nil, &block ) click to toggle source

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
Also aliased as: join_table
add_table( name, &block ) click to toggle source

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
Also aliased as: table
apply_defaults( table_name, name, *args ) click to toggle source

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( name ) click to toggle source

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( &block ) click to toggle source

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( out = Dumper.new ) click to toggle source

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_defaults( name ) click to toggle source

Get default options for given statement.

# File lib/miguel/schema.rb, line 512
def get_defaults( name )
  @defaults[ name ] || {}
end
join_table( id_left, table_left, id_right, table_right, name = nil, &block )
Alias for: add_join_table
named_tables( names ) click to toggle source

Get tables with given names.

# File lib/miguel/schema.rb, line 439
def named_tables( names )
  @tables.values_at( *names )
end
set_defaults( name, *args, &block ) click to toggle source

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( opts = self.opts ) click to toggle source

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
table( name, &block )
Alias for: add_table
table_names() click to toggle source

Get names of all tables.

# File lib/miguel/schema.rb, line 434
def table_names
  @tables.keys
end
tables() click to toggle source

Get all tables.

# File lib/miguel/schema.rb, line 429
def tables
  @tables.values
end