Rails ajax call 500 internal server error - ruby-on-rails

I'm having trouble working with respond_to method: when the model got saved, it returns in json format correctly. But, when the model didn't get saved, because a error like ActiveRecord::RecordNotUnique, rails returns another respond that it's not my: "Completed 500 Internal Server Error" and the respond returned goes to ajax error fuction.
My question here is, how can I send the correct respond, the one inside the else statement, so I can show to users the correct errors messages.
I'm in development mode.
I change the code a little just for simplicity.
Thanks.
Controller:
def create
#distribuicao = DistribuicaoPorCargo.new(distribuicao_por_cargo_params)
respond_to do |format|
if #distribuicao.save
format.json { render json: #distribuicao.distribuicao, status: :created }
else
format.json { render json: #distribuicao.errors.full_messages, status: :unprocessable_entity }
end
end
end
.js:
$.ajax
url: '/distribuicao_por_cargos'
type: 'POST'
dataType: 'JSON'
data: {
data: JSON.stringify(data_send_to_server)
}
success: (data, textStatus, jqXHR) ->
console.log("AJAX Sucesss: #{textStatus}")
error: (jqXHR, textStatus, errorThrown) ->
console.log("AJAX Failed: #{textStatus}")
log:
Started POST "/distribuicao_por_cargos" for 187.110.216.111 at 017-06-02 17:32:22 -0300
Cannot render console from 187.110.216.111! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by DistribuicaoPorCargosController#create as JSON
Parameters: {"concurso_id"=>"2", "local_da_prova_id"=>"6", "tabela"=>"{\"cargo_id\":\"4\",\"quantidade_de_candidatos\":\"10\"},{\"cargo_id\":\"9\",\"quantidade_de_candidatos\":\"10\"},{\"cargo_id\":\"12}]"}
Concurso Load (0.4ms) SELECT "concursos".* FROM "concursos" WHERE "concursos"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
LocalDaProva Load (0.2ms) SELECT "local_da_provas".* FROM "local_da_provas" WHERE "local_da_provas"."id" = $1 LIMIT $2 [["id", 6], ["LIMIT", 1]]
(0.1ms)
BEGIN
LocalDaProva Load (0.3ms) SELECT "local_da_provas".* FROM "local_da_provas" WHERE "local_da_provas"."id" = $1 LIMIT $2 [["id", 6], ["LIMIT", 1]] SQL (1.5ms)
INSERT INTO "distribuicao_por_cargos" ("created_at", "updated_at", "local_da_prova_id", "distribuicao") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", 2017-06-02 20:32:23 UTC], ["updated_at", 2017-06-02 20:32:23 UTC], ["local_da_prova_id", 6], ["distribuicao", "[{\"cargo_id\":\"4\",\"quantidade_de_candidatos\":\"10\"},{\"cargo_id}]"]]
(0.2ms)
ROLLBACK
Completed 500 Internal Server Error in 108ms (ActiveRecord: 6.3ms)
ActiveRecord::RecordNotUnique (PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_distribuicao_por_cargos_on_local_da_prova_id"
DETAIL: Key (local_da_prova_id)=(6) already exists.
: INSERT INTO "distribuicao_por_cargos" ("created_at", "updated_at", "local_da_prova_id", "distribuicao") VALUES ($1, $2, $3, $4) RETURNING "id"):

This happens because you are not validating in your model, so when it tries to create the record it fails, with an exception.
To avoid that, you could add a validation in your model, which will check if the column is unique; if validation succeeds then the record will be created, otherwise it will return false and create an error message (instead of raising an exception).
You can do this adding uniqueness: true in your model validations, like this:
class DistribuicaoPorCargo < ApplicationRecord
# scopes, callbacks, ...
validates :local_da_prova_id, uniqueness: true
# more validations
end
This way, your controller will respond with:
render json: #distribuicao.errors.full_messages, status: :unprocessable_entity
and won't raise an exception (which prevent Completed 500 Internal Server Error from showing up).

Related

How to identify Rails 6 bulk insert error

I have the following relations set up:
user has_many quizzes
quiz belongs_to user
quiz has_many questions
question belongs_to quiz
App is set up to use PostgreSQL. I'm trying to bulk insert a bunch of records using the insert_all! method
begin
quiz = user.quizzes.create!(title: title, slug: slug)
quiz_questions = params[:quiz][:questions].map! do |q|
# creating an attribute hash here (code removed for conciseness of question)
end
result = quiz.questions.insert_all!(quiz_questions)
This threw an error which was caught by my "catch all" block
rescue ActiveRecord::ActiveRecordError
render json: { message: ['Something went wrong'] }, status: 500
The running server console printed this message:
TRANSACTION (0.9ms) BEGIN
↳ app/controllers/quizzes_controller.rb:14:in `create'
Quiz Create (2.8ms) INSERT INTO "quizzes" ("title", "user_id", "slug", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["title", "a quiz"], ["user_id", 1], ["slug", "a-quizk2DqYk"], ["created_at", "2021-12-01 05:00:05.800134"], ["updated_at", "2021-12-01 05:00:05.800134"]]
↳ app/controllers/quizzes_controller.rb:14:in `create'
TRANSACTION (1.6ms) COMMIT
↳ app/controllers/quizzes_controller.rb:14:in `create'
Question Bulk Insert (0.6ms) INSERT INTO "questions" ("question","a","b","c","d","score","answer","quiz_id") VALUES ('what is name', 'str', 'char', 'num', 'bool', 5, 'A', 1), ('die', 'yes', 'no', 'ok', 'what', 5, 'B', 1) RETURNING "id"
↳ (eval):6:in `block in insert_all!'
Completed 500 Internal Server Error in 153ms (Views: 0.2ms | ActiveRecord: 38.1ms | Allocations: 49609)
So I think I am not calling insert_all! correctly because the server just does an insert without the BEGIN and COMMIT bookends. Also, I would like to know which error is being thrown and caught by the catch all block. What would be the correct way to do insert_all! ?
you could wrap your bulk insert into a transaction
def bulk_insert
ActiveRecord::Base.transaction do
quiz = user.quizzes.create!(title: title, slug: slug)
quiz_questions = params[:quiz][:questions].map! do |q|
# creating an attribute hash here
# ...
# note that you could validate attribute manually
raise ActiveRecord::Rollback if q.description.blank?
end
result = quiz.questions.insert_all!(quiz_questions)
end
rescue ActiveRecord::Rollback => e
puts e
end

Model create overrides custom params :created_at

I am uploading legacy articles to my Rails app. I am sending created_at as a parameter in my request as recommended in this answer. However, this attribute seemingly is not passed "through". I can puts(params[:created_at]) and see my custom created_at, yet in the logs the article is INSERTed with a created_at of the current timestamp.
Here is my articles controller:
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :update, :destroy]
...
# POST /articles
def create
#section = Section.friendly.find(params[:section_id])
# Can't let people publish by default
#article = #section.articles.build(
article_params.merge(is_published: false)
)
if #article.save
render json: #article, status: :created, location: #article
else
render json: #article.errors, status: :unprocessable_entity
end
end
end
My request is:
http POST :3000/articles title='example' section_id=1 content="<p>the section exists.</p>" slug="example" created_at="2017-06-109T17:57:55.149-05:00"
The logs:
Started POST "/articles" for 127.0.0.1 at 2017-11-24 12:05:06 -0500
Processing by ArticlesController#create as HTML
Parameters: {"title"=>"example", "section_id"=>"1", "content"=>"<p>the section exists.</p>", "slug"=>"example", "created_at"=>"2017-06-109T17:57:55.149-05:00", "article"=>{"title"=>"example", "slug"=>"example", "content"=>"<p>the section exists.</p>", "created_at"=>"2017-06-109T17:57:55.149-05:00", "section_id"=>"1"}}
Section Load (0.3ms) SELECT "sections".* FROM "sections" WHERE "sections"."slug" = $1 ORDER BY "sections"."id" ASC LIMIT $2 [["slug", "1"], ["LIMIT", 1]]
Section Load (0.4ms) SELECT "sections".* FROM "sections" WHERE "sections"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
SQL (0.6ms) INSERT INTO "articles" ("title", "slug", "content", "is_published", "created_at", "updated_at", "section_id") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["title", "example"], ["slug", "example"], ["content", "<p>the section exists.</p>"], ["is_published", "f"], ["created_at", "2017-11-24 12:05:06.175751"], ["updated_at", "2017-11-24 12:05:06.175751"], ["section_id", 1]]
(0.8ms) COMMIT
Completed 201 Created in 38ms (Views: 1.1ms | ActiveRecord: 8.0ms)
In my schema.rb:
create_table "articles", force: :cascade do |t|
...
t.datetime "created_at", null: false
My model has no extra methods/callbacks that would ruin the request. It only contains relationships. I don't know if this is relevant, but I saw it in a GitHub issue somewhere: articles and users are in a many2many with an authorships model, and I use devise to authenticate users.
In conclusion, the app is receiving the created_at param just fine, but it is overridden with the default timestamp. If I set record_timestamps to false, the created_at just becomes nil.
Why is my created_at just seemingly ignored?
Rails 5.1, Ruby 2.4.2, Postgres 10.1
Answer given by max: there was a syntatical error in my timestamp. It was not valid, and was ignored by Rails.

