Updating Rails 4 code to Rails 4.2 code - ruby-on-rails

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.

Related

Where does params[:id] come from in rails?

I am a beginner of Rails. I am learning rails with the book 'Beginning Rails 4' now. I want to ask you about 'parameter' passed to params method. The following is one of typical rails controllers.
class CommentsController < ApplicationController
before_action :load_article
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
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
def comment_params
params.require(:comment).permit(:name, :email, :body)
end
end
Yes, this is just a typical comment controller used to create a comment attached to an article. The Comment model 'belongs to' the Article model, and the Article model 'has many' comments.
Take look at the destroy method.
def destroy
#comment = #article.comments.find(params[:id])
-- snip --
end
It finds the comment associated with the article by find(params[:id]). My question is, where on earth does params[:id] come from?
Does it come from URL? Or does rails save params hash automatically whenever any comment record is created? So we can find any comment by find(params[:id])?
The load_article method is similar.
def load_article
#article = Article.find(params[:article_id])
end
It finds an article by params[:article_id]. Where does this params[:article_id] come from? How does rails find an article by this?
params[:id] is meant to be the string that uniquely identifies a (RESTful) resource within your Rails application. It is found in the URL after the resource's name.
For example, for a resource called my_model, a GET request should correspond to a URL like myserver.com/my_model/12345, where 12345 is the params[:id] that identifies that specific instance of my_model. Analogies follow for the other HTTP requests (PUT, DELETE etc) and their RESTful counterparts.
You should read about Rails routing and its interpretation of RESTful architecture if you're still confused about these concepts and terminologies.
params[:id] does come from the URL. When you use resources in your routes file, Rails will automatically generate the standard REST routes for you. In your destroy example, that would usually be a be a request to /comments/:id using the DELETE HTTP method, in which that :id is added to the params hash, i.e. params[:id].

respond_with Undefined method

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.

Issue with before_filter

Please help me try and understand what is happening here:
I need to approve a nested snippet but when I do it says it cannot find book. I think it may be an issue with the routes because the URL in the browser doesn't match the rake routes.
If someone could hold my hand and explain this as you would to a child :)
Couldn't find Book without an ID
Below is the controller with snippets#approve and the before_filter.
class SnippetsController < ApplicationController
before_filter :authenticate_user!
before_filter :find_book
def create
#raise params.inspect
#snippet = #book.snippets.create(params[:snippet])
#snippet.user = current_user
if #snippet.save
redirect_to #book
flash[:success] = "Snippet submitted and awaiting approval."
else
flash[:base] = "Someone else has submitted a snippet, please try again later"
redirect_to #book
end
end
def approve
#raise params.inspect
#snippet = #book.snippets.find(params[:id])
#snippet.update_attribute(:approved, true)
redirect_to admins_path
end
def edit
#snippet = #book.snippets.find(params[:id])
end
def update
#snippet = #book.snippets.find(params[:id])
respond_to do |format|
if #snippet.update_attributes(params[:snippet])
format.html { redirect_to #book, notice: 'Comment was successfully updated.' }
else
format.html { render action: "edit" }
end
end
end
private
def find_book
#raise params.inspect
#book = Book.find(params[:book_id])
end
end
Now I understand that since I'm doing a post my rake routes says this.
/books/:book_id/snippets/:id(.:format)
Here is the routes for the custom route:
active_snippet POST /snippets/:id/activate(.:format)
This is my custom routes for book && snippet :approval
post "books/:id/activate" => "books#approve", :as => "active_book"
post "snippets/:id/activate" => "snippets#approve", :as => "active_snippet"
I've currently got this in my browser ../snippets/2/activate
Erm.... Not sure if I'm thinking correctly.
You're sending a POST request to snippets/:id/activate which calls snippets#approve.
There is a before_filter on the entire SnippetsController that calls find_book which executes #book = Book.find(params[:book_id]). Because your path is snippets/:id/activate, params[:book_id] is nil and hence you are getting that error.
You need to either change your snippets#approve path to include the book_id, or pass the book_id as a POST param so that your before filter has access to it.

ActiveModel::ForbiddenAttributesError

i've been following rails guide on creating and mounting an engine here.Created blog post and when i tried to comment ,it returned "ActiveModel::ForbiddenAttributesError in Blorgh::CommentsController#create " error.
Comment controller
require_dependency "blorgh/application_controller"
module Blorgh
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment])
flash[:notice] = "Comment has been created!"
redirect_to posts_path
end
end
end
and here is comment model
module Blorgh
class Comment < ActiveRecord::Base
end
end
how to resolve the issue?
I guess you are using rails 4. You need to mark all the required parameters
here it goes :
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(post_params)
flash[:notice] = "Comment has been created!"
redirect_to posts_path
end
def post_params
params.require(:blorgh).permit(:comment)
end
hope this link helps...
I had the same error. So if you disect the params hash it easy to see the nested comment params with text key. Seems the tutorial is for Rails 3, so for the rails 4 way with trusted params the changes needed is to add the comment_params method as below.
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"uOCbFaF4MMAHkaxjZTtinRIOlpMj2QSOYf+Ugn5EMUI=",
"comment"=>{"text"=>"asfsadf"},
"commit"=>"Create Comment",
"post_id"=>"1"}
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comment_params)
flash[:notice] = "Comment has been created!"
redirect_to posts_path
end
private
# Only allow a trusted parameter "white list" through.
def comment_params
params.require(:comment).permit(:text)
end

New to jQuery/Ajax how can I add a user relation to creating a new review?

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

Resources