Setting local variables via eval

thought some here might appreciate this

   http://drawohara.com/post/28514698

kind regards

a @ http://codeforpeople.com/

···

--
share your knowledge. it's a way to achieve immortality.
h.h. the 14th dalai lama

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

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

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

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

Cheers
Robert

···

On Tue, Mar 11, 2008 at 5:20 AM, ara howard <ara.t.howard@gmail.com> wrote:

thought some here might appreciate this

   http://drawohara.com/post/28514698

kind regards

a @ http://codeforpeople.com/
--
share your knowledge. it's a way to achieve immortality.
h.h. the 14th dalai lama

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

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

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.

···

On 3/11/08, ara howard <ara.t.howard@gmail.com> wrote:

thought some here might appreciate this

   http://drawohara.com/post/28514698

kind regards

a @ http://codeforpeople.com/

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.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.

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/

···

On Mar 11, 2008, at 12:23 AM, Joel VanderWerf wrote:
--
sleep is the best meditation.
h.h. the 14th dalai lama

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

···

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

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

Cheers
Robert

---
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/
--
sleep is the best meditation.
h.h. the 14th dalai lama

i just realized that

   def scope
     binding
   end

is what i am acually using - sorry for confusion

a @ http://codeforpeople.com/

···

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.

--
share your knowledge. it's a way to achieve immortality.
h.h. the 14th dalai lama

Robert Dober 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) #=> <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 :slight_smile:

Clifford Heath.

Rick DeNatale wrote:

···

On 3/11/08, ara howard <ara.t.howard@gmail.com> 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 '<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>

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

ara howard wrote:

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

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

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

···

On Tue, Mar 11, 2008 at 4:52 PM, ara howard <ara.t.howard@gmail.com> wrote:

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

> 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 '<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

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

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

Rick DeNatale 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 '<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)

untitled

<body> 42 </body>
Program exited.

···

On 3/11/08, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:

> On 3/11/08, ara howard <ara.t.howard@gmail.com> wrote:

--
Rick DeNatale

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

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

···

On Wed, Mar 12, 2008 at 12:30 AM, Clifford Heath <no@spam.please.net> wrote:

Robert Dober 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) #=> <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 :slight_smile:

Clifford Heath.

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

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

Rick DeNatale wrote:

···

On 3/11/08, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:

Rick DeNatale wrote:
> On 3/11/08, ara howard <ara.t.howard@gmail.com> 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 '<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)

untitled

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

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Rick DeNatale wrote:

···

On 3/11/08, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:

Rick DeNatale wrote:
> On 3/11/08, ara howard <ara.t.howard@gmail.com> wrote:
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)

untitled

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

Oh, OK I missed that subtlety

···

On 3/11/08, Clifford Heath <no@spam.please.net> wrote:

Rick DeNatale wrote:
> On 3/11/08, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:
>> Rick DeNatale wrote:
>> > On 3/11/08, ara howard <ara.t.howard@gmail.com> wrote:

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

--
Rick DeNatale

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