Sequel::Plugins::InlineSchema::
ClassMethods
module
Sequel
plugin API – add these methods to model classes which load the plugin.
Extension callback – add some class instance variables to keep track of schema info.
def self::extended( model_class )
super
model_class.require_valid_table = false
model_class.singleton_class.attr_accessor( :view_dataset )
model_class.singleton_class.attr_accessor( :view_options )
end
Table-creation hook; called on a model class after its table is created.
def after_create_table
return true
end
View-creation hook; called after the backing view is created.
def after_create_view
return true
end
Table-drop hook; called after the table is dropped.
def after_drop_table
return true
end
View-drop hook; called after the backing view is dropped.
def after_drop_view
return true
end
Table-creation hook; called on a model class before its table is created.
def before_create_table
return true
end
View-creation hook; called before the backing view is created.
def before_create_view
return true
end
Table-drop hook; called before the table is dropped.
def before_drop_table
return true
end
View-drop hook; called before the backing view is dropped.
def before_drop_view
return true
end
create_table( *args, &block )
Creates table, using the column information from set_schema.
def create_table( *args, &block )
self.set_schema( *args, &block ) if block
self.before_create_table
self.db.create_table( self.table_name, generator: self.schema )
@db_schema = get_db_schema( true )
self.after_create_table
return self.columns
end
create_table!( *args, &block )
Drops the table if it exists and then runs create_table. Should probably not be used except in testing.
def create_table!( *args, &block )
self.drop_table?
return self.create_table( *args, &block )
end
create_table?( *args, &block )
Creates the table unless the table already exists
def create_table?( *args, &block )
self.create_table( *args, &block ) unless self.table_exists?
end
create_view( options={} )
Create the view for this model class.
def create_view( options={} )
dataset = self.view_dataset or raise "No view declared for this model."
options = self.view_options.merge( options )
self.before_create_view
self.db.log_info "Creating view %s(%p): %s" % [ self.table_name, options, dataset.sql ]
self.db.create_view( self.table_name, dataset, options )
@db_schema = get_db_schema( true )
self.after_create_view
end
create_view!( options={} )
Drops the view if it exists and then runs create_view
.
def create_view!( options={} )
self.drop_view?
return self.create_view
end
create_view?( options={} )
Creates the view unless it already exists.
def create_view?( options={} )
self.create_view( options ) unless self.view_exists?
end
Drops table. If the table doesn’t exist, this will probably raise an error.
def drop_table( opts={} )
self.before_drop_table
self.db.drop_table( self.table_name, opts )
self.after_drop_table
end
Drops table if it already exists, do nothing.
def drop_table?( opts={} )
self.drop_table( opts ) if self.table_exists?
end
Drop the view backing this model.
def drop_view( options={} )
self.before_drop_view
self.db.drop_view( self.table_name, self.view_options.merge(options) )
self.after_drop_view
end
Drop the view if it already exists, otherwise do nothing.
def drop_view?( options={} )
self.drop_view( options ) if self.view_exists?
end
Returns true
if the receiver is defined via a view rather than a table.
def is_view_class?
return self.respond_to?( :view_dataset ) && self.view_dataset ? true : false
end
Refresh the view for this model class. This can only be called on materialized views.
def refresh_view
self.db.refresh_view( self.table_name )
end
Returns the table schema created with set_schema.
def schema
if !@schema && @schema_block
self.set_dataset( self.db[@schema_name] ) if @schema_name
@schema = self.db.create_table_generator( &@schema_block )
self.set_primary_key( @schema.primary_key_name ) if @schema.primary_key_name
end
return @schema || ( superclass.schema unless superclass == Sequel::Model )
end
set_schema( name=nil, require_table: false, &block )
Defines a table schema (see Schema::CreateTableGenerator for more information).
This will also set the dataset if you provide a name
, as well as setting the primary key if you define one in the passed block.
Since this plugin allows you to declare the schema inline with the model class that acts as its interface, the table will not always exist when the class loads, so calling set_schema
will call require_valid_table to false
for you. You can disable this by passing require_table: true
.
def set_schema( name=nil, require_table: false, &block )
self.require_valid_table = require_table
@schema = nil
@schema_name = name
@schema_block = block
end
set_view_dataset( ds=nil, **options ) { |ds| ... }
Set the dataset to use for the model to ds
. If a block
is provided, it will be called with the specified ds
, and should return the modified dataset to use. Any options
that are given will be passed to Sequel::Database#create_or_replace_view
def set_view_dataset( ds=nil, **options )
ds = yield( ds ) if block_given?
self.view_dataset = ds
self.view_options = options
end
Returns true if table exists, false otherwise.
def table_exists?
return self.db.table_exists?( self.table_name )
end
Return an Array of model classes whose tables don’t yet exist, in the order they need to be created to satisfy foreign key constraints.
def uninstalled_tables
self.db.log_info " searching for unbacked model classes..."
self.tsort.find_all do |modelclass|
next unless modelclass.name && modelclass.name != '' && !modelclass.is_view_class?
!modelclass.table_exists?
end.uniq( &:table_name )
end
Return an Array of model classes whose views don’t yet exist, in the order they need to be created.
def uninstalled_views
return self.tsort.find_all( &:is_view_class? ).reject( &:table_exists? )
end
Returns true if the view associated with this model exists, false otherwise. :FIXME: This is PostgreSQL-specific, but there doesn’t appear to be any cross-driver way to check for a view.
def view_exists?
class_table = Sequel[:pg_catalog][:pg_class].as( :c )
ns_table = Sequel[:pg_catalog][:pg_namespace].as( :n )
is_visible = Sequel[:pg_catalog][:pg_table_is_visible]
_, table, _ = Sequel.split_symbol( self.table_name )
ds = db[ class_table ].
join( ns_table, oid: :relnamespace )
ds = ds.where( Sequel[:c][:relkind] => ['v', 'm'] ).
exclude( Sequel[:n][:nspname] => /^pg_toast/ ).
where( Sequel[:c][:relname] => table.to_s ).
where( Sequel.function(is_visible, Sequel[:c][:oid]) )
return ds.count == 1
end
Protected Instance Methods
Cancel the currently-running before_* hook. If a msg
is given, use it when constructing the HookFailed exception.
def cancel_action( msg=nil )
self.raise_hook_failure( msg )
end
raise_hook_failure( type=nil )
Raise an appropriate Sequel::HookFailure exception for the specified type
.
def raise_hook_failure( type=nil )
msg = case type
when String
type
when Symbol
"the #{type} hook failed"
else
"a hook failed"
end
raise Sequel::HookFailed.new( msg, self )
end
tsort_each_child( model_class ) { |model_class| ... }
TSort API – yield each of the given model_class
‘s dependent model classes.
def tsort_each_child( model_class )
model_class.ancestors[1..-1].
select {|cl| cl < self }.
select( &:name ).
each do |parentclass|
yield( parentclass )
end
model_class.association_reflections.each do |name, config|
next if config[:polymorphic]
associated_class = config.associated_class
yield( associated_class ) if config[:type] == :many_to_one
end
end
tsort_each_node( &block )
TSort API – yield each model class.
def tsort_each_node( &block )
self.descendents.select( &:name ).each( &block )
end