how to link to a nested route path inside a loop? - ruby-on-rails

In my application I have stories and substories. Substories are nested inside stories and on the storiesindex.html.erb. I'm looping trough all the stories, and inside I'm looping through all the substories.
here is the code:
<% #stories.each do |story| %>
<%= story.title %>
<%= story.plot %>
<%= link_to 'Show', story_path(story) %>
<%= link_to 'Edit', edit_story_path(story) %>
<%= link_to "Delete", story_path(story), method: :delete, data: { confirm: "Are you sure?" } %>
<% story.substories.each do |substories| %>
<%= substories.title %>
<%= substories.subplot %>
<% end %>
<% end %>
<%= link_to 'New Story', new_story_path %>
This works fine, but I want to link to the edit page of each substory by passing the following argument inside the second loop:
<%= link_to 'Edit', edit_story_substory_path(substory.story, substory) %>
I get a NameError in Stories#index undefined local variable or method 'substory', however this work fine in the substories index.html.erb file.
I've also tried the following:
<%= link_to 'Edit', edit_story_substory_path(#substory.story, #substory) %>
for which I get a NoMethodError in Stories#index undefined method 'story' for nil:NilClass
Here are my routes models and controllers:
#routes.rb
resources :stories do
resources :substories
end
#story.rb
has_many :substories
#substory.rb
belongs_to :story
stories_controller.erb
before_action :set_story, only: [:show, :edit, :update, :destroy]
def index
#stories = Story.all
end
def show
#substories = Substory.where(story_id: #story.id).order("created_at DESC")
end
def new
#story = Story.new
end
def edit
end
def create
#story = Story.new(story_params)
respond_to do |format|
if #story.save
format.html { redirect_to root_path, notice: 'Story was successfully created.' }
format.json { render :show, status: :created, location: root_path }
else
format.html { render :new }
format.json { render json: root_path.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #story.update(story_params)
format.html { redirect_to root_path, notice: 'Story was successfully updated.' }
format.json { render :show, status: :ok, location: root_path }
else
format.html { render :edit }
format.json { render json: #story.errors, status: :unprocessable_entity }
end
end
end
def destroy
#story.destroy
respond_to do |format|
format.html { redirect_to stories_url, notice: 'Story was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_story
#story = Story.find(params[:id])
end
def story_params
params.require(:story).permit(:title, :plot)
end
substories_controller.erb
before_action :set_substory, only: [:show, :edit, :update, :destroy]
before_action :set_story
def index
#substories = Substory.all
end
def show
end
def new
#substory = Substory.new
end
def edit
end
def create
#substory = Substory.new(substory_params)
#substory.user_id = current_user.id
#substory.story_id = #story.id
if
#substory.save
redirect_to #story
else
render 'new'
end
end
def update
respond_to do |format|
if #substory.update(substory_params)
format.html { redirect_to root_path, notice: 'Story was successfully updated.' }
format.json { render :show, status: :ok, location: root_path }
else
format.html { render :edit }
format.json { render json: #story.errors, status: :unprocessable_entity }
end
end
end
def destroy
#substory.destroy
redirect_to root_path
end
private
def set_story
#story = Story.find(params[:story_id])
end
def set_substory
#substory = Substory.find(params[:id])
end
def substory_params
params.require(:substory).permit(:title, :subplot)
end
What am I missing?

<% story.substories.each do |substory| %>
<%= substory.title %>
<%= substory.subplot %>
<% if substory %>
<%= link_to 'Edit', edit_story_substory_path(substory.story, substory) %>
<% end %>
<% end %>
You just made a typo. #substory would work too if you declare it on your Stories#index

Related

No route matches {:action=>"new", :controller=>"profiles", :id=>"address"}, missing required keys: [:user_id]

I work with Device and just added the 'wicked' gem to my Rails app. In my RegistrationsController I have defined the following:
class Users::RegistrationsController < Devise::RegistrationsController
def new
super
end
def create
super
end
def update
super
end
protected
def after_sign_up_path_for(resource)
user_steps_path
end
def after_update_path_for(resource)
new_user_profile_path(current_user.id)
end
end
Basically I wish my form to have one further step after sign up, which would be the address:
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :address
def show
#user = current_user
render_wizard
end
end
After the User has given his address, I want him/her to be redirected to their profile, where they can also give the information about themselves. My thinking, this is an update action in my RegistrationsController. Or how do I redirect to the profile, after my multistep form is finilised, meaning the step address is done? Here is address.html.erb:
<%= form_for #user, url: wizard_path do |f| %>
<div class="field">
<%= f.label :street %>
<%= f.text_area :street %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
For now ActionContoller complains about my header routes and looks for id address and I don't get why... Here is my ProfilesContorller:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
def show
#user = User.eager_load(:profile).find(params[:user_id])
#profile = #user.profile
#review = Review.new
#reviews = Review.where(profile: #profile)
end
def new
#user = current_user
#profile = Profile.new
end
def edit
#profile = #user.profile
end
def create
#user = current_user
#profile = #user.build_profile(profile_params)
respond_to do |format|
if #profile.save
format.html { redirect_to user_profile_path(current_user.id), notice: 'Profile was successfully created.' }
format.json { render :show, status: :created, location: #profile }
else
format.html { render :new, notice: 'Did not save' }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to user_profile_path(current_user.id), notice: 'Profile was successfully updated.' }
format.json { render :show, status: :ok, location: #profile }
else
format.html { render :edit }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'Profile was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_profile
#profile = current_user.profile
end
def profile_params
params.permit(:about, :avatar)
end
end
And here is the error part in my header.html.erb
<% if current_user.profile == nil %>
<li><span class="bg-primary text-white rounded"><%= link_to 'Create profile', new_user_profile_path%></span></li>
<% else %>
<li><span class="bg-primary text-white rounded"><%= link_to 'My profile', user_profile_path(#user) %></span></li>
<% end %>
<li><span class="bg-primary text-white rounded"><%= link_to 'Log out', destroy_user_session_path, method: :delete %></span></li>
ActionController basically complains about the second line and the "new_user_profile_path". How did address got into profiles contoller -> new -> id and how do I procees with the error mentioned in the title. Thank you!
As per your Error, user_id is missing in your route
Try, new_user_profile_path(#user) in second line
So finally it should be <%= link_to 'Create profile', new_user_profile_path(#user)%>

NoMethodError in Submissions#show

I'm getting the following error:
NoMethodError in Submissions#show
undefined method `comment_path'
I can login, enter a title and URL, and post a comment. When I hit the "Create Comment button"
this is where it errors out:
<%= link_to 'delete', comment, method: :delete, data: { confirm: 'are you sure?'} %>
Other than the error, I'm trying to ADD a Comment not DELETE one?
_comment.html.erb
<%= div_for(comment) do %>
<div class="comments-wrapper clearfix">
<div class="null-left">
<p><small> <%= comment.user.email %> <strong><%=
time_ago_in_words(comment.created_at) %></strong> ago </small></p>
<p class="lead"><%= comment.body %></p>
</div>
</div>
<% if comment.user == current_user %>
<%= link_to 'delete', comment, method: :delete, data: { confirm:
'are you sure?'} %>
<% end %>
<% end %>
comments_controller.rb
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
def create
#submission = Submission.find(params[:submission_id])
#comment = #submission.comments.new(comment_params)
#comment.user = current_user
respond_to do |format|
if #comment.save
format.html { redirect_to #submission, notice: 'Comment was successfully created.' }
else
format.html { render :new }
end
end
end
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to comments_url, notice: 'Comment was
successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_comment
#comment = Comment.find(params[:id])
end
def comment_params
params.require(:comment).permit(:submission_id, :body, :user_id)
end
end
submission_controller.rb
class SubmissionsController < ApplicationController
before_action :set_submission, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#submissions = Submission.all
end
def show
end
def new
#submission = current_user.submissions.build
end
def edit
end
def create
#submission = current_user.submissions.build(submission_params)
respond_to do |format|
if #submission.save
format.html { redirect_to #submission, notice: 'Submission was
successfully created.' }
format.json { render :show, status: :created, location: #submission
}
else
format.html { render :new }
format.json { render json: #submission.errors, status:
:unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #submission.update(submission_params)
format.html { redirect_to #submission, notice: 'Submission was
successfully updated.' }
format.json { render :show, status: :ok, location: #submission }
else
format.html { render :edit }
format.json { render json: #submission.errors, status:
:unprocessable_entity }
end
end
end
def destroy
#submission.destroy
respond_to do |format|
format.html { redirect_to submissions_url, notice: 'Submission was
successfully destroyed.' }
format.json { head :no_content }
end
end
def upvote
#submission = Submisson.find{params[:id]}
#submission.upvote_by current_user
redirect_to :back
end
def downvote
#submission = Submisson.find{params[:id]}
#submission.downvote_by current_user
redirect_to :back
end
private
def set_submission
#submission = Submission.find(params[:id])
end
def submission_params
params.require(:submission).permit(:title, :url)
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :submissions do
member do
put "Like", to: "submission#upvote"
put "Dislike", to: "submission#downvote"
end
resources :comments
end
root to: "submissions#index"
end
For delete, you need to pass submission id like
Your code
<%= link_to 'delete', comment, method: :delete, data: { confirm: 'are you sure?'} %>
After modified
<%= link_to 'Delete', [comment.submission, comment], method: :delete, data: { confirm: 'Are you sure?' } %>
On the comment form
<%= f.hidden_field :user_id, value: current_user.id %>
Create action on comment controller, remove this two lines
#comment = #submission.comments.new(comment_params)
#comment.user = current_user
After update delete link then test for comment create if not creating comment still then follow the below code.
Now your comment create action look like this
def create
#submission = Submission.find(params[:submission_id])
respond_to do |format|
if #submission.comments.create(comment_params)
format.html { redirect_to #submission, notice: 'Comment was successfully created.' }
else
format.html { render :new }
end
end
end
That code from my project which already working, if not working on your side then if have your public repository then post URL or post full error trace.
Update Based on GitHub Link
If you run the rake routes then you will see the comment delete routes format like
DELETE /submissions/:submission_id/comments/:id(.:format) comments#destroy
You need to pass submission_id plus id for comment delete
Update link_to delete looks like this
<%= link_to 'Delete', [comment.submission, comment], method: :delete, data: { confirm: 'Are you sure?' } %>
And your destroy action looks like this on the comments_controller
def destroy
#submission = Submission.find(params[:submission_id])
#comment = #submission.comments.find(params[:id])
#comment.destroy
redirect_to submission_path(#submission)
end
Hope it helps

What does it mean this error First argument in form cannot contain nil or be empty in RoR

I have this error First argument in form cannot contain nil or be empty
In the next code and have no idea how to resolve. I checked other similar questions but no luck.
I also share the controller which seems the issue but still no idea how to fix it
<%= form_for #upload do |f| %>
<% if #upload.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#upload.errors.count, "error") %> prohibited this document from being saved:</h2>
<ul>
<% #upload.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.file_field :file %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Controller:
This what makes the thing in my app. But I don't see the issue.
class UploadsController < ApplicationController
before_action :upload_params, only: [:show, :edit, :update, :destroy]
def index
#uploads = Upload.all
end
def show
send_data(#upload.file_contents,
type: #upload.content_type,
filename: #upload.filename)
end
def new
#upload = Upload.new
end
def edit
end
def create
#upload = Upload.new(upload_params)
respond_to do |format|
if #upload.save
format.html { redirect_to upload_path, notice: 'Document was successfully created.' }
format.json { render :show, status: :created, location: #upload }
else
format.html { render :new, notice: 'Wrong file' }
format.json { render json: #upload.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #upload.update(upload_params)
format.html { redirect_to #upload, notice: 'Document was successfully updated.' }
format.json { render :show, status: :ok, location: #upload }
else
format.html { render :edit }
format.json { render json: #upload.errors, status: :unprocessable_entity }
end
end
end
def destroy
#upload.destroy
respond_to do |format|
format.html { redirect_to uploads_url, notice: 'Document was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_upload
#upload = Upload.find(params[:id])
end
def upload_params
params.require(:upload).permit(:file)
end
end
It's pretty obvious from the error I would say. The #upload object must not be nil. Please make sure you initiate that object before rendering this view (usually in the controller).
Looking at your controller code, please change the before_action to call set_upload.
before_action :set_upload, only: [:show, :edit, :update, :destroy]

Rails - using link_to on application.html.erb for nested resource not getting id

I have a sidebar on application.html.erb, and the links should go to /brands/[brand_id]/coupons
I used brand_coupons_path(#brand) but I get an error saying 'No route matches {:action=>"index", :brand_id=>nil, :controller=>"coupons"} missing required keys: [:brand_id]'
resources :brands do
resources :coupons, :sales
end
class Brand < ActiveRecord::Base
has_many :coupons
has_many :sales
accepts_nested_attributes_for :coupons
accepts_nested_attributes_for :sales
end
class Coupon < ActiveRecord::Base
belongs_to :brand
end
<div class="sidebar">
<ul class="sidebar-list">
<li><a class="sidebar-header">Most Popular Brands</a></li>
<% #brands.each do |brand| %>
<% if current_page?(:controller => 'coupons') %>
<li><%= link_to brand.name, brand_coupons_path(#brand), :class => "sidebar-link" %></li>
<% else %>
<li><%= link_to brand.name, brand_sales_path(#brand), :class => "sidebar-link" %></li>
<% end %>
<% end %>
</ul>
</div>
class CouponsController < ApplicationController
before_action :set_coupon, only: [:show, :edit, :update, :destroy]
# before_filter :load_brand
def new
#coupon = Coupon.new
end
def index
#coupons = Coupon.where(brand_id: params[:brand_id])
end
def show
end
def create
#coupon = Coupon.new(coupon_params)
respond_to do |format|
if #coupon.save
format.html { redirect_to '/', notice: 'Coupon was successfully created.' }
format.json { render :show, status: :created, location: #coupon }
else
format.html { render :new }
format.json { render json: #coupon.errors, status: :unprocessable_entity }
end
end
end
class BrandsController < ApplicationController
before_action :set_brand, only: [:show, :edit, :update, :destroy]
# GET /brands
# GET /brands.json
def index
#brands = Brand.all
end
# GET /brands/1
# GET /brands/1.json
def show
end
# GET /brands/new
def new
#brand = Brand.new
end
# GET /brands/1/edit
def edit
end
# POST /brands
# POST /brands.json
def create
#brand = Brand.new(brand_params)
respond_to do |format|
if #brand.save
format.html { redirect_to #brand, notice: 'Brand was successfully created.' }
format.json { render :show, status: :created, location: #brand }
else
format.html { render :new }
format.json { render json: #brand.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /brands/1
# PATCH/PUT /brands/1.json
def update
respond_to do |format|
if #brand.update(brand_params)
format.html { redirect_to #brand, notice: 'Brand was successfully updated.' }
format.json { render :show, status: :ok, location: #brand }
else
format.html { render :edit }
format.json { render json: #brand.errors, status: :unprocessable_entity }
end
end
end
# DELETE /brands/1
# DELETE /brands/1.json
def destroy
#brand.destroy
respond_to do |format|
format.html { redirect_to brands_url, notice: 'Brand was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_brand
#brand = Brand.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def brand_params
params.require(:brand).permit(:name, :logo)
end
end
You're passing an undefined #brand variable into your routes helpers, rather than the block variable brand. Change:
<%= link_to brand.name, brand_coupons_path(#brand), :class => "sidebar-link" %>
<%= link_to brand.name, brand_sales_path(#brand), :class => "sidebar-link" %>
to
<%= link_to brand.name, brand_coupons_path(brand), :class => "sidebar-link" %>
<%= link_to brand.name, brand_sales_path(brand), :class => "sidebar-link" %>
What does #brand contains? I think you should put an object to #brand variable.
Like this. #brand = Brand.find(params[:id])
Can we see the controller of your index?

How to force rank songs by vote?

So, I have an app that allows users to up vote/like songs (using this gem). However, I'd like the songs with higher votes to be ranked up the list. As currently, they only appear in the order in which they were uploaded. How do I accomplish this?
songs#index.html.erb
<div id="layout-1">
<div class="left-side">
<h3>Songs</h3>
<ol>
<% #songs.each do |song| %>
<li><%= link_to song.title, song %><br></li>
<%=link_to '&#9650'.html_safe, vote_for_song_path(song), :remote => true, :method => :put %>
<%#=link_to '&#9660'.html_safe, vote_against_song_path(song), :remote => true, :method => :put %> |
Submitted <%= time_ago_in_words(song.created_at) + " ago" %>
<span class="comments"> | <%= pluralize(song.comments.size, 'comment') %></span> | <span class="votes"><%= pluralize(song.votes.count, 'like') %></span><br />
<%#= link_to 'Show', song, class: "button small secondary" %>
<%= link_to('Edit', edit_song_path(song), class: "button small secondary") if can? :update, song %>
<%= link_to('Destroy', song, method: :delete, data: {confirm: 'Are you sure?'}, class: "button small secondary") if can? :destroy, song %>
<% end %>
</ol>
</div>
<div class="right-side">
<%= image_tag('delorean.jpg')%>
</div></div>
<br />
songs_controller.rb
class SongsController < ApplicationController
before_filter :authenticate_user!, only: [:create ,:edit, :update, :destroy, :vote_for_song]
before_action :set_song, only: [:show, :edit, :update, :destroy, :vote_for_song]
def vote_for
#song = Song.find(params[:id])
current_user.vote_for(#song)
respond_to do |format|
format.js { render 'update_votes' }
end
end
def vote_against
#song = Song.find(params[:id])
current_user.vote_against(#song)
respond_to do |format|
format.js { render 'update_votes' }
end
end
# GET /Songs
# GET /Songs.json
def index
#songs = Song.all
end
# GET /Songs/1
# GET /Songs/1.json
def show
#comment = Comment.new(song: #song)
end
# GET /Songs/new
def new
#song = Song.new
end
# GET /Songs/1/edit
def edit
end
# POST /Songs
# POST /Songs.json
def create
#song = Song.new(song_params)
respond_to do |format|
if #song.save
format.html { redirect_to #song, notice: 'Song was successfully created.' }
format.json { render action: 'show', status: :created, location: #song }
else
format.html { render action: 'new' }
format.json { render json: #song.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /Songs/1
# PATCH/PUT /Songs/1.json
def update
respond_to do |format|
if #song.update(song_params)
format.html { redirect_to #song, notice: 'Song was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #song.errors, status: :unprocessable_entity }
end
end
end
# Song /Songs/1
# Song /Songs/1.json
def destroy
#song.destroy
respond_to do |format|
format.html { redirect_to songs_url }
format.json { head :no_content }
end
end
private
def set_song
#song = Song.find(params[:id])
end
def song_params
params.require(:song).permit(:title, :artist, :bio, :track, :user_id)
end
end
Your Song model should have a plusminus field or somesuch (looking at the gem briefly, that looks like plusminus, which is the sum of up plus down votes), which counts the number of thumbs-ups a given song has received. Then, simply query with a sort on that field:
def vote_for
#song = Song.find(params[:id])
current_user.vote_for(#song)
#song.plusminus = #song.voted_for
#song.save
respond_to do |format|
format.js { render 'update_votes' }
end
end
def index
#songs = Song.order("plusminus")
end

Resources