1
votes

In my Rails application there is a relationship defined between Products and Categories. Each product can have different categories.

I am displaying the product's category this way:

<p>
    <strong>Categories:</strong>
    <%= @product.categories.join(", ") unless @product.categories.nil? %>
</p>

And my Category model:

class Category < ActiveRecord::Base
    def to_s
        @name
    end
end

However, in the show page, the categories are displayed like this:

Categories: #<Category:0x007f2bf515b148>, #<Category:0x007f2bf515aef0>, #<Category:0x007f2bf515ac98>

Why is the to_s method not working? How can I solve this?

4
What you want to show? Product category name?Naim Rajiv
No need for the @ symbol if you want to show category nameMarcelo Risoli

4 Answers

1
votes

The result of @product.categories is an ActiveRecord::Relation, not an array. Therefore the method join is the one implemented inside the relation class, not the Array#join.

You should implicitly cast the list to array

<%= @product.categories.to_a.join(", ") unless @product.categories.nil? %>

or you should pay a little bit more attention of what you really need, avoid loading the entire objects and obtain a more efficient result.

<%= @product.categories.pluck(:name).join(", ") unless @product.categories.nil? %>

It is worth mentioning you can use the to_sentence method.

<%= @product.categories.pluck(:name).to_sentence unless @product.categories.nil? %>

Moreover, the .nil? check is completely useless because @product.categories is a relation and will never be nil.

And if you really want to write better code, you avoid implementing your logic in the view and you rely on helpers and models.

class Product
  def category_names
    categories.pluck(:name)
  end
end

module ProductsHelper
  def product_categories_list(product)
    product.category_names.to_sentence
  end
end

<%= product_categories_list(@product) %>
0
votes

try do define method like:

class Category < ActiveRecord::Base
    def to_s
        self.name
    end
end
0
votes

To access your attribute, if it is a column in your table, you should not use the @attr_name, as they are not stored as instance variables.

Try just to redefine your to_s method like following:

class Category < ActiveRecord::Base
  def to_s
    name
  end
end

It is really interesting the way this behaves:

class Role < ActiveRecord::Base
  def to_s
    nil
  end
end

Then

"#{Role.first}"
 => "#<Role:0x00000007a39f88>"

But:

Role.first.to_s
=> nil
0
votes

I have conducted an experiment to locate the problem and I suggest you look at results.

This is output of a pry REPL. It's not quite a Rails model but it points out the issue.

What I did: I defined a new class and instructed it to output the names af any non-existent methods that are called on it. This is a handy trick to determine implicit calls on your classes.

[1] pry(main)> class Hi
[1] pry(main)*   def method_missing(sym)
[1] pry(main)*     puts sym
[1] pry(main)*   end  
[1] pry(main)* end  
=> :method_missing
[2] pry(main)> a = [Hi.new, Hi.new]
=> [#<Hi:0x0000000405b028>, #<Hi:0x0000000405afd8>]
[3] pry(main)> a.join
to_str
to_str
to_ary
to_str
to_ary
=> "#<Hi:0x0000000405b028>#<Hi:0x0000000405afd8>"

It's not to_s. It's to_str, apparently, that is called.