On 11/11/09, Patrik Sundberg <patrik.sundberg / gmail.com> wrote:
> Hi,
>
> I'm interested in figuring out a way to get a separate scope. I do not
> need any other traditional sandbox features, i.e. any code goes, I
> just want code evaluated in the scope to no affect the parent scope
> (at all). Example illustrating what I am after:
>
> a = 10
> SeparatedScope.new do
>  a = a + 1
>  p a
> end
> p a
>
> yielding:
> 11
> 10
>
> I'd like it to have the same initialization/separation also for
> instance variables and even global variables if possible.

Here's a macro which seems to do the job. Comments follow.

#file separated_scope_user.rb
require 'rubygems'
require 'macro'
Macro.require 'example/separated_scope'

#file separated_scope.rb
macro separated_scope
  code=yield
  localnames=[]
  code.depthwalk{|parent,i,j,node|
    if RedParse::VarNode===node
      node.name<<'_'
      localnames<<node.name
    end
  }
  :(
    ^localnames.uniq.inject(:(nil)){|sum,lvar|
      RedParse::AssignNode[ RedParse::VarNode[lvar],'=',sum ]
    }
    eval local_variables.map{|lvar| lvar+"_="+lvar}.join(';')
    ^code
  )
end

a = 10
separated_scope do
a = a + 1
p a #=>11
end
p a #=>10

Commentary:
1) This is considerably more complicated (=uglier) than Bill's
attempt. There's even an eval lurking in there, which oughtn't be
necessary when using macros. However, it does seem to fulfill all your
requirements.

2) A more sophisticated variable renaming scheme may be required if
you want your program to have variables which end with an underscore.

3) Use of eval inside a separated_scope will confuse things. Variables
used in the eval arg will refer to the outer scope, not the separated
scope.

4) Macros are not a standard part of ruby syntax; they require my
macro preprocessor, rubymacros. do a 'gem install rubymacros' to get
it.
Also see http://github.com/coatl/rubymacros

5) Code that defines or uses macros must be pulled into the
interpreter using a special version of require: Macro.require. That's
why there's a separate file which requires 'macro', then calls
Macro.require.