ActiveRecord::RecordNotFound in StoriesController#edit - ruby-on-rails

I have correctly defined the params in my controller. It also says No route matches {:action=>"edit", :controller=>"stories", :id=>nil} missing required keys: [:id]'.
However didn't I define it to find the 'id' in the edit method below in my controller? Also, I don't understand why my destroy method isn't working either :/
class StoriesController < ApplicationController
before_action only: [:destroy, :show, :edit, :update]
def index
#stories = Story.order('created_at DESC')
end
def new
#story = current_user.stories.build
end
def create
#story = current_user.stories.build(story_params)
if #story.save
flash[:success] = "Your beautiful story has been added!"
redirect_to root_path
else
render 'new'
end
end
def edit
#story = Story.find(params[:id])
end
def update
if #story.update.attributes(story_params)
flash[:success] = "More knowledge, more wisdom"
redirect_to root_path
else
render 'edit'
end
end
def destroy
if #story.destroy
flash[:success] = "I think you should have more confidence in your storytelling"
else
flash[:error] = "Can't delete this story, sorry"
end
end
def show
#stories = Story.all
end
private
def story_params
params.require(:story).permit(:name, :description)
end
end
Index.html.erb:
<p id="notice"><%= notice %></p>
<h1>This is a list of posts</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>User</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #stories.each do |story| %>
<tr>
<td><%= story.name %></td>
<td><%= story.description %></td>
<td><%= story.user.email %></td>
<td><%= link_to 'Show', story %></td>
<% if user_signed_in? %>
<td><%= link_to 'Edit', edit_story_path(#story) %></td>
<td><%= link_to 'Destroy', story, method: :delete, data: { confirm: 'Are you sure?'} %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
<%= link_to 'New Story', new_story_path %>
Routes.rb:
Rails.application.routes.draw do
resources :stories
devise_for :users
root to: 'stories#index'
end
rake routes:
Prefix Verb URI Pattern Controller#Action
stories GET /stories(.:format) stories#index
POST /stories(.:format) stories#create
new_story GET /stories/new(.:format) stories#new
edit_story GET /stories/:id/edit(.:format) stories#edit
story GET /stories/:id(.:format) stories#show
PATCH /stories/:id(.:format) stories#update
PUT /stories/:id(.:format) stories#update
DELETE /stories/:id(.:format) stories#destroy

In your view,
<td><%= link_to 'Edit', edit_story_path(#story) %></td>
Should be,
<td><%= link_to 'Edit', edit_story_path(story) %></td>
Because, your loop is,
You should be having story variable inside loop because you have taken |story| inside the parameters as |story|
So, you form will be.
<% #stories.each do |story| %>
<tr>
<td><%= story.name %></td>
<td><%= story.description %></td>
<td><%= story.user.email %></td>
<td><%= link_to 'Show', story %></td>
<% if user_signed_in? %>
<td><%= link_to 'Edit', edit_story_path(#story) %></td>
<td><%= link_to 'Destroy', story_path(story),method: :delete,data: { confirm: 'Are you sure?' } %></td>
<% end %>
</tr>
<% end %>
Now, the delete link should be like this,
<td><%= link_to 'Destroy', story_path(story),method: :delete,data: { confirm: 'Are you sure?' } %></td>

Related

How to allow users to only see edit and delete link if the article is their own?

My users are set up through Devise. I also can use CanCanCan.
I set up an articles model and any user can create articles. They can only delete and edit their own article creations. On the index, they can view all articles that have been created by all users. There is currently an option to View, Edit, and Delete. I only want that option visible on the articles that are owned by the user. I want all other article lines to be blank. (Except for admin of course.)
Users can view posts on views/articles/index.html.erb
<table>
<tr>
<th>Title</th>
<th>Description</th>
</tr>
<% #articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.description %></td>
<td><%= link_to 'View', article_path(article) %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Delete', article_path(article),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
How can I allow users to only see the Edit and Delete button on the post that they own?
I tried this but it does not work:
<table>
<tr>
<th>Title</th>
<th>Description</th>
</tr>
<% #articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.description %></td>
<td><%= link_to 'View', article_path(article) %></td>
<% if user_signed_in? && current_user.articles.exists?(#article.id) %>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Delete', article_path(article),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
<% end %>
</tr>
<% end %>
</table>
I've also tried:
<% if current_user && current_user.articles.exists?(#article.id) %>
Here is what my articles controller looks like: (I know I need to make it look better.)
def create
#article = current_user.articles.build(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
#article = Article.find(params[:id])
if user_signed_in? && current_user.articles.exists?(#article.id)
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
elsif current_user && current_user.admin_role?
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
else
redirect_to #article
end
end
def destroy
#article = Article.find(params[:id])
if user_signed_in? && current_user.articles.exists?(#article.id)
#article.destroy
redirect_to articles_path
elsif current_user && current_user.admin_role?
#article.destroy
redirect_to articles_path
else
redirect_to articles_path
end
end
As you have access to the current_user helper provided by Devise, then you can compare it with the article's owner. This can be in the view, to render the proper links to perform the actions:
<% #articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.description %></td>
<td><%= link_to 'View', article_path(article) %></td>
<% if current_user == article.user %>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Delete', article_path(article),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
<% end %>
</tr>
<% end %>
You can move this verification to a helper, in the ApplicationHelper to be available in most of your views, like:
module ApplicationHelper
def owner?(object)
current_user == object.user
end
end
You pass the object, and this returns true or false depending if the current user is equal to the object user. The view only changes to:
<% if owner?(article) %>
If you want to move this verification to the controller, or also if you want to use it in both cases, then you can do the same verification in the controller. As the owner? helper method isn't available in the controller, you can just redirect back in case the current_user isn't the article's owner, like:
def edit
unless current_user == #article.user
redirect_back fallback_location: root_path, notice: 'User is not owner'
end
end
If you want to move this part to a before callback, to be able to use it in the edit and destroy method, then you can add it as a private method, and having access to the #article you can do such comparison, it'd be:
private
def owner?
unless current_user == #article.user
redirect_back fallback_location: root_path, notice: 'User is not owner'
end
end
This way you just need to add it as a before_action callback:
before_action :owner?, only: %i[edit destroy]
Most probably before the one that defines the #article variable.
Note the use of redirect_back in Rails 5, previous versions might use redirect_to :back.

NoMethodError in Welcome#index - Getting Started with Rails

Screenshot Photo
Need help for this error. I can't figure out what's wrong with the code. I did research for related topics online but I can't find any solution. Below are the codes.
<!-- index.html.erb -->
<h1>Hello, Rails!</h1>
<%= link_to 'My Blog', controller: 'articles' %>
<%= link_to 'New article', new_article_path %>
<table>
<tr>
<th>Title</th>
<th>Text</th>
</tr>
<% #articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Destroy', article_path(article),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
Here's the code from the controller.
# welcome_controller.rb
class WelcomeController < ApplicationController
def index
end
end
Config codes
# routes.rb
Rails.application.routes.draw do
get 'welcome/index'
resources :articles
root 'welcome#index'
end
Any help would be appreciated!
Undefined method each for nil:NilClass
The error is due to #articles is nil. You should define it in the welcome#index
class WelcomeController < ApplicationController
def index
#articles = Article.all
end
end
However you can tweak the index.html.erb to avoid such errors
<% unless #articles.blank? %>
<% #articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Destroy', article_path(article), method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
<% end %>
#Pavan's answer is correct and will solve your issue! As you getting started, I decided to write this answer to explain a little more what was happening:
You're routing root to 'welcome#index', i.e., when you hit http://localhost:300/ it you call method index from WelcomeController. We are used to call it index action from WelcomeController.
After run this method, it will (by default) render the file app/views/welcome/index.html.erb. See the pattern? The action name is the same as the file name and the controller name is the same as the folder name containing this file.
In this file you were using #articles. This is a variable that was defined in WelcomeController#index.
Your issue: this variable wasn't defined in the controller, resulting a nil object. I.e, it doesn't existed.
Solution: define this variable as #Pavan suggested.
But you could fall into the same exception again: if you haven't articles saved. To prevent this case you just need to check if #articles is nil, as #Pavan suggested too.
Hope this answer will clarify the issue and the suggestions to solve it!

NoMethodError in MicropostsController#index

I cannot figure this out-
NoMethodError in MicropostsController#index
and
I have searched the web and cannot seem to make the right change. I am doing Michael Hartl's Rails Tutorial and I am in ch 2 where we do microposts.
Here's index.html.erb
Listing Microposts
<table>
<thead>
<tr>
<th>Content</th>
<th>User</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #microposts.each do |micropost| %>
<tr>
<td><%= micropost.content %></td>
<td><%= micropost.user_id %></td>
<td><%= link_to 'Show', micropost %></td>
<td><%= link_to 'Edit', edit_micropost_path(micropost) %></td>
<td><%= link_to 'Destroy', micropost, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Micropost', new_micropost_path %>
and
Microposts Controller
Class MicropostsController < ApplicationController
def index
end
def show
end
def new
end
def edit
end
def create
end
def update
end
def destroy
end
end
You are getting this error because there is no microposts instance variable in index action, in index action you should set #microposts like this:
def index
#microposts = Micropost.all
end

Ajax edit page in index

I have this page Index page:
<h1>Listing users</h1>
<table border="1">
<tr>
<th>Name</th>
<th>Description</th>
<th>Who</th>
<th></th>
<th></th>
<th></th>
</tr>
<% #users.each do |user| %>
<tr>
<td><%= user.name %></td>
<td><%= user.description %></td>
<td><%= user.who %></td>
<%= render 'users/form' %>
<td><%= link_to 'Show', user %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New User', new_user_path %>
Scaffold controller
def index
#users = User.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users }
end
end
I need to edit user in index page, not to go url /users/2/edit, stay on users page.
How I do this in Ajax?
Add remote:true to the Edit line in order to make an ajax call:
<td><%= link_to 'Edit', edit_user_path(user), remote:true %></td>
Also, add an edit.js.erb file (a javascript file with embedded ruby) in which you do all the javascript actions to be executed after the Edit. That edit.js.erb file is generated when the edit method is done. This is where you write all the changes to the javascript code which affect the current page (the page will not be re-rendered).
Additionally, add the following to the edit method:
respond_to do |format|
format.js
end

Sunspot search doesn't work

I'm trying to make an search option in my site, but it won't show results after an search query.
I have followed this tutorial: http://collectiveidea.com/blog/archives/2011/03/08/full-text-searching-with-solr-and-sunspot/
My routes.rb:
resources :users do
collection do
get :search
end
member do
get :following, :followers
end
end
My index.html.erb
<table>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
<th></th>
<th></th>
</tr>
<% #users.each do |user| %>
<tr>
<td><%= user.name %></td>
<td><%= user.email %></td>
<td><%= link_to 'Show', user %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %>
</td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New User', new_user_path %>
My model user.rb
searchable do
text :name
end
And my user controller
def index
#users = User.paginate(page: params[:page])
end
def search
#users = User.search do
keywords params[:query]
end.results
respond_to do |format|
format.html { render :action => "index" }
end
end
What am i doing wrong?

Resources