Class: Miguel::Schema

Inherits:
Object
  • Object
show all
Defined in:
lib/miguel/schema.rb

Overview

Class for defining database schema.

Defined Under Namespace

Modules: Output Classes: Column, ForeignKey, Index, Table

Constant Summary collapse

ZERO_TIME =

String denoting zero time in MySQL.

'0000-00-00 00:00:00'.freeze
DEFAULT_TIME =

String denoting default time.

'2000-01-01 00:00:00'.freeze
LOCK =

Mutex protecting access to thread sensitive variables.

Mutex.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Schema

Create new schema.



429
430
431
432
433
434
435
# File 'lib/miguel/schema.rb', line 429

def initialize( opts = {} )
  @tables = {}
  @aliases = {}
  @defaults = {}
  @callbacks = {}
  @opts = self.class.default_options.merge(opts)
end

Instance Attribute Details

#optsObject (readonly)

Schema options.



426
427
428
# File 'lib/miguel/schema.rb', line 426

def opts
  @opts
end

Class Method Details

.default_optionsObject

Get default schema options.



650
651
652
# File 'lib/miguel/schema.rb', line 650

def default_options
  sync{ @opts || {} }
end

.default_options=(opts) ⇒ Object Also known as: set_default_options

Set default schema options.



655
656
657
# File 'lib/miguel/schema.rb', line 655

def default_options=( opts )
  sync{ @opts = opts.nil? ? nil : opts.dup }
end

.define(opts = {}, &block) ⇒ Object

Define schema with provided block.



661
662
663
664
665
# File 'lib/miguel/schema.rb', line 661

def define( opts = {}, &block )
  sync do
    set_schema( new( opts ).define( &block ) )
  end
end

.load(name, opts = {}) ⇒ Object

Load schema from given file.



668
669
670
671
672
673
674
675
676
677
# File 'lib/miguel/schema.rb', line 668

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

Instance Method Details

#add_join_table(id_left, table_left, id_right, table_right, name = nil, &block) ⇒ Object Also known as: join_table

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.



471
472
473
474
475
476
477
478
479
480
# File 'lib/miguel/schema.rb', line 471

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(name, &block) ⇒ Object Also known as: table

Add table with given name, optionally defined with provided block.



453
454
455
456
457
458
459
# File 'lib/miguel/schema.rb', line 453

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_defaults(table_name, name, *args) ⇒ Object

Apply default options to given +add_table+ block statement. See +set_defaults+ for detailed explanation.



605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
# File 'lib/miguel/schema.rb', line 605

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) ⇒ Object

Clear defaults and aliases for given statement.



513
514
515
516
517
518
# File 'lib/miguel/schema.rb', line 513

def clear_defaults( name )
  @aliases.delete( name )
  @defaults.delete( name )
  @callbacks.delete( name )
  self
end

#define(&block) ⇒ Object

Define schema with the provided block.



637
638
639
640
641
642
# File 'lib/miguel/schema.rb', line 637

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) ⇒ Object

Dump table definition to given output.



629
630
631
632
633
634
# File 'lib/miguel/schema.rb', line 629

def dump( out = Dumper.new )
  for table in tables
    table.dump( out )
  end
  out
end

#get_defaults(name) ⇒ Object

Get default options for given statement.



521
522
523
# File 'lib/miguel/schema.rb', line 521

def get_defaults( name )
  @defaults[ name ] || {}
end

#named_tables(names) ⇒ Object

Get tables with given names.



448
449
450
# File 'lib/miguel/schema.rb', line 448

def named_tables( names )
  @tables.values_at( *names )
end

#set_defaults(name, *args, &block) ⇒ Object

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.



503
504
505
506
507
508
509
510
# File 'lib/miguel/schema.rb', line 503

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) ⇒ Object

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 :fulltext, :index, :type => :full_text

: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.



549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
# File 'lib/miguel/schema.rb', line 549

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 and fulltext indexes.

  set_defaults :unique, :index, :unique => true
  set_defaults :fulltext, :index, :type => :full_text do |opts,args,table|
    opts[ :name ] ||= [ table, *args, :fulltext ].join( '_' ).to_sym
  end

  # 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[ :zero_timestamps ] || ( opts[ :mysql_timestamps ] && opts[ :zero_timestamps ].nil? ) ) ? ZERO_TIME : DEFAULT_TIME
  set_defaults :Time, :timestamp, :default => default_time
  set_defaults :Time?, :timestamp, :default => nil

  self
end

#table_namesObject

Get names of all tables.



443
444
445
# File 'lib/miguel/schema.rb', line 443

def table_names
  @tables.keys
end

#tablesObject

Get all tables.



438
439
440
# File 'lib/miguel/schema.rb', line 438

def tables
  @tables.values
end