0

I was reading what an around_action does:

begin
    # Do before action...
    logger.info 'I am the before action'

    # Do the action, which is passed as a block to your "around filter"
    # Note that if you were to delete this line, the action will never be called!
    yield

    # Do after action...
    logger.info 'I am the after action'
ensure
    raise ActiveRecord::Rollback
end

and I understand the example up until

ensure
    raise ActiveRecord::Rollback

What does ensure raise ActiveRecord::Rollback do exactly?

Note: a similar convention is used in the rails guides, although I think it must be assumed knowledge because the guide doesn't provide a direct explanation either

stevec
  • 41,291
  • 27
  • 223
  • 311
  • 1
    It depends on the context. What that is saying is that regardless of what happens in the `begin` block, this code will ensure that the `ActiveRecord::Rollback` error class gets raised. This specific error is often used to trigger a database rollback effectively making it as if this code never ran from the database's perspective. – Aaron Sep 21 '20 at 21:41
  • @Aaron if I understand correctly, the contents of `ensure` will only run if something went wrong between `begin` and `ensure` ? Also, how does `ActiveRecord::Rollback` know what to rollback? (does it 'know' about what happened just before hand?) – stevec Sep 21 '20 at 21:47
  • 1
    Does this answer your question? [Begin, Rescue and Ensure in Ruby?](https://stackoverflow.com/questions/2191632/begin-rescue-and-ensure-in-ruby) – jvillian Sep 21 '20 at 21:50
  • 1
    @stevec `ensure` _always_ runs on success of failure. It's like JavaScript's [`finally()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally) if you're familiar with that. Looking at the original question, it looks like they want to run the `show` action in a transaction to absolutely guarantee no changes occur on the database. `AR::Rollback` is a special error (explained in the [docs](https://api.rubyonrails.org/) which are quite good comparatively) that performs the undoing of any changes that happened in the `transaction` block. – Aaron Sep 23 '20 at 12:32
  • 1
    The above should say "success _or_ failure." – Aaron Oct 03 '20 at 18:50

1 Answers1

1

The code in ensure is always run even if the begin section raises an unrescued exception.

begin
  puts "Hello"
ensure
  puts "World!"
end
max@pop-os ~/p/playground> ruby runme.rb
Hello
World!
begin
  puts "Hello"
  raise "Oh noes!"
ensure
  puts "World!"
end
max@pop-os ~/p/playground> ruby runme.rb
Hello
World!
Traceback (most recent call last):
runme.rb:3:in `<main>': Oh noes! (RuntimeError)

Note the order here - the ensure section is executed before the execution is halted by the exception.

Thats why its used for things like closing file handlers, connections or rolling back transactions which otherwise could leave the system unstable or tie up resources.

In that particular example they are hinting on how you could preview the state change caused by an action and then undo it by always performing a rollback - even if an unexpected exception occurs.

max
  • 96,212
  • 14
  • 104
  • 165
  • Thanks, that's a great example. I still don't get why anyone would want `ensure raise ActiveRecord::Rollback`. Wouldn't that mean that whatever was done is reversed? (and if so, why would that be desirable?) – stevec Sep 21 '20 at 22:19
  • In this example you would probally do the db transformation and then render in the block which sets the response body so the user can still preview the changes even if there are no permanent effects. – max Sep 21 '20 at 22:27
  • 1
    Its probally not the best example of how ensure or around callbacks work since it seems to have confused so many ppl. https://stackoverflow.com/questions/2191632/begin-rescue-and-ensure-in-ruby has some better examples. – max Sep 21 '20 at 22:30
  • I can understand why `raise ActiveRecord::Rollback` would be helpful if there were many db tasks and one failed, then it's clean to roll back all of them. But I can't imagine why it would be useful to `raise ActiveRecord::Rollback` if everything succeeded. In any case, I'll read up the link you provide. Thanks again – stevec Sep 21 '20 at 22:38