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>