Forum: Ruby on Rails Explanation of "alias_method"

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
29ba98b6e6e6c9d7ccb3c03b527b9084?d=identicon&s=25 d|vision|factory (Guest)
on 2006-02-01 14:11
(Received via mailing list)
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
Ad7805c9fcc1f13efc6ed11251a6c4d2?d=identicon&s=25 Alex Young (Guest)
on 2006-02-01 14:41
(Received via mailing list)
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...
58479f76374a3ba3c69b9804163f39f4?d=identicon&s=25 Eric Hodel (Guest)
on 2006-02-01 19:19
(Received via mailing list)
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
Ad7805c9fcc1f13efc6ed11251a6c4d2?d=identicon&s=25 Alex Young (Guest)
on 2006-02-01 19:46
(Received via mailing list)
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.
58479f76374a3ba3c69b9804163f39f4?d=identicon&s=25 Eric Hodel (Guest)
on 2006-02-01 22:21
(Received via mailing list)
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
3def758b6946696573c208082d915dde?d=identicon&s=25 Dimitrij Denissenko (Guest)
on 2006-02-02 10:17
(Received via mailing list)
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
58479f76374a3ba3c69b9804163f39f4?d=identicon&s=25 Eric Hodel (Guest)
on 2006-02-02 21:02
(Received via mailing list)
On Feb 2, 2006, at 1:15 AM, Dimitrij Denissenko wrote:

>  def self.find(*args)
> 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
132a94ca65959bda6c74fae54bff2425?d=identicon&s=25 Ezra Zygmuntowicz (Guest)
on 2006-02-02 21:02
(Received via mailing list)
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:

>  def self.find(*args)
> class SomeObject < MyRecord
> 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
This topic is locked and can not be replied to.