Ruby Koans: Why convert list of symbols to strings
I'm referring to this test in about_symbols.rb in Ruby Koans https://github.com/edgecase/ruby_koans/blob/master/src/about_symbols.rb#L26
def test_method_names_become_symbols
symbols_as_strings = Symbol.all_symbols.map { |x| x.to_s }
assert_equal true, 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?
Why exactly do we have to convert that list into strings first?
This has to do with how symbols work. For each symbol, only one of it actually exists. Behind the scenes, a symbol is just a number referred to by a name (starting with a colon). Thus, when comparing the equality of two symbols, you're comparing object identity and not the content of the identifier that refers to this symbol.
If you were to do the simple test :test == "test", it will be false. So, if you were to gather all of the symbols defined thus far into an array, you would need to convert them to strings first before comparing them. You can't do this the opposite way (convert the string you want to compare into a symbol first) because doing that would create the single instance of that symbol and "pollute" your list with the symbol you're testing for existence.
Hope that helps. This is a bit of an odd one, because you have to test for the presence of a symbol without accidentally creating that symbol during the test. You usually don't see code like that.
Because if you do:
assert_equal true, all_symbols.include?(:test_method_names_become_symbols)
It may (depending on your ruby implementation) automatically be true, because :test_method_names_become_symbols
creates the symbol. See this bug report.
Both answers above are correct, but in light of Karthik's question above I thought I would post a test that illustrates how one might accurately pass a symbol to the include
method
def test_you_create_a_new_symbol_in_the_test
array_of_symbols = []
array_of_symbols << Symbol.all_symbols
all_symbols = Symbol.all_symbols.map {|x| x}
assert_equal false, array_of_symbols.include?(:this_should_not_be_in_the_symbols_collection) #this works because we stored all symbols in an array before creating the symbol :this_should_not_be_in_the_symbols_collection in the test
assert_equal true, all_symbols.include?(:this_also_should_not_be_in_the_symbols_collection) #This is the case noted in previous answers...here we've created a new symbol (:this_also_should_not_be_in_the_symbols_collection) in the test and then mapped all the symbols for comparison. Since we created the symbol before querying all_symbols, this test passes.
end
An additional note about the Koans: make use of puts
statements as well as custom tests if you don't understand anything. For instance, if you see:
string = "the:rain:in:spain"
words = string.split(/:/)
and have no idea what words
might be, add the line
puts words
and run rake
at the command line. Likewise, tests like the one I added above can be helpful in terms of understanding some of the nuances of Ruby.