I'm currently using the respond_with and respond_to methods for the first time. My controller file looks like this:
class CommentsController < ApplicationController
respond_to :html, :js
def create
#post = Post.find(params[:post_id])
#comment = current_user.comments.build(comment_params)
#comment.post = #post
#comment.user = current_user
authorize #post
if #comment.save
flash[:notice] = "Comment was saved."
redirect_to [#post.topic, #post]
else
flash[:error] = "There was an error saving the comment. Please try again."
redirect_to [#post.topic, #post]
end
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
authorize #comment
if #comment.destroy
flash[:notice] = "Comment was removed."
else
flash[:error] = "Comment couldn't be deleted. Try again."
end
end
respond_with(#comment) do |format|
format.html { redirect_to [post.topic, #post] }
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
I'm getting the error, "RoutingError: Undefined Method respond_with".
I've googled "respond_with", and in the Rails Guides it says that I need a gem called 'responders', which makes sense. When I try to add that, though, I run into trouble; apparently, 'responders' needs another gem, 'railties', which rails requires a different version of that gem. My rails has been working just fine without 'railties', at least until now.
Does anyone know whether I actually need 'responders' in the first place, or what else might be causing my error message, or which version of 'railties' I should install, if any?
Thanks!
Try moving the respond_with... code to be within the destroy method.
Related
currently doing an tutorial and ran into an issue with redirect.
Rails prints out the following message:
undefined method ` redirect_to' for #<PortfoliosController:0x00007fc6ec31b108> Did you mean? redirect_to
PortfolioController:
class PortfoliosController < ApplicationController
def index
#portfolio_items = Portfolio.all
end
def new
#portoflio_item = Portfolio.new
end
def create
#portfolio_item = Portfolio.new(params.require(:portfolio).permit(:title, :subtitle, :body))
respond_to do |format|
if #portfolio_item.save
format.html { redirect_to portfolios_path, notice: 'Portfolio was successfully created.' }
else
format.html { render :index }
end
end
end
def edit
#portfolio_item = Portfolio.find(params[:id])
end
def update
#portfolio_item = Portfolio.find(params[:id])
respond_to do |format|
if #portfolio_item.update(params.require(:portfolio).permit(:title, :subtitle, :body))
format.html { redirect_to portfolios_path, notice: 'Portfolio item successfully updated!'}
else
format.html { render :edit }
end
end
end
end
As you can see, i use redirect_to also in the def create without any problems, but def update says redirect_to is undefined?
Any ideas?
Thanks in advance!
As you've typed redirect_to, that's almost correct, and also Rails is telling you probably you mean redirect_to, then the problem is like in most of the cases, when opening curly braces, accidentally a non-breaking space is added. Unless you're using an editor that handles non-visible characters then you can realize of this, otherwise the error will be there until Rails throws the error.
Just delete the space between the { and respond_to.
For Atom see atom-highlight-bad-chars.
For Sublime see Dealing with Alt+Space (non-breakable space) in Sublime Text.
Following is the controller
class CommentsController < InheritedResources::Base
def comment_params
params.require(:comment).permit(:name, :email, :body, :post_id)
end
def create
#comment = Comment.new(params[:comment_params])
if #comment.save
flash[:notice] = 'Comment was succesfully posted.'
redirect_to(#comment.post)
else
flash[:notice] = "Error creating comments: #{#comment.errors}"
redirect_to(#comment.post)
end
end
end
All I see is,
ActionController::ActionControllerError in CommentsController#create
Cannot redirect to nil!
Rails.root: c:/sites/myrubyblog
Application Trace | Framework Trace | Full Trace
app/controllers/comments_controller.rb:13:in `create'
By commenting the redirect_to in else block will through a different error saying missing template!
did try the few solutions from previously asked questions, but nothing is helping!
In your else block are also redirecting to #comment.post even though it's not saved. So basically it is nil.
I don't think your comment is being saved. Try doing this to find out if there is any error
if #comment.save! # this will show errors if any
...
else
post = Post.find(params[:post_id])
redirect_to post
end
I am assuming your params has post_id in it. If not #comment.post will also be nil. Causing that error in both if and else blocks.
not params[:comment_params] but comment_params, you should change your create action to this and ensure your post_id is set:
class CommentsController < InheritedResources::Base
def comment_params
params.require(:comment).permit(:name, :email, :body, :post_id)
end
def create
#comment = Comment.new(comment_params)
if #comment.save
flash[:notice] = 'Comment was succesfully posted.'
redirect_to(#comment.post)
else
flash[:notice] = "Error creating comments: #{#comment.errors}"
redirect_to(#comment.post)
end
end
end
else
flash[:notice] = "Error creating comments: #{#comment.errors}"
redirect_to(#comment.post) <--
end
When it failed to save, it tries to redirect to #comment.post, but it's nil because you didn't set post_id correctly
#comment = Comment.new(params[:comment_params])
Which should be
#comment = Comment.new(comment_params)
I really want to start learning Rails best practices, especially following the "fat model, skinny controller" logic.
Say I have the following comment controller
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comment_params)
#comment.user_id = current_user.id if current_user
#comment.save!
if #comment.save
redirect_to post_path(#post)
else
render 'new'
end
end
def edit
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
end
def update
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
if #comment.update(params[:comment].permit(:comment))
redirect_to post_path(#post)
else
render 'Edit'
end
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
private
def comment_params
params.require(:comment).permit(:comment)
end
What's a good place to start refactoring the code?
Immediately I think I an make the #post and #comment in both edit and update into a separate method, follow by calling a before_action on the method. But that is still putting all the code in the controller.
Are there any code that I can move to the model? If so, how should I structure them?
This code doesn't have much room for improvement, it's a basic crud, here's an example of a before_action like you suggested
before_action :load_post_and_comment, only: %i(edit update destroy)
def load_post_and_comment
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
end
And here a couple of other notes
def create
# ...
#comment.save!
if #comment.save
# ...
else
# ..
end
end
In this codition the you should remove the extra #comment.save! you only need to save once.
def update
# ...
if #comment.update(params[:comment].permit(:comment))
# ...
else
# ...
end
end
You already have the comment_params method, use it, because if you at any point add a new attribute to the comment, you'll update that method but you'll probably forget this part and you'll get werid errors till you notice that you need to permit here too.
If you want to really go all out with the skinny controller model, there is this gem: https://github.com/NullVoxPopuli/skinny_controllers
Where, you'd configure your CommentsController like so:
class CommentsController < ApplicationController
include SkinnyControllers::Diet
def create
if model.errors.present?
render 'new'
else
redirect_to post_path(model)
end
end
def update
redirect_to post_path(model)
end
# ... etc
private
def comment_params
params.require(:comment).permit(:comment)
end
end
I'm working through the Apress "Beginning Rails 4, 3rd edition" book. The book introduces you to Rails by building a blog application step by step. I'm about halfway through, and have received the following error message:
ActiveModel::ForbiddenAttributesError in CommentsController#create
I've traced this to my comments_controller.rb file, which looks like this:
class CommentsController < ApplicationController
before_filter :load_article
def create
#comment = #article.comments.new(params[:comment])
if #comment.save
redirect_to #article, :notice => 'Thanks for your comment'
else
redirect_to #article, :alert => 'Unable to add comment'
end
end
def destroy
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to #article, :notice => 'Comment deleted'
end
private
def load_article
#article = Article.find(params[:article_id])
end
end
Specifically, the problem seems to be caused by line 5:
#comment = #article.comments.new(params[:comment])
From what I've gathered, the problem seems to be that the book I'm working through was written for an earlier version of Rails. I'm using Rails 4.2.0, and it seems that I ned to use different syntax. What do I need to change to get my code to work?
You need a method that's private in your controller called comment_params (convention, you could call it anything)
Controller:
def create
#comment = #article.comments.new(comment_params)
if #comment.save
redirect_to #article, :notice => 'Thanks for your comment'
else
redirect_to #article, :alert => 'Unable to add comment'
end
end
private
def comment_params
params.require(:comment).permit!
end
It's called strong_parameters and is a gem so you can google it to find it on github
params.require(:comment).permit! will permit anything though, you will probably want to limit it by passing attributes params.require(:comment).permit(:name, :message) - assuming you have name and message attributes.
You'll want to replace params[:comment] with the call to the comment_params method in your update method if you have one.
you need to do this before you create your model object. Rails has to sanitize the parameters before you are allowed to put it in there.
comment_params = params.require(:comments).permit(:attribute1, :attribute2)
#comment = #article.comments.new(comment_params)
Under the method load_article add the following method:
def comment_params
params.require(:comment).permit( ... )
end
and replace the three dots with the attributes you need to allow.
Then in your create function you can write
#comment = #article.comments.new(comment_params)
You will probably need to do something similar in your update function.
I have been trying to get to grips with jQuery and been following a railscast on adding an Ajax add review form, which works fine but I would now like to add into it the ability for a review to belong to a user as well as a venue.
Reviews controller
def create
#review = Review.create!(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end
views\reviews\create.js.erb
$("#new_review").before('<div id="flash_notice"><%= escape_javascript(flash.delete(:notice)) %></div>');
$("#reviews_count").html("<%= pluralize(#review.venue.reviews.count, 'Review') %>");
$("#reviews").append("<%= escape_javascript(render(:partial => #review)) %>");
$("#new_review")[0].reset();
I have tried changing the controller to:
def create
#review = #current_user.reviews.create!(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end
but it just wont submit, with no errors.
I think I have the models set correctly with belongs_to and has_many, I think this is a controller issue I'll add other code bits if needed.
Development log
NoMethodError (undefined method `reviews' for nil:NilClass):
app/controllers/reviews_controller.rb:14:in `create'
Thanks for any help!
It appears that your error is residing with #current_user. According to your development log, #current_user is nil when you call #current_user.reviews on it. I would say track down where this #current_user instance variable is being set and find out why it is nil. Now, what kind of authentication are you using? Most authentication plugins, especially those used by Ryan Bates of the Railscasts you mentioned, use a local variable, say just current_user, as the means to access the currently signed in user. I know I do in all my code.
So, rewrite the line as
#review = current_user.reviews.create!(params[:review])
and see if that works. If it doesn't, change it back and then track down where this #current_user is being set. Chances are good it is being set in a before_filter :method_name at the beginning of your controller.
Calling create! (with exclamation mark) will throw an exception and thus abort your create action if saving fails. Check your log/development.log for these exceptions.
Use build instead of create and lose the exclamation mark.
def create
#review = #current_user.reviews.build(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end