Reputation: 3940
When I try to loop through an array that has records from two tables it gives an error when it can't find the attribute from the other record.
The array has two different types of elements. One starts like: #<Question id: 1,
, the other starts with #<Answer id: 1,
. How do you get the value of one without it looking for it in the other?
Error:
undefined method `option' for #<Question:0x007fcc2c6610e8>
How would you bypass or loop through this array to show the attribute value when wanted?
The controller:
@pdf = []
@test = Test.find(params[:id])
@test_questions = @test.questions
answers = Answer.all
@all_answers = answers.group_by(&:question_id)
@test_questions.each do |q|
@pdf << q
@pdf += @all_answers[q.id]
end
The html where I am trying to only show the content attribute value first, then all the option attribute values
<tbody>
<% @pdf.each do |test| %>
<tr>
<td><%= test.id %></td>
<% if test.content %>
<td><%= test.content %></td>
<% end %>
<% if test.option %>
<td><%= test.option %></td>
<% end %>
</tr>
<% end %>
</tbody>
This is what the array looks like:
[#<Question id: 1, content: "How did the chicken cross the road?", question_type: "MC", category: "ip_voice", product_id: 8, active: true, created_at: "2014-05-07 17:10:14", updated_at: "2014-05-07 17:10:14", user_id: 1>, #<Answer id: 1, option: "It walked", question_id: 1, correct: false, created_at: "2014-05-07 17:10:14", updated_at: "2014-05-07 17:10:14">, #<Answer id: 2, option: "It was thrown", question_id: 1, correct: true, created_at: "2014-05-07 17:10:14", updated_at: "2014-05-07 17:10:14">, #<Answer id: 3, option: "It got run over and pushed", question_id: 1, correct: false, created_at: "2014-05-07 17:10:14", updated_at: "2014-05-07 17:10:14">, #<Question id: 2, content: "Is this working?", question_type: "TF", category: "ip_voice", product_id: 6, active: true, reated_at: "2014-05-13 16:10:53", updated_at: "2014-05-13 16:10:53", user_id: 1>, #<Answer id: 4, option: "False", question_id: 2, correct: true, created_at: "2014-05-13 16:10:53", updated_at: "2014-05-13 16:10:53">, #<Answer id: 5, option: "True", question_id: 2, correct: false, created_at: "2014-05-13 16:10:53", updated_at: "2014-05-13 16:10:53">]
Attempt for SimonW
<% @options = [] %>
<% @test_questions.each do |q| %>
<tr>
<td width="40px;">1.</td>
<td><%= q.content %></td>
<% @all_answers.each do |a| %>
<% @options << a.find(q.id) %>
<% end %>
</tr>
<% @options.each do |o| %>
<tr>
<%= o.option %>
</tr>
<% end %>
<% end %>
Error:
undefined method `option' for #<Enumerator:0x007fcc4181e678>
Upvotes: 0
Views: 104
Reputation: 6057
How about iterating through object that response to option method (Duck typing), anyways I agree with SimonW that it's a bad design:
<% @options.select{|i| i.respond_to?(:option)}.each do |o| %>
<tr>
<%= o.option %>
</tr>
<% end %>
Upvotes: 0
Reputation: 44725
Try instead:
@test = Test.find(params[:id])
@test_questions = @test.questions
answers = Answer.all
@all_answers = answers.group_by(&:question_id)
@pdf = @test_questions.map { |q| [q, @all_answers[q.id]] }
Then in view:
<tbody>
<% @pdf.each do |question, answers| %>
<tr>
<td><%= question.id %></td>
<td><%= question.content %></td>
<% answers.each do |answer|
<td><%= answer.option %></td>
<% end %>
</tr>
<% end %>
</tbody>
Controller:
@test_questions = @test.questions.includes(:answers)
View:
<tbody>
<% @test_questions.each do |question| %>
<tr>
<td><%= question.id %></td>
<td><%= question.content %></td>
<% question.answers.each do |answer|
<td><%= answer.option %></td>
<% end %>
</tr>
<% end %>
</tbody>
Upvotes: 2
Reputation: 356
You can use respond_to?
to avoid that error and ensure you don't call the option
method on an object which does not know what to do with it:
<% if test.respond_to?(:option) && test.option %>
Upvotes: 1