1

Good morning,

I'm starting on Ruby, wanted to create a small tool that fetches my public IP and sends it over by email. I'm stumbling on a basic problem with a string comparison and an if/else block that won't process.

Code is quite simple (see below). The problem comes at the string comparison at line 21. What I'd want is that whenever the IP address changed from what was recorded in a file, the new IP overwrites the one in the file, and other actions (not in the current code) will ensue.

It looks like that sometimes the comparison is not executed, thus the statements following the if do not execute.

I'd like to understand why is that. Line 21 has been changed at times for if (!oldIP == ip) to if (oldIP != ip), same result. I also suspect the return value to be ignored (dead code path ?) sometimes.

Here's the code

#!/usr/bin/env ruby

require "net/http"

puts "\e[H\e[2J"

def FetchIPAddress()
  oldIP = ""
  if File::exists?('/tmp/wanipaddress.txt')
    iFile = File.open('/tmp/wanipaddress.txt')
    oldIP = iFile.read()
    iFile.close()
  end

  oFile = File.open('/tmp/wanipaddress.txt', "w+")
  ip =  Net::HTTP.get(URI("https://api.ipify.org"))

  puts "old = " + oldIP
  puts "new = " + ip

  if (!oldIP == ip)
    puts "changed"
    oFile.puts ip
    oFile.close()
  else
    ip = "unchanged"
    puts "unchanged"
  end
  return ip
end

Really, I do see some erratic behaviour here; I suspect it's just me being a newbie with Ruby.

  • Are you certain `if (oldIP != ip)` does not correct the problem? Note you open the file for writing, but only write to it when the `if` clause is `true`. If the `if` clause is `false` the (then empty) file will be closed by Ruby when you return from the method. That is wrong but also will cause confusion when running your code. One moment your current IP is saved to file, the next it is no longer there! Try this (after removing `ofile = ...`): `if (oldIP != ip); puts "changed"; File.write('/tmp/wanipaddress.txt', ip); else...`. – Cary Swoveland Jan 21 '17 at 22:50
  • Another problem is that `oFile.puts ip` adds a newline to the ip before writing it to file. For example, "154.5.147.124\n". When that is read it obviously will not match the current ip, which doesn't end with a newline. Note too that, by convention, "snake-case" is used when naming methods (and variables). Among other things, what means they begin with a lower-case letter. `FetchIPAddress`, though it works, is a constant because it begins with a capital letter. The snake-case equivalent is `fetch_ip_address` or `fetch_IP_address`. – Cary Swoveland Jan 21 '17 at 23:16
  • "file will be closed by Ruby when you return from the method" — returning from a method does not close files. The dangling file is closed when the program ends or maybe when the object gets garbage collected. – akuhn Jan 21 '17 at 23:58
  • @Cary thanks a lot. Regarding the exact logic behind where to open/close files, disregard my code; this is a truncated version of what is really going on in the tool; some parts were sniped. About the naming convention, I'll have to lookup on that; the names right now might look at best inconsistent. I should have thought about puts appending \n to my string. I was aware that puts() might not be the best solution for everything, and wasn't aware of PERL-era chomp() :). Thanks for pointing that out. – J.F.Gratton Jan 22 '17 at 00:19
  • @akuhn, thank you for setting me straight. In this case it would be closed during garbage-collection. JörgWMittag has a good explanation of the process [here](http://stackoverflow.com/questions/4795447/rubys-file-open-and-the-need-for-f-close). – Cary Swoveland Jan 22 '17 at 00:20

2 Answers2

2

Your file likely contains a line break.

Try this

if old_ip.chomp != ip.chomp
  ...
end

chomp removes a trailing linebreak.

Best use p to print values for debugging, this will escape whitespace and thus make trailing linebreaks visible. You should never use puts for debugging.


And here is why !a == b will never ever work.

!a == b is the same as a.!().==(b) and executed as follows

  • first the ! method is called on object a, which returns false for a string
  • then ==(b) method is called on the resulting boolean
  • and since a boolean is never equal to a string the comparison will always fail
akuhn
  • 27,477
  • 2
  • 76
  • 91
1

The problem with this line (if (!oldIP == ip)) is pretty simple - what you want it to do is check whether the oldIP is different from the new IP.
What you do instead is take oldIP, check whether it's true or false, then negate it (that's what the ! does), then compare it to ip. Since oldIP is a String, and thus always true, which gets negated to false, and ip is (I'm guessing) a String, it will always be true, your line essentially reads if (false == true).

To solve this problem, you could use the != comparison operator, like if oldIP != ip, or, if you like the negation, if !(oldIP == ip).

Nils Landt
  • 3,104
  • 18
  • 18