views:

194

answers:

1

I was trying to understand this call:

deprecate :new_record?, :new?

which uses this deprecate method:

   def deprecate(old_method, new_method)
      class_eval <<-RUBY, __FILE__, __LINE__ + 1
        def #{old_method}(*args, &block)
          warn "\#{self.class}##{old_method} is deprecated," + 
                "use \#{self.class}##{new_method} instead"
          send(#{new_method.inspect}, *args, &block)
        end
      RUBY
    end

I don't really understand the metaprogramming that's being used here. But, is this just another way of aliasing the new_record? method - so in effect, new_record? is still available but it issues a warning when you use it? Would anyone like to explain how this works?

+6  A: 

ok, so what happens here is that all the functionality of old_method has been moved to new_method by the programmer. To make both names point to the same functionality but note the deprecation, the programmer puts in the deprecate line. This causes the string specified in the <-RUBY heredoc (http://en.wikipedia.org/wiki/Heredoc) to be interpreted as code (evaluated) at the class level. The string interpolations work just as they do in normal ruby strings.

The code then looks something like this (if we were to expand out the metaprogramming)

class SomeClass
  def new?; true; end

  deprecate :new_record?, :new # this generates the following code

  def new_record?(*args, &block)
    warn "SomeClass#new_record? is deprecated," + 
            "use SomeClass#new? instead"
    send(:new?, *args, &block)
  end
end

I hope that makes sense

Ben Hughes
Yes, thanks. That makes sense. Just one thing I still don't get - this syntax: <<-RUBY, __FILE__, __LINE__ + 1If "<<-RUBY" starts the heredoc, what is the rest for? This part: __FILE__, __LINE__ + 1
Hola
If I change the definition from deprecate to: "alias_method :new_record?, :new?", will it have the same effect as the above except that I won't receive the warning?
Hola
to the best of my knowledge, yes. The __FILE__ and __LINE__ info are optional positioning arguments for class_eval. If they were missing and an exception were raised, the backtrace would include something like `(eval):3 new_record?`. __FILE__ is the current source file and __LINE__ is the current line number, so in case of failure, the backtrace will point to where the eval statement was defined.
Ben Hughes
I thought everything after the start of the heredoc ("<<-RUBY") would be treated as part of the string/heredoc. But I'll take your explanation as a given.
Hola
heredocs are kind of funny in ruby. They don't actually start until the next line. You can do even funnier things with them http://log.gmarik.info/2007/12/rubys-here-document-heredoc-mini.html (The last example).
Ben Hughes
Ah, that's why! Thanks for the link. Yeah, that last example is pretty freaky :)
Hola
no problem. Ruby starts to almost make sense after long enough, I promise!
Ben Hughes
There would be a difference in using alias_method. With alias_method, you get two identical but independent methods. If you change the behavior of the new method, it won't change the old one. But with this deprecate implementation, the old method just issues the warning and calls through to the new one.
Chuck