On Tue, Nov 17, 2009 at 1:29 AM, Shugo Maeda <shugo / ruby-lang.org> wrote:

> However, I guess "to add the dynamic scope _after_ the lexical scope,
> rather than _before_ it" doesn't affect class variable lookup, because
> only the first element of Module.nesting is used during class variable
> lookup.

Maeda-san,

I'm intrigued by this thread, because I have a suspicion that it might
have something to do with a mystery I've been unable to understand
which seems to relate to the differences in 1.9 in either the eval
methods, class variable access, or both.

I've recently converted a Rails application to run on 1.9, and have a
strange phenomenon that if I am using Ruby 1.9 as my 'default' ruby
the rails generator scripts fail

$ which ruby
/Users/rick/.rvm/ruby-1.9.1-p243/bin/ruby
 $ script/generate model foo
undefined method `exists' for #<ActiveSupport::BufferedLogger:0x00000102b3a250>

In order to run script/generate I need to switch to Ruby 1.8

This rails project makes use of the logbuddy gem
http://github.com/relevance/log_buddy

defines a module, whose init method is run when the gem is intialized:

module LogBuddy
  # Configure and include LogBuddy into Object.
  # You can pass in any of the following configuration options:
  #
  # * <tt>:logger</tt> - the logger instance that LogBuddy should use
(if not provided,
  #   tries to default to RAILS_DEFAULT_LOGGER, and then to a STDOUT logger).
  # * <tt):log_to_stdout</tt> - whether LogBuddy should _also_ log to
STDOUT, very helpful for Autotest (default is +true+).
  def self.init(options = {})
    @logger = options[:logger]
    @log_to_stdout = options.has_key?(:log_to_stdout) ?
options[:log_to_stdout] : true
    mixin_to_object
  end

  # Add the LogBuddy::Mixin to Object instance and class level.
  def self.mixin_to_object
    Object.class_eval {
      include LogBuddy::Mixin
      extend LogBuddy::Mixin
    }
  end

  class << self
    include LogBuddy::Utils
    def logger
      return @logger if @logger
      @logger = init_default_logger
    end

    def log_to_stdout?
      @log_to_stdout
    end

    private

    def init_default_logger
      if Object.const_defined?("RAILS_DEFAULT_LOGGER")
        @logger = Object.const_get("RAILS_DEFAULT_LOGGER")
      else
        require 'logger'
        @logger = Logger.new(STDOUT)
      end
    end

  end
end

and LogBuddy::Mixin looks (in part) like this:

module LogBuddy
   module Mixin
   def logger
      LogBuddy.logger
    end
  end
end

Now the generate script is blowing up because Rails generators also
expect a method named logger and expect it to return a different kind
of logger.

That logger method is implemented as a cattr (another bit of
ActiveSupport 'metamagic'

module Rails
  # Rails::Generator is a code generation platform tailored for the Rails
  # web application framework.
  module Generator
    # The base code generator is bare-bones.  It sets up the source and
    # destination paths and tells the logger whether to keep its trap shut.
    class Base
     # ...
      cattr_accessor :logger
    end
  end
end

and cattr_accessor calls both cattr_reader and cattr_writer,
cattr_reader generates class and instance methods

class Class
  def cattr_reader(*syms)
    syms.flatten.each do |sym|
      next if sym.is_a?(Hash)
      class_eval(<<-EOS, __FILE__, __LINE__)
        unless defined? @@#{sym}  # unless defined? @@hair_colors
          @@#{sym} = nil          #   @@hair_colors = nil
        end                       # end
                                  #
        def self.#{sym}           # def self.hair_colors
          @@#{sym}                #   @@hair_colors
        end                       # end
                                  #
        def #{sym}                # def hair_colors
          @@#{sym}                #   @@hair_colors
        end                       # end
      EOS
    end
  end

  def cattr_writer(*syms)
    #  code snipped for brevity.
  end

  def cattr_accessor(*syms)
    cattr_reader(*syms)
    cattr_writer(*syms)
  end
end

In Ruby 1.8 things work as expected, in an instance of
Rails::Generator::Base or one of its subclasses, the logger method
produced by cattr_reader is found when the logger method is invoked.

But for some reason I haven't been able to decipher, in Ruby 1.9 the
logger method generated by the cattr_reader method isn't found, and
the Object#logger method defined by LogBuddy is executed instead.

I'd love any help in understanding what's going on here.

-- 
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale