Rails 3: How to report error message from the controller's "create" action? - ruby-on-rails-3

In my "create" action on a form, I successfully save (1) MyObject to my local database and (2) OtherObject to a third-party database via its Ruby API. When something goes wrong with the save to the third party, I get an error in the form of a Ruby exception.
My question is: How do I stop the form submit and report the exception message to the client?
If this is not possible, what would be the best alternative?

Depending on whether you want to rollback your local database call, you might want to consider using Transactions. Something along these lines:
def create
ActiveRecord::Base.transaction do
#myobject = MyObject.create!(params[:myobject])
begin
# call third-party
rescue Exception => e
flash[:exception] = e.message
raise ActiveRecord::Rollback # Raise this to cause a rollback on MyObject
end
end
# redirect_to or render... might have to pick depending on if you got an exception
end
This will store the exception message into the flash which you can use to display to the user. Note Do not store the entire Exception object into the flash, you will definitely see overflow errors if your exception objects are too big.
If you're not too concerned about rolling back the MyObject creation, then you can just use a simple begin...rescue similar to what I showed in my example. You may need to determine whether you want to do a redirect_to or render depending on whether an exception occurred, but you can always conditionally determine that based on whether flash[:exception].nil? is true or not.

Related

Custom strategy for warden not getting called

I am trying to use a different warden strategy to authenticate my action cable end points.
But the strategy is not getting called. I tried to place warden.authenticate!(:action_cable_auth) in a controller to test but none of the debug statements are getting printed on console.
Below are the relevant part of the code.
config/initializers/warden.rb
Warden::Strategies.add(:action_cable_auth) do
def valid?
#check if its a websocket request & for action cable?
#Rails.logger.error request.inspect
p 'checking if strategy is valid?'
true
end
def authenticate!
p 'unauthenticate the user'
fail!('user not active')
end
end
in my controller
warden.authenticate!(:action_cable_auth)
Assuming that you are setting your initializer in the proper place, please recall that if your session is already instantiated somewhere else (for example if you authenticate the user at the point your action is being called, then your strategy will never be called.
This is basically how warden works: if some valid? strategy returns a success! then no other will be called as soon as any authenticate! method in the list of strategies is successful.
Please also be sure that if you want your strategy up the list of strategies to check you may need to also shift it up on the list, such as:
manager.default_strategies(scope: :user).unshift(:action_cable_auth)
Where the manager is your Warden::Manager instance. The scope may also be optional (this is an example where the user scope is used alongside Devise), but you may check your instance .default_strategies to figure out where it is and where you want it.

How to determine the database host from ActiveSupport::Notifications.subscribe 'sql.active_record'

I am using Notifications in Rails 3 (as described in this railscast http://asciicasts.com/episodes/249-notifications-in-rails-3) to log slow sql queries produced on my app. But these include queries that come from my read-only database. Therefore I would like to know if there is a way to tell which database the sql query is being executed on. Any help is much appreciated. My code is below.
ActiveSupport::Notifications.subscribe 'sql.active_record' do |*args|
begin
event = ActiveSupport::Notifications::Event.new(*args)
if event.duration > 250
Rails.logger.error "[SLOW QUERY] | #{event.duration}ms | #{event.payload[:sql]}
end
rescue
end
end
The key point you may be missing is that the payload contains a connection_id element, which is just a Ruby object_id. So, you should be able to do something like this:
def get_connection_host(payload)
connection_object_id = payload[:connection_id]
return nil unless connection_object_id.present?
connection_object = ObjectSpace._id2ref(connection_object_id)
return nil unless connection_object
#If the connection object descends from ActiveRecord::Base, it'll have this.
if connection_object.respond_to?(:connection_config)
return connection_object.connection_config[:host]
end
#If we're dealing with straight SQL, the AR connection adapter will be the connection object and have this variable.
if connection_object.instance_variable_defined?('#connection_options')
return connection_object.instance_variable_get('#connection_options').try(:[], :host)
end
rescue => e
#log error, raise, do whatever is appropriate in your context
end
This code isn't super future-proof, relying on instance variables, and I'm not 100% sure what would happen if the connection object had already been garbage collected. However, it should give you some ideas.
The suggestion to use ActiveRecord::Base.connection makes sense, but I don't think it's reliable if you're working in an application that connects to multiple databases, which is exactly where you might really want to do some type of filtering by the host or database name.
You can access this through ActiveRecord::Base.connection.raw_connection.host. If this returns nil, you're connecting to a local database, probably via a socket. If that's the case, then you can assume the host is localhost.

Deletion on succes (Active record, Rails)

I'm developing a Rails application that has a background process that updates some user information. In order to do so, this method has to delete all the existing information (stored in another table) and get new information from the Internet. The problem is that if something goes wrong in the midtime users don't have information until the process runs again.
there is something to do like:
transaction = EntityUser.delete_all(:user_id => #current_user.id)
#instructions that adds new entity
...
...
transaction.commit
can anyone suggest something that i can do to avoid this kind of problem?
thank you
Read about ActiveRecord::Transactions. You can wrap everything in a block
ActiveRecord::Base.transaction do
EntityUser.delete_all(:user_id => #current_user.id)
# ...
end
If something goes wrong, you can call raise ActiveRecord::Rollback within that block to cause all changes to the database within the transaction block to be reverted.
Another thought is to soft-delete the EntityUser instance(s) for a User instead of actually removing them from the database. There are gems to help you with this functionality such as acts_as_paranoid.
You would
soft-delete the EntityUser instance(s)
Fetch and build the new EntityUser instance(s)
(Optionally) flush the soft-deleted instance(s) from the database if everything went well
If something goes wrong using this method it's easy to just un-delete the soft-deleted records.

Rails3: Make update/create fail from model?

There's got to be an easy way to do this, but I cannot find an answer...
When some creates or updates a WorkRequest in my app, I do other processing, including creating a Workflow object. I do some checking to make sure, for example, there isn't more than one Workflow already created for this WorkRequest. If there is, I want the update/create to fail with an error message. I just can't see how to do this. I tried returing false from my before_update callback method, but this did not work.
Do I raise an error and rescue it in my controller? What is the right way to do this in Rails 3?
Any help would be much appreciated.
This is why you have validations. You can implement an own validation like this:
class ...
validate :my_validation
def my_validation
if workflows > 1
errors.add(:workflow, "cannot be more than one" )
end
end
end

Ruby on Rails - Suppress an error message

I am using Rails 3 and AJAX and have a parent object which is being created through and AJAX request. This parent is sent with children and saves them all. However, if a child has an error, Rails will stop the request. Is there any way to tell Rails to ignore this? I understand the proper thing to do is find the problem within the Javascript sending the request and fix it. However, for the sake of learning, is there a way to tell Rails that some errors might be ignorable?
To save without validating use:
#parent.save(:validate => false)
Also, don't forget you can create conditional validation rules if needs be. For example, add a virtual attribute (an instance variable that is not persisted to the DB) accessible via bare_bones?. Then modify a validator like so:
validates_presence_of :nickname, :unless => "bare_bones?"
Then, in your controller you would do something like this:
#parent = Parent.new params[:parent]
#parent.bare_bones = true
#parent.save()
Hope this helps.
You are looking for exception handling.
begin
#code that may contain errors
rescue
#what to do if an error is encountered
end