Wrote a
gem in ruby 1.9.3, then realized it's application deserves being compatible with 1.8.7. After switching, my tests caught an array getting returned in a reversed order. It doesn't actually matter in my case, since it's just two command line flags being joined, and they can be in either order.
I'm not asking for help with my code, rather I'm wanting to understand
how it's possible that ruby would do the following:
This is the line of code that returns in one order in 1.9.3, and the reverse order in 1.8.7 in my tests:
{ :y => $options[:assume_yes], :s => $options[:dry_run] }.collect { |k, v| if v ; "-#{k}" ; end }.join(' ').stripHad an irb session open for a while trying various things. I then tried to repeat the reversed array problem in irb to understand where it's happening:
{ :y => true, :s => true }.collect { |k, v| if v ; "-#{k}" ; end }.join(' ').stripCould NOT repeat, it kept returning in the same order as 1.9.3. OK, sanity check, restart irb, run the same line again:
{ :y => true, :s => true }.collect { |k, v| if v ; "-#{k}" ; end }.join(' ').stripBoom! It returns in reversed order. So I went back through my previous irb session restarting irb each time and individually trying each command I had previously run in irb to see if I could repeat the weirdness. Well I found it, and it literally relates in no way to the line in question:
start irb,
{ :y => true, :s => true }.collect { |k, v| if v ; "-#{k}" ; end }.join(' ').strip => '-s -y'exit irb.
start irb,
a = { :a => nil }
{ :y => true, :s => true }.collect { |k, v| if v ; "-#{k}" ; end }.join(' ').strip => '-s -y'exit irb.
start irb,
b = { :a => nil }
{ :y => true, :s => true }.collect { |k, v| if v ; "-#{k}" ; end }.join(' ').strip => '-y -s'exit irb.
ALSO:
start irb,
a = { :b => nil }
{ :y => true, :s => true }.collect { |k, v| if v ; "-#{k}" ; end }.join(' ').strip => '-s -y'exit irb.
start irb,
a = { :d => nil }
{ :y => true, :s => true }.collect { |k, v| if v ; "-#{k}" ; end }.join(' ').strip => '-y -s'exit irb.
You can go on through the alphabet and various letters will cause the reverse action or not, reliably. Since irb has to be restarted each time, after manually going through most of the alphabet, I wrote a simple
shell script to start irb, run the ruby code, and exit irb - for each letter of the alphabet. Whether using the script, or starting up irb by hand each time, these are the results you'll get:
a = { :a => nil } ; (code) => '-s -y' a = { :a => nil } ; (code) => '-s -y'
a = { :b => nil } ; (code) => '-s -y' b = { :a => nil } ; (code) => '-s -y'
a = { :c => nil } ; (code) => '-s -y' c = { :a => nil } ; (code) => '-s -y'
a = { :d => nil } ; (code) => '-y -s' d = { :a => nil } ; (code) => '-y -s'
a = { :e => nil } ; (code) => '-s -y' e = { :a => nil } ; (code) => '-s -y'
a = { :f => nil } ; (code) => '-s -y' f = { :a => nil } ; (code) => '-s -y'
a = { :g => nil } ; (code) => '-y -s' g = { :a => nil } ; (code) => '-y -s'
a = { :h => nil } ; (code) => '-y -s' h = { :a => nil } ; (code) => '-y -s'
a = { :i => nil } ; (code) => '-s -y' i = { :a => nil } ; (code) => '-s -y'
a = { :j => nil } ; (code) => '-y -s' j = { :a => nil } ; (code) => '-y -s'
a = { :k => nil } ; (code) => '-s -y' k = { :a => nil } ; (code) => '-s -y'
a = { :l => nil } ; (code) => '-s -y' l = { :a => nil } ; (code) => '-s -y'
a = { :m => nil } ; (code) => '-s -y' m = { :a => nil } ; (code) => '-s -y'
a = { :n => nil } ; (code) => '-s -y' n = { :a => nil } ; (code) => '-s -y'
a = { :o => nil } ; (code) => '-y -s' o = { :a => nil } ; (code) => '-y -s'
a = { :p => nil } ; (code) => '-s -y' p = { :a => nil } ; (code) => '-s -y'
a = { :q => nil } ; (code) => '-y -s' q = { :a => nil } ; (code) => '-y -s'
a = { :r => nil } ; (code) => '-y -s' r = { :a => nil } ; (code) => '-y -s'
a = { :s => nil } ; (code) => '-s -y' s = { :a => nil } ; (code) => '-s -y'
a = { :t => nil } ; (code) => '-s -y' t = { :a => nil } ; (code) => '-s -y'
a = { :u => nil } ; (code) => '-y -s' u = { :a => nil } ; (code) => '-y -s'
a = { :v => nil } ; (code) => '-s -y' v = { :a => nil } ; (code) => '-s -y'
a = { :w => nil } ; (code) => '-y -s' w = { :a => nil } ; (code) => '-y -s'
a = { :x => nil } ; (code) => '-y -s' x = { :a => nil } ; (code) => '-y -s'
a = { :y => nil } ; (code) => '-s -y' y = { :a => nil } ; (code) => '-s -y'
a = { :z => nil } ; (code) => '-y -s' z = { :a => nil } ; (code) => '-y -s'As you can see, each side is has the same pattern. I also noticed:
It does not matter what you set the
value of the hash to.
It does not matter if you use a string for a key in the has instead of symbol.
It has nothing to do with join, strip, or collect (any enumeration action will do.)
It does not matter if you have letters following the string or symbol key name. (Like
a = { :zash => nil }.)
I want to understand the scope of the language in which I'm working. How is it possible in any ruby version that setting certain arbitrary hash variables before enumerating over an arbitrary array could reverse the outcome?Thanks!