steven_noble
steven_noble

Reputation: 4203

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

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

Upvotes: 2

Views: 15459

Answers (5)

Julik
Julik

Reputation: 7856

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).

Upvotes: 3

khelll
khelll

Reputation: 23990

Is that a local variable in some partial? if so, then even doing current_part.nil? will raise an error in case that variable is not passed to the partial.

to overcome this do:

counter_part = defined?(counter_part) ? : counter_part : nil

BTW: Normally, Ruby looks for an assignment statement to determine whether something is a variable—if a name hasn’t been assigned to, then Ruby assumes that name is a method call, so executing the following statement will raise an error if x wasn't initialized:

irb(main):001:0> puts "yes nil" if x.nil?
#=>NameError: undefined local variable or method `x' for main:Object

Upvotes: 0

Kathy Van Stone
Kathy Van Stone

Reputation: 26271

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?

Upvotes: 5

Tobias Wärre
Tobias Wärre

Reputation: 803

If the object is nil, then you can't use any of its member because the object itself does not exist. So the comparison should be of the object and nil, not a member of the object and nil.

It's like a null pointer exception.

You should use something like

x = get_some_object if x.nil?

to initialize the variable x if uninitialized.

Upvotes: 0

Atul
Atul

Reputation: 1151

Tobias is right. You cannot access any member of the object if it is nil (as it does not exist). You must check for nil value before performing any operation or before accessing any member varible or function.

In cpp, is it like:

if(!current_part) { perform operation }

` This is a very common NPE(Null Pointer Exception) in almost every programming language.

Upvotes: 0

Related Questions