28780

Rails generic errors array

Question:

In my Rails 4 app I have a Service object that handles communication with Stripe Payments Processor. I want it as a service object so that multiple Controllers/Models can utilize the methods within it.

However, I also need to be able to trap errors when communicating with the Stripe API which then causes the problem as the errors need to be assigned to a particular object.

Here is a method in my StripeCommunicator.rb class:

def create_customer(token,object) customer = Stripe::Customer.create(:description => 'Accommodation', :email => object.email, :card => token) return customer rescue Stripe::CardError => e @account.errors.add :base, e.message false end

as you can see - the errors are being added to the @account object - which essentially renders it useless when I want to use this method from another controller with a View that refers to another object to display errors.

Any ideas?

Answer1:

Simplest thing is to just pass the @account instance in as another argument. Errors is going to be on any model instance, e.g.

def create_customer(token,object,model_instance) Stripe::Customer.create(description: 'Accommodation', email: object.email, card: token) # return customer <- don't need this. whatever is last evaluated will be returned rescue Stripe::CardError => e model_instance.errors.add :base, e.message false end

If you were doing the error handling in the controller instead of a service object, you could take advantage of rescue_from which can handle exceptions falling out from action methods, e.g. in your controller or ApplicationController, etc., do the following:

rescue_from Stripe::CardError, with: :add_error_message_to_base def add_error_message_to_base(e) # this assumes that you set @instance in the controller's action method. @instance.errors.add :base, e.message respond_with @instance end

or more generically:

rescue_from Stripe::CardError, with: :add_error_message_to_base def add_error_message_to_base(e) model_class_name = self.class.name.chomp('Controller').split('::').last.singularize instance_value = instance_variable_get("@#{model_class_name}") instance_value.errors.add :base, e.message if instance_value respond_with instance_value end

or in a concern, you could do either of the above, putting the rescue_from into the included block:

module StripeErrorHandling extend ::ActiveSupport::Concern included do rescue_from Stripe::CardError, with: :add_error_message_to_base end def add_error_message_to_base(e) # see comment above... @instance.errors.add :base, e.message respond_with @instance end end

And you can use config.exceptions_app to handle errors at the Rack-level as José Valim describes <a href="http://blog.plataformatec.com.br/2012/01/my-five-favorite-hidden-features-in-rails-3-2/" rel="nofollow">here</a>.

You could also inherit the method vs. having a separate service class, or have a concern/module. You might even do through hooks, e.g.:

# not exactly what you were doing but just for example. # could put in app/controller/concerns among other places. module ActionsCreateStripeCustomer extend ::ActiveSupport::Concern included do around_action :create_stripe_customer end def create_stripe_customer # this (indirectly) calls the action method, and you will # set @instance in your action method for this example. yield customer = Stripe::Customer.find_or_create_by(description: 'Accommodation', email: object.email, card: token) # could set customer on @instance here and save if needed, etc. rescue Stripe::CardError => e if @instance @instance.errors.add :base, e.message respond_with @instance else logger.warn("Expected @instance to be set by #{self.class.name}##{params[:action]}") raise e end end end

Then in the controller:

include ActionsCreateStripeCustomer

There is also before_action, after_action, etc. Also, you can just include modules and when instance methods are called they call on the including class instance first, then the first included module, then the second, etc. if you do super if defined?(super) to call the prior method, and it automatically puts in all the arguments and block.

And, if it were about getting the model class name rather than the instance, that is easy, too. Say the class you were calling from was AccountStripeCommunicator, then @model_class after the following would be Account:

qualified_class_name = self.class.name.chomp('StripeCommunictor') @model_class = qualified_class_name.split('::').last.singularize.constantize

All kinds of possibilities.

Recommend

  • Simplifying the use of meshgrid in Matlab
  • How to trick Node.js to load .js files as ES6 modules?
  • When interface inheritance in Java is useful?
  • How to remove last utf8 char of a python string
  • Firefox extension testing and developing - I'm confused
  • Time taken for Hadoop job to execute
  • Obtain actual browser URL in PHP
  • How does inheritance and polymorphism work in this situation?
  • Is there a way to choose which files are displayed to the user via the standard OPENFILE dialogs?
  • Manually Timing out a C# Thread
  • What does “t” refer to in this SQL?
  • JSR-330 support in Picocontainer : @Inject … @Named(\"xxx)
  • Creating a DropDownList
  • Who propagate bugfixes across branches (corporate development)?
  • What and where is mdimport
  • Does it make sense to call System.gc() and Thread.sleep() when working on Bitmaps?
  • d3 v4 drag and drop with TypeScript
  • Sencha Touch 2.0 Controller refs attribute not working?
  • Refering to the class itself from within a class mehod in Objective C
  • Why does access(2) check for real and not effective UID?
  • Exception “firebase.functions() takes … no argument …” when specifying a region for a Cloud Function
  • Highlight one bar in a series in highcharts?
  • Scrapy recursive link crawler
  • Illegal mix of collations for operation for date/time comparison
  • Get object from AWS S3 as a stream
  • How to apply VCL Styles to DLL-based forms in Inno Setup?
  • ActionScript 2 vs ActionScript 3 performance
  • To display the title for the current loaction in map in iphone
  • R: gsub and capture
  • Calling of Constructors in a Java
  • Traverse Array and Display in markup
  • bootstrap to use multiple ng-app
  • Cannot Parse HTML Data Using Android / JSOUP
  • PHP: When would you need the self:: keyword?
  • How to set the response of a form post action to a iframe source?
  • How get height of the a view with gone visibility and height defined as wrap_content in xml?
  • Getting Messege Twice Using IMvxMessenger
  • How to get Windows thread pool to call class member function?
  • Binding checkboxes to object values in AngularJs
  • How to load view controller without button in storyboard?