Using transaction in Ruby On Rails controller method
Asked Answered
L

2

7

I love Ruby On Rails and every day I am learning and improving my skills. Currently I am working on an application that is used by several clients and I want to refactor my code so I can feel confident about the quality of my code.

I am struggling with how I should implement exception handling and make use of Transactions. I have a controller that has to create and update three objects.To simplify my situation.

in my controller:

def update
  @user.change_value_x  #(1) effects Model 'User'
  if condition == y 
    @user.create_coupon #(2) effects Model 'Coupon' via Model 'User' 
  end
  create_a_history_item #(3) effect Model 'History' 
end

The first (1) and the second method (2) are located in the User Model and the third method (3) is also used by other controllers and is located in a Module in the /lib directory. All methods are updating/saving/creating database items.

And if one of the actions fails all database actions should roll back and give feedback about the problem.

I know that 'transaction' is a good solution for this and I have also red, in different posts, that a transaction should not be used in a controller.
Question: Why is this?

So I guess this code is not the right way to implement transaction.

def update
  ActiveRecord::Base.transaction do
    @user.change_value_x  #(1) effects Model 'User'
    if condition == y 
      @user.create_coupon #(2) effects Model 'Coupon' via Model 'User' 
    end
    create_a_history_item #(3) effect Model 'History'
  end
  rescue
  #some code that gives feedback to user about the problem
end

What is the best/right way to handle this?

Lining answered 10/6, 2015 at 22:23 Comment(0)
F
4

Transactions should be kept at the model level as much as possible, since they are related to the model, not to the logic. A transaction in a controller is like a SQL request in a controller: it feels out of place.

That being said, there is nothing wrong with your solution, but maybe having methods on your models (or a service layer) would help keep things clean in your controller? Methods like User#update_x_and_create_coupon and User#update_x_and_create_history_item for example.

Feature answered 10/6, 2015 at 22:41 Comment(3)
Thanks! As I understand I better wrap all the code in the transaction block in a single method inside my model and call that method from inside my controller so that there is only one call left in my update controller. Am I correct?Lining
Notre necessarily a single method, there might be multiple methods, each doing different actions. That depends on your model design.Feature
No. Controller is the best place to begin/end the transaction because transactions must be kept out of the business logic. Normally transactions are handled by the service layer or in languages like .NET or Java, they are usually handled by interceptors at the service or controller layer. Since in Rails, controllers (by convention) take the role of the service layer, controller is the best place to wrap a single unit of work within a transaction ( if you don't have a separate service layer)Corium
R
1

You can use Transaction in your controller if you want to,but it's a bad practice, but if you want to do it, just wrap it with User.transaction do.

The reason this is a bad practice is that it doesn't properly separate concerns according to the MVC paradigm. Your controller shouldn't be concerned with your data persistence implementation. A better approach would be to add a method to User.

Ravin answered 11/6, 2015 at 8:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.