Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build vs new in Rails 4

My question is similar to Build vs new in Rails 3.

In Rails 3, I could build an object in the view to check authorization via cancan.

<% if can? :create, @question.answers.new %>
  # Code...
<% end %>

In Rails 3, the difference between .new and .build was that .build added the newly built object to the parent's collection, which then resulted in an additional record in the view, which obviously was not desired.

In Rails 4, however, both add the object to the collection, rendering an empty record in the view.

Has anyone any advice on how to solve this? Checking if a record is .persisted? in the view would be an option but somehow I feel I shouldn't have to do that.

Edit: To clarify, the CanCan model looks like this:

can :manage, Answer do |answer|
  user.belongables.include?(answer.question.try(:belongable))
end

Because of this, I can't just check by class. An actual instance is actually needed to compare based on the relation.

like image 879
pdu Avatar asked Jul 24 '13 10:07

pdu


2 Answers

I'm not fully up-to-date on CanCan but unless the ability to create is tied to a specific @question instance in CanCan it looks like you can check authorization against the class directly. No instance needs to be built and there's no extraneous object in your view.

<% if can? :create, Answer %>
  # Code..
<% end %>

https://github.com/ryanb/cancan/wiki/Checking-Abilities#checking-with-class

EDIT:

Based on the edit, try building a stand-alone Answer with the association to the question that you need.

<% if can? :create, Answer.new(question: @question) %>
  # Code..
<% end %>

this should not, at least, add a Answer instance to your @question.answers collection.

like image 82
GSP Avatar answered Oct 13 '22 08:10

GSP


I could solve the problem and found out two ways.

First, as https://github.com/rails/rails/issues/9167 points out, using scoped solves this. So, instead I am using @question.answers.scoped.new. As I explained, a simple Answer.new(question: @question) did not succeed since there was more data needed, and the example was oversimplified.

Second, holding onto the MVC pattern. The controller is responsible for preparing the data. So, when looping through the questions, you prepare the data in your controller, like @answers = @question.answers. Now, the @answers collection is not being affected by .new or .build on the association.

like image 2
pdu Avatar answered Oct 13 '22 07:10

pdu