setting local variables via eval

Discussion in 'Ruby' started by ara howard, Mar 11, 2008.

  1. ara howard

    ara howard Guest

    ara howard, Mar 11, 2008
    #1
    1. Advertising

  2. ara howard wrote:
    > http://drawohara.com/post/28514698


    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>


    --
    vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
    Joel VanderWerf, Mar 11, 2008
    #2
    1. Advertising

  3. ara howard

    Robert Dober Guest

    On Tue, Mar 11, 2008 at 5:20 AM, ara howard <> 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
    >
    >
    >
    >


    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

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

    ---
    Whereof one cannot speak, thereof one must be silent.
    Ludwig Wittgenstein
    Robert Dober, Mar 11, 2008
    #3
  4. ara howard

    ara howard Guest

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

    > ara howard wrote:
    >> http://drawohara.com/post/28514698

    >
    > 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/
    --
    sleep is the best meditation.
    h.h. the 14th dalai lama
    ara howard, Mar 11, 2008
    #4
  5. ara howard

    ara howard Guest

    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 :)
    >
    > 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/
    --
    sleep is the best meditation.
    h.h. the 14th dalai lama
    ara howard, Mar 11, 2008
    #5
  6. ara howard

    ara howard Guest

    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/
    --
    share your knowledge. it's a way to achieve immortality.
    h.h. the 14th dalai lama
    ara howard, Mar 11, 2008
    #6
  7. 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
    Joel VanderWerf, Mar 11, 2008
    #7
  8. 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 :)

    Clifford Heath.
    Clifford Heath, Mar 11, 2008
    #8
  9. On 3/11/08, ara howard <> 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.
    --
    Rick DeNatale

    My blog on Ruby
    http://talklikeaduck.denhaven2.com/
    Rick DeNatale, Mar 11, 2008
    #9
  10. ara howard

    Robert Dober Guest

    On Tue, Mar 11, 2008 at 4:52 PM, ara howard <> 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 :)
    > >
    > > 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



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

    ---
    Whereof one cannot speak, thereof one must be silent.
    Ludwig Wittgenstein
    Robert Dober, Mar 11, 2008
    #10
  11. Rick DeNatale wrote:
    > On 3/11/08, ara howard <> 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
    Joel VanderWerf, Mar 12, 2008
    #11
  12. On 3/11/08, Joel VanderWerf <> wrote:
    > Rick DeNatale wrote:
    > > On 3/11/08, ara howard <> 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.
    --
    Rick DeNatale

    My blog on Ruby
    http://talklikeaduck.denhaven2.com/
    Rick DeNatale, Mar 12, 2008
    #12
  13. Rick DeNatale wrote:
    > On 3/11/08, Joel VanderWerf <> wrote:
    >> Rick DeNatale wrote:
    >> > On 3/11/08, ara howard <> 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
    Joel VanderWerf, Mar 12, 2008
    #13
  14. Rick DeNatale wrote:
    > On 3/11/08, Joel VanderWerf <> wrote:
    >> Rick DeNatale wrote:
    >> > On 3/11/08, ara howard <> 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.
    Clifford Heath, Mar 12, 2008
    #14
  15. On 3/11/08, Clifford Heath <> wrote:
    > Rick DeNatale wrote:
    > > On 3/11/08, Joel VanderWerf <> wrote:
    > >> Rick DeNatale wrote:
    > >> > On 3/11/08, ara howard <> 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.


    Oh, OK I missed that subtlety

    --
    Rick DeNatale

    My blog on Ruby
    http://talklikeaduck.denhaven2.com/
    Rick DeNatale, Mar 12, 2008
    #15
  16. ara howard

    Robert Dober Guest

    On Wed, Mar 12, 2008 at 12:30 AM, Clifford Heath <> 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 :)
    >
    > Clifford Heath.
    >
    >

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


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

    ---
    Whereof one cannot speak, thereof one must be silent.
    Ludwig Wittgenstein
    Robert Dober, Mar 12, 2008
    #16
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Peter Luciak

    eval() and local variables

    Peter Luciak, Apr 20, 2004, in forum: Python
    Replies:
    1
    Views:
    973
    Peter Otten
    Apr 20, 2004
  2. Chad Dressler
    Replies:
    0
    Views:
    634
    Chad Dressler
    Dec 30, 2006
  3. Sullivan WxPyQtKinter
    Replies:
    10
    Views:
    660
    Antoon Pardon
    Nov 8, 2007
  4. Replies:
    16
    Views:
    199
    Sean O'Halpin
    Nov 10, 2007
  5. FangQ
    Replies:
    3
    Views:
    91
    Ilya Zakharevich
    May 15, 2006
Loading...

Share This Page