On Wed, Mar 5, 2014 at 1:31 PM, Martin Ennemoser <lists / ruby-forum.com> wrote:

> I want to be able to modify Ruby code at runtime. I know that it is
> possible to add classes/methods at runtime. But that is not what I want.
> I want to insert statements or method calls at runtime. For example if I
> have a method:
>
> def foo a
>  x = a + 5
> end
>
> I want to change it at runtime to:
>
> def foo a
>  puts a
>  x = a + 5
> end

This specific example could be done with method decoration. I once
hacked a crude piece of Ruby code together that is able to add pre,
mid and post execute hooks to methods.  Maybe you can find it in the
archives.

> That means a Ruby application has to be able to modify itself. I am
> pretty sure that this is not trivial.
> My approach would be as follows:
>
> 1. Learn how MRI works internally. I was already able to download and
> compile the interpreter. But I don't know any good sources which
> describes how the interpeter works internally. A book that I have came
> across is "Ruby under a microscope"
> (http://patshaughnessy.net/ruby-under-a-microscope). Is this book
> recommendable?
>
>
> 2. Learn how YARV Bytecode works. Maybe I can add/modify the bytecode
> instructions at runtime. But I don't know how complicated this is.

You do not need that: meta programming is sufficient.

> Another approach that came into my mind would be to intercept the
> loader, inject my statements (like "puts a" above) and send the modified
> file to the loader. The problem is that this can't be done at runtime,
> since a ruby script gets loaded only once when the application starts.
>
> What do you think?

You can redefine the method and delegate to the old one.  Simplistic
code that would work for your case:

class Module
  def prefix_method(method, &code)
    tmp = instance_method(method)

    define_method(method) do |*args, &b|
      code[*args]
      tmp.bind(self).call(*args, &b)
    end
  end
end

class Foo
  def out(x)
    printf "out: <%s>\n", x
    yield x if block_given?
  end
end

f = Foo.new
f.out 123

class Foo
  prefix_method :out do |*args|
    printf "before out: <%p>\n", args
  end
end

f.out 456

f.out 789 do |x|
  printf "even with block in out: <%p>\n", x
end


Kind regards

robert


-- 
[guy, jim].each {|him| remember.him do |as, often| as.you_can - without end}
http://blog.rubybestpractices.com/