Instance_eval, regexp with block : losing $1

To build a wiki-like engine (or DSL) with dynamically generated
methods, I do the following :

class Base
  def self.match(name, regexp, &block)
    define_method(name) do |text|
      text.gsub!(regexp) do
        puts "$1 in match method : #{$1}"
        #block.call
        instance_eval(&block)
      end
    end
  end
end

class Foo < Base
  match :g, /(.)g$/i do
    puts "$1 in passed block : #{$1}" #Unfortunately $1 is lost !
    'ggg' #so only hardcoded values :frowning:
  end
end

f = Foo.new
f.g('bla') #bla
f.g('doing') #doiggg ; problem is I cannot use $1 in block

After some research I found these 2 links
http://eigenclass.org/hiki/binding.of_caller+substitute
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/202600
(BTW I am trying to avoid any third-party libraries)

But I don't manage to get these to work...
I need to get $1, $2, etc. special values
in the block I pass in Base subclasses.
How can I do that ?

Thanks,

Vincent

···

--
Posted via http://www.ruby-forum.com/.

f.g('doing') #doiggg ; problem is I cannot use $1 in block

# maybe pass it as a parameter or save it away to some global variable
or 'per thread' variable.
-R

···

--
Posted via http://www.ruby-forum.com/\.

I finally managed to get it to work :

class Base
  def self.match(name, regexp, &block)
    define_method(name) do |text|
      puts "#{text}"
      text.gsub!(regexp) do
        #hack to restore magical $~ (and so $1, $2, etc.) in block
        eval('lambda{|x| $~ = x}', block).call($~)
        block.call #or instance_eval(&block)
      end
      puts "=> #{text}"
    end
  end
end

class Foo < Base
  match :g, /(.)g$/i do
    $1.upcase + '_ggg'
  end
end

f = Foo.new
f.g('bla')
f.g('doing')

···

--
Posted via http://www.ruby-forum.com/.

Vincent L. wrote:

class Foo < Base
  match :g, /(.)g$/i do
    puts "$1 in passed block : #{$1}" #Unfortunately $1 is lost !
    'ggg' #so only hardcoded values :frowning:
  end
end

...

But I don't manage to get these to work...
I need to get $1, $2, etc. special values
in the block I pass in Base subclasses.
How can I do that ?

The backref and numbered group globals are tied to the containing method/class body. So here, where you have a match call with a block inside a class body, the capture would be the one that lives in that body. That's part of the problem with relying on these special variables and the side effect of e.g. gsub: you can't implement the same behavior in Ruby because certain core methods like gsub have "special" side effects impossible to emulate.

- Charlie

Vincent L. wrote:

I finally managed to get it to work :

class Base
  def self.match(name, regexp, &block)
    define_method(name) do |text|
      puts "#{text}"
      text.gsub!(regexp) do
        #hack to restore magical $~ (and so $1, $2, etc.) in block
        eval('lambda{|x| $~ = x}', block).call($~)
        block.call #or instance_eval(&block)

instance_eval(&block) works definitely better here.

···

--
Posted via http://www.ruby-forum.com/\.