Setting local variables via eval

thought some here might appreciate this

http://drawohara.com/post/28514698

kind regards

a @ http://codeforpeople.com/

ara howard wrote:

http://drawohara.com/post/28514698

Nice hack, as usual :slight_smile:

Isn’t

class Object
def scope
lambda{ }
end
end

enough? You don’t need to return binding from the lambda, AFAICT. The
lambda never gets called.

Also, an alternative to mucking with ObjectSpace:

     Thread.current[:val] = v
     definition = "#{ var } = Thread.current[:val]"

in place of

     value = "ObjectSpace._id2ref #{ v.object_id }"
     definition = "#{ var } = #{ value }"

Of course, the value of Thread.current[:val] should be saved and
restored. Maybe this makes the hack jruby friendly.

There is one case in which the double_quoted_heredoc is not hygienic,
when a line of @template begins with template:

view2 = View.new ‘template
view3 = View.new view2.render
puts view3.render

But I don’t see any hygienic solution (indenting and using <<- doesn’t
work, because the interpolated strings might have line breaks). Maybe
ruby needs a heredoc variant that runs to eof.

With these changes, your code becomes:

class Object
def scope
lambda{ }
end
end

class View
def initialize template
@template = template
end

 def render locals = {}
   old_val = Thread.current[:val]
   context = scope
   locals.each do |k, v|
     var = k
     Thread.current[:val] = v
     definition = "#{ var } = Thread.current[:val]"
     eval definition, context
   end
   double_quoted_heredoc = ['<<__template__', @template,

template’].join(“\n”)
eval double_quoted_heredoc, context
ensure
Thread.current[:val] = old_val
end
end

view = View.new ‘ #{ x + y } ’

puts view.render(:x => 40, :y => 2) #=> 42

On Tue, Mar 11, 2008 at 5:20 AM, ara howard [email protected]
wrote:

h.h. the 14th dalai lama

Ara

Obviously I miss something, but I have the feeling that this
simplification still works, which use cases did I miss?
The idea is crazy of course :slight_smile:

class View
def initialize template
@template = template
end

def render locals = {}
  context = binding
  locals.each do |k, v|
    definition = "#{ k } = #{ v }"
    eval definition, context
  end
  double_quoted_heredoc = ['<<__template__', @template,

template’].join(“\n”)
eval double_quoted_heredoc, context
end
end

view = View.new ‘ #{ x + y } ’
puts view.render(:x => 40, :y => 2) #=> 42

Cheers
Robert


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

On Mar 11, 2008, at 12:23 AM, Joel VanderWerf wrote:

end
end

enough? You don’t need to return binding from the lambda, AFAICT.
The lambda never gets called.

that’s what i head originally - but blows up in 1.9 which requires a
binding

   definition = "#{ var } = #{ value }"

Of course, the value of Thread.current[:val] should be saved and
restored. Maybe this makes the hack jruby friendly.

oh very nice. yeah, pushing and popping from Thread.current is very
good

yes. in real code i generate the hdoc in a loop roughly like so

loop {
hdoc =
“__a
#{ rand(230).to_i }__b#{ rand(230).to_i }c#{ rand(2**30).to_i }

break unless template =~ Regexp.escape(hdoc)

}

etc

great suggestions - the jruby/Thread.current esp.

thx.

a @ http://drawohara.com/

On Mar 11, 2008, at 5:28 AM, Robert D. wrote:

end

view = View.new ‘ #{ x + y } ’
puts view.render(:x => 40, :y => 2) #=> 42

Cheers
Robert

cfp2:~ > cat a.rb
require ‘yaml’

locals = {
‘K’ => ‘or any thing else really’,
‘k’ => %w( list of words ),
}

context = binding

locals.each do |k, v|
begin
definition = “#{ k } = #{ v }”

 y 'k' => k, 'v' => v, 'definition' => definition

 eval definition, context

rescue Object => e
y ‘error’ => “#{ e.message } (#{ e.class })”
end
end

cfp2:~ > ruby a.rb

v:

  • list
  • of
  • words
    k: k
    definition: k = listofwords

