Trying Ruby with the help of Ruby Koans. There is following test there:
def test_method_names_become_symbols
symbols_as_strings = Symbol.all_symbols.map { |x| x.to_s }
assert_equal __, symbols_as_strings.include?("test_method_names_become_symbols")
end
# THINK ABOUT IT:
#
# Why do we convert the list of symbols to strings and then compare
# against the string value rather than against symbols?
I tried to do the same thing in the irb console and it returned false for the undefined method. But then I tried the same in some test.rb file and true was returned to both existing and unexisting methods.
Sample code:
def test_method
end
symbols = Symbol.all_symbols.map { |x| x }
puts symbols.include?(:test_method) # returns true in both cases
puts symbols.include?(:test_method_nonexistant) # returns false in irb, true if executed directly
The questions are: why do we convert symbols to strings in such case and why there are different results in irb and normal file?
Thanks!
Let us go through a slightly modified version of your test code as it is seen by
irband as a stand alone script:When you try this in
irb, each line will be parsed and evaluated before the next line. When you hit the second line,symbolswill contain:test_methodbecausedef test_method;endhas already been evaluated. But, the:test_method_nonexistentsymbol hasn’t been seen anywhere when we hit line 2 so lines 4 and 5 will say “false”. Line 6 will, of course, give us another false because:really_not_theredoesn’t exist until afterevalreturns. Soirbsays this:If you run this as a Ruby script, things happen in a slightly different order. First Ruby will parse the script into an internal format that the Ruby VM understands and then it goes back to the first line and starts executing the script. When the script is being parsed, the
:test_methodsymbol will exist after the first line is parsed and:test_method_nonexistentwill exist after the fifth line has been parsed; so, before the script runs, two of the symbols we’re interested in are known. When we hit line six, Ruby just sees anevaland a string but it doesn’t yet know that theevalcause a symbol to come into existence.Now we have two of our symbols (
:test_methodand:test_method_nonexistent) and a simple string that, when fed toeval, will create a symbol (:really_not_there). Then we go back to the beginning and the VM starts running code. When we run line 2 and cache our symbols array, both:test_methodand:test_method_nonexistentwill exist and appear in thesymbolsarray because the parser created them. So lines 3 through 5:will print “true”. Then we hit line 6:
and “false” is printed because
:really_not_thereis created by theevalat run-time rather than during parsing. The result is that Ruby says:If we add this at the end:
Then we’ll get another “true” out of both
irband the stand-alone script becauseevalwill have created:really_not_thereand we will have grabbed a fresh copy of the symbol list.