Raymond Lucke
2006-Jul-04 00:08 UTC
[Rails] [ANN] inherits_from: Multiple table inheritance
Hey guys, I just implemented very simple and primitive class table inheritance in ActiveRecord. The goal is for this to become mature enough to become a true feature in Rails. Check it out here: http://guest@blog.raylucke.com/svn/inherits_from Here?s how it looks in action: create_table "books", :force => true do |t| t.column "product_id", :integer t.column "pages", :integer t.column "author", :string end create_table "products", :force => true do |t| t.column "name", :string t.column "price", :integer t.column "type", :string end create_table "videos", :force => true do |t| t.column "product_id", :integer t.column "minutes", :integer t.column "starring", :string end class Product < ActiveRecord::Base end class Book < ActiveRecord::Base inherits_from :product end class Video < ActiveRecord::Base inherits_from :product end Book.create(:name => "Agile Development with Rails", :pages => 350) Video.create(:name => "Twilight Zone Season 1", :minutes => 490) Book.find(1).name => "Agile Development with Rails" Note: this plugin is not production ready. I?m hoping some people who are much smarter than me will send me patches to make it primetime. Known bugs: * Inherited column validation/error handling does not currently work * Book.find_by_name() (inherited column) does not work * Only basic attributes are passed along right now Feel free to send me any comments to ray @ this domain (raylucke.com). I''ll be posting updates on my blog as well. Regards, Raymond W. Lucke IV http://blog.raylucke.com/ -- Posted via http://www.ruby-forum.com/.
Ezra Zygmuntowicz
2006-Jul-04 01:47 UTC
[Rails] [ANN] inherits_from: Multiple table inheritance
Hi! On Jul 3, 2006, at 5:07 PM, Raymond Lucke wrote:> Hey guys, > > I just implemented very simple and primitive class table > inheritance in > ActiveRecord. The goal is for this to become mature enough to become a > true feature in Rails. Check it out here: > > http://guest@blog.raylucke.com/svn/inherits_from > > Here?s how it looks in action: >> <snip>> Feel free to send me any comments to ray @ this domain (raylucke.com). > > I''ll be posting updates on my blog as well. > > Regards, > > Raymond W. Lucke IV > http://blog.raylucke.com/Raymond- This looks pretty cool and I would love to play with it. Unfortunately your link you posted here and on your blog requires a password apparently. Can you tell me what I need to do to download the plugin? Thanks- -Ezra
Raymond Lucke
2006-Jul-04 02:00 UTC
[Rails] Re: [ANN] inherits_from: Multiple table inheritance
Hi Ezra, Try entering guest as the username and sending a blank password. That should work. Regards, Ray Ezra Zygmuntowicz wrote:> Hi! > > On Jul 3, 2006, at 5:07 PM, Raymond Lucke wrote: > >> >> <snip> > >> Feel free to send me any comments to ray @ this domain (raylucke.com). >> >> I''ll be posting updates on my blog as well. >> >> Regards, >> >> Raymond W. Lucke IV >> http://blog.raylucke.com/ > > > Raymond- > > This looks pretty cool and I would love to play with it. > Unfortunately your link you posted here and on your blog requires a > password apparently. Can you tell me what I need to do to download > the plugin? > > Thanks- > -Ezra_______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails-- Posted via http://www.ruby-forum.com/.
Josh Susser
2006-Jul-04 03:40 UTC
[Rails] Re: [ANN] inherits_from: Multiple table inheritance
Raymond Lucke wrote:> I just implemented very simple and primitive class table inheritance in > ActiveRecord. The goal is for this to become mature enough to become a > true feature in Rails. Check it out here: > > http://guest@blog.raylucke.com/svn/inherits_from > > ... > > Raymond W. Lucke IV > http://blog.raylucke.com/How interesting! I''m impressed you were able to get this to work in as little code as you did. Pretty good for a science project. However, I think your approach has some serious limitations and you may be right about it being fundamentally flawed. By the way, I think in a typical ClassTI design, the PKs of the segments are synced across tables, so you don''t need to keep a FK for the parent record in the child record. My big gripe with ClassTI is that you have to do a select or join for each level of inheritance to get a single object from the database. That doesn''t make it a non-starter, but it''s a pretty big performance hurdle to get over. Have you looked into the Concrete Table Inheritance pattern? I think that would be more useful for Rails type apps, though it could be a polymorphic nightmare doing a find(:all) on the parent class! -- Josh Susser http://blog.hasmanythrough.com -- Posted via http://www.ruby-forum.com/.
Ashley Moran
2006-Jul-04 09:14 UTC
[Rails] Re: [ANN] inherits_from: Multiple table inheritance
On Tuesday 04 July 2006 04:40, Josh Susser wrote:> How interesting! ?I''m impressed you were able to get this to work in as > little code as you did. Pretty good for a science project. However, I > think your approach has some serious limitations and you may be right > about it being fundamentally flawed. >I haven''t seen the code yet but Class TI is a feature I''d really like in Rails. There''s a chance I''ll get to do some work on it at work so I was hoping to see someone else have a go for inspiration. One issue I''ve wondered about is when you have non-mutually exclusive subclasses. Eg Worker (def work; ...; end) / \ Manager First Aider (def shout; ...; end) (def heal; ...; end) Should entries be allowed in *both* the managers and first_aiders tables? If so I guess you have to use mixins to allow extra code funcionality from both subcategories.> By the way, I think in a typical ClassTI design, the PKs of the segments > are synced across tables, so you don''t need to keep a FK for the parent > record in the child record.I assume by this you mean that you don''t have auto-incremented surrogate keys in the subclass tables, and the primary key is manually set to the same value as the corresponding superclass entry? I think that''s what I''ve seen before.> My big gripe with ClassTI is that you have to do a select or join for > each level of inheritance to get a single object from the database. That > doesn''t make it a non-starter, but it''s a pretty big performance hurdle > to get over.If you don''t like the complexity of the joins, don''t forget you can define views to represent the "whole" subclass. I think the performance hit of joins is often over-estimated. Fetching everything in the combined join will take longer than a single table, but most queries only involve a subset of data. (Also some database servers can cache the result of joins.) Generally it''s probably better to design for simplicity first, and only change your design for performance if really necessary. I imagine the worst thing you could do with a Class TI design is to iterate over the entities, fetching each one in turn. That would make the app query every subclass table to find what class it belongs to.> Have you looked into the Concrete Table Inheritance pattern? I think > that would be more useful for Rails type apps, though it could be a > polymorphic nightmare doing a find(:all) on the parent class!I haven''t given it THAT much thought, but I''m not too fond of Concrete TI. Mainly because: - it complicates table design (changes must be made to multiple tables) - it makes it harder to turn one entity type into another (in Class TI, just insert or delete a sub-entity record) - it completely rules out multi-class entities (unless you want really nasty redundancy in the data) Let me know your thoughts everyone hopefully I''ll be able to join in the Class TI effort some time. Ashley -- "If you do it the stupid way, you will have to do it again" - Gregory Chudnovsky
Raymond Lucke
2006-Jul-04 11:54 UTC
[Rails] Re: Re: [ANN] inherits_from: Multiple table inheritance
Ashley, It would seem to me that you could accomplish a much of what you''re talking about with something that looks like this: class Worker < ActiveRecord::Base has_one :manager has_one :first_aider def manager? !manager.nil? end def first_aider? !first_aider.nil? end end class Manager < ActiveRecord::Base belongs_to :worker end class FirstAider < ActiveRecord::Base belongs_to :worker end john = Worker.find_by_name("john") # John the manager will shout now if he is one. john.manager.shout if john.manager? # John the first aider will heal if he is one. john.first_aider.heal if john.first_aider? In fact, using a method similar to what I did in my plugin, you can create a proxy accessor if you really want to call john.shout and not john.manager.shout. One of the design goals of my plugin is functionality through a simple means. If you notice, it''s really nothing more than an attribute proxy method generator that iterates over the parent''s attributes. You could theoretically even add something like this: class Worker < ActiveRecord::Base has_one :manager alias_associated_attr(''manager'', ''shout'') def manager? !manager.nil? end end steve = Worker.find_by_name("steve") steve.shout if steve.manager? I''m not sure if I quite answered your question here, but I guess my point is that I don''t think you really need inheritance at all for the scenario you pointed out above. It even can yield a bit cleaner code sometimes when you can encapsulate the role-specific methods/attributes, in this case for manager, in their own class. Regards, Ray Ashley Moran wrote:> On Tuesday 04 July 2006 04:40, Josh Susser wrote: >> How interesting! ?I''m impressed you were able to get this to work in as >> little code as you did. Pretty good for a science project. However, I >> think your approach has some serious limitations and you may be right >> about it being fundamentally flawed. >> > > I haven''t seen the code yet but Class TI is a feature I''d really like in > Rails. There''s a chance I''ll get to do some work on it at work so I was > hoping to see someone else have a go for inspiration. > > One issue I''ve wondered about is when you have non-mutually exclusive > subclasses. Eg > > Worker > (def work; ...; end) > / \ > Manager First Aider > (def shout; ...; end) (def heal; ...; end) > > Should entries be allowed in *both* the managers and first_aiders > tables? If > so I guess you have to use mixins to allow extra code funcionality from > both > subcategories. > > >> By the way, I think in a typical ClassTI design, the PKs of the segments >> are synced across tables, so you don''t need to keep a FK for the parent >> record in the child record. > > I assume by this you mean that you don''t have auto-incremented surrogate > keys > in the subclass tables, and the primary key is manually set to the same > value > as the corresponding superclass entry? I think that''s what I''ve seen > before. > > >> My big gripe with ClassTI is that you have to do a select or join for >> each level of inheritance to get a single object from the database. That >> doesn''t make it a non-starter, but it''s a pretty big performance hurdle >> to get over. > > If you don''t like the complexity of the joins, don''t forget you can > define > views to represent the "whole" subclass. > > I think the performance hit of joins is often over-estimated. Fetching > everything in the combined join will take longer than a single table, > but > most queries only involve a subset of data. (Also some database servers > can > cache the result of joins.) Generally it''s probably better to design > for > simplicity first, and only change your design for performance if really > necessary. > > I imagine the worst thing you could do with a Class TI design is to > iterate > over the entities, fetching each one in turn. That would make the app > query > every subclass table to find what class it belongs to. > > >> Have you looked into the Concrete Table Inheritance pattern? I think >> that would be more useful for Rails type apps, though it could be a >> polymorphic nightmare doing a find(:all) on the parent class! > > I haven''t given it THAT much thought, but I''m not too fond of Concrete > TI. > Mainly because: > - it complicates table design > (changes must be made to multiple tables) > - it makes it harder to turn one entity type into another > (in Class TI, just insert or delete a sub-entity record) > - it completely rules out multi-class entities > (unless you want really nasty redundancy in the data) > > > Let me know your thoughts everyone hopefully I''ll be able to join in the > Class > TI effort some time. > > Ashley-- Posted via http://www.ruby-forum.com/.
Ashley Moran
2006-Jul-05 09:21 UTC
[Rails] Re: Re: [ANN] inherits_from: Multiple table inheritance
On Tuesday 04 July 2006 12:54, Raymond Lucke wrote:> Ashley, > > It would seem to me that you could accomplish a much of what you''re > talking about with something that looks like this: > > class Worker < ActiveRecord::Base > ? has_one :manager > ? has_one :first_aider > > ? def manager? > ? ? !manager.nil? > ? end > > ? def first_aider? > ? ? !first_aider.nil? > ? end > end > > class Manager < ActiveRecord::Base > ? belongs_to :worker > end > > class FirstAider < ActiveRecord::Base > ? belongs_to :worker > end > > john = Worker.find_by_name("john") > > # John the manager will shout now if he is one. > john.manager.shout if john.manager? > > # John the first aider will heal if he is one. > john.first_aider.heal if john.first_aider?You could do this, but the problem is that if each Worker has a Manager, and each Manager has a Manager, it would get very confusing calling, say, john.manager_subclass.shout john.manager.shout> In fact, using a method similar to what I did in my plugin, you can > create a proxy accessor if you really want to call john.shout and not > john.manager.shout. > > One of the design goals of my plugin is functionality through a simple > means. If you notice, it''s really nothing more than an attribute proxy > method generator that iterates over the parent''s attributes. You could > theoretically even add something like this:I had a look at last, and you''re right, you found a really straightforward way to do it. When I get a moment I''ll try it out.> > class Worker < ActiveRecord::Base > ? has_one :manager > > ? alias_associated_attr(''manager'', ''shout'') > > ? def manager? > ? ? !manager.nil? > ? end > end > > steve = Worker.find_by_name("steve") > steve.shout if steve.manager? > > I''m not sure if I quite answered your question here, but I guess my > point is that I don''t think you really need inheritance at all for the > scenario you pointed out above. It even can yield a bit cleaner code > sometimes when you can encapsulate the role-specific methods/attributes, > in this case for manager, in their own class.I can see your argument, but if I had multiple inheritance I''d like to be able to do this: class Worker < ActiveRecord::Base def message; "I work"; end def shout; message; end end class Manager < ActiveRecord::Base def message; "I manage"; end end class FirstAider < ActiveRecord::Base def message; "I heal"; end end john = Worker.find_by_name("John T Manager") john.shout => "I manage" billy = Worker.find_by_name("Billy F Aider") billy.shout => "I heal" Of course it all goes to pot when Jack A Trader is both a Manager and a First Aider, when multiple inheritance turns ugly. Which makes me wonder if it''s worth even trying to support it, in ClTI Another thing is that I don''t believe Rails supports reloading modules in development mode (please correct me if that isn''t true), which I assumed would be the only way to implement it. If you started using separate classes, then method overriding would have to be implemented manually, and it''s Ruby''s job to provide that. My feeling right now is that it''s just not worth it, and the best solution would be single ClTI inheritance in ActiveRecord and some sort of role-based code in the app. I guess until I''ve had a go I won''t know how it works out. Thanks for your feedback Ashley -- "If you do it the stupid way, you will have to do it again" - Gregory Chudnovsky
Juan Felipe García
2006-Jul-05 15:06 UTC
[Rails] Re: Re: Re: [ANN] inherits_from: Multiple table inheritance
I propose a turnaround of your setup that makes this seem more "right" to me, i''ll try it myself, though I''m only a week old ruby/rails newbie: create_table "products", :force => true do |t| t.column "id", :string t.column "name", :string t.column "price", :integer t.column "type", :string #this is either book|video t.column "entity_id" :integer #this will be the id in the corresponding "#{type}s" table end create_table "books", :force => true do |t| t.column "id", :integer t.column "pages", :integer t.column "author", :string end create_table "videos", :force => true do |t| t.column "id", :integer t.column "minutes", :integer t.column "starring", :string end class Product < ActiveRecord::Base end class Book < ActiveRecord::Base inherits_from :product end class Video < ActiveRecord::Base inherits_from :product end Book.create(:name => "Agile Development with Rails", :pages => 350) Video.create(:name => "Twilight Zone Season 1", :minutes => 490) Book.find(1).name => "Agile Development with Rails" Video.find(1).name => "Twilight Zone Season 1" Product.find(1).name => "Agile Development with Rails" Product.find(2).name => "Twilight Zone Season 1" -- Posted via http://www.ruby-forum.com/.
Ashley Moran
2006-Jul-06 09:26 UTC
[Rails] Re: Re: Re: [ANN] inherits_from: Multiple table inheritance
On Wednesday 05 July 2006 16:06, Juan Felipe Garc?a wrote:> I propose a turnaround of your setup that makes this seem more "right" > to me, i''ll try it myself, though I''m only a week old ruby/rails newbie: > > create_table "products", :force => true do |t| > t.column "id", :string > t.column "name", :string > t.column "price", :integer > t.column "type", :string #this is either book|video > t.column "entity_id" :integer #this will be the id in the > corresponding "#{type}s" table > end > > > create_table "books", :force => true do |t| > t.column "id", :integer > t.column "pages", :integer > t.column "author", :string > end > > create_table "videos", :force => true do |t| > t.column "id", :integer > t.column "minutes", :integer > t.column "starring", :string > end > > class Product < ActiveRecord::Base > end > > class Book < ActiveRecord::Base > inherits_from :product > end > > class Video < ActiveRecord::Base > inherits_from :product > end > > Book.create(:name => "Agile Development with Rails", :pages => 350) > Video.create(:name => "Twilight Zone Season 1", :minutes => 490) > > Book.find(1).name => "Agile Development with Rails" > Video.find(1).name => "Twilight Zone Season 1" > Product.find(1).name => "Agile Development with Rails" > Product.find(2).name => "Twilight Zone Season 1"Hmmm, I can see what you''re trying to do but I''m not sure it will work well. You lose Ruby class inheritance, so if you have a method in Product (say Product#price_in_euros) it won''t be available to instances of Book and Video. Bear in mind that videos and books are a "nice" case, where the two subclasses are mutually exclusive. In the case of managers and first-aiders, a person could be both at the same time. I''m not sure if a class-table inheritance solution should allow this (or could allow this). Watch out because "type" is a reserved column name that ActiveRecord uses to determine the class in single table inheritance. This has bit me before. The canonical way to manage the ID columns is to give each of the subclass tables non-autogenerated ID fields that match the auto-generated ID of the top level class. I think the syntax is like this: create_table "products", :force => true do |t| ? # ...column defs... end create_table "books", :id => false do |t| ? t.column "id", :integer # always refers to a value in table "products" ? # ...column defs... end create_table "videos", :id => false do |t| ? t.column "id", :integer # always refers to a value in table "products" ? # ...column defs... end If you''re new to Ruby then this is a very ambitious project! Although it should get you up to speed in no time. I recommending read chapters 22-24 of the Pickaxe first though, so you understand Ruby inheritance thoroughly (not that I''m an expert- there are plenty of things in chapter 24 I need to refer to often). Ashley -- "If you do it the stupid way, you will have to do it again" - Gregory Chudnovsky