1

Simplified version of my code:

1.upto(2) do |try|
  begin
    puts "Start"
  rescue => e
    if try == 2
      puts "Error - #{e}"
    end
  else
    puts "Success"
  end
end

My problem is that when I have an error, this is what happens:

Start
Start
Error - some message
Success

Really scratching my head here, because this is not what's supposed to happen... Any thoughts?

Update

OK I think I did myself a disservice by putting in the simplified version of the code, because that works, but the full version below, does not. I.e., when Stripe throws an API exception, the email is still triggering.

1.upto(2) do |try|
  Stripe.api_key = ENV['Stripe_Secret_Key']
  begin
    customer = Stripe::Customer.create(
      :source => params[:stripeToken],
      :email => @rentalrequest.email
    )
    @charge = Stripe::Charge.create(
      amount: (@rentalrequest.actual_amount * 100), # Stripe requires cents
      currency: "usd",
      customer: customer.id,
      description: "Payment from #{@rentalrequest.email} for gear rental"
    )
  rescue => e
    if try == 2
      logger.fatal "Stripe error: #{e.class}, #{e.message}"
      StripeErrorEmail.new.async.perform(e.class, e.message, @rentalrequest.id)
      render "rental_requests/new" and return
    end
  else
    RoadrunnerEmailAlert.new.async.perform(@rentalrequest.id)
  end
end

In other words, both StripeErrorEmail and RoadrunnerEmailAlert are firing

UPDATE for KM's answer

def error_handle(e, id)
  # Since it's a decline, Stripe::CardError will be caught
  body = e.json_body
  err  = body[:error]

  puts "Status is: #{e.http_status}"
  # => 400
  puts "Type is: #{err[:type]}"
  # => invalid_request_error
  puts "Code is: #{err[:code]}"
  # => nil
  puts "Param is: #{err[:param]}"
  # => nil
  puts "Message is: #{err[:message]}"
  # => You cannot use a Stripe token more than once

  logger.fatal "Stripe error: #{e.class}, #{e.message}"
  StripeErrorEmail.new.async.perform(e.class, e.message, id)
  render "rental_requests/new" and return
end

def charge
  @rentalrequest = RentalRequest.find(params[:rentalrequest_id])

  # Up to two tries
  1.upto(2) do |try|
    Stripe.api_key = ENV['Stripe_Secret_Key']
    begin
      customer = Stripe::Customer.create(
        :source => params[:stripeToken],
        :email => @rentalrequest.email
      )
      @charge = Stripe::Charge.create(
        amount: (@rentalrequest.actual_amount * 100), # Stripe requires cents
        currency: "usd",
        customer: customer.id,
        description: "Payment from #{@rentalrequest.email} for gear rental"
      )
    rescue Stripe::CardError => e
      if try == 2
        error_handle(e, @rentalrequest.id)
      end
    rescue Stripe::InvalidRequestError => e
      if try == 2
        error_handle(e, @rentalrequest.id)
      end
    rescue Stripe::AuthenticationError => e
      if try == 2
        error_handle(e, @rentalrequest.id)
      end
    rescue Stripe::APIConnectionError => e
      if try == 2
        error_handle(e, @rentalrequest.id)
      end
    rescue Stripe::StripeError => e
      if try == 2
        error_handle(e, @rentalrequest.id)
      end
    rescue => e
      if try == 2
        error_handle(e, @rentalrequest.id)
      end  
    else
      RoadrunnerEmailAlert.new.async.perform
    end
  end
