Dodging the NoMethodError
How to circumvent NoMethedError in ActiveRecord :: ConnectionAdapters :: ClosedTransaction


Offentliggjort torsdag d. 28. november 2013 ('denglish' only) by Walther H. Diechmann


Rails 3.2 has served us just fine, thank you – but we were eager to load (sorry, couldn't resist it) the new Rails 4.0 and I must confess that I'm convinced! 4.0 has its fair share of newness but all in all it is a very polished experience, hands down!

This article is not streaming cheers though; it is how to dodge the NoMethodError on ActiveRecord::ConnectionAdapters::ClosedTransaction

TL;DR; use Threads :)

Scenario

We use Rails for a number of applications and in particular ERP. One issue with ERP is the requirement for unique IDs in documents and not just any random IDs but sequential IDs!

In a single-user environment you'd solve that easily with:

SystemParameter.next_number('a sequential counter')

and implement it somewhat along the lines of

def next_number(lbl)
  rec=SystemParameter.find_by_system_label(lbl)
  val=rec.value
  rec.value = rec.value.to_i + 1
  rec.save
  val
end

– but in a multi-user environment you'll have to watch out for race-conditions and what not! Here you're better of doing something like

def next_number(lbl) 
  begin
    ActiveRecord::Base.connection
      .execute( "CALL IncrementNumber('%s')" % lbl ).first[0]
  rescue
    return nil
  ensure
    ActiveRecord::Base.connection
      .reconnect! unless ActiveRecord::Base.connection.active?
  end
end

with the ensure part only being necessary because the execute method tends to close the current connection on the mysql2 adaptor!

But then on Ruby 2.0 / Rails 4.0.0 – then what?

Well – 4.0 leaves you with this badass console output, should you ever dare calling that next_number

NoMethodError - undefined method rollback for #<ActiveRecord::ConnectionAdapters::ClosedTransaction:0x007f835c1f58b0>:

The solution is to make sure that this particular call gets its own connection – and then it can close it all it likes :)

def next_number(lbl) 
  bc = nil
  result = Thread.new do
    begin
      sc = SystemParameter.connection
      sc.reconnect! unless sc.active?
      bc = sc.execute( "CALL IncrementNumber('%s')" % lbl ).first[0]
      sc.close
    rescue
    end
  end.join
  bc
end

The hint came from reading this answer on SO by Frederick Cheung – thank you for sharing, Frederick Cheung!


Copyright © ALCO Company 17-08-2017


Åbningstider:

mandag-fredag 08:00 - 16:00

(principielt kan du ringe til os døgnet rundt, men efter kl 16 på hverdage, og i weekenden/helligdage er det dyrere)


ALCO Company
Åbrinken 28
7700 Thisted
+45 9791 1470
+45 9791 1471
sales@alco.dk