Can't convert nil to string -- edited to restate all the failing attempts

14,150

Solution 1

The test current_part.to_s == "" returns true on my ruby system when current_part is nil. Unlike some other languages, you can say nil.to_s and nil.nil? and have them work. I think there is something else that is causing the problem. Can you show more of the code?

(My tests were in ruby 1.8.6)

Edit: Looking around, what usually causes the above error is an expression such as "text" + nil, not nil.to_s. Do you have anything like that around?

Solution 2

The problem is in your truncated line where concept.title meets the plus.

When you do

"Foo" + some_obj.some_attr

and some_attr in the object is nil, Ruby won't autocast it to string. Might happen often (!) since Rails casts NULL value in the DB to nils. Workarounds are in-string evaluation:

"Foo #{some_obj.attr_that_can_be_nil}"

pattern substitution (automatically truncates nil)

"Foo %s" % some_obj.attr_that_can_be_nil

or array joining (idem ditto)

["Foo ", some_obj.attr_that_can_be_nil].join

The reason you could not find it is that your "truncated line" deserves it's own 5-6 lines unwrapped properly, that way it would be much easier for you to spot the problem.

On a sidenote, you don't need “ and friends - just type it literally since Rails is UTF-8 nowadays anyway. Moreover, when passing stuff to tag helpers you might get this thing converted to “ which is totally not what you want (if helpers escape entities - I don't remember if they do, but Builder certainly does).

Share:
14,150
steven_noble
Author by

steven_noble

Updated on June 05, 2022

Comments

  • steven_noble
    steven_noble almost 2 years

    At a point in my code, I expect current_part to sometimes be nil, and I want to run some code (inside an if block) when that's not the case.

    Using script/server --debugger, I've established that current_part is in fact nil at that point when the following errors occur.

    All the following versions generate the can't convert nil into String error on the second line:

    #

      def map_concepts_to_part(part, current_part)
         if current_part
            part.concepts.map { |concept| content_tag(:li, "stuff...")}.join
          end
      end
    

    #

      def map_concepts_to_part(part, current_part)
         if test_if_exists(current_part)
            part.concepts.map { |concept| content_tag(:li, "stuff...")}.join
          end
      end
    
      def test_if_exists(test_subject)
        test_subject rescue nil
      end
    

    #

      def map_concepts_to_part(part, current_part)
         if test_if_complete(current_part)
            part.concepts.map { |concept| content_tag(:li, "stuff...")}.join
          end
      end
    
      def test_if_complete(test_subject)
        test_subject.id rescue nil
      end
    

    #

      def test_if_complete(part, current_part)
         unless current_part.to_s == ""
            part.concepts.map { |concept| content_tag(:li, "stuff...")}.join
          end
      end
    

    #

      def test_if_complete(part, current_part)
         unless current_part.nil?
            part.concepts.map { |concept| content_tag(:li, "stuff...")}.join
          end
      end
    

    #

    PS, the truncated line in each of the above is:

    part.concepts.map { |concept| content_tag(:li, "Concept: “" + concept.title + "”", :class => "one_concept") + content_tag(:li, "Attached images (" + concept.images.size.to_s + ")", :class => "all_images") + content_tag(:li, "Attached docs (XX)", :class => "all_docs")}.join