error: undefined local variable or method `listofwords’ for
main:Object (NameError)


v: or any thing else really
k: K
definition: K = or any thing else really


error: |-
compile error
b.rb:8: syntax error, unexpected kOR
K = or any thing else really
^ (SyntaxError)

;-))

a @ http://drawohara.com/

ara howard wrote:

yes. in real code i generate the hdoc in a loop roughly like so

loop {
hdoc = “__a#{ rand(230).to_i }__b#{ rand(230).to_i }c#{
rand(2**30).to_i }

break unless template =~ Regexp.escape(hdoc)
}

Or:

   hdoc = "__template"
   while /^#{hdoc}/ =~ @template
     hdoc.succ!
     p hdoc
   end
   double_quoted_heredoc = ["<<#{hdoc}", @template, hdoc].join("\n")
   eval double_quoted_heredoc, context

On Mar 11, 2008, at 12:23 AM, Joel VanderWerf wrote:

enough? You don’t need to return binding from the lambda, AFAICT.
The lambda never gets called.

i just realized that

def scope
binding
end

is what i am acually using - sorry for confusion

a @ http://codeforpeople.com/

On 3/11/08, ara howard [email protected] wrote:

thought some here might appreciate this

http://drawohara.com/post/28514698

kind regards

a @ http://codeforpeople.com/

No need for Object#context

class View
def initialize template
@template = template
end

def render locals = {}
  context = binding
  locals.each do |k, v|
    var = k
    value = "ObjectSpace._id2ref #{ v.object_id }"
    definition = "#{ var } = #{ value }"
    eval definition, context
  end
  double_quoted_heredoc = ['<<__template__', @template,

template’].join(“\n”)
eval double_quoted_heredoc, context
end
end

view = View.new ‘ #{ x + y } ’

puts view.render(:x => 40, :y => 2) #=> 42

Works just as well.

Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Tue, Mar 11, 2008 at 4:52 PM, ara howard [email protected]
wrote:

def initialize template
template’].join(“\n”)

cfp2:~ > cat a.rb
require ‘yaml’

locals = {
‘K’ => ‘or any thing else really’,
‘k’ => %w( list of words ),

}

context = binding
context = binding has to be in the render_locals method, so I fail to
understand the point of the example, if
eval ‘x=1’, binding
worked on the top level this thread would not even exist, right?

Robert


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

Rick DeNatale wrote:

    definition = "#{ var } = #{ value }"

puts view.render(:x => 40, :y => 2) #=> 42

Works just as well.

Not quite.

view = View.new ’ #{ x + y } #{locals} ’
puts view.render(:x => 40, :y => 2) # ==> 42 x40y2

Robert D. wrote:

Obviously I miss something, but I have the feeling that this
simplification still works, which use cases did I miss?

puts view.render(:x => 40, :y => 2) #=> 42

Try running it with the following:

puts view.render(:context => 40, :y => 2) #=> TypeError

Ara’s “scope” method contains no local variables for yours to step on.

Nice trick Ara - I always appreciate your insights. I had to play with
this one a bit before properly understanding it :slight_smile:

Clifford H…

On 3/11/08, Joel VanderWerf [email protected] wrote:

No need for Object#context
value = “ObjectSpace._id2ref #{ v.object_id }”

puts view.render(:x => 40, :y => 2) #=> 42

Works just as well.

Not quite.

view = View.new ‘ #{ x + y } #{locals} ’
puts view.render(:x => 40, :y => 2) # ==> 42 x40y2

Not on my machine:

RubyMate r8136 running Ruby r1.8.6
(/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby)

untitled

42 Program exited. -- Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Rick DeNatale wrote:

RubyMate r8136 running Ruby r1.8.6
(/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby)

untitled

42 Program exited.

I don’t know what’s different about your environment, Rick, but the
point I made elsewhere in this thread is that the version that both
you and Robert offered doesn’t present a clean scope for the added
locals, it just re-uses the local scope of “render”. That means you
can both expand and clobber render’s internal variables, as I showed.

Clifford H…

On 3/11/08, Clifford H. [email protected] wrote:

Not on my machine:
point I made elsewhere in this thread is that the version that both
you and Robert offered doesn’t present a clean scope for the added
locals, it just re-uses the local scope of “render”. That means you
can both expand and clobber render’s internal variables, as I showed.

Oh, OK I missed that subtlety


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Wed, Mar 12, 2008 at 12:30 AM, Clifford H. [email protected]
wrote:

Ara’s “scope” method contains no local variables for yours to step on.

Nice trick Ara - I always appreciate your insights. I had to play with
this one a bit before properly understanding it :slight_smile:

Clifford H…

Thx Clifford this point eluded me, well spotted.
And yes Rick he is right :wink:
Robert


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

Rick DeNatale wrote:

    var = k

view = View.new ’ #{ x + y } ’

Not on my machine:

RubyMate r8136 running Ruby r1.8.6
(/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby)

untitled

42

Hm. Unless there are at least 2 spaces after the Answer (to the Ultimate
Question et al), you’re not running my two lines of code there… (Even
if locals was nil, you’d still get two spaces.)