Instance_eval with local variable

I have an object with an instance variable:
obj = Object.new
obj.instance_variable_set :@who, “foo”

I have a proc with arity 1:
@who= “main”
holla = lambda{ |str| p @who, str }

I want to evaluate the lambda inside the scope of obj, but also pass
in a local variable. I can’t figure out how to do this (or if it’s
even possible).

I can’t instance_eval the proc in the context of obj, because the proc
has arity:
obj.instance_eval( &holla )
#=> ArgumentError: wrong number of arguments (0 for 1)

And just invoking the proc within the context of obj doesn’t set the
scope:
obj.instance_eval{ holla[ “hoorah” ] }
#=> “main”
#=> “hoorah”

Any sweet meta trickery you can think of to accomplish what I want?

For those interested in second-guessing my approach and coming up with
a better way to achieve the same end goal, the application is a
documentation system which converts markup (markdown, textile, haml,
etc.) into CHM. I have a system for registering an arbitrary number of
HTML post-processors with the application. The post-processors
associate a regexp with a proc that converts the matched code:

module DocuBot
@snippets = {}
def self.handle_snippet( regexp, &handler )
@snippets[ regexp ] = handler
end
def self.process_snippets( html )
@snippets.inject(html){ |h,(regexp,handler)| h.gsub( regexp,
&handler ) }
end
end

Represents an entire documentation site

class DocuBot::Bundle
def write_html
@pages.each do |page|
html = DocuBot.process_snippets( page.to_html )

end
end
end

Mark $$foo$$ as glossary items.

DocuBot.handle_snippet /$$\w[^$]+$$/ do |match|
# TODO: look up the glossary term in the bundle’s glossary
# and insert appropriate text, or flag a new glossary term
#{match[2…-3]}
end

This works fine, except for the TODO above. I want to be able to
instance_eval that block within the scope of a particular bundle so I
can look at @glossary before returning the correct final HTML for gsub
to use.

On Jan 12, 12:23 pm, Phrogz [email protected] wrote:
[snip]

For those interested in second-guessing my approach and coming up with
a better way to achieve the same end goal
FWIW, there is a workaround I’ve come up with. Instead of trying to
evaluate the proc in the scope and access instance variables directly,
I can pass the object to the proc and work from its public interface.
My original code is thus changed:

def self.process_snippets( html )
@snippets.inject(html){ |h,(regexp,handler)| h.gsub( regexp, &handler ) }
def self.process_snippets( html, bundle )
@snippets.inject(html){ |h,(r,proc)| h.gsub®{ |s| proc
[s,bundle] } }

Mark $$foo$$ as glossary items.

DocuBot.handle_snippet /$$\w[^$]+$$/ do |match|
# TODO: look up the glossary term in the bundle’s glossary
# and insert appropriate text, or flag a new glossary term
#{match[2…-3]}
end
DocuBot.handle_snippet /$$\w[^$]+$$/ do |match, bundle|
term = match[2…-3]
defn = bundle.glossary[term]
#{term}
end

I’m thus unblocked in my endeavors, but still interested in the
metaprogramming needed to accomplish my original goal. I was actually
a little surprised that instance_eval doesn’t take an arbitrary number
of arguments in the block form and splat them along to the block’s
parameters.

On Jan 12, 2:57 pm, Phrogz [email protected] wrote:

@snippets.inject(html){ |h,(regexp,handler)| h.gsub( regexp, &handler ) }
end
of arguments in the block form and splat them along to the block’s
parameters.

Well, I’m not sure if this is the particular sort of nastiness you’re
looking for, but… defining a new method with the proc works…

[email protected]:~/projects $ cat nasty.rb
obj = Object.new
obj.instance_variable_set :@who, “foo”
@who = “main”
holla = lambda {|str| p @who, str}

class << obj
def eigen
class << self; self; end
end
end

obj.eigen.send(:define_method, :holla, &holla)
obj.holla “horrah”
[email protected]:~/projects $ ruby nasty.rb
“foo”
“horrah”

On Jan 12, 3:04 pm, pharrington [email protected] wrote:

FWIW, there is a workaround I’ve come up with. Instead of trying to

#{term}

end

obj.eigen.send(:define_method, :holla, &holla)
obj.holla “horrah”
[email protected]:~/projects $ ruby nasty.rb
“foo”
“horrah”

FWIW, I tend to only uses procs when metaprogramming when I want their
closure properties. Otherwise, pretty much everything else Ruby offers
is a better fit.

Gavin K. wrote:

I have a proc with arity 1:
@who= “main”
holla = lambda{ |str| p @who, str }

I believe your lambda is bound to the @who in the scope where it was
defined, you can’t dynamically rebind it.

To demonstrate:

class A
def initialize
@x = 123
end
def run
fn = lambda { puts “@x = #{@x}” }
B.new(fn).run
end
end
class B
def initialize(fn)
@x = 456
@fn = fn
end
def run
@fn.call
end
end

A.new.run # => @x = 123

In other words, @who is not the same as
self.instance_variable_get(:@who), as far as I can see anyway.

I think that what you ask for would lead to very surprising behaviour,
especially when using blocks:

@count = 0
something.whatever { @who += 1 }

You wouldn’t want @who ever to be bound to some other object further
down the call chain.

For those interested in second-guessing my approach and coming up with
a better way to achieve the same end goal

Perhaps rather than using lambdas, define methods on the object which
contains the instance variables you’re interested in, then call those
methods (or use method(:name) to get a Method object, which you can pass
around pretty much like a lambda)

On Jan 12, 1:19 pm, Brian C. [email protected] wrote:

I believe your lambda is bound to the @who in the scope where it was
defined, you can’t dynamically rebind it.

Nope:

cat tmp.rb
@who = “main”
proc = ->{ p @who }
proc[]

bar = Object.new; bar.instance_variable_set :@who, “bar”
bar.instance_eval( &proc )

ruby -v tmp.rb
ruby 1.9.1p243 (2009-07-16 revision 24175) [i386-mingw32]
“main”
“bar”

instance_eval can rebind the proc, but doesn’t let you pass arguments
to it.

I think that what you ask for would lead to very surprising behaviour,
especially when using blocks:

@count = 0
something.whatever { @who += 1 }

Sure, the concept of a closure is great and 99% of the time should be
preserved. I wasn’t asking for my edge-case behavior to be the norm,
but rather trying to figure if it’s possible at all.

For those interested in second-guessing my approach and coming up with
a better way to achieve the same end goal

Perhaps rather than using lambdas, define methods on the object which
contains the instance variables you’re interested in, then call those
methods (or use method(:name) to get a Method object, which you can pass
around pretty much like a lambda)

Interesting. Slight downside is the requirement that all plug-ins have
unique method names. Or…hrm, perhaps some insanity involving
define_method on the receiving end of the proc. Thanks for the
suggestion, anyhow.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs