ruby-on-railsformsacts-as-votable

Render in a different order on the page breaks the routing


I have Challenges containing Puns, and it is possible to vote on puns. On the Challenge Show page, all puns are rendered and show their votes count. This is currently on the view page:

  <%= render @challenge.puns.reverse %>
  <br>
  <div id="form">
    <%= render "puns/form" %>
  </div>

I want the puns form to appear above the items (puns) already submitted. But if swap them around, like this:

  <div id="form">
    <%= render "puns/form" %>
  </div>
  <%= render @challenge.puns.reverse %>

I get a controller error and pun.id is not suddenly not available and the voting link breaks.

No route matches {:action=>"upvote", :challenge_id=>"9", :controller=>"puns", :id=>nil}, missing required keys: [:id]

Here is the puns/form part that is causing the issue

<% if signed_in? %>
  <% if current_user.voted_for? pun %>
    <%= pun.votes_for.size %>
    <span class="pun_text"><%= link_to pun.pun_text, challenge_pun_path(@challenge, pun.id) %></span>
  <% else %>
    <%= link_to like_challenge_pun_path(@challenge, pun.id), method: :put do %>
      <span class="heart_like">&#10084;</span>&nbsp;<%= pun.votes_for.size %>&nbsp;
    <% end %>
      <span class="pun_text"><%= link_to pun.pun_text, challenge_pun_path(@challenge, pun.id) %></span>
  <% end %>
<% end %>

It is the like_challenge_pun_path that throws an error but I cannot understand why. I am declaring @challenge again here, so it should be able to get the id.

Here is the form for the puns:

<%= form_for([@challenge, @challenge.puns.build]) do |f| %>
  <span class=".emoji-picker-container">
    <%= f.text_area :pun_text, placeholder: "Add pun", data: { emojiable: true } %>
  </span>
  <%= f.submit %>
<% end %>

Also, here is my routes setup

  resources :challenges do
    resources :puns do
      member do
        put "like", to: "puns#upvote"
        put "dislike", to: "puns#downvote"
      end
    end
  end

and the corresponding action to upvote

def upvote
  @pun = @challenge.puns.find(params[:id])
  @pun.upvote_by current_user
  redirect_to @challenge
end

Can anyone help?


Solution

  • I think the code is for the puns collection. I assume the issue is that in the form you have something like:

    @challenge.puns.build
    

    So in @challenge.puns collection appears not persisted record (without id), so path for this model cannot be generated.

    As a quick solution I suggest:

    <%= render @challenge.puns.reverse.select(&:persisted?) %>
    

    UPDATE: As I assumed you have

    <%= form_for([@challenge, @challenge.puns.build]) do |f| %>
    

    You can also try:

    <%= form_for([@challenge, Pun.new]) do |f| %>
    

    Or solve it in the controller. But need to see controller code for it.