Module | Sequel::Plugins::ClassTableInheritance
In: | lib/sequel/plugins/class_table_inheritance.rb

The class_table_inheritance plugin allows you to model inheritance in the database using a table per model class in the hierarchy, with only columns unique to that model class (or subclass hierarchy) being stored in the related table. For example, with this hierarchy:

          Employee
         /        \
      Staff     Manager
                   |
               Executive

the following database schema may be used (table - columns):

- employees - id, name, kind
- staff - id, manager_id
- managers - id, num_staff
- executives - id, num_managers

The class_table_inheritance plugin assumes that the main table (e.g. employees) has a primary key field (usually autoincrementing), and all other tables have a foreign key of the same name that points to the same key in their superclass‘s table. For example:

- employees.id - primary key, autoincrementing
- staff.id - foreign key referencing employees(id)
- managers.id - foreign key referencing employees(id)
- executives.id - foreign key referencing managers(id)

When using the class_table_inheritance plugin, subclasses use joined datasets:

      Employee.dataset.sql  # SELECT * FROM employees
      Manager.dataset.sql   # SELECT * FROM employees
                            # INNER JOIN managers USING (id)
      Executive.dataset.sql # SELECT * FROM employees
                            # INNER JOIN managers USING (id)
                            # INNER JOIN executives USING (id)

This allows Executive.all to return instances with all attributes loaded. The plugin overrides the deleting, inserting, and updating in the model to work with multiple tables, by handling each table individually.

This plugin allows the use of a :key option when loading to mark a column holding a class name. This allows methods on the superclass to return instances of specific subclasses. This plugin also requires the lazy_attributes plugin and uses it to return subclass specific attributes that would not be loaded when calling superclass methods (since those wouldn‘t join to the subclass tables). For example:

      a = Employee.all # [<#Staff>, <#Manager>, <#Executive>]
      a.first.values # {:id=>1, name=>'S', :kind=>'Staff'}
      a.first.manager_id # Loads the manager_id attribute from the database

Usage:

      # Set up class table inheritance in the parent class
      # (Not in the subclasses)
      Employee.plugin :class_table_inheritance

      # Set the +kind+ column to hold the class name, and
      # set the subclass table to map to for each subclass
      Employee.plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff}

### Methods

apply configure

### Classes and Modules

Module Sequel::Plugins::ClassTableInheritance::ClassMethods
Module Sequel::Plugins::ClassTableInheritance::InstanceMethods

### Public Class methods

apply(model, opts={}, &block)

The class_table_inheritance plugin requires the lazy_attributes plugin to handle lazily-loaded attributes for subclass instances returned by superclass methods.

[Source]

        # File lib/sequel/plugins/class_table_inheritance.rb, line 71
    71:       def self.apply(model, opts={}, &block)
    72:         model.plugin :lazy_attributes
    73:       end

configure(model, opts={}, &block)

Initialize the per-model data structures and set the dataset‘s row_proc to check for the :key option column for the type of class when loading objects. Options:

- :key - The column symbol holding the name of the model class this is an instance of. Necessary if you want to call model methods using the superclass, but have them return subclass instances.
- :table_map - Hash with class name symbol keys and table name symbol values. Necessary if the implicit table name for the model class does not match the database table name

[Source]

        # File lib/sequel/plugins/class_table_inheritance.rb, line 84
    84:       def self.configure(model, opts={}, &block)
    85:         model.instance_eval do
    86:           m = method(:constantize)
    87:           @cti_base_model = self
    88:           @cti_key = key = opts[:key]
    89:           @cti_tables = [table_name]
    90:           @cti_columns = {table_name=>columns}
    91:           @cti_table_map = opts[:table_map] || {}
    92:           dataset.row_proc = if key
    93:             lambda{|r| (m.call(r[key]) rescue model).load(r)}
    94:           else
    95:             lambda{|r| model.load(r)}
    96:           end
    97:         end
    98:       end

[Validate]