Proper use of ipaddr library?

T

Tom Lynch

Greetings,

I'm very new to Ruby, but have been playing around with validating IP addresses from a .yml file. Everything works but would like to make sure I'm doing things the proper Ruby way (I program in Perl non-OO and bash). The code:

require 'ipaddr'
require 'yaml'
yml = YAML.load_file("/data/dna.yml")

#
# Check hash for key...
#
if (yml.has_key?(board))
puts "Found Key: #{board}"
if (yml[board]["ports"])
yml[board]["ports"].each do |ip|
ipaddr = IPAddr.new "#{ip}"
if (ipaddr.ipv4?)
puts "Good: IPV4: #{ip}"
elsif (ipaddr.ipv6?)
puts "Good: IPV6: #{ip}"
else
puts "ERROR: Not an IPV4 or IPV6 format #{ip}..."
end
end
else
puts "Ports not found?"
end
else
puts "Key Not Found: #{board}"
end

My concern is in the each loop with IPAddr.new "#{ip}", is this the proper way to do this in Ruby? In Perl it might have been "ipv4($ip);" or equivalent without need to do the IPAddr.new on each run through the loop.

Just need to understand Ruby/Libraries and bast practices usage.

Thanks for any help in advance.

Tom
 
R

Robert Klemme

Greetings,

I'm very new to Ruby, but have been playing around with validating IP addresses from a .yml file. Everything works but would like to make sure I'm doing things the proper Ruby way (I program in Perl non-OO and bash). The code:

require 'ipaddr'
require 'yaml'
yml = YAML.load_file("/data/dna.yml")

#
# Check hash for key...
#
if (yml.has_key?(board))
puts "Found Key: #{board}"
if (yml[board]["ports"])
yml[board]["ports"].each do |ip|
ipaddr = IPAddr.new "#{ip}"
if (ipaddr.ipv4?)
puts "Good: IPV4: #{ip}"
elsif (ipaddr.ipv6?)
puts "Good: IPV6: #{ip}"
else
puts "ERROR: Not an IPV4 or IPV6 format #{ip}..."
end
end
else
puts "Ports not found?"
end
else
puts "Key Not Found: #{board}"
end

My concern is in the each loop with IPAddr.new "#{ip}", is this the
proper way to do this in Ruby? In Perl it might have been
"ipv4($ip);" or equivalent without need to do the IPAddr.new on each
run through the loop.

It's OK IMHO but you do not need the string interpolation. If "ip" is a
String already just use it as is, if not use ip.to_s.

Note, the Perl version might also create a new object on every
iteration. It just looks different. And creating objects in OO
languages isn't bad. ;-)

A few other stylistic remarks: you can spare the brackets after if. And
I would store values you have retrieved in a variable - partly for
efficiency reasons and partly to make sure the state cannot change in
between. So:

board_val = yml[board]

if board_val
puts "Found key: #{board}"
ports = board_val["ports"]

if ports
ports.each do |ip|
ipaddr = IPAddr.new ip
# ipaddr = IPAddr.new ip.to_s
....
end
else
puts 'Ports not found.'
end
....

Kind regards

robert
 
S

Simon Krahnke

* Tom Lynch said:
Greetings,

I'm very new to Ruby, but have been playing around with validating IP addresses from a .yml file. Everything works but would like to make sure I'm doing things the proper Ruby way (I program in Perl non-OO and bash). The code:

require 'ipaddr'
require 'yaml'
yml = YAML.load_file("/data/dna.yml")

#
# Check hash for key...
#

The only use of the comment seems to be to declare yml a Hash.
if (yml.has_key?(board))

What is board?
puts "Found Key: #{board}"
if (yml[board]["ports"])
yml[board]["ports"].each do |ip|
ipaddr = IPAddr.new "#{ip}"

That ip already is a string, right? You don't seem to be the kind if
person that does "== true". Either way, that is a strange and costly way
of doing ip.to_s.
if (ipaddr.ipv4?)
puts "Good: IPV4: #{ip}"
elsif (ipaddr.ipv6?)
puts "Good: IPV6: #{ip}"

Maybe you should use the parsed version here. "Good: IPV4: #{ipaddr}"
else
puts "ERROR: Not an IPV4 or IPV6 format #{ip}..."
end
end
else
puts "Ports not found?"
end
else
puts "Key Not Found: #{board}"
end

My concern is in the each loop with IPAddr.new "#{ip}", is this the
proper way to do this in Ruby? In Perl it might have been "ipv4($ip);"
or equivalent without need to do the IPAddr.new on each run through
the loop.

You don't seem to have a problem doing string interpolation every run
through the loop.

The biggest work in creating the object seems to be parsing the string,
which you need to be done anyway. How much it costs to get rid of the
object again, that depends on the garbage collector. If it's a
generational one, it doesn't take anything at all.
Just need to understand Ruby/Libraries and bast practices usage.

Sure.

mfg, simon .... l
 
T

Tom Lynch

Thanks for all the help! I've incorporated some of the suggestions:

#
# This is how you read in a YAML file into a hash table.
#
require 'yaml'
yml = YAML.load_file("/h/tlynch/bin/dna.yml")
board_value = yml[board]

#ipaddr = IPAddr.new
if board_value
puts "Found Key for : #{board}"
ports = board_value["ports"]
if ports
ports.each do |ip|
puts "IP: #{ip}"
# ipaddr = ip
ipaddr = IPAddr.new ip
if ipaddr.ipv4?
puts "Good: IPV4: #{ip}"
elsif ipaddr.ipv6?
puts "Good: IPV6: #{ip}"
else
puts "ERROR: Not correct IPV4 or IPV6 format #{ip}..."
end
end
else
puts "Ports not found?"
end
else
puts "Key Not Found: #{board}"
end

