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