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
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.
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
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
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
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