Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RAILS 3 - Transactions in controllers

I have an example Action in a Controller.

def some_action
 product = Product.new
 product.name = "namepro"
  if product.save
   client.update_attribute(:product_id,product.id)
  end
end

How to add transactions for this code? I try with this example code:

def some_action
 **transaction do**
  product = Product.new
  product.name = "namepro"
   if product.save
    client.update_attribute(:product_create,Time.now)
   end
 **end**
end

But it produces this error:

undefined method `transaction'

I read about using transactions in Controllers is a bad practice but I don't know why is the reason (http://markdaggett.com/blog/2011/12/01/transactions-in-rails/)

In the example, if product has been created and saved and the client update fail... Rails must not do nothing.

thanks.

like image 244
user1364684 Avatar asked Mar 27 '13 10:03

user1364684


1 Answers

You can use a transaction in a controller if you really want to. As you noted, it's bad practice, but if you want to do it, just call Product.transaction do instead of transaction do. transaction is a class method on ActiveRecord::Base, so you need to call it on an ActiveRecord-derived class. Any model class in your application will do (nit-picking caveat: if you are connecting to different databases for different models, that may not be true...but you're probably not doing that).

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 so concerned with your data persistence implementation. A better approach would be to add a method to Product. Maybe something like this:

def save_and_update_create_time
  transaction do
    if save
      client.update_attribute(:product_create, Time.now)
    end
  end
end

Then instead of calling product.save in your controller, call product.save_and_update_client_create_time. You may need to pass client to that method too; it's unclear from your code where client comes from. If it's an attribute on product, then the method above should work.

There are better, more Railsy ways to do this, too, especially if a product knows about its client without needing any controller data. Then you can just use an after_save callback, like this (add to Product class):

after_save :update_client

private

def update_client(product)
  product.client.update_attribute(:product_create, Time.now)
end

Then every time a Product is saved, the field on the associated client will be updated. You'll possibly have to introduce some code to check for the existence of a client first.

The benefit to using callbacks, besides cleaner code, is that the entire callback chain runs in a single transaction along with the save; you don't need to create the transaction manually. You can read more about callbacks in the Rails documentation.

like image 104
Jim Stewart Avatar answered Nov 06 '22 08:11

Jim Stewart