setting local variables via eval

J

Joel VanderWerf

ara said:

Nice hack, as usual :)

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 '<body> #{ x + y } </body>'

puts view.render:)x => 40, :y => 2) #=> <body> 42 </body>
 
R

Robert Dober

thought some here might appreciate this

http://drawohara.com/post/28514698

kind regards

a @ http://codeforpeople.com/

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 :)

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 '<body> #{ x + y } </body>'
puts view.render:)x => 40, :y => 2) #=> <body> 42 </body>

Cheers
Robert
 
A

ara howard

Nice hack, as usual :)

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.

that's what i head originally - but blows up in 1.9 which requires a
binding
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.

oh very nice. yeah, pushing and popping from Thread.current is very
good
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.



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

loop {
hdoc =
"__a
#{ rand(2**30).to_i }__b#{ rand(2**30).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/
 
A

ara howard

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 :)

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 '<body> #{ x + y } </body>'
puts view.render:)x => 40, :y => 2) #=> <body> 42 </body>

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/
 
J

Joel VanderWerf

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

loop {
hdoc = "__a#{ rand(2**30).to_i }__b#{ rand(2**30).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
 
C

Clifford Heath

Robert said:
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) #=> <body> 42 </body>

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 :)

Clifford Heath.
 
R

Rick DeNatale

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 '<body> #{ x + y } </body>'

puts view.render:)x => 40, :y => 2) #=> <body> 42 </body>

Works just as well.
 
R

Robert Dober

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 :)

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 '<body> #{ x + y } </body>'
puts view.render:)x => 40, :y => 2) #=> <body> 42 </body>

Cheers
Robert


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?
<skipped rest of example>

Robert
 
J

Joel VanderWerf

Rick said:
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 '<body> #{ x + y } </body>'

puts view.render:)x => 40, :y => 2) #=> <body> 42 </body>

Works just as well.

Not quite.

view = View.new '<body> #{ x + y } #{locals} </body>'
puts view.render:)x => 40, :y => 2) # ==> <body> 42 x40y2 </body>
 
R

Rick DeNatale

Rick said:
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 '<body> #{ x + y } </body>'

puts view.render:)x => 40, :y => 2) #=> <body> 42 </body>

Works just as well.


Not quite.

view = View.new '<body> #{ x + y } #{locals} </body>'
puts view.render:)x => 40, :y => 2) # ==> <body> 42 x40y2 </body>

Not on my machine:

RubyMate r8136 running Ruby r1.8.6
(/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby)
<body> 42 </body>
Program exited.
 
J

Joel VanderWerf

Rick said:
Rick said:
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 '<body> #{ x + y } </body>'

puts view.render:)x => 40, :y => 2) #=> <body> 42 </body>

Works just as well.


Not quite.

view = View.new '<body> #{ x + y } #{locals} </body>'
puts view.render:)x => 40, :y => 2) # ==> <body> 42 x40y2 </body>

Not on my machine:

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

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.)
 
C

Clifford Heath

Rick said:
Not on my machine:

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

<body> 42 </body>
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 Heath.
 
R

Rick DeNatale

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.

Oh, OK I missed that subtlety
 
R

Robert Dober

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 :)

Clifford Heath.
Thx Clifford this point eluded me, well spotted.
And yes Rick he is right ;)
Robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top