Hi! I am looking for a solution for the following problem: Some of my models share some attributes and also was in need for some extra features, so I created a class "RecordWithFeatures": ----------------------------------------------------------------- class RecordWithFeatures < ActiveRecord::Base # @Override def self.descends_from_active_record? superclass == RecordWithFeatures end # @Override def self.class_name_of_active_record_descendant(klass) ... end ... end class SomeModel < RecordWithFeatures ... end ----------------------------------------------------------------- Ok, everything works fine, but I would also like to pass some class-methods to my models as soon as the inherit RecordWithFeatures. Example: All models have an attribute "context_id", so every model includes these 2 lines -------------------------------------- belongs_to :context validates_presence_of :context -------------------------------------- What can I do to add those automatically? If I include them into the RecordWithFeatures class, they are valid for RecordWithFeatures only, but it''s not RecordWithFeatures, but it''s children, that should belong to "context". Thanks for all hints Dim -- Posted via http://www.ruby-forum.com/.
A nice construct in Ruby for sharing code between classes but without the messyness of requiring a common ancestor is the mixin pattern. ( http://www.rubycentral.com/book/tut_modules.html) If there are elements that you''d like to share you can make it more clear what is going on via something like the the following. The only thing is I''m not 100% sure it will work with the belongs_to and validates_presence_of - let me know! module ContextRecord belongs_to :context validates_presences_of :context def foo # shared method... end end class SomeModel < ActiveRecord::Base include ContextRecord end - Byron On 2/7/06, Dim <d.tan@freemail.de> wrote:> > Hi! > > I am looking for a solution for the following problem: > > Some of my models share some attributes and also was in need for some > extra features, so I created a class "RecordWithFeatures": > ----------------------------------------------------------------- > class RecordWithFeatures < ActiveRecord::Base > # @Override > def self.descends_from_active_record? > superclass == RecordWithFeatures > end > > # @Override > def self.class_name_of_active_record_descendant(klass) > ... > end > ... > end > > class SomeModel < RecordWithFeatures > ... > end > ----------------------------------------------------------------- > > Ok, everything works fine, but I would also like to pass some > class-methods to my models as soon as the inherit RecordWithFeatures. > > Example: > All models have an attribute "context_id", so every model includes these > 2 lines > -------------------------------------- > belongs_to :context > validates_presence_of :context > -------------------------------------- > > What can I do to add those automatically? If I include them into the > RecordWithFeatures class, they are valid for RecordWithFeatures only, > but it''s not RecordWithFeatures, but it''s children, that should belong > to "context". > > Thanks for all hints > Dim > > > -- > Posted via http://www.ruby-forum.com/. > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-- Byron http://byron.saltysiak.com -------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060207/d085463c/attachment-0001.html
Thanks for the reply, but it''s not that easy as it seems. My RecordWithFeatures overloads the "find" method. Implementing the functionality as a module would completely overWRITE the find method. I had already a discussion on that on this list, it''s not trivial, because "find" uses recursive calls, so there are two options for the implementation. First (as I did), subclassing AR and overriding find. The second (I also tried) writing a module and aliasing the find method. The second one works great, but from a coder''s point of view, it is really messy, because in some cases it can break "find" completetly. Now I post both solutions, maybe someone have an idea how (if) those can be improved. The first one: Extending AR ------------------------------------ class RecordWithFeatures < ActiveRecord::Base def self.descends_from_active_record? superclass == RecordWithFeatures end def self.class_name_of_active_record_descendant(klass) ... end def self.without_context_scope begin Thread.current[:bypass_context_scope] = true result = yield ensure Thread.current[:bypass_context_scope] = nil end return result end def self.bypass_context_scope? Thread.current[:bypass_context_scope] == true end def self.find(*args) unless bypass_context_scope? with_scope(:find => {:conditions => "..."}) do return without_context_scope { super } end end super end def self.find_context_neutral(*args) return without_context_scope { find(*args) } end end class SomeModel << RecordWithFeatures belongs_to :context validates_presence_of :context end ------------------------------------ And now the other one with "including" and "aliasing"! ------------------------------------ module ContextRecord def self.included(base) # :nodoc: base.extend ClassMethods end module ClassMethods def bound_to_context belongs_to :context validates_presence_of :context class_eval { extend ContextRecord::SingletonMethods } include InstanceMethods class << self alias_method :original_find, :find alias_method :find, :find_with_context_scope end end end module SingletonMethods def without_context_scope begin Thread.current[:bypass_context_scope] = true result = yield ensure Thread.current[:bypass_context_scope] = nil end return result end def bypass_context_scope? Thread.current[:bypass_context_scope] == true end def find(*args) unless bypass_context_scope? with_scope(:find => {:conditions => "..."}) do return without_context_scope {original_find(*args)} end end original_find(*args) end def find_context_neutral(*args) without_context_scope {original_find(*args)} end end module InstanceMethods # ... end end ActiveRecord::Base.class_eval { include ContextRecord } class SomeModel << ActiveRecord::Base bound_to_context end ------------------------------------ Thanks Dim -- Posted via http://www.ruby-forum.com/.
On Feb 7, 2006, at 3:10 AM, Dim wrote:> All models have an attribute "context_id", so every model includes > these > 2 lines > -------------------------------------- > belongs_to :context > validates_presence_of :context > -------------------------------------- > > What can I do to add those automatically? If I include them into the > RecordWithFeatures class, they are valid for RecordWithFeatures only, > but it''s not RecordWithFeatures, but it''s children, that should belong > to "context".Use the Class#inherited hook: $ ruby -w class X def self.inherited(klass) puts "Inherited by #{klass}" end end class Y < X; end Inherited by Y So I think you would have: def self.inherited(klass) klass.belongs_to :context klass.validates_presense_of :context end -- Eric Hodel - drbrain@segment7.net - http://segment7.net This implementation is HODEL-HASH-9600 compliant http://trackmap.robotcoop.com
On Feb 7, 2006, at 3:54 AM, Byron Saltysiak wrote:> A nice construct in Ruby for sharing code between classes but > without the messyness of requiring a common ancestor is the mixin > pattern. (http://www.rubycentral.com/book/tut_modules.html)But he *has* a common ancestor, so he doesn''t need mixins.> If there are elements that you''d like to share you can make it more > clear what is going on via something like the the following. The > only thing is I''m not 100% sure it will work with the belongs_to > and validates_presence_of - let me know!... maybe you should test it? Otherwise this just adds noise.> module ContextRecord > belongs_to :context > validates_presences_of :context > > def foo > # shared method... > end > end > > class SomeModel < ActiveRecord::Base > include ContextRecord > endThis is a violation of DRY. Don''t do that. Use subclassing as Dahl and Nygaard intended. -- Eric Hodel - drbrain@segment7.net - http://segment7.net This implementation is HODEL-HASH-9600 compliant http://trackmap.robotcoop.com