Search code examples
cssrubynokogiriopen-uri

undefined method `attr' for nil:NilClass (NoMethodError)


I have a method in my CLI app which is giving me an error.

The method is:

def self.deal_page(input, product_url)
    self.open_deal_page(input)
    deal = {}
    html = open(@product_url)
    doc = Nokogiri::HTML(html)
    data = doc.text.strip
    deal[:name] = doc.css("h1").text.strip
    deal[:discription] = doc.css(".textDescription").text.strip
    @purchase_link = nil
    @purchase_link= doc.at_css("div.detailLeftColumn a.success").attr("href")
        if @purchase_link.nil?
         deal[:purchase] = @product_url
       else
         deal[:purchase] = @purchase_link
       end
     deal
  end

and the error is:

/home/himachhag-45739/code/popular-deals-from-slickdeals.net-cli/lib/popular_deals/newdeals.rb:54:in `deal_page': undefined method `attr' for nil:NilClass (NoMethodError) 
        from /home/himachhag-45739/code/popular-deals-from-slickdeals.net-cli/lib/popular_deals/cli.rb:70:in `disply_deal'                                                 
        from /home/himachhag-45739/code/popular-deals-from-slickdeals.net-cli/lib/popular_deals/cli.rb:49:in `menu'                                                        
        from /home/himachhag-45739/code/popular-deals-from-slickdeals.net-cli/lib/popular_deals/cli.rb:9:in `call'                                                         
        from /home/himachhag-45739/code/popular-deals-from-slickdeals.net-cli/bin/popular-deals:10:in `<top (required)>'                                                   
        from /usr/local/rvm/gems/ruby-2.3.1/bin/popular-deals:22:in `load'                                                                                                 
        from /usr/local/rvm/gems/ruby-2.3.1/bin/popular-deals:22:in `<main>'                                                                                               
        from /usr/local/rvm/gems/ruby-2.3.1/bin/ruby_executable_hooks:15:in `eval'                                                                                         
        from /usr/local/rvm/gems/ruby-2.3.1/bin/ruby_executable_hooks:15:in `<main>'

I tried xpath, at_css, unless, if..else, but doesn't help. Also, I don't get this error everytime, but I would like to get rid of it.


Solution

  • One way to address this problem is to be a bit paranoid:

    @purchase_link = doc.at_css("div.detailLeftColumn a.success").try(:attr, "href")
    
    deal[:purchase] = @purchase_link || @product_url
    

    A couple of things to keep in mind here. In Ruby only nil and false are logically false, so it's very rare you need to specifically test nil? on things. The only case that's necessary is when you want to distinguish between nil and false, which as you can imagine is not very often.

    So in this case either you hit something with at_css or you didn't, in which case the try call doesn't do anything. If it finds something the try call carries on for one more call. Then you can do an assignment with a simple || (or) operator to pick them in priority.

    Another thing is since this code is inside a class method, casually using instance variables can be trouble. If things like purchase_link are only used within this method, remove the @ which makes them persistent.

    Another thing to be careful about is how your method is defined as:

    def self.deal_page(input, product_url)
    

    That declares an argument product_url, but inside:

    html = open(@product_url)
    

    This references the class instance variable @product_url which is not the same. You may be calling open with the wrong variable here.