Forum: Ruby Can anyone tell me why my code isn't working?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Adam P. (Guest)
on 2008-10-21 02:37
Hi there,

For some reason the get_mac_by_printer(host_name) method isn't working.
All the other bits work, but I have a feeling that I'm running into a
problem with variables defined within blocks, but I'm at a loss as to
how to negotiate it. The resulting mac is returning as nilClass and it
definitely isn't nil! Code as follows:

#Class for handling the plist file.
class PlistHash
  def initialize(path)
    @path=path
    @plist_hash=Plist::parse_xml(@path)
  end

  def get_mac_by_host(host_name)
    servers=@plist_hash['servers'].each do |s|
  mac=s['mac'] if s['name']==host_name
  return mac
  end
  end

  def get_mac_by_printer(printer_name)
    printers=@plist_hash['printers']
        printers.each do |v|
     host=v['host'] if v['name']==printer_name
     return host
    end
    mac=get_mac_by_host(host)
    return mac
  end

  def get_broadcast_ip
    broadcast_ip=@plist_hash['broadcastIP']
  end
end

Thanks for your help,

Adam
Adam P. (Guest)
on 2008-10-21 03:39
Solved it.

I came up with this solution to force the each loops to return a
specific value from the array of hashes. Explicitly returning the value
of the block each block did the trick. Still don't think I've got the
hang of variable scope though. :-(

class PlistHash
  def initialize(path)
    @plist_hash=Plist::parse_xml(path)
  end

  def get_mac_by_host(host_name)
    servers=@plist_hash['servers'].each do |s|
  return s['mac'] if s['name']==host_name
  end
  end

  def get_host_by_printer(printer_name)
  printers=@plist_hash['printers']
        printers.each do |v|
     return v['server'] if v['name']==printer_name
     end
  end
  def get_mac_by_printer(printer_name)
     host=self.get_host_by_printer(printer_name)
   return self.get_mac_by_host(host)
  end

  def get_broadcast_ip
    broadcast_ip=@plist_hash['broadcastIP']
  end
end
Brian C. (Guest)
on 2008-10-21 11:59
Adam P. wrote:
> For some reason the get_mac_by_printer(host_name) method isn't working.
> All the other bits work, but I have a feeling that I'm running into a
> problem with variables defined within blocks, but I'm at a loss as to
> how to negotiate it.

No, nothing to do with variable scoping.

> The resulting mac is returning as nilClass and it
> definitely isn't nil! Code as follows:
...
>   def get_mac_by_host(host_name)
>     servers=@plist_hash['servers'].each do |s|
>       mac=s['mac'] if s['name']==host_name
>       return mac
>     end
>   end

It definitely *is* nil.

Note that your 'each' loop will onlyever run for one iteration, since
'return mac' will always return from the enclosing method
(get_mac_by_host) during the first iteration. So, unless the passed
host_name happens to match the first entry of @plist_hash['servers'],
then the assignment

   mac = s['mac'] if s['name']==host_name

will not be executed (because it's qualified by "if ... false condition
..."), so the value of the local variable 'mac' will be nil.

To demonstrate:

  if 1 == 2
    foo = "oops"
  end
  puts foo     # nil

So your loop is similar to:

  def mycode
    (1..5).each do |v|
       res = v if v == 3
       return res
    end
  end
  puts mycode   # nil

You could fix this by changin "return res" to "return res if v == 3", or
"return res if res", but more simply

  def mycode
    (1..5).each do |v|
      return v if v == 3
    end
  end
  puts mycode   # 3
Jesús Gabriel y Galán (Guest)
on 2008-10-21 12:35
(Received via mailing list)
On Tue, Oct 21, 2008 at 9:59 AM, Brian C. <removed_email_address@domain.invalid>
wrote:

> You could fix this by changin "return res" to "return res if v == 3", or
> "return res if res", but more simply
>
>  def mycode
>    (1..5).each do |v|
>      return v if v == 3
>    end
>  end
>  puts mycode   # 3

Also check Enumerable#find:

irb(main):002:0> a = [{:name => "a", :value => 3}, {:name => "b",
:value => 4}, {:name => "c", :value => 10}]
=> [{:value=>3, :name=>"a"}, {:value=>4, :name=>"b"}, {:value=>10,
:name=>"c"}]
irb(main):003:0> a.find {|s| s[:name] == "b"}
=> {:value=>4, :name=>"b"}

So, you method could be done as:

def get_mac_by_host(host_name)
    s = @plist_hash['servers'].find {|s| s['name'] == host_name}
    return s['mac'] if s
    nil
end

(not tested). Hope this helps,

Jesus.
Brian C. (Guest)
on 2008-10-21 12:48
Just to make the semantics of 'return' clearer:

  def foo
    1
    return 2
    raise "Never get here"
    3
  end

  puts foo     # 2

return doesn't cause store a value for returning later; it causes the
method to return immediately, returning that value.
This topic is locked and can not be replied to.