Render in a different order on the page breaks the routing - ruby-on-rails

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">❤</span> <%= pun.votes_for.size %>
<% 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?

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.

Related

How to use authentication logic to create two different link_to locations (Ruby on Rails & Devise)

First post on StackOverflow. :) Am new to Ruby on Rails (and coding in general), and I am struggling the most with understanding documentation. So I am sure this answer is out there somewhere, but if it is, I didn't find/understand it.
I am trying to create two options:
Click on logo before login- route to root_path
Click on logo after login- route to alerts_path
This solution below works, but there must be a better, more tidy and concise way to write it instead of repeating all of that code???
<% if user_signed_in? %>
<%= link_to alerts_path, class: "d-flex flex-row navbar-brand", input_html: {data: {bs_toggle: "offcanvas", bs_target: "#offcanvas"}} do %>
<%= cl_image_tag("ouicity_logo_j5rhro") %>
<h2 id="logo" class="ms-3">ouicity</h2>
<% end %>
<% else %>
<%= link_to root_path, class: "d-flex flex-row navbar-brand", input_html: {data: {bs_toggle: "offcanvas", bs_target: "#offcanvas"}} do %>
<%= cl_image_tag("ouicity_logo_j5rhro") %>
<h2 id="logo" class="ms-3">ouicity</h2>
<% end %>
<% end %>
A possible simple refactor (DRY code) is too just add logic for the url change (using the ternary operator):
<%= link_to user_signed_in? ? alerts_path : root_path, class: "d-flex flex-row navbar-brand", input_html: {data: {bs_toggle: "offcanvas", bs_target: "#offcanvas"}} do %>
<%= cl_image_tag("ouicity_logo_j5rhro") %>
<h2 id="logo" class="ms-3">ouicity</h2>
<% end %>
You can eventually extract this logic to a helper to keep your view simpler:
def logo_path
user_signed_in? ? alerts_path : root_path
end
<%= link_to logo_path, ...

Rails 5. How to submit from the form_tag to custom action?

