Hi!
I''m trying to extend ActiveRecord''s find method (Rails 1.0,
Ruby 1.8.2),
but I recognize a strange behaviour of the "alias_method" call.
I wrote a very simple script to explain my problem:
------------------------------------------------------
module ActiveRecordExtension
def self.included(base)
base.extend(ClassMethods)
base.class_eval do
class << self
p "Aliasing find" # Used to check if alias_method
isn''t
called twice
alias_method :find_without_someting, :find
alias_method :find, :find_with_someting
end
end
end
module ClassMethods # :nodoc:
def find_with_something(*args)
p "0"
x = find_without_something(*args)
p "1"
return x
end
end
end
------------------------------------------------------
After including this script I called:
------------------------------------------------------
p " --> find"
user = User.find(1)
p " --> find_with_something"
user = User.find_with_something(1)
p " --> find_without_something"
user = User.find_without_something(1)
------------------------------------------------------
and the result output was:
------------------------------------------------------
" --> find"
"0"
"0"
"0"
"1"
"1"
"1"
" --> find_with_something"
"0"
"0"
"0"
"1"
"1"
"1"
" --> find_without_something"
"0"
"0"
"1"
"1"
------------------------------------------------------
Can anyboby explain me what actully happens there. In my opinion,
''find_without_something'' should represent the original
''find'' method, so
there should not be any output. For a strange reason it outputs 0,0,1,1
which in fact means that it calls ''find_with_something'' twice.
Many thanks in advance for any hint or explanation.
Dim
d|vision|factory wrote:> Can anyboby explain me what actully happens there. In my opinion, > ''find_without_something'' should represent the original ''find'' method, so > there should not be any output. For a strange reason it outputs 0,0,1,1 > which in fact means that it calls ''find_with_something'' twice.It might be something to do with the fact that the base find() method calls itself recursively, which means your alias will intercept that recursive call as well as the initial. Then again, it might not... -- Alex
On Feb 1, 2006, at 5:08 AM, d|vision|factory wrote:> I''m trying to extend ActiveRecord''s find method (Rails 1.0, Ruby > 1.8.2), but I recognize a strange behaviour of the "alias_method" > call. > I wrote a very simple script to explain my problem:Don''t do that. Use inheritance as Alan Kay Himself intended. This aliasing is a very bad software engineering practice when you have. When you alias you have no idea what the method call order is unless you read each file and make at least a mental map of the call order. (No, really, tell me what the call order for ActionController::Base#process is.) When you use inheritance the call order is explicit from the inheritance chain. -- Eric Hodel - drbrain@segment7.net - http://segment7.net This implementation is HODEL-HASH-9600 compliant http://trackmap.robotcoop.com
Eric Hodel wrote:> When you use inheritance the call order is explicit from the inheritance > chain....mostly. Multiple inheritance can make a right dog''s dinner out of it, because the call order then becomes dependent on the order of module inclusion. -- Alex
On Feb 1, 2006, at 10:47 AM, Alex Young wrote:> Eric Hodel wrote: >> When you use inheritance the call order is explicit from the >> inheritance chain. > ...mostly. Multiple inheritance can make a right dog''s dinner out > of it, because the call order then becomes dependent on the order > of module inclusion.Which is plainly visible via Module#ancestors. But this is only using single inheritance, so I don''t see how that''s relevant. -- Eric Hodel - drbrain@segment7.net - http://segment7.net This implementation is HODEL-HASH-9600 compliant http://trackmap.robotcoop.com
Thanks a lot for the hint. I am a quite experienced coder (but a newbie
to Ruby), so I appreciate your argument, that "aliasing" is not clean
method at all, but I still have a problem.
Let''s start from the beginning:
I need to extend ActiveRecord''s "find" method so it should by
default
use a "with_scope" argument. When I extend ActiveRecord, it would look
like the following:
class MyRecord < ActiveRecord::Base
def self.find(*args)
with_scope(:find => {:conditions => "..."}) do
return find_without_scope(*args)
end
end
def self.find_without_scope(*args)
return ???
end
end
class SomeObject < MyRecord
end
My problem is, how can I access the parent-class ''find''
method? I can''t
just call ''MyRecord.superclass.find(*args)'' because it would
call
''ActiveRecord::Base.find'' which is actually not the same as
''SomeObject
.find''. If I try that I get ''ActiveRecord::Base
doesn''t belong in a
hierarchy descending from ActiveRecord'', as expected.
Are they any suggestions how the ActiveRecord''s
''find'' method can
generally be extended in a ''clean'' manner, without
''aliasing'' (I''ve done
it similarly to the ''acts_as_paranoid'' extension using
aliasing, but
it''s rather a hack than clean coding).
Thanks once again,
Dim
Dim-
If I were you I would not try to over-ride AR::Base.find like that
You should just make your own method: find_with_scope. Then in that
method you can add your scope to the :conditions and then sedn it
along its way to AR::Base.find. then you wont have any problems with
find calling itself recursively or other code or plugins trying to
override find and making your code explode.
In my new plugin that deals with building the :conditions hash with
pure ruby like syntax, I wanted to override find but found it much
better to make my own ez_find that did what it needed to do and then
called AR::Base.find with the new :conditions. Here is my code for
that, you could adopt something similar and put it in a plugin so you
caoul d use it in your other apps as well.:
module EZ
#
# EZ::Condition plugin for generating the :conditions where clause
# for ActiveRecord::Base.find. And an extension to
ActiveRecord::Base
# called AR::Base.find_with_conditions that takes a block and
builds
# the where clause dynamically for you.
#
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
# The block recieves the condition for the model itself, as
well as for any :include associated model:
# Model.ez_find( :all, :include => [:comments, :readers]) do |
model, attachment, reader|
# model.title =~ ''%article%''
# attachment.type = ''image/png''
# reader.id = 2
# end
def ez_find(what, *args, &block)
options = args.last.is_a?(Hash) ? args.last : {}
options[:include] ||= []; options[:include] = [options
[:include]] if options[:include].kind_of?(Symbol)
outer_mapping = options.delete(:outer) || {} # preset :outer
value for each :include subcondition, defaults to :and
outer_mapping.default = :and
inner_mapping = options.delete(:inner) || {} # preset :inner
value for each :include subcondition, defaults to :and
inner_mapping.default = :and
if block_given?
klass = self.name.downcase.to_sym
conditions = [ez_condition(:outer => outer_mapping
[klass], :inner => inner_mapping[klass])] # conditions on self first
options[:include].uniq.each do |assoc|
conditions << reflect_on_association
(assoc).klass.ez_condition(:outer => outer_mapping[assoc], :inner =>
inner_mapping[assoc])
end
yield *conditions
condition = Caboose::EZ::Condition.new
conditions.each { |c| condition << c }
options[:conditions] = condition.to_sql
# p options[:conditions] if $DEBUG
end
self.find(what, options)
end
alias :find_with_conditions :ez_find
end # EZ module
and then:
ActiveRecord::Base.send :include, EZ #to include the code into rails.
See that last line? self.find(what, options)? Thats the call to
AR::Base.find, it just uses all the other trickery i do to build the
:conditions and send it to find. You can get the source of my plugin
here:
http://opensvn.csie.org/ezra/rails/plugins/dev/ez_where/
I would recommend you stay away from aliasing or overiding
AR::Base.find and make your own method, it will save you many headaches.
Cheers-
-Ezra
On Feb 2, 2006, at 1:15 AM, Dimitrij Denissenko wrote:
> Thanks a lot for the hint. I am a quite experienced coder (but a
> newbie to Ruby), so I appreciate your argument, that "aliasing"
is
> not clean method at all, but I still have a problem.
>
> Let''s start from the beginning:
> I need to extend ActiveRecord''s "find" method so it
should by
> default use a "with_scope" argument. When I extend ActiveRecord,
it
> would look like the following:
>
> class MyRecord < ActiveRecord::Base
> def self.find(*args)
> with_scope(:find => {:conditions => "..."}) do
> return find_without_scope(*args)
> end
> end
>
> def self.find_without_scope(*args)
> return ???
> end
> end
>
> class SomeObject < MyRecord
> end
>
> My problem is, how can I access the parent-class ''find''
method? I
> can''t just call
''MyRecord.superclass.find(*args)'' because it would
> call ''ActiveRecord::Base.find'' which is actually not the
same as
> ''SomeObject .find''. If I try that I get
''ActiveRecord::Base doesn''t
> belong in a hierarchy descending from ActiveRecord'', as expected.
>
> Are they any suggestions how the ActiveRecord''s
''find'' method can
> generally be extended in a ''clean'' manner, without
''aliasing'' (I''ve
> done it similarly to the ''acts_as_paranoid'' extension
using
> aliasing, but it''s rather a hack than clean coding).
>
> Thanks once again,
> Dim
>
> _______________________________________________
> Rails mailing list
> Rails@lists.rubyonrails.org
> http://lists.rubyonrails.org/mailman/listinfo/rails
>
-Ezra Zygmuntowicz
Yakima Herald-Republic
WebMaster
http://yakimaherald.com
509-577-7732
ezra@yakima-herald.com
On Feb 2, 2006, at 1:15 AM, Dimitrij Denissenko wrote:> Thanks a lot for the hint. I am a quite experienced coder (but a > newbie to Ruby), so I appreciate your argument, that "aliasing" is > not clean method at all, but I still have a problem. > > Let''s start from the beginning: > I need to extend ActiveRecord''s "find" method so it should by > default use a "with_scope" argument. When I extend ActiveRecord, it > would look like the following: > > class MyRecord < ActiveRecord::Base > def self.find(*args) > with_scope(:find => {:conditions => "..."}) do > return find_without_scope(*args) > end > end > > def self.find_without_scope(*args) > return ??? > end > end > > class SomeObject < MyRecord > end > > My problem is, how can I access the parent-class ''find'' method? I > can''t just call ''MyRecord.superclass.find(*args)'' because it would > call ''ActiveRecord::Base.find'' which is actually not the same as > ''SomeObject .find''. If I try that I get ''ActiveRecord::Base doesn''t > belong in a hierarchy descending from ActiveRecord'', as expected.super> Are they any suggestions how the ActiveRecord''s ''find'' method can > generally be extended in a ''clean'' manner, without ''aliasing'' (I''ve > done it similarly to the ''acts_as_paranoid'' extension using > aliasing, but it''s rather a hack than clean coding).Look at CachedModel in the cached_model gem here: http://rubyforge.org/projects/rctools/ It overrides find and find_by_sql. -- Eric Hodel - drbrain@segment7.net - http://segment7.net This implementation is HODEL-HASH-9600 compliant http://trackmap.robotcoop.com