Using #include at the instance level?

Discussion in 'Ruby' started by Intransition, Dec 29, 2009.

  1. Intransition

    Intransition Guest

    I would like to use #include at an instance level, such that it
    behaves just as it does at a class level. After a number of
    experiments I thought for sure it would work if I ran the include
    through the object's singleton. Alas, submodules remain inaccessible,
    eg.

    module M
    def m1; "m1"; end
    module N
    def self.n1; "n1"; end
    end
    end

    class X
    def initialize(*mods)
    (class << self; self; end).class_eval do
    include *mods
    end
    end
    def m ; m1 ; end
    def n ; N.n1 ; end
    end

    x = X.new(M)
    p x.m
    p x.n #=> uninitialized constant X::N (NameError)

    Is there any way to achieve this?
    Intransition, Dec 29, 2009
    #1
    1. Advertising

  2. Intransition

    fkocherga Guest

    On Dec 28, 2009, at 10:29 PM, Intransition wrote:

    > I would like to use #include at an instance level, such that it
    > behaves just as it does at a class level. After a number of
    > experiments I thought for sure it would work if I ran the include
    > through the object's singleton. Alas, submodules remain inaccessible,
    > eg.
    >=20
    > module M
    > def m1; "m1"; end
    > module N
    > def self.n1; "n1"; end
    > end
    > end
    >=20
    > class X
    > def initialize(*mods)
    > (class << self; self; end).class_eval do
    > include *mods
    > end
    > end
    > def m ; m1 ; end
    > def n ; N.n1 ; end
    > end
    >=20
    > x =3D X.new(M)
    > p x.m
    > p x.n #=3D> uninitialized constant X::N (NameError)
    >=20
    > Is there any way to achieve this?
    >=20

    You may write:

    class X
    def eigenclass
    class << self; self; end;
    end
    =20
    def initialize(*mods)
    eigenclass.instance_eval do
    include *mods=20
    end
    end
    def m ; m1 ; end
    def n; eigenclass::N.n1 ; end
    end

    I guess constants not like methods are not searched within eigenclass, =
    and that's the reason why you are getting an error.
    --
    Fedor Kocherga
    http://sidenotes.kocherga.info/
    fkocherga, Dec 29, 2009
    #2
    1. Advertising

  3. 2009/12/29 Intransition <>:
    > I would like to use #include at an instance level, such that it
    > behaves just as it does at a class level. After a number of
    > experiments I thought for sure it would work if I ran the include
    > through the object's singleton. Alas, submodules remain inaccessible,
    > eg.
    >
    > =A0module M
    > =A0 =A0def m1; "m1"; end
    > =A0 =A0module N
    > =A0 =A0 =A0def self.n1; "n1"; end
    > =A0 =A0end
    > =A0end
    >
    > =A0class X
    > =A0 =A0def initialize(*mods)
    > =A0 =A0 =A0(class << self; self; end).class_eval do
    > =A0 =A0 =A0 =A0include *mods
    > =A0 =A0 =A0end
    > =A0 =A0end
    > =A0 =A0def m ; m1 ; end
    > =A0 =A0def n ; N.n1 ; end


    Hm, this method above won't work as scope resolution rules for
    constants are different.

    > =A0end
    >
    > =A0x =3D X.new(M)
    > =A0p x.m
    > =A0p x.n =A0 =A0#=3D> uninitialized constant X::N (NameError)
    >
    > Is there any way to achieve this?


    Did you consider using #extend?

    class X
    def initialize(*mods)
    extend *mods
    end
    end

    What do you really want to achieve? Do you have a more telling example?

    Kind regards

    robert

    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
    Robert Klemme, Dec 29, 2009
    #3
  4. Intransition

    Intransition Guest

    On Dec 29, 3:29=A0am, Robert Klemme <> wrote:
    > 2009/12/29 Intransition <>:
    >
    >
    >
    > > I would like to use #include at an instance level, such that it
    > > behaves just as it does at a class level. After a number of
    > > experiments I thought for sure it would work if I ran the include
    > > through the object's singleton. Alas, submodules remain inaccessible,
    > > eg.

    >
    > > =A0module M
    > > =A0 =A0def m1; "m1"; end
    > > =A0 =A0module N
    > > =A0 =A0 =A0def self.n1; "n1"; end
    > > =A0 =A0end
    > > =A0end

    >
    > > =A0class X
    > > =A0 =A0def initialize(*mods)
    > > =A0 =A0 =A0(class << self; self; end).class_eval do
    > > =A0 =A0 =A0 =A0include *mods
    > > =A0 =A0 =A0end
    > > =A0 =A0end
    > > =A0 =A0def m ; m1 ; end
    > > =A0 =A0def n ; N.n1 ; end

    >
    > Hm, this method above won't work as scope resolution rules for
    > constants are different.
    >
    > > =A0end

    >
    > > =A0x =3D X.new(M)
    > > =A0p x.m
    > > =A0p x.n =A0 =A0#=3D> uninitialized constant X::N (NameError)

    >
    > > Is there any way to achieve this?

    >
    > Did you consider using #extend?
    >
    > =A0class X
    > =A0 =A0def initialize(*mods)
    > =A0 =A0 =A0extend *mods
    > =A0 =A0end
    > end
    >
    > What do you really want to achieve? =A0Do you have a more telling example=

    ?

    It's for encapsulating test cases. Eg.

    TestCase.new(SomeClass) do
    ...
    end

    Within the test case block it would help to handle #include, to make
    tests less verbose.

    Instead of making an instance of TestCase for each case, at this point
    it looks like I'll have to create a new subclass of it.
    Intransition, Dec 29, 2009
    #4
  5. Intransition

    Intransition Guest

    On Dec 29, 1:35=A0am, fkocherga <> wrote:

    > I guess constants not like methods are not searched within eigenclass, an=

    d that's the reason why you are getting an error.

    You are right. That appears to be the case. I tried setting a constant
    directly in the eigenclass and the instance could not see it either.
    The behavior surprises me.
    Intransition, Dec 29, 2009
    #5
  6. 2009/12/29 Intransition <>:
    >
    >
    > On Dec 29, 3:29=A0am, Robert Klemme <> wrote:
    >> 2009/12/29 Intransition <>:
    >>
    >>
    >>
    >> > I would like to use #include at an instance level, such that it
    >> > behaves just as it does at a class level. After a number of
    >> > experiments I thought for sure it would work if I ran the include
    >> > through the object's singleton. Alas, submodules remain inaccessible,
    >> > eg.

    >>
    >> > =A0module M
    >> > =A0 =A0def m1; "m1"; end
    >> > =A0 =A0module N
    >> > =A0 =A0 =A0def self.n1; "n1"; end
    >> > =A0 =A0end
    >> > =A0end

    >>
    >> > =A0class X
    >> > =A0 =A0def initialize(*mods)
    >> > =A0 =A0 =A0(class << self; self; end).class_eval do
    >> > =A0 =A0 =A0 =A0include *mods
    >> > =A0 =A0 =A0end
    >> > =A0 =A0end
    >> > =A0 =A0def m ; m1 ; end
    >> > =A0 =A0def n ; N.n1 ; end

    >>
    >> Hm, this method above won't work as scope resolution rules for
    >> constants are different.
    >>
    >> > =A0end

    >>
    >> > =A0x =3D X.new(M)
    >> > =A0p x.m
    >> > =A0p x.n =A0 =A0#=3D> uninitialized constant X::N (NameError)

    >>
    >> > Is there any way to achieve this?

    >>
    >> Did you consider using #extend?
    >>
    >> =A0class X
    >> =A0 =A0def initialize(*mods)
    >> =A0 =A0 =A0extend *mods
    >> =A0 =A0end
    >> end
    >>
    >> What do you really want to achieve? =A0Do you have a more telling exampl=

    e?
    >
    > It's for encapsulating test cases. Eg.
    >
    > =A0TestCase.new(SomeClass) do
    > =A0 =A0...
    > =A0end
    >
    > Within the test case block it would help to handle #include, to make
    > tests less verbose.


    I see to be a bit slow today: how do you want to use modules there?
    Do you want to include them in the test code? What about:

    class TestCase
    def new(cl, &bl)
    @cl =3D cl
    instance_eval(&bl)
    end

    alias include extend
    end

    TestCase.new String do
    extend Foo
    include Bar
    end

    > Instead of making an instance of TestCase for each case, at this point
    > it looks like I'll have to create a new subclass of it.


    Sorry, you lost me here.

    Cheers

    robert


    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
    Robert Klemme, Dec 29, 2009
    #6
  7. Intransition

    Intransition Guest

    On Dec 29, 9:37=A0am, Robert Klemme <> wrote:
    > 2009/12/29 Intransition <>:


    > I see to be a bit slow today: how do you want to use modules there?
    > Do you want to include them in the test code? =A0What about:
    >
    > class TestCase
    > =A0 def new(cl, &bl)
    > =A0 =A0 =A0@cl =3D cl
    > =A0 =A0 =A0instance_eval(&bl)
    > =A0 end
    >
    > =A0 alias include extend
    > end
    >
    > TestCase.new String do
    > =A0 extend Foo
    > =A0 include Bar
    > end


    No no. You got it exactly. Problem is the module's constants are not
    coming through the #extend (which is effectively the same as the
    include in the singleton class). Let say I have a library:

    module MyApp
    module SomeSpace
    class FooClass
    ...
    end
    end
    end

    In the test cases, instead of having to spell out
    MyApp::SomeSpace::FooClass everywhere it is needed, it would be nice
    to include MyApp::SomeSpace, and then just reference FooClass in the
    tests.


    > > Instead of making an instance of TestCase for each case, at this point
    > > it looks like I'll have to create a new subclass of it.


    > Sorry, you lost me here.


    Instead of the code you presented I'd have to do something like:

    class TestCase
    alias :_new, :new
    def self.new(&block)
    Class.new(self, &block)._new
    end
    end

    Haven't tested it yet, but that should allow #include to work no
    problem. Unfortunately it means defining all my dsl methods at the
    class level --not even sure the instance level would be of any use in
    this case either, in which case the ._new can be dropped -- kind of
    stupid, just to get #include to work. But what else can I do?

    I almost feel like I'm having a mental block and there is actually an
    easy way to do this.
    Intransition, Dec 29, 2009
    #7
  8. On 29.12.2009 17:37, Intransition wrote:

    > I almost feel like I'm having a mental block and there is actually an
    > easy way to do this.


    Maybe this gets you on track:

    "One of those pleasing little Ruby snippets: module Helpers; include
    *constants.map(&amp;:const_get); end"
    http://twitter.com/pragdave/status/7147303788

    --
    Phillip Gawlowski
    Phillip Gawlowski, Dec 29, 2009
    #8
  9. 2009/12/29 Intransition <>:
    >
    >
    > On Dec 29, 9:37=A0am, Robert Klemme <> wrote:
    >> 2009/12/29 Intransition <>:

    >
    >> I see to be a bit slow today: how do you want to use modules there?
    >> Do you want to include them in the test code? =A0What about:
    >>
    >> class TestCase
    >> =A0 def new(cl, &bl)


    Btw, that method's name should have read "initialize".

    >> =A0 =A0 =A0@cl =3D cl
    >> =A0 =A0 =A0instance_eval(&bl)
    >> =A0 end
    >>
    >> =A0 alias include extend
    >> end
    >>
    >> TestCase.new String do
    >> =A0 extend Foo
    >> =A0 include Bar
    >> end

    >
    > No no. You got it exactly. Problem is the module's constants are not
    > coming through the #extend (which is effectively the same as the
    > include in the singleton class). Let say I have a library:
    >
    > =A0module MyApp
    > =A0 =A0module SomeSpace
    > =A0 =A0 =A0class FooClass
    > =A0 =A0 =A0 =A0...
    > =A0 =A0 =A0 end
    > =A0 =A0end
    > =A0end
    >
    > In the test cases, instead of having to spell out
    > MyApp::SomeSpace::FooClass everywhere it is needed, it would be nice
    > to include MyApp::SomeSpace, and then just reference FooClass in the
    > tests.
    >
    >
    >> > Instead of making an instance of TestCase for each case, at this point
    >> > it looks like I'll have to create a new subclass of it.

    >
    >> Sorry, you lost me here.

    >
    > Instead of the code you presented I'd have to do something like:
    >
    > =A0class TestCase
    > =A0 =A0alias :_new, :new
    > =A0 =A0def self.new(&block)
    > =A0 =A0 =A0Class.new(self, &block)._new
    > =A0 =A0end
    > =A0end


    Then your block needs to contain a class definition. You can use the
    block only for _one_ thing - either class / module def or code you
    want to execute. Granted, you can have code executed in a class
    definition as well but the definition becomes only usable *after* the
    definition has been executed in its entirety. Maybe you haven't
    decided yet what you want the block for and that is causing your
    headaches.

    > Haven't tested it yet, but that should allow #include to work no
    > problem. Unfortunately it means defining all my dsl methods at the
    > class level --not even sure the instance level would be of any use in
    > this case either, in which case the ._new can be dropped -- kind of
    > stupid, just to get #include to work. But what else can I do?
    >
    > I almost feel like I'm having a mental block and there is actually an
    > easy way to do this.


    What stops you from doing the include outside?

    include Your::Module::Of::Choice

    TestCase.new Foo do
    x =3D ChoiceClass.new
    x.method_invocation(123)
    end

    Kind regards

    robert


    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
    Robert Klemme, Dec 29, 2009
    #9
  10. Intransition

    Intransition Guest

    On Dec 29, 11:55=A0am, Robert Klemme <> wrote:

    > Then your block needs to contain a class definition. =A0You can use the
    > block only for _one_ thing - either class / module def or code you
    > want to execute. =A0Granted, you can have code executed in a class
    > definition as well but the definition becomes only usable *after* the
    > definition has been executed in its entirety.


    That's true. But the execution of the block is only for the definition
    of things which get run later.

    >=A0Maybe you haven't
    > decided yet what you want the block for and that is causing your
    > headaches.


    The block is for defining unit tests. Eg.

    testcase SomeClass do
    testunit :somemethod =3D> "some concern" do
    ...
    end
    end

    > > Haven't tested it yet, but that should allow #include to work no
    > > problem. Unfortunately it means defining all my dsl methods at the
    > > class level --not even sure the instance level would be of any use in
    > > this case either, in which case the ._new can be dropped -- kind of
    > > stupid, just to get #include to work. But what else can I do?

    >
    > > I almost feel like I'm having a mental block and there is actually an
    > > easy way to do this.

    >
    > What stops you from doing the include outside?
    >
    > include Your::Module::Of::Choice
    >
    > TestCase.new Foo do
    > =A0 x =3D ChoiceClass.new
    > =A0 x.method_invocation(123)
    > end


    Yes, I am able to do that. But I want to avoid polluting the top
    level.

    You know what really gets me is that even though I get an
    "uninitialized constant" error, there is no place I seem to be able to
    define a #const_missing method to catch it.
    Intransition, Dec 29, 2009
    #10
  11. Intransition

    Intransition Guest

    This is really becoming annoying. I can't even do it via a dynamic
    class definitions either.

    module M
    def m1; "m1"; end
    module N
    def self.n1; "n1"; end
    end
    end

    class X
    class << self
    alias _new new
    def new(&block)
    klass = Class.new(self)
    klass.class_eval(&block)
    klass._new
    end
    end

    def m ; m1 ; end
    def n ; N.n1 ; end
    end

    x = X.new do
    include M
    end

    p x.m
    p x.n #=> uninitialized constant X::N (NameError)

    Did constant lookup change between 1.8.6 and 1.8.7? Seems to me this
    used to be possible, and I find it unacceptable that normal constant
    lookup would not apply to dynamic class definitions, let alone the
    singleton classes.
    Intransition, Dec 29, 2009
    #11
  12. 2009/12/29 Intransition <>:
    > This is really becoming annoying. I can't even do it via a dynamic
    > class definitions either.
    >
    > =A0module M
    > =A0 =A0def m1; "m1"; end
    > =A0 =A0module N
    > =A0 =A0 =A0def self.n1; "n1"; end
    > =A0 =A0end
    > =A0end
    >
    > =A0class X
    > =A0 =A0class << self
    > =A0 =A0 =A0alias _new new
    > =A0 =A0 =A0def new(&block)
    > =A0 =A0 =A0 =A0klass =3D Class.new(self)
    > =A0 =A0 =A0 =A0klass.class_eval(&block)
    > =A0 =A0 =A0 =A0klass._new
    > =A0 =A0 =A0end
    > =A0 =A0end
    >
    > =A0 =A0def m ; m1 ; end
    > =A0 =A0def n ; N.n1 ; end
    > =A0end
    >
    > =A0x =3D X.new do
    > =A0 =A0include M
    > =A0end
    >
    > =A0p x.m
    > =A0p x.n =A0 #=3D> uninitialized constant X::N (NameError)
    >
    > Did constant lookup change between 1.8.6 and 1.8.7? Seems to me this
    > used to be possible, and I find it unacceptable that normal constant
    > lookup would not apply to dynamic class definitions, let alone the
    > singleton classes.


    AFAIK it hasn't changed and your code could never work in any version
    of Ruby because the const lookup in method #n is done _statically_.
    Apart from that you are defining method X#n and not <subclass of X>#n
    so the lookup could never work if you would instantiate X.

    Also, on a more abstract level: it is at least a bit odd to define
    class X which can only ever work if you make sure you include "N"
    (whichever way).

    I believe you haven't yet fully analyzed the problem you want to
    solve. I suggest to put it to sleep for a while and then get back
    later to it. I can't help you any more because to me it is not clear
    what you are trying to achieve. So far we only went through technical
    issues but the problem you are trying to solve isn't cleat to me yet.

    Kind regards

    robert


    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
    Robert Klemme, Dec 29, 2009
    #12
  13. Intransition

    Intransition Guest

    On Dec 29, 3:16=A0pm, Robert Klemme <> wrote:
    > 2009/12/29 Intransition <>:
    >
    >
    >
    > > This is really becoming annoying. I can't even do it via a dynamic
    > > class definitions either.

    >
    > > =A0module M
    > > =A0 =A0def m1; "m1"; end
    > > =A0 =A0module N
    > > =A0 =A0 =A0def self.n1; "n1"; end
    > > =A0 =A0end
    > > =A0end

    >
    > > =A0class X
    > > =A0 =A0class << self
    > > =A0 =A0 =A0alias _new new
    > > =A0 =A0 =A0def new(&block)
    > > =A0 =A0 =A0 =A0klass =3D Class.new(self)
    > > =A0 =A0 =A0 =A0klass.class_eval(&block)
    > > =A0 =A0 =A0 =A0klass._new
    > > =A0 =A0 =A0end
    > > =A0 =A0end

    >
    > > =A0 =A0def m ; m1 ; end
    > > =A0 =A0def n ; N.n1 ; end
    > > =A0end

    >
    > > =A0x =3D X.new do
    > > =A0 =A0include M
    > > =A0end

    >
    > > =A0p x.m
    > > =A0p x.n =A0 #=3D> uninitialized constant X::N (NameError)

    >
    > > Did constant lookup change between 1.8.6 and 1.8.7? Seems to me this
    > > used to be possible, and I find it unacceptable that normal constant
    > > lookup would not apply to dynamic class definitions, let alone the
    > > singleton classes.

    >
    > AFAIK it hasn't changed and your code could never work in any version
    > of Ruby because the const lookup in method #n is done _statically_.
    > Apart from that you are defining method X#n and not <subclass of X>#n
    > so the lookup could never work if you would instantiate X.
    >
    > Also, on a more abstract level: it is at least a bit odd to define
    > class X which can only ever work if you make sure you include "N"
    > (whichever way).
    >
    > I believe you haven't yet fully analyzed the problem you want to
    > solve. =A0I suggest to put it to sleep for a while and then get back
    > later to it. =A0I can't help you any more because to me it is not clear
    > what you are trying to achieve. =A0So far we only went through technical
    > issues but the problem you are trying to solve isn't cleat to me yet.


    All I am trying to do is emulate test/unit but using a DSL.
    Essentially:

    class MyTest < Test::Unit::TestCase

    include M

    def test_N_n1
    assert_equal(N.n1, "n1")
    end

    end

    Becomes:

    testcase :MyTest do

    include M

    testunit :N_n1 do
    assert_equal(N.n1, "n1")
    end

    end

    That's it. But I can't do it exactly b/c I can't make the include
    work.
    Intransition, Dec 29, 2009
    #13
  14. Intransition

    fkocherga Guest

    >=20
    > AFAIK it hasn't changed and your code could never work in any version
    > of Ruby because the const lookup in method #n is done _statically_.


    But it should not, consider:

    module M
    module N
    end
    end

    #Class.new do <---- This does not work in 1.8.7!
    class B < Object=20
    include M
    N
    end

    When 'class B < Object' replaced with 'Class.new do' the Ruby 1.8.7 =
    interpreter complains about uninitialized constant N. What is the big =
    reason for dynamically defined class to behave so differently? It would =
    be very non obvious and unexpected behavior. Actually both cases work as =
    expected in Ruby 1.9.1 and this is correct behavior according to the =
    Ruby Draft.=20

    --
    Fedor Kocherga
    http://sidenotes.kocherga.info/
    fkocherga, Dec 30, 2009
    #14
  15. Intransition

    fkocherga Guest

    > All I am trying to do is emulate test/unit but using a DSL.
    > Essentially:
    >=20
    > class MyTest < Test::Unit::TestCase
    >=20
    > include M
    >=20
    > def test_N_n1
    > assert_equal(N.n1, "n1")
    > end
    >=20
    > end
    >=20
    > Becomes:
    >=20
    > testcase :MyTest do
    >=20
    > include M
    >=20
    > testunit :N_n1 do
    > assert_equal(N.n1, "n1")
    > end
    >=20
    > end
    >=20


    Not sure why you need a DSL for, but technically it is doable (in Ruby =
    1.9.1):
    http://gist.github.com/265864

    --
    Fedor Kocherga
    http://sidenotes.kocherga.info/
    fkocherga, Dec 30, 2009
    #15
  16. 2009/12/29 Intransition <>:
    >
    > On Dec 29, 3:16=A0pm, Robert Klemme <> wrote:


    >> I believe you haven't yet fully analyzed the problem you want to
    >> solve. =A0I suggest to put it to sleep for a while and then get back
    >> later to it. =A0I can't help you any more because to me it is not clear
    >> what you are trying to achieve. =A0So far we only went through technical
    >> issues but the problem you are trying to solve isn't cleat to me yet.

    >
    > All I am trying to do is emulate test/unit but using a DSL.
    > Essentially:
    >
    > =A0class MyTest < Test::Unit::TestCase
    >
    > =A0 =A0include M
    >
    > =A0 =A0def test_N_n1
    > =A0 =A0 =A0 assert_equal(N.n1, "n1")
    > =A0 =A0 end
    >
    > =A0end
    >
    > Becomes:
    >
    > =A0testcase :MyTest do
    >
    > =A0 =A0include M
    >
    > =A0 =A0testunit :N_n1 do
    > =A0 =A0 =A0 assert_equal(N.n1, "n1")
    > =A0 =A0 end
    >
    > =A0end
    >
    > That's it. But I can't do it exactly b/c I can't make the include
    > work.


    Ah, now I see. I believe your problem comes from the fact that you
    are mixing two styles: DSL and "regular" Ruby. If you integrate
    defining those modules into your DSL (i.e. not via "module ... end")
    then integrating lookups might be easier as well. I don't have a
    clear idea yet how to do that but maybe it's worth exploring.

    Kind regards

    robert

    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
    Robert Klemme, Dec 30, 2009
    #16
  17. On Wed, Dec 30, 2009 at 4:34 AM, Robert Klemme
    <> wrote:

    > To sum it up: Ruby 1.9 changed lookup rules from lexical (aka static)
    > to dynamic.


    That change may not be permanent.

    There was a long thread on ruby-core a month or two ago, prompted by a
    question raised by Yehuda Katz about the problems the change was
    causing for existing DSLs like ActiveRecord and RSpec. After various
    proposals to modify the 1.9 behavior to make it more compatible,
    Maeda-san posted this:

    http://osdir.com/ml/ruby-core/2009-12/msg00001.html

    Matz seems to be in favor of returning to the 1.8 behavior.

    --
    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
    Rick DeNatale, Dec 30, 2009
    #17
  18. Intransition

    Intransition Guest

    On Dec 29, 9:04=A0pm, fkocherga <> wrote:
    > > AFAIK it hasn't changed and your code could never work in any version
    > > of Ruby because the const lookup in method #n is done _statically_.

    >
    > But it should not, consider:
    >
    > module M
    > =A0 module N
    > =A0 end
    > end
    >
    > #Class.new do <---- This does not work in 1.8.7!
    > class B < Object
    > =A0 include M
    > =A0 N
    > end
    >
    > When 'class B < Object' replaced with 'Class.new do' the Ruby 1.8.7 inter=

    preter complains about uninitialized constant N. What is the big reason for=
    dynamically defined class to behave so differently? It would be very non o=
    bvious and unexpected behavior. Actually both cases work as expected in Rub=
    y 1.9.1 and this is correct behavior according to the Ruby Draft.

    Nice clarification.

    Are there two issues here? This issue and the resolution of constants
    via instance_eval? Or are these two aspects of the same issue?

    In either case, there is no doubt in my mind that 'class B' and 'B =3D
    Class.new do' should work the same.
    Intransition, Dec 30, 2009
    #18
  19. Intransition

    Intransition Guest

    On Dec 30, 4:34=A0am, Robert Klemme <> wrote:
    > Thanks for the test case! =A0It never occurred to me to do a const
    > lookup in a class defined with a class body so I never stumbled across
    > this. =A0I did a bit of research and this is what I found:
    >
    > Statement from Matz about the change: "In 1.8, constant uses lexical
    > look-up, even within the block given to instance_eval(). =A0We changed
    > this behavior in 1.9 to simplify things."http://blade.nagaokaut.ac.jp/cgi=

    -bin/scat.rb/ruby/ruby-talk/181646
    >
    > Found viahttp://eigenclass.org/hiki/withdrawn-experimental-ruby-features
    > - I am not so sure about the "experimental" status of this though -
    > maybe this is due to the last update date of the page in 2007...
    >
    > Adam Gardner also made an interesting observation about the difference
    > between const_get and direct constant lookup:http://blade.nagaokaut.ac.jp=

    /cgi-bin/scat.rb/ruby/ruby-talk/333677
    > (I have changed the test output to easier interpret it. See attachment.)
    >
    > There's also this discussionhttp://blade.nagaokaut.ac.jp/cgi-bin/vframe.r=

    b/ruby/ruby-core/25865?2...
    >
    > And thishttp://blade.nagaokaut.ac.jp/cgi-bin/vframe.rb/ruby/ruby-talk/334=

    016?...
    >
    > To sum it up: Ruby 1.9 changed lookup rules from lexical (aka static)
    > to dynamic. =A0IMHO this explains the observed different behavior.
    > Thanks again for poking.


    Very helpful. Thanks for going to all the trouble, Robert. I now see
    the issue I am facing laser clear.
    Intransition, Dec 30, 2009
    #19
  20. Intransition

    Intransition Guest

    On Dec 30, 10:29=A0am, Rick DeNatale <> wrote:
    > On Wed, Dec 30, 2009 at 4:34 AM, Robert Klemme
    >
    > <> wrote:
    > > To sum it up: Ruby 1.9 changed lookup rules from lexical (aka static)
    > > to dynamic.

    >
    > That change may not be permanent.
    >
    > There was a long thread on ruby-core a month or two ago, prompted by a
    > question raised by Yehuda Katz about the problems the change was
    > causing for existing DSLs like ActiveRecord and RSpec. =A0 After various
    > proposals to modify the 1.9 behavior to make it more compatible,
    > Maeda-san posted this:
    >
    > http://osdir.com/ml/ruby-core/2009-12/msg00001.html
    >
    > Matz seems to be in favor of returning to the 1.8 behavior.


    What a tangle.

    Seems to me there are two desired behaviors: 1) domain behavior, where
    by lookup follows from the point of evaluation, and 2) closure
    behavior, whereby lookup follows from the point of definition. Both
    are perfectly reasonably and depend solely on the need of the
    developer's usecase. There is also the compromise position, first try
    domain behavior, failing that try closure behavior.

    So if I understand correctly, 1.8 and older used closure behavior, 1.9
    switched to domain behavior. There were issues with this change. Some
    people suggested the compromise position, but Matz rejected that and
    has decided to return to the original closure behavior. Is that a
    correct summary?

    Short of the compromise position, I would think the only complete
    solution would be to allow for both approaches via different methods,
    eg. instance_eval vs. instance_domain_eval, or perhaps an option,
    instance_eval(&b, :closure=3D>false).
    Intransition, Dec 30, 2009
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. pabbu
    Replies:
    8
    Views:
    710
    Marc Boyer
    Nov 7, 2005
  2. Andreas Bogenberger
    Replies:
    3
    Views:
    886
    Andreas Bogenberger
    Feb 22, 2008
  3. shankha
    Replies:
    0
    Views:
    663
    shankha
    May 5, 2009
  4. Eric
    Replies:
    10
    Views:
    448
  5. Eric
    Replies:
    0
    Views:
    247
Loading...

Share This Page