I'm a real newbie at Ruby and Rails, and I've been looking for the solution for two days. I need to submit data from form_tag to action 'create' in my controller to add new entries to database, but looks like I'm doing something terribly wrong, because absolutely nothing happens, and it seems that form_tag doesn't even redirect to needed action.
Here's the page code:
<h1>Todos</h1>
<% #projects.each do |project| %>
<tr>
<h2><%= project.title %></h2>
<% project.todos.each do |todo| %>
<ul style="list-style-type:disc">
<li><%= todo.text %></li>
</ul>
<% end %>
</tr>
<% end %>
<%= form_tag({controller: "mega", action: "create"}, method: "get", remote: true) do %>
<h2>New todo</h2>
<p>
<%= text_field_tag 'text' %>
</p>
<p>
<%= select_tag 'title', options_from_collection_for_select(#projects, 'id', 'title') %>
</p>
<p>
<%= link_to 'CANCEL' %>
<%= link_to 'OK', "", :onclick => "$('#form_id').submit()" %>
</p>
<% end %>
And the controller:
class MegaController < ApplicationController
def index
#projects = Project.all
#todos = Todo.all
end
def update
end
def create
#newTodo = Todo.create(text: params[:text])
#newProject = Project.find_by(title: params[:title])
#newProject.todos << #todo
#newTodo.save
end
end
My routes file. I seriously don't know how it works:
Rails.application.routes.draw do
get 'mega/index'
root 'mega#index'
get 'mega/update'
post 'mega/create'
resources :todos
resources :projects
end
You create resources with a POST request. Never GET.
GET requests should be idempotent - they should not update or alter resources on the server. One very important reason is that they are stored in the browser's history, so pressing the back button will cause unintended consequences for the user.
In Rails flavor MVC instead of tacking the action name on the path of the route you use the HTTP verb to create routes to the correct action:
GET /things things#index
POST /things things#create
I'm not going to attempt to salvage your code (it's deeply flawed) and instead show you how you would solve this the rails way as it is much simpler:
<%= form_for(Todo.new) do |f| %>
<h2>New todo</h2>
<%= f.text_field :text %>
<%= f.collection_select(:project_id, #projects, :id, :title, prompt: true) %>
<%= f.submit %>
<% end %>
This would submit to todos#create - if you want to route it to an unconventional action you can use the url option:
<%= form_for(Todo.new, url: polymorphic_path(controller: 'foo', action: 'bar')) do |f| %>
It's best to learn the rules before you break them.

param is missing or the value is empty: plant

I have added a custom action in my controller called transplant. I simply want to render a dropdown form to select where to be located based on the 'tray_id'
my routes look like this:
resources :plants do
member do
get 'transplant'
end
resources :plantdats, :plant_cycles, :tasks
end
My controller looks like this:
before_action :set_plant, only: [:show, :edit, :update, :destroy, :transplant]
def transplant
if #plant.update(plant_params)
redirect_to #plant
flash[:success] = "Transplanted successfully"
end
end
def set_plant
#plant = Plant.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def plant_params
params.require(:plant).permit(:title, :notes, :category_id, :tray_id, images_files: [])
end
Here is my button calling the action
<%= link_to 'TRANSPLANT', transplant_plant_path(#plant), class: "btn btn-raised btn-info hoverable" %>
Here is my transplant page _transplant.html.erb
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="jumbotron">
<%= form_for(#plant, html: {class: 'form-horizontal'}) do |f| %>
<% if #plant.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#plant.errors.count, "error") %> prohibited this grow from being saved:</h2>
<ul>
<% #plant.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label 'NAME' %>
<%= f.hidden_field :tray_id, value: params[:tray_id] %>
<% if params[:tray_id].nil? %>
<%= f.collection_select(:tray_id, Tray.all, :id, :title) %></br></br>
<% end %>
<%= f.submit class: 'btn btn-raised hoverable btn-success' %>
<% end %>
</div>
</div>
</div>
EDIT
After implementing the route to post 'transplant
and changing my link code to
<%= link_to "TRANSPLANT", transplant_plant_path(#plant, tray_id: #plant.tray_id), method: "post", class: "btn btn-raised hoverable" %>
I still get the same error. It points right to the plant_params code in my controller.
These are the params that are being passed:
{"_method"=>"post",
"authenticity_token"=>"fhSKt2DpgTwt1J4HsoBqYFSs0B9+pgSvDDxrS/u6yo4c3gvSxYlrrFDmhbPXq+cMho/eTHY+194WZ8zpcb1txA==",
"id"=>"1",
"format"=>"1"}
Im simply trying to update the :tray_id
Ive been at this all day, can anyone help with the error that Im getting?
You should probably provide your code for your transplant action and view. Based on what you provided, it seems like you're trying to build a link, which changes the tray of a plant when clicked. If that's the case, transplant should probably be a POST route instead of GET. Also, you probably want to provide the tray_id in your post link like this:
<%= link_to "TRANSPLANT", transplant_plant_path(#plant, tray_id: {{your id}}), method: "post", class: "..." %>
Then you can get tray_id in your transplant through params[:tray_id] and re-associate your plant and tray
Essentially what I was trying to do was not easily done and my approach needed to change. I simply rendered the transplant form in my view and it works fine now. Thanks again :)
Check your routes you just have defined get route and your request is post after the form submission

Adding user's photo to contest, call method with link_to

I came from a JS background so Rails is weird to me. I currently have a show.html.erb for the contest model:
<h1>Contest name: <%= #contest.name %></h1>
<h2>Contest criteria: <%= #contest.criteria %></h2>
<h3>Photos: </h3>
<%= link_to "Enter Contest", "#" %>
<% #contest.photos.each do |photo| %>
<%= image_tag("#{photo}") %>
<% end %>
With the link_to I'm trying to render all photos that belongs to the current_user and pick one of them to assign it to the current contest. The params passing seems all so mysterious to me. Can you guys point me in the right direction of how I should tackle this problem? Thanks
What I understand you need to select an image from list of images and pass the id. So, try this
<%= label_tag "Enter Contest" %>
<% #contest.photos.each do |photo| %>
<%= link_to (image_tag("#{some photo path}")), some_controller_method_path(:photo_id => photo.id) %>
<% end %>
And in controller you can use params[:photo_id]

Rendering a partial in rails. Specifying the partial for a resource gives an error, but not specifying a partial works fine. What gives?

I've got this working now quite accidentally, but I don't understand what causes it to break when I explicitly specify what partials are to be used for rendering the resource/s. Can anyone explain it?
The index template for my Posts controller contained the following line, which was giving me an error:
<%= render partial: 'posts', collection: #posts %>
The error (in my browser) said:
NoMethodError in Posts#index
Showing /Users/applebum/Sites/rails_projects/eventful2/app/views/posts/_posts.html.erb where line #1 raised:
undefined method `any?' for #<Post:0x000001064b21f0>
Extracted source (around line #1):
1: <% if posts.any? %>
2: <div id="posts">
3: <% posts.each do |post| %>
4: <%= render partial: "posts/post", locals: { post: post } %>
Changing the problem line to
<%= render #posts %>
made the error disappear and the posts appear (displayed nicely in markup from the appropriate partials) as I had wanted and expected them to.
Here's my _posts.html.erb partial:
<% if posts.any? %>
<div id="posts">
<% posts.each do |post| %>
<%= render partial: "posts/post", locals: { post: post } %>
<% # render :partial => "comments/comments", :collection => post.comments %>
<% end %>
</div>
<% end %>
And the _post.html.erb partial it's referring to, if that matters:
<div class="post" id="post_<%= "#{post.id}" %>">
<div class="post_inner">
<%= link_to avatar_for(post.user, size: "small"), post.user.profile %>
<div class="post_body">
<div class="user-tools">
<% if can? :destroy, post %>
<%= link_to '<i class="fi-x"></i>'.html_safe, post, :method => :delete, remote: true, :class => "delete", :confirm => "Are you sure you want to delete this post?", :title => post.content %>
<% end %>
</div>
<h5 class="username">
<%= link_to post.user.name, post.user.profile %>
<span class="timestamp">• <%= time_ago_in_words(post.created_at) %> ago</span>
</h5>
<div class="content">
<%= post.content %>
</div>
<ul class="foot">
<li>Like<li>
<li>Share</li>
</ul>
</div>
</div>
</div>
And the relevant bits from the controller:
class PostsController < ApplicationController
respond_to :html, :js # Allow for AJAX requests as well as HTML ones.
before_filter :load_postable
load_and_authorize_resource
def index
#post = Post.new
#posts = #postable.posts
end
private #################
def load_postable
klass = [User, Event].detect { |c| params["#{c.name.underscore}_id"] } # Look for which one of these there's a ***_id parameter name for
#postable = klass.find(params["#{klass.name.underscore}_id"]) # Call find on that, passing in that parameter. eg Event.find(1)
end
Can anyone explain to me what's going on here? I couldn't find anything in the Layouts and Rendering guide at rubyonrails.org.
Thanks!
Your error comes from assuming :collection and #posts mean the same thing when rendering. From Rails Docs (point 3.4.5):
Partials are very useful in rendering collections. When you pass a collection to a partial via the :collection option, the partial will be inserted once for each member in the collection
So, if you use that, for each post, you will be doing post.any? which fails as any? isn't defined for a single post.
From the same docs, you should check if render returns Nil to see if the collection is empty:
<h1>Posts</h1>
<%= render(#posts) || "There are no posts." %>
PD: Use the partial to render only one post, not all of them.
GL & HF.

Resources