end
james
  • 3,989
  • 8
  • 47
  • 102
  • 2
    Do you ever see the error message? I tried it locally and the rescue block never executes because, well, there is no exception. I recommend you take a look at [this post](http://stackoverflow.com/questions/2191632/begin-rescue-and-ensure-in-ruby), which has a very good explanation of the Ruby begin-rescue flow. – vich Aug 10 '15 at 20:56
  • 1
    What are you expecting to happen? – mikej Aug 10 '15 at 21:02

1 Answers1

2

Nope, the else block is not executed if there is an exception.

I have modified your code to raise an exception so that we can test it:

1.upto(2) do |try|  
  begin
    puts "Start"
    raise 'xxxx'
  rescue => e
    if try == 2
      puts "Error - #{e}"
    end
  else
    puts "Success"
  end
end

And this is the output:

Start
Start
Error - xxxx

which clearly depicts that the else block is not executed when there is an exception.

The else block is for when the block completes without an exception thrown. The another kind is ensure which is executed anyway whether the block completes successfully or not.

Update

Looking at your latest code, looks like it's a stripe issue. Try to handle stripe errors more gracefully like this:

begin
  customer = Stripe::Customer.create(
  :source => params[:stripeToken],
  :email => @rentalrequest.email
)

@charge = Stripe::Charge.create(
  amount: (@rentalrequest.actual_amount * 100), # Stripe requires cents
  currency: "usd",
  customer: customer.id,
  description: "Payment from #{@rentalrequest.email} for gear rental"
)
rescue Stripe::CardError => e
  # Since it's a decline, Stripe::CardError will be caught
  body = e.json_body
  err  = body[:error]

  puts "Status is: #{e.http_status}"
  puts "Type is: #{err[:type]}"
  puts "Code is: #{err[:code]}"
  # param is '' in this case
  puts "Param is: #{err[:param]}"
  puts "Message is: #{err[:message]}"
rescue Stripe::InvalidRequestError => e
  # Invalid parameters were supplied to Stripe's API
rescue Stripe::AuthenticationError => e
  # Authentication with Stripe's API failed
  # (maybe you changed API keys recently)
rescue Stripe::APIConnectionError => e
  # Network communication with Stripe failed
rescue Stripe::StripeError => e
  # Display a very generic error to the user, and maybe send
  # yourself an email
rescue => e
  # Something else happened, completely unrelated to Stripe
end

and see if that solves your problem.

K M Rakibul Islam
  • 33,760
  • 12
  • 89
  • 110
  • yes... this is exactly what's supposed to happen... but I think I did myself a disservice by putting in the simplified code... because using your example, I also get that outcome, but it doesn't work in my actual code, which I'll amend – james Aug 10 '15 at 21:33
  • @james Look at my edited code and see if that works for you. If the `else` block is still executed, that means the exception was NOT caught properly for some reason. We need to figure that out! Let me know! – K M Rakibul Islam Aug 10 '15 at 22:18
  • Hey first off thanks so much for the detailed answer. I have tried it and it's still not being caught properly. I have tried different errors on Stripe as well. No matter what the error, what the situation, somehow `else` still gets executed – james Aug 10 '15 at 22:24
  • Could you share your latest code exactly as it is in your question section? Also, can you print the error that's being generated by stripe API? Print those info unsing: `rescue Stripe::CardError => e # Since it's a decline, Stripe::CardError will be caught body = e.json_body err = body[:error] puts "Status is: #{e.http_status}" puts "Type is: #{err[:type]}" puts "Code is: #{err[:code]}" # param is '' in this case puts "Param is: #{err[:param]}" puts "Message is: #{err[:message]}"` And update your question. – K M Rakibul Islam Aug 10 '15 at 22:28
  • Okay, I will take a look again once I go home. – K M Rakibul Islam Aug 10 '15 at 22:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/86652/discussion-between-k-m-rakibul-islam-and-james). – K M Rakibul Islam Aug 10 '15 at 22:52
  • Thanks for your help. I figured it out, though I'm still not sure what's happening... the reason is that the `and return` is not working for some reason, so the FIRST time it is successful, and the `else` executes, but the SECOND time it is NOT successful (Stripe doesn't allow repeats) so the `rescue` executes. There is a `render and return` that I have just not been copying. Anyways, I'm going to just remove the `1.upto(2)` tries for now. Thanks again! – james Aug 10 '15 at 23:15
  • @james glad to hear that you found the real issue. So, it was something else as I mentioned. Given the fact that, my answer explains the true behavior of Ruby's `begin` `rescue` `else` block, would you consider accepting my answer? Thanks! – K M Rakibul Islam Aug 11 '15 at 00:17
  • Upvoted! I can't accept because the actual answer is still not exactly clear to me... It would be misleading, because the answer isn't actually different than what I had. Rather it's an entirely different problem. – james Aug 11 '15 at 00:49
  • Okay, makes sense. Thanks! – K M Rakibul Islam Aug 11 '15 at 00:51