This works great as long as the IP address is correct, but I'm looking for badly
formed (fat fingered) IP Addresses... When I run a bad IP address I get:

Found Key for : LDS6522
IP: 172.4.35
/usr/lib/ruby/1.8/ipaddr.rb:464:in `in6_addr': invalid address (ArgumentError)
from /usr/lib/ruby/1.8/ipaddr.rb:432:in `initialize'
from rubyex4.rb:48:in `new'
from rubyex4.rb:48
from rubyex4.rb:46:in `each'
from rubyex4.rb:46

Obviously I'm doing something wrong and thought I should move the new Object
out side the loop and the assign to check the IP, but that doesn't work either.
Any ideas what I'm doing wrong? Should I be using a different library, one that
can check both IPV4 and IPV6? Thanks for any help!

Tom
 
R

Robert Klemme

Thanks for all the help! I've incorporated some of the suggestions:

You're welcome!
This works great as long as the IP address is correct, but I'm looking for badly
formed (fat fingered) IP Addresses... When I run a bad IP address I get:

Found Key for : LDS6522
IP: 172.4.35
/usr/lib/ruby/1.8/ipaddr.rb:464:in `in6_addr': invalid address (ArgumentError)
from /usr/lib/ruby/1.8/ipaddr.rb:432:in `initialize'
from rubyex4.rb:48:in `new'
from rubyex4.rb:48
from rubyex4.rb:46:in `each'
from rubyex4.rb:46

That's an exception.
Obviously I'm doing something wrong and thought I should move the new Object
out side the loop and the assign to check the IP, but that doesn't work either.
Any ideas what I'm doing wrong? Should I be using a different library, one that
can check both IPV4 and IPV6? Thanks for any help!

The string triggering the error is not a valid IP - neither IPv4 nor
IPv6. So IPv6 support won't change anything. And, btw. IPAddr _does_
support IPv6:

irb(main):002:0> IPAddr.new "1.2.3.4"
=> #<IPAddr: IPv4:1.2.3.4/255.255.255.255>
irb(main):003:0> IPAddr.new "1::4"
=> #<IPAddr:
IPv6:0001:0000:0000:0000:0000:0000:0000:0004/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>

The solution is to put a begin rescue end block into your code. Where
exactly depends on whether you want to terminate the whole loop or just
stop processing the single string. So, if you want to terminate the
whole program you basically do not need to do anything unless you want
to create a nicer error message. To continue iterating and only stop
the loop you need to do something like

if board_value
puts "Found Key for : #{board}"
ports = board_value["ports"]
if ports
ports.each do |ip|
begin
puts "IP: #{ip}"
# ipaddr = ip
ipaddr = IPAddr.new ip
if ipaddr.ipv4?
puts "Good: IPV4: #{ip}"
elsif ipaddr.ipv6?
puts "Good: IPV6: #{ip}"
else
puts "ERROR: Not correct IPV4 or IPV6 format #{ip}..."
end
rescue ArgumentError => e
$stderr.puts "Not an IP: #{ip} (#{e.message})"
end
end
else
puts "Ports not found?"
end
else
puts "Key Not Found: #{board}"
end


I suggest you read a bit about exception handling in Ruby, e.g.
http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_exceptions.html

Kind regards

robert
 
T

Tom Lynch

Thanks for all the help! I've incorporated some of the suggestions:



You're welcome!


This works great as long as the IP address is correct, but I'm looking for badly
formed (fat fingered) IP Addresses... When I run a bad IP address I get:

Found Key for : LDS6522
IP: 172.4.35
/usr/lib/ruby/1.8/ipaddr.rb:464:in `in6_addr': invalid address (ArgumentError)
from /usr/lib/ruby/1.8/ipaddr.rb:432:in `initialize'
from rubyex4.rb:48:in `new'
from rubyex4.rb:48
from rubyex4.rb:46:in `each'
from rubyex4.rb:46



That's an exception.


Obviously I'm doing something wrong and thought I should move the new Object
out side the loop and the assign to check the IP, but that doesn't work either.
Any ideas what I'm doing wrong? Should I be using a different library, one that
can check both IPV4 and IPV6? Thanks for any help!



The string triggering the error is not a valid IP - neither IPv4 nor

IPv6. So IPv6 support won't change anything. And, btw. IPAddr _does_

support IPv6:



irb(main):002:0> IPAddr.new "1.2.3.4"

=> #<IPAddr: IPv4:1.2.3.4/255.255.255.255>

irb(main):003:0> IPAddr.new "1::4"

=> #<IPAddr:

IPv6:0001:0000:0000:0000:0000:0000:0000:0004/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>



The solution is to put a begin rescue end block into your code. Where

exactly depends on whether you want to terminate the whole loop or just

stop processing the single string. So, if you want to terminate the

whole program you basically do not need to do anything unless you want

to create a nicer error message. To continue iterating and only stop

the loop you need to do something like



if board_value

puts "Found Key for : #{board}"

ports = board_value["ports"]

if ports

ports.each do |ip|

begin

puts "IP: #{ip}"

# ipaddr = ip

ipaddr = IPAddr.new ip

if ipaddr.ipv4?

puts "Good: IPV4: #{ip}"

elsif ipaddr.ipv6?

puts "Good: IPV6: #{ip}"

else

puts "ERROR: Not correct IPV4 or IPV6 format #{ip}..."

end

rescue ArgumentError => e

$stderr.puts "Not an IP: #{ip} (#{e.message})"

end

end

else

puts "Ports not found?"

end

else

puts "Key Not Found: #{board}"

end





I suggest you read a bit about exception handling in Ruby, e.g.

http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_exceptions.html



Kind regards



robert

Thank you very much! That was just what I was looking for. I'll read up about exception handling in Ruby. Didn't know about that before.

Tom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top