Hi I have this "old" database mainly modeled for Hibernate. The java model has something called Asset. Asset is inherited by SomeAsset and AnotherAsset and the similar stuff is in the asset table and the specific data for SomeAsset is in table some_asset and for AnotherAsset in another_asset. This is then reflected in hibernate using a <joined-subclass/> element. What would be the best way to model this in ActiveRecord? I cannot change the exisiting database structure beyond adding new columns (ie I cannot move values from the join tables back to the main table and use STI). Could I implement method_missing in Asset to just delegate to methods that might exist in SomeAsset or AnotherAsset? I only use duck typing so from a type perspective it would be fine. This would make it hard to override behaviour from Asset in subclasses though (since they aren't subclasses...). Is there another better solution? Best Regards, Marcus
on 28.06.2006 13:22
on 28.06.2006 14:53
On 6/28/06, Marcus Andersson <m-lists@bristav.se> wrote: > > I have this "old" database mainly modeled for Hibernate... > [snip] Depending on how your model looks, one idea would be to use RBatis which is a more flexible O/R mapper. If SQL doesn't scare you that is. See http://jutopia.tirsen.com/articles/2006/05/23 for more details. /Peter
on 28.06.2006 15:06
Marcus Andersson wrote: > change the exisiting database structure beyond adding new columns (ie I > cannot move values from the join tables back to the main table and use > STI). > > Could I implement method_missing in Asset to just delegate to methods > that might exist in SomeAsset or AnotherAsset? I only use duck typing so > from a type perspective it would be fine. This would make it hard to > override behaviour from Asset in subclasses though (since they aren't > subclasses...). > > Is there another better solution? I'd have SomeAsset and AnotherAsset instances each hold a delegate Asset object that they proxy any calls they can't answer themselves to. I've played around a little with trying to implement a full class table inheritance scheme for AR on that basis, but never really had enough need for it myself to polish it up.
on 28.06.2006 16:31
Marcus Andersson schrieb:
> STI).
I don't know ActiveRecord, but are you allowed to add a (SQL) view to
your database schema? This way you might be able to use single table
inheritance. Some databases allow even DML Operations on views.
Regards,
Pit
on 28.06.2006 19:21
Alex Young skrev: > I'd have SomeAsset and AnotherAsset instances each hold a delegate > Asset object that they proxy any calls they can't answer themselves > to. I've played around a little with trying to implement a full class > table inheritance scheme for AR on that basis, but never really had > enough need for it myself to polish it up. > If I do like that I have to search in several different tables when I want to search for assets when I don't care about the type? Or did I misunderstand? /Marcus
on 28.06.2006 19:56
On 6/28/06, Alex Young <alex@blackkettle.org> wrote: > I'd have SomeAsset and AnotherAsset instances each hold a delegate Asset > object that they proxy any calls they can't answer themselves to. I've > played around a little with trying to implement a full class table > inheritance scheme for AR on that basis, but never really had enough > need for it myself to polish it up. I agree with Alex here. While inheritance was what the tables were originally trying to emulate, composition was the resulting design anyways. With Ruby and it's duck typing, inheritance isn't required for the ability to pass a SomeAsset where an Asset is expected. Java's static typing would require SomeAsset to "be a" Asset, but Ruby only needs SomeAsset to "quack like" an asset, which can be covered with judicious use of composition and method_missing. I'd actually provide a mixin that sets up this method_missing. Example: class Asset < ActiveRecord::Base # we're leaving out the reverse relationship to the "child" classes # -- just as a parent class implementation wouldn't really know about # the classes descended from it def xyzzy # model specific functionality end def hidden # model specific functionality that's not delegatable end module Delegator AssetDelegates = [ :prop1, :prop1=, :xyzzy ] def self.included(other) other.module_eval <<-END # we use belongs_to rather than has_one because the foreign key # is in this table belongs_to :asset alias :__asset_delegator_old_mm :method_missing def method_missing(method, *args, &block) if AssetDelegates.include?(method) self.asset.send(method, *args, &block) else __asset_delegator_old_mm(method, *args, &block) end end END end end end class SomeAsset < ActiveRecord::Base include Asset::Delegator def xyzzy # override Asset's implementation of xyzzy, feels just like # inheritance, except that if we want to call up to the "parent" # version, we need to use asset.xyzzy rather than super end end class AnotherAsset < ActiveRecord::Base include Asset::Delegator # make the hidden method, which wasn't explicitly delegated, available # from this "subclass" def hidden asset.hidden end end Jacob Fugal
on 28.06.2006 20:14
Read about polymorphic associations... here's an example http://www.fromdelhi.com/2006/06/15/polymorphic-association-in-rails/ -------- Original-Nachricht -------- Datum: Thu, 29 Jun 2006 02:11:05 +0900 Von: Marcus Andersson <m-lists@bristav.se> An: ruby-talk@ruby-lang.org Betreff: Re: How to model Hibernate multi-table inheritance with ActiveRecord?
on 28.06.2006 20:22
delegation is powerful, no question...
> but if SomeAsset and AnotherAsset instances each hold a delegate Asset
why not simply use inheritance?
it's not that bad :-)
class SomeAsset < Asset
class AnotherAsset < Asset
waaay tooo much java engineers here :-)
-------- Original-Nachricht --------
Datum: Thu, 29 Jun 2006 02:17:24 +0900
Von: Jacob Fugal <lukfugl@gmail.com>
An: ruby-talk@ruby-lang.org
Betreff: Re: How to model Hibernate multi-table inheritance with
ActiveRecord?
on 28.06.2006 20:30
On 6/28/06, Peter Ertl <pertl@gmx.org> wrote: > delegation is powerful, no question... > > > but if SomeAsset and AnotherAsset instances each hold a delegate Asset > > why not simply use inheritance? > > it's not that bad :-) Woah... no one said inheritance was bad. Indeed, inheritance is what is desired here. But due to constraints from both ActiveRecord and the existing DB schema, we're just trying to find any way that works. And inheritance won't work here. Particularly because a model class can only map to one table. If you subclass an ActiveRecord model, the subclass is assumed to live in the same table (Single Table Inheritance). ActiveRecord doesn't currently support Multiple Table Inheritance (as far as I know). Thus inheritance, sadly, doesn't work for us here. > waaay tooo much java engineers here :-) Nope. Not the case. :) Just trying to solve a problem under constraint... Jacob Fugal
on 28.06.2006 22:08
> Particularly because a model class can only map to one table.
Why should that be the case?
Did you try ActiveRecord::Base.set_table_name ?
class SomeAsset < Asset
set_table_name 'some_asset'
end
Another approach would be to include the Asset base functionality using
'include'
class SomeAsset < ActiveRecord::Base
include Asset
end
It should be more efficient than delegating methods...
Jacob Fugal schrieb:
on 28.06.2006 22:25
Pete skrev: > > Particularly because a model class can only map to one table. > > Why should that be the case? > > Did you try ActiveRecord::Base.set_table_name ? > > class SomeAsset < Asset > set_table_name 'some_asset' > end > But still, even if you override the default name of the table you map to you can only map to one table. > > Another approach would be to include the Asset base functionality > using 'include' > > class SomeAsset < ActiveRecord::Base > include Asset > end > > It should be more efficient than delegating methods... Huh? I can only include modules as far as I know. And I can't map a module to a table using ActiveRecord so I think that would be hard. No? /Marcus
on 28.06.2006 22:38
On 6/28/06, Pete <pertl@gmx.org> wrote: > > Particularly because a model class can only map to one table. > > Why should that be the case? > > Did you try ActiveRecord::Base.set_table_name ? > > class SomeAsset < Asset > set_table_name 'some_asset' > end As Marcus mentioned, this lets you *change* the table for the model, but not *aggregate* multiple tables for the same model. The case Marcus proposed in his original post requires an aggregation of multiple tables to define one model. > Another approach would be to include the Asset base functionality using > 'include' > > class SomeAsset < ActiveRecord::Base > include Asset > end > > It should be more efficient than delegating methods... That's actually pretty much what I'm trying to achieve with my Asset::Delegator. The reason I can't just include Asset is 1) only modules can be included, as Marcus pointed out and 2) importing (or delegating) *all* of the methods from Asset can conflict with the already existing method_missing functionality for SomeAsset provided by ActiveRecord. So I threw together a quick example of a module that *can* be mixed in that feels like you're mixing in Asset itself, but with a limited method set. True, the number of methods that need to be enumerated in Asset::Delegator::AssetDelegates will probably be high, depending on usage, but I can't see anyway around that without polluting method_missing. Jacob Fugal
on 28.06.2006 22:54
Hello Jacob, I sort of like this solution but I have one problem. Querying. Say I want to query the complete set of Assets and I don't care what underlying [other]Asset they have. Then I have to do: assets = Asset.find(:all, :condition => "somecondition") But, I always need to get the delegate from the assets in order to work with them. Thus the above expression changes to: assets = Asset.find(:all, :condition => "somecondition").map(&:delegate_asset) Now I have the correct asset instances to work with. The problem is that I have to do that extra map() all over the place. 1) It violates DRY and 2) someone will forget it Is there a solution to this (apart from decreing project wide that you may never query Asset outside of Asset itself and thereby encapsulate all queries)? /Marcus
on 28.06.2006 23:11
On 6/28/06, Marcus Andersson <m-lists@bristav.se> wrote:
> I sort of like this solution but I have one problem. Querying.
This is a good point. I actually noticed it in your earlier reply to
Alex, but couldn't think of anything to add. As far as I can tell, the
situation is indeed as you tell it. :(
Anyone else have any ideas on this?
Jacob Fugal
on 28.06.2006 23:21
Peter Krantz skrev: > On 6/28/06, Marcus Andersson <m-lists@bristav.se> wrote: >> >> I have this "old" database mainly modeled for Hibernate... >> [snip] > > Depending on how your model looks, one idea would be to use RBatis > which is a more flexible O/R mapper. If SQL doesn't scare you that is. > See http://jutopia.tirsen.com/articles/2006/05/23 for more details. > Heh, I'm not scared of SQL. I've actually used iBatis (I used to be a hardcore java junkie...) in the past so I'm quite familiar with its concepts. It's actually possible to use pure SQL queries with ActiveRecord as well. The problem is that I don't know how to solve this problem using straight SQL either without making the lives of the Asset clients a lot harder. /Marcus
on 28.06.2006 23:28
So far I have two solutions: 1) implement method_missing in Asset and delegate to [other]Asset where Asset cannot handle the requested method 2) implement method_missing in [other]Asset and delegate back to Asset where [other]Asset cannot handle the request (or a variant thereof) 1) gives easy querying (I can just query Asset for all objects) but I cannot override methods from Asset in [other]Asset. Not really good. This solution could be good if I could somehow make Asset ask [other]Asset if they have the requested method before calling the method in itself instead? Sort of like a default before handler on every method. 2) In order for this to work I always need to work on instances of [other]Asset. That in turns mean that I either have to make the query on [other]Asset class to get instances of [other]Asset or make the query on Asset and then start off by getting the [other]Asset instance from it and work with that (ie using Asset.find(:all).map(&:delegate_asset)). Neither of them feels really polymorphic. A solution here would be if one could bend ActiveRecord to return [other]Asset instead of Asset instances when querying the Asset class. I suppose this would be the solution to multi table inheritance though if I could solve it... What I'm after is a solution to make it transparet for the clients of the Asset class (and associates). I don't care how much/ugly code I have to hide in Asset to just make it transparent to the clients (since I have control over Asset, not over the clients). Have I missed anything? 2) sounds easy and good but I can't grip the querying situation (beyond what I described above). Have I missed something there? Thanks for the answers. /Marcus