Skip to content

Commit

Permalink
Add subclasses plugin, for recording all of a models subclasses and d…
Browse files Browse the repository at this point in the history
…escendent classes

The Subclasses plugin keeps track of all subclasses of the
current model class.  Direct subclasses are available via the
subclasses method, and all descendent classes are available via the
descendents method.

  c = Class.new(Sequel::Model)
  c.plugin :subclasses
  sc1 = Class.new(c)
  sc2 = Class.new(c)
  ssc1 = Class.new(sc1)
  c.subclasses    # [sc1, sc2]
  sc1.subclasses  # [ssc1]
  sc2.subclasses  # []
  ssc1.subclasses # []
  c.descendents   # [sc1, ssc1, sc2]
  • Loading branch information
jeremyevans committed Sep 7, 2009
1 parent 6d0611c commit b75bf19
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
=== HEAD

* Add subclasses plugin, for recording all of a models subclasses and descendent classes (jeremyevans)

* Add looser_typecasting extension, for using .to_f and .to_i instead of Kernel.Float and Kernel.Integer when typecasting floats and integers (jeremyevans)

* Catch database errors when preparing statements or setting variable values when using the native MySQL adapter (jeremyevans)
Expand Down
45 changes: 45 additions & 0 deletions lib/sequel/plugins/subclasses.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module Sequel
module Plugins
# The Subclasses plugin keeps track of all subclasses of the
# current model class. Direct subclasses are available via the
# subclasses method, and all descendent classes are available via the
# descendents method.
#
# c = Class.new(Sequel::Model)
# c.plugin :subclasses
# sc1 = Class.new(c)
# sc2 = Class.new(c)
# ssc1 = Class.new(sc1)
# c.subclasses # [sc1, sc2]
# sc1.subclasses # [ssc1]
# sc2.subclasses # []
# ssc1.subclasses # []
# c.descendents # [sc1, ssc1, sc2]
module Subclasses
# Initialize the subclasses instance variable for the model.
def self.apply(model, &block)
model.instance_variable_set(:@subclasses, [])
end

module ClassMethods
# All subclasses for the current model. Does not
# include the model itself.
attr_reader :subclasses

# All descendent classes of this model.
def descendents
subclasses.map{|x| [x] + x.descendents}.flatten
end

# Add the subclass to this model's current subclasses,
# and initialize a new subclasses instance variable
# in the subclass.
def inherited(subclass)
super
subclasses << subclass
subclass.instance_variable_set(:@subclasses, [])
end
end
end
end
end
52 changes: 52 additions & 0 deletions spec/extensions/subclasses_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require File.join(File.dirname(__FILE__), "spec_helper")

describe Sequel::Model, "Subclasses plugin" do
before do
@c = Class.new(Sequel::Model)
@c.plugin :subclasses
end

specify "#subclasses should record direct subclasses of the given model" do
@c.subclasses.should == []

sc1 = Class.new(@c)
@c.subclasses.should == [sc1]
sc1.subclasses.should == []

sc2 = Class.new(@c)
@c.subclasses.should == [sc1, sc2]
sc1.subclasses.should == []
sc2.subclasses.should == []

ssc1 = Class.new(sc1)
@c.subclasses.should == [sc1, sc2]
sc1.subclasses.should == [ssc1]
sc2.subclasses.should == []
end

specify "#descendents should record all descendent subclasses of the given model" do
@c.descendents.should == []

sc1 = Class.new(@c)
@c.descendents.should == [sc1]
sc1.descendents.should == []

sc2 = Class.new(@c)
@c.descendents.should == [sc1, sc2]
sc1.descendents.should == []
sc2.descendents.should == []

ssc1 = Class.new(sc1)
@c.descendents.should == [sc1, ssc1, sc2]
sc1.descendents.should == [ssc1]
sc2.descendents.should == []
ssc1.descendents.should == []

sssc1 = Class.new(ssc1)
@c.descendents.should == [sc1, ssc1, sssc1, sc2]
sc1.descendents.should == [ssc1, sssc1]
sc2.descendents.should == []
ssc1.descendents.should == [sssc1]
sssc1.descendents.should == []
end
end
1 change: 1 addition & 0 deletions www/pages/plugins
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<li><a href="rdoc-plugins/classes/Sequel/Plugins/Schema.html">schema</a>: Adds backwards compatibility for Model.set_schema and Model.create_table.</li>
<li><a href="rdoc-plugins/classes/Sequel/Plugins/Serialization.html">serialization</a>: Supports serializing column values and storing them as either marshal, yaml, or json in the database.</li>
<li><a href="rdoc-plugins/classes/Sequel/Plugins/SingleTableInheritance.html">single_table_inheritance</a>: Sets up STI by making subclasses of this model only load rows that match the class, and returning instances of the subclasses.</li>
<li><a href="rdoc-plugins/classes/Sequel/Plugins/Subclasses.html">subclasses</a>: Allows easy access all model subclasses and descendent classes, without using ObjectSpace.</li>
<li><a href="rdoc-plugins/classes/Sequel/Plugins/TacticalEagerLoading.html">tactical_eager_loading</a>: Allows you to eagerly load an association for all objects retreived from the same dataset when calling the association method on any of the objects in the dataset.</li>
<li><a href='rdoc-plugins/classes/Sequel/Plugins/Timestamps.html'>timestamps</a>: Creates hooks for automatically setting create and update timestamps.</li>
<li><a href='rdoc-plugins/classes/Sequel/Plugins/TypecastOnLoad.html'>typecast_on_load</a>: Fixes bad database typecasting when loading model objects.</li>
Expand Down

0 comments on commit b75bf19

Please sign in to comment.