From: Karl Date: 2010-03-03T02:25:15+09:00 Subject: Re: Compute value inside define_method On Mar 2, 9:37 am, Jesús Gabriel y Galán wrote: > On Tue, Mar 2, 2010 at 5:25 PM, Karl wrote: > > On Mar 2, 9:15 am, Jesús Gabriel y Galán > > wrote: > >> On Tue, Mar 2, 2010 at 5:00 PM, Karl wrote: > >> > I know there is a 'trick' for forcing a value to compute, but I can't > >> > seem to remember or find it. The following routine needs to computer > >> > the value of 'register += 1' and not insert it literally. > > >> > into.class_eval do > >> >              define_method "#{dow}_#{tod}_time" do > >> >                @registers.get_value( "#{register += 1}"to_i ) > >> >              end > >> >    ...snip... > >> > end > > >> > You can see my attempt above, but it doesn't work. Neither does > >> > 'eval("register += 1")' > > >> > What's the trick? or should I approach the problem from a different > >> > angle? > > >> What is register in your code above? Because this should work: > > >> irb(main):005:0> register = 0 > >> => 0 > >> irb(main):006:0> "#{register += 1}".to_i > >> => 1 > >> irb(main):008:0> register > >> => 1 > >> irb(main):009:0> "#{register += 1}".to_i > >> => 2 > > >> On the other hand, if register is already and int and the get_value > >> method receives an int, why not just: > > >> @registers.get_value(register += 1) > > >> ? > > >> Jesus. > > > 'register' is an int. > > > The problem is one of scope. When this is wrapped in the define_method > > it carries as a variable int object when it is included in a class. > > Thus, when executed via MyClass.weekeday_morning_time two consecutive > > times, 'register += 1' increments. I don't want it to increment when > > called, I just need it to increment when it is *defining* the method. > > > This is part of a dynamic method creation loop, and the register > > values are fixed. The dynamic method creation allows me to create > > several hundred methods accessing consecutive registers without > > writing all that code. So if I'm defining a method 'weekday_day_time' > > it is 'get_value' from register 21. The 'register += 1' is just > > incrementing the counter through the creation loop. > > > Make sense? I know, this is a bit obscure. > > It's not a problem of scope. The problem is that define_method > receives a block that closes over the register variable. The block > passed to define_method is not executed when the define_method is > executed, but when the method is invoked, as you have observed. All > defined methods carry the binding to the same register variable and so > every invocation of one of those methods will increment the same > variable. One way to avoid that is force the execution of evaluating > the register variable when defining the method, for example like: > > irb(main):012:0> 3.times do |i| > irb(main):013:1* instance_eval "def test_#{i}; #{i};end" > irb(main):014:1> end > => 3 > irb(main):015:0> test_0 > => 0 > irb(main):016:0> test_1 > => 1 > irb(main):018:0> test_2 > => 2 > > The *eval method family can also receive a String, which is obviously > not a closure, and so my #{i} above is executed when the instance_eval > is executed. Maybe someone can come up with a cleaner solution. > Translated to your problem (untested): > > into.class_eval do >              instance_eval <                 def #{dow}_#{tod}_time >                    @registers.get_value(#{register += 1}) >                 end >              END >    ...snip... > end > > Hope this helps. > > Jesus. That was it. I couldn't remember passing a string to class_eval forces the computation of #{} strings. Thanks. If there's a better way, of course I'd love to know.