0

I am stumped about this. In a previous commit my forms to submit new records for a particular model worked perfectly, but I must have done something along the way that messed it up because now my controller just renders the 'new' action whenever I submit a form.

The validations are all passing, but for some reason the records are not saving.

I did not write tests during development, which I am regretting now as I am going back and writing them.

But since I am new to rails, I just want to ask about what is the best way to debug this. I've tried looking at my local server log but it does not offer anything helpful. My controller's create action is below:

def create
    @product = Product.find(params[:product_id])
    @review = @product.reviews.new(params[:review])
    current_user.reviews << @review
    if @review.save
        flash[:notice] = "Successfully created review."
        redirect_to current_user
    else
        render :action => 'new'
    end
end

Apologize is this is super vague (I will delete the question if it is)

Also just to note, none of my models are saving now so it seems like an application-wide issue since they were all working at an earlier commit.

2
  • try @product.save instead of @review.save Commented Jun 6, 2013 at 3:02
  • not sure if ur problem is just the redirect, but redirect_to root_url instead of redirect_to current_user should do the redirection., cheers Commented Jun 6, 2013 at 3:33

3 Answers 3

4

In terms of debugging things like this, I recommend two gems: better_errors and pry. Either/Or. Preferably both.

Better Errors

https://github.com/charliesome/better_errors Just add it to your gemfile and you're done:

group :development do
  gem 'better_errors'
  gem 'binding_of_caller'
end

Raise an error in your controller, and you'll get dumped into an IRB session right at the point that the exception was raised. Then you can poke around and see what's happening directly.

Pry

http://pryrepl.org/

Again, just install the gem:

gem 'pry-rails'

Then restart your server (use rails s directly - passenger or foreman might screw with this) and call binding.pry from your controller:

def create
    @product = Product.find(params[:product_id])
    @review = @product.reviews.new(params[:review])
    current_user.reviews << @review

    binding.pry # <---- You will get dumped right here
    if @review.save
        flash[:notice] = "Successfully created review."
        redirect_to current_user
    else
        render :action => 'new'
    end
end

When you submit the form the server will stop and you should get dumped into a console right at that line. You can then poke around - inspect all the variables, call @review.save to see what happens.

Pry has a ton of features that you'll want to check out while you're in there.

And you can configure better_errors to use pry by default.

Oh, and specs are good :D

Sign up to request clarification or add additional context in comments.

Comments

1

1.If you want the product_id to be set on the review, your product should be an existing record. If not you can simply do

Review.new(params[:review])

instead of

@product = Product.new(params[:product])
@review = @product.reviews.new(params[:review])

2.current_user.reviews << @review creates the review record in the database with current_user_id. you don't have to do the @review.save again.

3.Do @review.save! to test. It will raise an exception if there is problem with saving the record.

2 Comments

Sorry that was a typo regarding your first point. It should be 'find' instead of 'new'. I am associating the review with an existing product. Are you saying I can user the current_user.reviews << @review line to replace the '@review.save' part?
you can't replace @review.save with current_user.reviews << @review because the later doesn't return a boolean. All i am saying is you have two calls to the database unnecessarily. You could do @review.user = current_user instead of current_user.reviews << @review
1

The sequence in which you're instantiating a new object and then saving it is not quite correct.

Rather than appending your uncommitted @review to current_user.reviews prior to saving it, you should only append it based conditionally on whether or not it saved correctly.

The Rails Way of accomplishing what you're attempting would be something akin to this:

def create
    @product = Product.find(params[:product_id])
    @review = @product.reviews.build(params[:review])
    if @review.save
        current_user.reviews << @review
        flash[:notice] = "Successfully created review."
        redirect_to current_user
    else
        render :action => :new
    end
end

This way, if @review.save returns false, you'll know that something was invalid because the :action => :new will be rendered.

EDIT:

Since you're looking to debug without specs, you might try employing the save! method. Rather than returning false upon failure, it actually throws an exception (which is pretty handy for debugging):

@review.save!
#=> ActiveRecord::RecordInvalid: Validation failed: `reason for validation failure`

2 Comments

Thanks, all the responses have been very helpful since I am new to this whole debugging process
Glad to hear it! For the benefit of other readers, if one of these answers was particularly helpful in resolving the particulars of your question, you may want to consider accepting it as correct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.