Ruby metaprogramming: define_method block not maintaining scope












0















I'm working on dynamically patching a bunch of classes and methods(most of the time these methods are not simple "puts" like a lot of examples I've been able to find on the internet)



Say for instance I have the following code:
foo.rb



module Base
class Foo
def info
puts 'Foo#info called'
end
end
end


& I also have the following class:
test.rb



module Base
class Test
def print
puts "Test#print called"
Foo.new.info
end
end
end


Then in main.rb I have the following where I want to add a method that uses a class within the same module(Foo in this case)



require_relative './foo'
require_relative './test'


new_method_content = "puts 'hi'
Foo.new.info"

Base::Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end


Which, when executed will net the following:



Uncaught exception: uninitialized constant Foo


Which sort of makes sense to me because the main.rb file doesn't know that I want Base::Foo, however, I need a way to maintain lookup scope because Base::Test should be able to find the class Foo that I want.



Base::Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end


I've done a fair bit of googling and SO'ing but haven't found anything about how to maintain constant lookup scope while class_eval/instance_eval/module_eval/define_method(I've tried a lot of Ruby's dark magic methods all of which have ended in varying degrees of failure lol)



https://cirw.in/blog/constant-lookup




Confusingly however, if you pass a String to these methods, then the String is evaluated with Module.nesting containing just the class itself (for class_eval) or just the singleton class of the object (for instance_eval).




& also this:
https://bugs.ruby-lang.org/issues/6838




Evaluates the string or block in the context of mod, except that when
a block is given, constant/class variable lookup is not affected.




So my question is:
How can I redefine a method BUT maintain constant/class scope?



I've been trying a bunch of other things(in the context of main.rb):



Base::Test.class_eval('def asdf; puts "Test#asdf called"; Foo.new.info; end')
Base::Test.new.asdf
=>
Test#asdf called
Uncaught exception: uninitialized constant Base::Test::Foo
Did you mean? Base::Foo


(which is a diff problem in that it's trying to look it up from the evaluated module nesting? I'm not sure why it doesn't try all module paths available from Base::Test though, I would think it would try Base::Test::Foo which doesn't exist, so then it would go up the module tree looking for the class(Base::Foo) which would exist)










