Rails handling ActiveRecord::AssociationTypeMismatch as exception - ruby-on-rails

I have the following code:
def set_tags_from_string
list = self.tags_string.split(/ /)
list.each do |tag|
begin
self.partner_tags << PartnerTag.find_by(name: tag)
rescue
self.partner_tags << PartnerTag.create(name: tag)
end
end
end
The problem comes when I try pushing a PartnerTag into self.partner_tags and that tag doesn't exist yet. The console shows the following:
ActiveRecord::AssociationTypeMismatch: PartnerTag(#28272720) expected, got NilClass(#8501040)
I was hoping to be able to handle this as an exception but it still gives the error. I've never done exception handling in Ruby before so I figure I might be missing something, but I followed this tutorial and I don't see what I'm missing. The Rails API says that it is in fact an exception which ends up inheriting from StandardError. The Ruby documentation says that a rescue clause should handle all StandardErrors, so I have no idea what I'm doing wrong.

Try this instead:
self.partner_tags << PartnerTag.find_by(name: tag) || PartnerTag.create(name)
Also if you are on Rails 4, you can do:
self.partners_tags << PartnerTag.find_or_create_by(tag: name)
Note however that it doesn't prevent you from ending up with two identical tags unless you have unique constraint in a database.

Related

why no implicit conversion of nil into Hash?

after setup a search into a serializer!
Rails spits out
no implicit conversion of nil into Hash
So, please someone can point out whats wrong with this code?
class SearchController < ApplicationController
def results
results_query = PgSearch.multisearch(params[:q]).paginate(page: page, per_page: 20)
result = results_query.map(&:searchable).map do |result_item|
case result_item.class.name
when 'Post'
PostSerializer.new(result_item)
else
raise NotImplementedError
end
end
render json: {
items: result,
page: page,
pages: results_query.total_pages
}
end
def page
params[:page] || 1
end
def serialize(data, serializer)
ActiveModel::Serializer::CollectionSerializer.new(data, each_serializer: serializer)
end
end
Since your case statement isn't checking many values, you could always make it into a standard if/else statement:
if result_item && result.class.name == 'Post'
PostSerializer.new(result_item)
else
raise NotImplementedError
end
Well, on the screenshots you've provided we can see the log message specifies that the error is on line 5.
According to your code, line 5 is: case result_item.class.name
The error message is TypeError (no implicit conversion of nil into Hash).
You're trying to get the class then the name of result_item. So the problem is with result_item which is equal to nil.
In order the resolve your problem you might want to check the ouput of results_query.map(&:searchable).map.
Based on the screenshot you've provided, I've quickly checked the source code. The offending line seems to be this one: https://github.com/Casecommons/pg_search/blob/master/lib/pg_search/document.rb#L22. The only reason why this would raise the described TypeError is if PgSearch.multisearch_options is nil – which, as far as I understand the code, would only be possible if you accidentally overwrote it in a wrong way. So I'd suggest doublechecking your global setup for PgSearch.multisearch_options to make sure this is actually set.
The east way to check the setting is by using a debugger or putting something like puts PgSearch.multisearch_options or Rails.logger.info 'PgSearch.multisearch_options' into the controller directly above the call that's failing.

Guard against invalid page number (0) for will_paginate?

The will_pagify gem will_paginate will throw an error if you send it the param page=0. Is there a better way than declaring page = nil (or 1) if param[:page] =~ /^[0]{,}$/? Hoping this is to support zero-index pages and I can disable this with a function parameter.
*There may be other invalid inputs I haven't tested. Strings go to nil.
Update:
Trying to avoid:
begin
model = Model.where(...).paginate(page: params[:page])
<additional code>
rescue
model = Model.where(...).paginate(page: '1')
<redo additional code again or use function***>
vs.
current_page = clean_page(params[:page])
model = Model.where(...).paginate(page: current_page)
You can leverage what WillPaginate has already done to handle all cases as follows:
def clean_page(page)
begin
WillPaginate::PageNumber(page)
rescue WillPaginate::InvalidPage
1
end
end
and that will handle all the same issues because you are leveraging their validation process and anything that fails defaults to page 1.
Working Example

Simple Mongoid Validation for create! - how to display error messages

I'm using Rails 3 with mongoid 2 and have a simple question regarding mongoid validation.
if #forum.topics.create!(name: params[:topic][:name])
# success, do something
else
#should handle errors but doesn't
render 'new'
end
If I use the .create! method, it runs validations on a mongoid model class correctly, but it is not getting to the else block to display the error. Instead it returns a rails error page saying...
Mongoid::Errors::Validations in TopicsController#create
Validation failed - Name can't be blank.
That's good, but how do I display that in a view instead of getting an ugly rails error message page?
Try this way:
new_topic = #forum.topics.new(name: params[:topic][:name])
if new_topic.save
# success, do something
else
render 'new', errors: new_topic.errors.full_messages
end
with this way you will have the local variable errors which is a Hash formated like following:
new_topic.errors.full_messages # => ["\"Name\" can't be blank"]
you can rescue the Mongoid::Errors::Validations and use it's instance method to get the errors
new_topic = #forum.topics.new(name: params[:topic][:name])
new_topic.create!
rescue Mongoid::Errors::Validations => e
summary = e.summary
problem = e.problem
res = e.resolution
using the above error messages you can display the error
Documentaion link
https://docs.mongodb.com/mongoid/6.2/api/Mongoid/Errors/Validations.html

How do you access database error information when using Rails and Postgres

I am using find_by_sql to connect to a Postgres database and execute a database function. The database function executes a number of SQL statements and raises exceptions as required.
How do I trap the error code and error message raised by the Postgres function in Rails?
def self.validate_email(systemuserid, emailaddress)
begin
result = (self.find_by_sql(["SELECT fn_systemuser_validate_email(?, ?) AS returncode",
systemuserid, emailaddress])).first
rescue => err
# I want to get access to the error code and error message here and act accordingly
# errno = ??
# errmsg = ??
if errno == 10000
end
end
return result[:returncode]
end
I started by trying to find this information in the connection object - no such luck.
Any help much appreciated.
Currently active record replaces the original error with an internal one without passing on the original with the new error. I cant understand why any one would want this.
So the only solution right now is to monkey patch ;)
module ActiveRecord
module ConnectionAdapters
class AbstractAdapter
def translate_exception(e, message)
ActiveRecord::WrappedDatabaseException.new(message,e)
end
# Replaces
# def translate_exception(e, message)
# # override in derived class
# ActiveRecord::StatementInvalid.new(message)
# end
end
end
end
Now you can get the original_exception.
def self.validate_email(systemuserid, emailaddress)
begin
result = (self.find_by_sql(["SELECT fn_systemuser_validate_email(?, ?) AS returncode", systemuserid, emailaddress])).first
rescue ActiveRecord::WrappedDatabaseException => e
pgerror = e.original_exception
# Exact api depends on PG version, check the docs for your version.
puts "Doing my stuff: #{pgerror.result.result_error_message}"
end
end
This works with pg version 0.11 and Rails 3.0.9. Will probably work with later versions.
I let this one go for a while, (9 months!) but picked it up again due to a new impetus.
I used the monkey patch suggested by Darwin (sorry that the pull request didnt get the vote) and have then discovered that the code I need (with reference to http://deveiate.org/code/pg/PG/Result.html) is as follows:
rescue ActiveRecord::WrappedDatabaseException => e
pgerror = e.original_exception
sqlstate = pgerror.result.error_field(PG::Result::PG_DIAG_SQLSTATE )
end
Just look at .cause.
begin
# whatever.
rescue => err
p err.cause
end
You can user the errors array of your model, like others database:
errmsg = YourModel.errors[0].full_messages

rescuing from Mysql2::Error

I have a simple question. I have a join table which has an index that ensure that (col 1, col 2) is unique.
I am adding to that table using mysql2 gem and am trying to catch the Mysql2::Error if the attempt results in a duplicate key error. While I am getting the duplicate key error, my rescue body is not being executed.
begin
self.foo << bar
rescue Mysql2::Error
logger.debug("#{$!}")
end
I am receiving the following error upon executing self.foo << bar
Mysql2::Error: Duplicate entry '35455-6628' for key 'index_foos_bars_on_foo_id_and_bar_id': INSERT INTO foos_bars (foo_id, bar_id) VALUES (35455, 6628)
BUT my rescue statement is not being hit! The exception is not be successfully rescued from. What am I doing wrong? If I remove Mysql2::Error and rescue for everything, then it works. But that is bad practice - I just want to rescue from Mysql2::Error which in the event of a duplicate entry.
Thanks,
Mysql2::Error is wrapped in another exception class now. Change your code to:
begin
self.foo << bar
rescue Exception => e # only for debug purposes, don't rescue Exception in real code
logger.debug "#{e.class}"
end
...and you'll see the real exception class that you need to rescue.
Edit: It seems in this case it turned out to be ActiveRecord::RecordNotUnique

Resources