Rails Action Mailer with Devise

I understand this has been asked before and I've been following Rails ActionMailer Guides as well as looking through a few related questions on stackoverflow. I'm running a localhost and trying to send emails from there. From the rails guides, I followed every step and double checked everything is written as in the guides. I've also read a few questions found here, but still my emails aren't sending from localhost. Also, I'm not getting any errors within my server.
config/environments/development.rb
EDIT
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
#Mailers
config.action_mailer.delivery_method = :sendmail
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_options = {from: 'dogseeker7#gmail.com'}
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: 'smtp.gmail.com',
port: 587,
domain: 'localhost:3000',
user_name: ENV["ADMIN_EMAIL"],
password: ENV["DOG_SEEKER_GMAIL"],
authentication: 'plain',
enable_starttls_auto: true
}
mailers/admin_mailer.rb
Class AdminMailer < Devise::Mailer
helper :application
include Devise::Controllers::UrlHelpers
Edit
def welcome_email(admin)
#admin = admin
#login_url = "localhost:3000/admins/sign_in"
mail(to: #admin.email, subject: "Welcome to Dog Seeker!")
end
app/admin/admin_controllers.rb
def create
#admin = Admin.new(params[:admin])
respond_to do |format|
if #admin.save
AdminMailer.welcome_email(#admin).deliver_now
format.html { redirect_to(#admin, notice: 'Admin was successfully created.') }
format.json { render json: #admin, status: :created, location: #admin }
else
format.html { redirect_to new_admin_registration_path }
format.json { render json: #admin.errors, status: :unprocessable_entity }
end
end
end
UPDATE
Log when new admin Signs Up
Started POST "/admins" for 127.0.0.1 at 2017-08-05 22:09:15 -0700
Processing by Devise::RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"<token>==", "admin"=>{"email"=>"stenglinephoto#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
(0.2ms) BEGIN
Admin Exists (0.6ms) SELECT 1 AS one FROM "admins" WHERE "admins"."email" = $1 LIMIT $2 [["email", "stenglinephoto#gmail.com"], ["LIMIT", 1]]
SQL (0.6ms) INSERT INTO "admins" ("email", "encrypted_password", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["email", "stenglinephoto#gmail.com"], ["encrypted_password", "<password>"], ["created_at", "2017-08-06 05:09:15.904920"], ["updated_at", "2017-08-06 05:09:15.904920"]]
(2.0ms) COMMIT
(0.1ms) BEGIN
SQL (0.5ms) UPDATE "admins" SET "sign_in_count" = $1, "current_sign_in_at" = $2, "last_sign_in_at" = $3, "current_sign_in_ip" = $4, "last_sign_in_ip" = $5, "updated_at" = $6 WHERE "admins"."id" = $7 [["sign_in_count", 1], ["current_sign_in_at", "2017-08-06 05:09:15.909337"], ["last_sign_in_at", "2017-08-06 05:09:15.909337"], ["current_sign_in_ip", "127.0.0.1/32"], ["last_sign_in_ip", "127.0.0.1/32"], ["updated_at", "2017-08-06 05:09:15.909955"], ["id", 29]]
(0.5ms) COMMIT
Redirected to http://localhost:3000/
Completed 302 Found in 172ms (ActiveRecord: 4.5ms)
Started GET "/" for 127.0.0.1 at 2017-08-05 22:09:15 -0700
Processing by HomepagesController#index as HTML
Rendering homepages/index.html.erb within layouts/application
Rendered homepages/index.html.erb within layouts/application (0.5ms)
Admin Load (0.7ms) SELECT "admins".* FROM "admins" WHERE "admins"."id" = $1 ORDER BY "admins"."id" ASC LIMIT $2 [["id", 29], ["LIMIT", 1]]
Rendered shared/_header.html.erb (5.5ms)
Rendered shared/_main.html.erb (1.0ms)
Completed 200 OK in 18ms (Views: 16.2ms | ActiveRecord: 0.7ms)
Another update
I have another mailer that gets sent when a dog is created and that works fine from localhost and with all the above configurations. My guess is the reason that mailers won't send when an admin account is created is due to the devise registrations controller overriding my controller. In other words, it's not hitting my create action in the admin_controller, but is hitting the registration within devises registration controller.
Please let me know if you need additional information.
Had to follow the guide from Devise Wiki and was able to make it work when admins sign up.

Change html response to json response in rails during cross platform response

I have created cross platform form to submit data. It sends data in json format, but rails controller response it as html. So that I always get error as.
Object {readyState: 0, responseJSON: undefined, status: 0, statusText: "error"}
test.html?batch[status]=0:88
Navigated to file:///home/shital/workspace/shitalluitel.github.io/test.html?batch%5Bstatus%5D=0
Here is my rails log after my form submit...
Started POST "/batches" for 127.0.0.1 at 2017-01-13 08:55:59 +0545
Processing by BatchesController#create as HTML
Parameters: {"batch"=>{"name"=>"eight", "course_id"=>"9", "start_date"=>"2016-12-12", "end_date"=>"2016-12-14", "status"=>"1"}}
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
Course Load (0.5ms) SELECT "courses".* FROM "courses" WHERE
"courses"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "batches" ("name", "course_id", "start_date", "end_date", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["name", "eight"], ["course_id", 9], ["start_date", Mon, 12 Dec 2016], ["end_date", Wed, 14 Dec 2016], ["created_at", 2017-01-13 03:10:59 UTC], ["updated_at", 2017-01-13 03:10:59 UTC]]
(133.8ms) COMMIT
entered to true part
Completed 200 OK in 147ms (Views: 0.8ms | ActiveRecord: 135.6ms)
You can use
render json: {your_response: "value"}, status: 200
or if you want nothing then
render :nothing => true
You can change the status base on you needs.
you can try this in the controller response will automatically varies according to your platform
respond_to do |format|
format.html { render 'filename.html'}
format.json { render json: #batches }
end

How do I pass commentable into mailer?

I have this in the Model:
after_create do |comment|
CommentMailer.comment_email(self).deliver
end
This in CommentMailer:
class CommentMailer < ActionMailer::Base
helper ActionView::Helpers::UrlHelper
include CommentHelper
helper :comment
def comment_email(user, comment, commentable)
mail(to: user.email,
subject: "You have left a comment",
from: "comments#lumeo.com",
bcc: "brian#lumeo.com")
end
end
And this in CommentHelper:
module CommentHelper
def find_commentable
#comment = Comment.find(params[:comment])
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
end
I'm getting this error:
Started POST "/requests/6/comments" for 127.0.0.1 at 2012-11-30 17:28:55 -0800
Processing by CommentsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"R62NH5/EE34FPapEqy7mfpa0wKz18GtSdhH8MGYq2Ec=", "comment"=>{"content"=>"post", "show"=>"true"}, "commit"=>"Create Comment", "request_id"=>"6"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2 ORDER BY users.created_at DESC LIMIT 1
Request Load (0.3ms) SELECT "requests".* FROM "requests" WHERE "requests"."id" = $1 LIMIT 1 [["id", "6"]]
CACHE (0.0ms) SELECT "requests".* FROM "requests" WHERE "requests"."id" = $1 LIMIT 1 [["id", "6"]]
(0.1ms) BEGIN
SQL (0.4ms) INSERT INTO "comments" ("commentable_id", "commentable_type", "content", "created_at", "show", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["commentable_id", 6], ["commentable_type", "Request"], ["content", "post"], ["created_at", Sat, 01 Dec 2012 01:28:55 UTC +00:00], ["show", true], ["updated_at", Sat, 01 Dec 2012 01:28:55 UTC +00:00], ["user_id", 2]]
(0.2ms) ROLLBACK
Completed 500 Internal Server Error in 136ms
ArgumentError (wrong number of arguments (1 for 3)):
app/mailers/comment_mailer.rb:5:in `comment_email'
app/models/comment.rb:27:in `block in <class:Comment>'
app/controllers/comments_controller.rb:22:in `create'
Looks like simple typos.
Line 7, as noted in the exception:
commentable = #comment.commentable
So, the issues:
You're calling #comment.commentabe, but #comment is nil
Hence the error: undefined method 'commentable' for nil:NilClass
#comment is nil in your mailer method because you're passing it in as comment NOT #comment, yet you're trying to reference it as #comment.
Also, why are you passing in commentable as a parameter, but on line 7 you're setting commentable again - this is redundant? Just use the already available commentable variable that you're passing in as a param. In fact, you seem to be doing this with several variables, yet I can't tell (because you don't show the mailer template) whether or not you're actually using them.
It could be that you could use something simpler like:
So, this should (probably) work:
def comment_email(user, comment, commentable)
mail(to: user.email,
subject: "You have left a comment",
from: "comments#lumeo.com",
bcc: "brian#lumeo.com")
end
If you post your mail template (so I can see what the body of the email looks like) I can help you get the variables into the template.

Resources