share|improve this question



























    0















    I'm working on dynamically patching a bunch of classes and methods(most of the time these methods are not simple "puts" like a lot of examples I've been able to find on the internet)



    Say for instance I have the following code:
    foo.rb



    module Base
    class Foo
    def info
    puts 'Foo#info called'
    end
    end
    end


    & I also have the following class:
    test.rb



    module Base
    class Test
    def print
    puts "Test#print called"
    Foo.new.info
    end
    end
    end


    Then in main.rb I have the following where I want to add a method that uses a class within the same module(Foo in this case)



    require_relative './foo'
    require_relative './test'


    new_method_content = "puts 'hi'
    Foo.new.info"

    Base::Test.instance_eval do
    def asdf
    puts "Test#asdf called"
    Foo.new.info
    end
    end


    Which, when executed will net the following:



    Uncaught exception: uninitialized constant Foo


    Which sort of makes sense to me because the main.rb file doesn't know that I want Base::Foo, however, I need a way to maintain lookup scope because Base::Test should be able to find the class Foo that I want.



    Base::Test.instance_eval do
    def asdf
    puts "Test#asdf called"
    Foo.new.info
    end
    end


    I've done a fair bit of googling and SO'ing but haven't found anything about how to maintain constant lookup scope while class_eval/instance_eval/module_eval/define_method(I've tried a lot of Ruby's dark magic methods all of which have ended in varying degrees of failure lol)



    https://cirw.in/blog/constant-lookup




    Confusingly however, if you pass a String to these methods, then the String is evaluated with Module.nesting containing just the class itself (for class_eval) or just the singleton class of the object (for instance_eval).




    & also this:
    https://bugs.ruby-lang.org/issues/6838




    Evaluates the string or block in the context of mod, except that when
    a block is given, constant/class variable lookup is not affected.




    So my question is:
    How can I redefine a method BUT maintain constant/class scope?



    I've been trying a bunch of other things(in the context of main.rb):



    Base::Test.class_eval('def asdf; puts "Test#asdf called"; Foo.new.info; end')
    Base::Test.new.asdf
    =>
    Test#asdf called
    Uncaught exception: uninitialized constant Base::Test::Foo
    Did you mean? Base::Foo


    (which is a diff problem in that it's trying to look it up from the evaluated module nesting? I'm not sure why it doesn't try all module paths available from Base::Test though, I would think it would try Base::Test::Foo which doesn't exist, so then it would go up the module tree looking for the class(Base::Foo) which would exist)










    share|improve this question

























      0












      0








      0








      I'm working on dynamically patching a bunch of classes and methods(most of the time these methods are not simple "puts" like a lot of examples I've been able to find on the internet)



      Say for instance I have the following code:
      foo.rb



      module Base
      class Foo
      def info
      puts 'Foo#info called'
      end
      end
      end


      & I also have the following class:
      test.rb



      module Base
      class Test
      def print
      puts "Test#print called"
      Foo.new.info
      end
      end
      end


      Then in main.rb I have the following where I want to add a method that uses a class within the same module(Foo in this case)



      require_relative './foo'
      require_relative './test'


      new_method_content = "puts 'hi'
      Foo.new.info"

      Base::Test.instance_eval do
      def asdf
      puts "Test#asdf called"
      Foo.new.info
      end
      end


      Which, when executed will net the following:



      Uncaught exception: uninitialized constant Foo


      Which sort of makes sense to me because the main.rb file doesn't know that I want Base::Foo, however, I need a way to maintain lookup scope because Base::Test should be able to find the class Foo that I want.



      Base::Test.instance_eval do
      def asdf
      puts "Test#asdf called"
      Foo.new.info
      end
      end


      I've done a fair bit of googling and SO'ing but haven't found anything about how to maintain constant lookup scope while class_eval/instance_eval/module_eval/define_method(I've tried a lot of Ruby's dark magic methods all of which have ended in varying degrees of failure lol)



      https://cirw.in/blog/constant-lookup




      Confusingly however, if you pass a String to these methods, then the String is evaluated with Module.nesting containing just the class itself (for class_eval) or just the singleton class of the object (for instance_eval).




      & also this:
      https://bugs.ruby-lang.org/issues/6838




      Evaluates the string or block in the context of mod, except that when
      a block is given, constant/class variable lookup is not affected.




      So my question is:
      How can I redefine a method BUT maintain constant/class scope?



      I've been trying a bunch of other things(in the context of main.rb):



      Base::Test.class_eval('def asdf; puts "Test#asdf called"; Foo.new.info; end')
      Base::Test.new.asdf
      =>
      Test#asdf called
      Uncaught exception: uninitialized constant Base::Test::Foo
      Did you mean? Base::Foo


      (which is a diff problem in that it's trying to look it up from the evaluated module nesting? I'm not sure why it doesn't try all module paths available from Base::Test though, I would think it would try Base::Test::Foo which doesn't exist, so then it would go up the module tree looking for the class(Base::Foo) which would exist)










      share|improve this question














      I'm working on dynamically patching a bunch of classes and methods(most of the time these methods are not simple "puts" like a lot of examples I've been able to find on the internet)



      Say for instance I have the following code:
      foo.rb



      module Base
      class Foo
      def info
      puts 'Foo#info called'
      end
      end
      end


      & I also have the following class:
      test.rb



      module Base
      class Test
      def print
      puts "Test#print called"
      Foo.new.info
      end
      end
      end


      Then in main.rb I have the following where I want to add a method that uses a class within the same module(Foo in this case)



      require_relative './foo'
      require_relative './test'


      new_method_content = "puts 'hi'
      Foo.new.info"

      Base::Test.instance_eval do
      def asdf
      puts "Test#asdf called"
      Foo.new.info
      end
      end


      Which, when executed will net the following:



      Uncaught exception: uninitialized constant Foo


      Which sort of makes sense to me because the main.rb file doesn't know that I want Base::Foo, however, I need a way to maintain lookup scope because Base::Test should be able to find the class Foo that I want.



      Base::Test.instance_eval do
      def asdf
      puts "Test#asdf called"
      Foo.new.info
      end
      end


      I've done a fair bit of googling and SO'ing but haven't found anything about how to maintain constant lookup scope while class_eval/instance_eval/module_eval/define_method(I've tried a lot of Ruby's dark magic methods all of which have ended in varying degrees of failure lol)



      https://cirw.in/blog/constant-lookup




      Confusingly however, if you pass a String to these methods, then the String is evaluated with Module.nesting containing just the class itself (for class_eval) or just the singleton class of the object (for instance_eval).




      & also this:
      https://bugs.ruby-lang.org/issues/6838




      Evaluates the string or block in the context of mod, except that when
      a block is given, constant/class variable lookup is not affected.




      So my question is:
      How can I redefine a method BUT maintain constant/class scope?



      I've been trying a bunch of other things(in the context of main.rb):



      Base::Test.class_eval('def asdf; puts "Test#asdf called"; Foo.new.info; end')
      Base::Test.new.asdf
      =>
      Test#asdf called
      Uncaught exception: uninitialized constant Base::Test::Foo
      Did you mean? Base::Foo


      (which is a diff problem in that it's trying to look it up from the evaluated module nesting? I'm not sure why it doesn't try all module paths available from Base::Test though, I would think it would try Base::Test::Foo which doesn't exist, so then it would go up the module tree looking for the class(Base::Foo) which would exist)







      ruby metaprogramming






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 20 '18 at 17:02









      user3550687user3550687

      31




      31
























          1 Answer
          1






          active

          oldest

          votes


















          1














          When you reference the class Base::Test like this, ruby does not take the Base:: as the module context to look up constants. That is the normal behaviour and would also not work, if you would define the moethod directly.



          But you could do it in this way:



          module Base
          Test.instance_eval do
          def asdf
          puts "Test#asdf called"
          Foo.new.info
          end
          end
          end





          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53397974%2fruby-metaprogramming-define-method-block-not-maintaining-scope%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            1














            When you reference the class Base::Test like this, ruby does not take the Base:: as the module context to look up constants. That is the normal behaviour and would also not work, if you would define the moethod directly.



            But you could do it in this way:



            module Base
            Test.instance_eval do
            def asdf
            puts "Test#asdf called"
            Foo.new.info
            end
            end
            end





            share|improve this answer




























              1














              When you reference the class Base::Test like this, ruby does not take the Base:: as the module context to look up constants. That is the normal behaviour and would also not work, if you would define the moethod directly.



              But you could do it in this way:



              module Base
              Test.instance_eval do
              def asdf
              puts "Test#asdf called"
              Foo.new.info
              end
              end
              end





              share|improve this answer


























                1












                1








                1







                When you reference the class Base::Test like this, ruby does not take the Base:: as the module context to look up constants. That is the normal behaviour and would also not work, if you would define the moethod directly.



                But you could do it in this way:



                module Base
                Test.instance_eval do
                def asdf
                puts "Test#asdf called"
                Foo.new.info
                end
                end
                end





                share|improve this answer













                When you reference the class Base::Test like this, ruby does not take the Base:: as the module context to look up constants. That is the normal behaviour and would also not work, if you would define the moethod directly.



                But you could do it in this way:



                module Base
                Test.instance_eval do
                def asdf
                puts "Test#asdf called"
                Foo.new.info
                end
                end
                end






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 20 '18 at 17:39









                Lars SchirrmeisterLars Schirrmeister

                1,9201722




                1,9201722






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53397974%2fruby-metaprogramming-define-method-block-not-maintaining-scope%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    MongoDB - Not Authorized To Execute Command

                    How to fix TextFormField cause rebuild widget in Flutter

                    in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith