How to handle find command return undefined method `[]' for nil:nilclass (nomethoderror)

I’m using the find command to collect the information I need from the Puppet Database as below.
vm_name = puppetdb.find {|details| details[‘certname’]==(hostname) and details[‘name’]==‘az_metadata’}[‘value’][‘compute’][‘name’]

Some of the parameters don’t have ‘az_metadata’ parameters, and that causes the find function to show “undefined method `[]’ for nil: nil class (no method error).” How can I handle those undefined method values from the find result for that specific variable?

Rgds,

Hi Maung,

The error is caused by trying to call [] on nil. In your case, Puppetdb.find is not finding any matching record and thus returning nil.

You can add a safe navigation operator & to prevent the error:

vm_name = puppetdb.find { |details| details['certname'] == hostname && details['name'] == 'az_metadata' }&['value']&['compute']&['name']

This way, if one step returns nil, it won’t try to execute the next step.

Best regards,
Bobby the Bot.

1 Like

Hi Bobby,

Thanks for your kind explanation. I’ve added as your suggestion as below and and got undefined method `&’ for {“certname”=>“vm01”, “environment”=>“production”, “name”=>“az_metadata”, etc.

vm_name = puppetdb.find {|details| details[‘certname’]==(hostname) and details[‘name’]==‘az_metadata’}&[‘value’]&[‘compute’&][‘name’]

Please advise.

Rgds,

Is it possible to first fetch the value, and then only look at its details if it’s not nil?

vm_record = puppetdb.find {|details| details[‘certname’]==(hostname) and details[‘name’]==‘az_metadata’}
unless vm_record.nil?
  vm_name = vm_record['value']['compute']['name'] 
end

If you really want a one-line solution, there are various approaches. One way to remove the ‘nil’ is to use “select” instead of “find” to get an array, and then map the lookup:

vm_name = puppetdb.select {|details| details[‘certname’]==(hostname) and details[‘name’]==‘az_metadata’}.map{|it| it[‘value’][‘compute’][‘name’]}.first

This should leave either name or nil in vm_name.

Is this correct syntax? Like the OP, I get errors when trying to use it:

irb(main):054> nil&[:a]
=> false
irb(main):055> {a: 1}&[:a]
(irb):55:in `<main>': undefined method `&' for an instance of Hash (NoMethodError)

I can make the safe navigation operator work with fetch:

irb(main):052> {a: 1}&.fetch(:a)
=> 1
irb(main):053> nil&.fetch(:a)
=> nil

So, for the original case:

vm_name = puppetdb.select {|details| details[‘certname’]==(hostname) and details[‘name’]==‘az_metadata’}&.fetch('value', nil)&.fetch('compute', nil)&.fetch('name', nil)

Hi pcl,

I tried both steps you provided and got below error.

For Fetch
agent_review.rb:31: syntax error, unexpected ‘.’
…tails[‘name’]==‘az_metadata’}&.fetch(‘value’, nil)&.fetch(‘c…
… ^
agent_review.rb:31: syntax error, unexpected ‘.’
…tadata’}&.fetch(‘value’, nil)&.fetch(‘compute’, nil)&.fetch(…
… ^
agent_review.rb:31: syntax error, unexpected ‘.’
…, nil)&.fetch(‘compute’, nil)&.fetch(‘name’, nil)

Use Select
agent_review.rb:31:in block (2 levels) in <main>': undefined local variable or method ‘value’’ for main:Object (NameError)
from aagent_review.rb:31:in map' from agent_review.rb:31:in block in ’
from agent_review.rb:22:in each' from agent_review.rb:22:in

Regards,

M

Hi pcl,

Fist issue has been resolved after follow your provided steps,

vm_record = puppetdb.find {|details| details[‘certname’]==(hostname) and details[‘name’]==‘az_metadata’}
unless vm_record.nil?
vm_name = vm_record[‘value’][‘compute’][‘name’]
end

Now I’m using that same method to the array value and return undefined local variable or method `accountname’ for main:Object (NameError)

Below the code I’m using for this.

localaccounts_record = puppetdb.find {|details| details[‘certname’]==(hostname) and details[‘name’]==‘localaccounts’}
unless localaccounts_record.nil?
localaccounts = localaccounts_record[‘value’]
localaccounts.each do |k,v|
accountname = k
dayslastlogon = v[‘dayslastlogon’]
enabled = v[‘enabled’]
lastLogon = v[‘lastLogon’]
membership = v[‘membership’]
passwordexpires = v[‘passwordexpires’]
passwordlastset = v[‘passwordlastset’]
status = v[‘status’]
end
end

Please advise.

Regards,

M