--Multipart_Tue_Nov_17_11:41:43_2009-1
Content-Type: text/plain; charset=US-ASCII

Hi,

At Tue, 17 Nov 2009 06:48:42 +0900,
Yehuda Katz <wycats / gmail.com> wrote:
> For instance, you can do the following in Rails:
> 
> module ActionController::MimeResponds
>   extend ActiveSupport::Concern
> 
>     included do
>       inheritable_accessor :responder, :mimes_for_respond_to,
> :instance_writer false
>       self.responder  ctionController::Responder
>       clear_respond_to
>     end
> end

Do you mean that `self.responder  ctionController::Responder' can be replaced
by `self.responder  esponder' on Ruby 1.8?

Then, does it really work on Ruby 1.8?
I guess MimeResponds should be nested in ActionController as follows:

module ActionController
  module MimeResponds
    ...
  end
end

> > This is a wrapper around the very common:
> 
> def self.included(klass)
>   klass.class_eval do
>     # code here
>   end
> end
 
For Matz and Koichi (and other people who aren't familiar with Rails),
included and append_features are overridden in ActiveSupport::Concern
as follows:

    def included(base  il, &block)
      if base.nil?
        @_included_block  lock
      else
        super
      end
    end

    def append_features(base)
      if super
        ...
        base.class_eval(&@_included_block) if instance_variable_defined?("@_incl
uded_block")
      end
    end

> Because I understand the utility in the Ruby 1.9 approach, I would like to
> suggest that users be allowed to choose which scoping they want. I suggest
> that module_eval, by default, revert to Ruby 1.8 behavior. I also suggest
> that we add a new method (or flag to module_eval) to enable the new
> behavior.

I basically prefer the behavior of Ruby 1.9.  I think class variables
should especially be lookuped like Ruby 1.9 because they can't be
accessed outside of a class or its instance.  Then, I guess your
problem may be able to fixed differently.

The problem is not that Ruby 1.9 prepends the receiver of class_eval to
the constant lookup path, but that a wrapper of class_eval can't be
implemented on Ruby 1.9.

The following program works on both Ruby 1.8 and 1.9:

class Foo
end

module ActionController
  Responder  This is a Responder"
  module MimeResponds
    Foo.class_eval do
      p Responder
    end
  end
end

However, the following program doesn't work on Ruby 1.9:

def my_class_eval(klass, &block)
  klass.class_eval(&block)
end

class Foo
end

module ActionController
  Responder  This is a Responder"
  module MimeResponds
    my_class_eval(Foo) do
      p Responder
    end
  end
end

It's because class_eval prepends the reciver to the constant lookup
path at the time of invocation of class_eval.  I think it should
prepends the receiver to the constant lookup path which the given block
holds.

I have attached a patch to fix it.  If it's acceptable, I'll write a
test and commit them.

-- 
Shugo Maeda <shugo / ruby-lang.org>

--Multipart_Tue_Nov_17_11:41:43_2009-1
Content-Type: application/octet-stream; type=patch
Content-Disposition: attachment; filename="class_eval.diff"
Content-Transfer-Encoding: 7bit

Index: insns.def
--- insns.def	(revision 25805)
+++ insns.def	(working copy)
@@ -944,7 +944,7 @@ defineclass
 	rb_bug("unknown defineclass type: %d", (int)define_type);
     }
 
-    COPY_CREF(class_iseq->cref_stack, vm_cref_push(th, klass, NOEX_PUBLIC));
+    COPY_CREF(class_iseq->cref_stack, vm_cref_push(th, klass, NOEX_PUBLIC, NULL));
 
     /* enter scope */
     vm_push_frame(th, class_iseq,
Index: vm_eval.c
--- vm_eval.c	(revision 25805)
+++ vm_eval.c	(working copy)
@@ -17,7 +17,7 @@ static inline VALUE vm_yield_with_cref(rb_thread_t
 static inline VALUE vm_yield(rb_thread_t *th, int argc, const VALUE *argv);
 static inline VALUE vm_backtrace(rb_thread_t *th, int lev);
 static int vm_backtrace_each(rb_thread_t *th, int lev, rb_backtrace_iter_func *iter, void *arg);
-static NODE *vm_cref_push(rb_thread_t *th, VALUE klass, int noex);
+static NODE *vm_cref_push(rb_thread_t *th, VALUE klass, int noex, rb_block_t *blockptr);
 static VALUE vm_exec(rb_thread_t *th);
 static void vm_set_eval_stack(rb_thread_t * th, VALUE iseqval, const NODE *cref);
 static int vm_collect_local_variables_in_heap(rb_thread_t *th, VALUE *dfp, VALUE ary);
@@ -1100,13 +1100,14 @@ yield_under(VALUE under, VALUE self, VALUE values)
 {
     rb_thread_t *th  ET_THREAD();
     rb_block_t block, *blockptr;
-    NODE *cref  m_cref_push(th, under, NOEX_PUBLIC);
+    NODE *cref;
 
     if ((blockptr  C_GUARDED_PTR_REF(th->cfp->lfp[0])) ! ) {
 	block  blockptr;
 	block.self  elf;
 	th->cfp->lfp[0]  C_GUARDED_PTR(&block);
     }
+    cref  m_cref_push(th, under, NOEX_PUBLIC, &block);
 
     if (values Qundef) {
 	return vm_yield_with_cref(th, 0, 0, cref);
@@ -1120,7 +1121,7 @@ yield_under(VALUE under, VALUE self, VALUE values)
 static VALUE
 eval_under(VALUE under, VALUE self, VALUE src, const char *file, int line)
 {
-    NODE *cref  m_cref_push(GET_THREAD(), under, NOEX_PUBLIC);
+    NODE *cref  m_cref_push(GET_THREAD(), under, NOEX_PUBLIC, NULL);
 
     if (rb_safe_level() > ) {
 	StringValue(src);
Index: vm_insnhelper.c
--- vm_insnhelper.c	(revision 25805)
+++ vm_insnhelper.c	(working copy)
@@ -1066,14 +1066,17 @@ vm_get_cref(const rb_iseq_t *iseq, const VALUE *lf
 }
 
 static NODE *
-vm_cref_push(rb_thread_t *th, VALUE klass, int noex)
+vm_cref_push(rb_thread_t *th, VALUE klass, int noex, rb_block_t *blockptr)
 {
     rb_control_frame_t *cfp  m_get_ruby_level_caller_cfp(th, th->cfp);
     NODE *cref  EW_BLOCK(klass);
     cref->nd_file  ;
     cref->nd_visi  oex;
 
-    if (cfp) {
+    if (blockptr) {
+	cref->nd_next  m_get_cref(blockptr->iseq, blockptr->lfp, blockptr->dfp);
+    }
+    else if (cfp) {
 	cref->nd_next  m_get_cref(cfp->iseq, cfp->lfp, cfp->dfp);
     }
 

--Multipart_Tue_Nov_17_11:41:43_2009-1--