module Sequel module Plugins # Sequel's built in Single Table Inheritance plugin makes subclasses # of this model only load rows where the given key field matches the # subclass's name. If the key given has a NULL value or there are # any problems looking up the class, uses the current class. # # You should only use this in the parent class, not in the subclasses. # # You shouldn't call set_dataset in the model after applying this # plugin, otherwise subclasses might use the wrong dataset. # # The filters and row_proc that sti_key sets up in subclasses may not work correctly if # those subclasses have further subclasses. For those middle subclasses, # you may need to call set_dataset manually with the correct filter and # row_proc. module SingleTableInheritance # Set the sti_key and sti_dataset for the model, and change the # dataset's row_proc so that the dataset yields objects of varying classes, # where the class used has the same name as the key field. def self.configure(model, key) m = model.method(:constantize) model.instance_eval do @sti_key = key @sti_dataset = dataset dataset.row_proc = lambda{|r| (m.call(r[key]) rescue model).load(r)} end end module ClassMethods # The base dataset for STI, to which filters are added to get # only the models for the specific STI subclass. attr_reader :sti_dataset # The column name holding the STI key for this model attr_reader :sti_key # Copy the sti_key and sti_dataset to the subclasses, and filter the # subclass's dataset so it is restricted to rows where the key column # matches the subclass's name. def inherited(subclass) super sk = sti_key sd = sti_dataset subclass.set_dataset(sd.filter(sk=>subclass.name.to_s), :inherited=>true) subclass.instance_eval do @sti_key = sk @sti_dataset = sd @simple_table = nil end end end module InstanceMethods # Set the sti_key column to the name of the model. def before_create return false if super == false send("#{model.sti_key}=", model.name.to_s) unless send(model.sti_key) end end end end end