tags:

views:

41

answers:

3

I've been working on trying to better understand Ruby and here's something I'm having trouble with:

$SAFE = 1
puts $SAFE # 1  
proc {
  $SAFE = 2
  puts $SAFE  # 2
}.call
puts $SAFE # 1   

The above code is partially taken from eRB's source rewritten to better highlight the example. Basically within the proc one can set the value of $SAFE to whatever value one wants and after the proc, the value of SAFE returns back to what it was before the proc.

If instead of using the word $SAFE I change it to a different word, such as $DOOR:

$DOOR = 1
puts $DOOR 
proc {
  $DOOR = 2
  puts $DOOR  
}.call
puts $DOOR  

then the value of $DOOR after the proc is 2 and not 1. Why the difference between the two examples?

+2  A: 

I don't know exactly why $SAFE is working that way but I do know that it's a predefined global variable with a magic meaning related to tainted external data and threads.

So don't use it as a program object.

See http://ruby-doc.org/docs/ProgrammingRuby/html/taint.html

It is not, btw, supposed to be possible to lower the value of $SAFE with an assignment, but it is attached to the execution context and a multithreaded program, for example, can have multiple $SAFE values in different threads...

DigitalRoss
I did read up on the taint stuff and i do see that $SAFE is a predefined global variable. But I don't quite understand why predefined global vars behave differently than other global vars.
Francois
The reason varies by variable, in the case of $SAFE it is because the whole point is to make a one-way escalation of paranoia. If $SAFE could decrease via assignment, injected code could simply include an assignment to $SAFE.
DigitalRoss
+1  A: 

The Value of $DOOR must be 2 . because $DOOR global variable has been reinitialized to from 1 to 2 . More detail on Global Variables.

$SAFE safe levels by setting the $SAFE variable. By default it is set to zero.

and $SAFE within the proc will be a there in memory till the end of scope .Hence it is displaying the previously set value i.e 1. check more on this here and also docs

YetAnotherCoder
+5  A: 

It's rather simple, really: the reason why $SAFE doesn't behave like you would expect from a global variable is because it isn't a global variable. It's a magic unicorn thingamajiggy.

There are quite a few of those magic unicorn thingamajiggies in Ruby, and they are unfortunately not very well documented (not at all documented, in fact), as the developers of the alternative Ruby implementations found out the hard way. These thingamajiggies all behave differently and (seemingly) inconsistently, and pretty much the only two things they have in common is that they look like global variables but don't behave like them.

Some have local scope. Some have thread-local scope. Some magically change without anyone ever assigning to them. Some have magic meaning for the interpreter and change how the language behaves. Some have other weird semantics attached to them.

$SAFE has almost all of the above: it is thread-local, meaning that if you change it in one thread, it doesn't affect other threads. It is local, meaning if you change it in a local scope (like a class, module, method or block), it doesn't affect the outer scope (as you have discovered). It has magic meaning for the interpreter, since setting it to a value different than 0 makes certain things not work. And it has additional weird semantics in that you can only ever increase its value, never decrease it.

Jörg W Mittag
ok, thats exactly what I wanted to know. I was looking and hoping for consistency but here instead I find that we're dealing with a magic unicorn thingamajiggy. :-)
Francois