module Sequel::ActiveRecordConnection
Constants
- ACTIVERECORD_CALLBACKS
- Error
- TRANSACTION_ISOLATION_MAP
Attributes
Public Class Methods
# File lib/sequel/extensions/activerecord_connection.rb, line 18 def self.extended(db) db.activerecord_model = ActiveRecord::Base db.opts[:test] = false unless db.opts.key?(:test) begin require "sequel/extensions/activerecord_connection/#{db.adapter_scheme}" db.extend Sequel::ActiveRecordConnection.const_get(db.adapter_scheme.capitalize) rescue LoadError # assume the Sequel adapter already works with Active Record end end
Public Instance Methods
Ensure Sequel
is not creating its own connection anywhere.
# File lib/sequel/extensions/activerecord_connection.rb, line 33 def connect(*) raise Error, "creating a Sequel connection is not allowed" end
Log executed queries into Active Record logger as well.
# File lib/sequel/extensions/activerecord_connection.rb, line 45 def log_connection_yield(sql, conn, args = nil) sql += "; #{args.inspect}" if args activerecord_log(sql) { super } end
Avoid calling Sequel's connection pool, instead use Active Record's.
# File lib/sequel/extensions/activerecord_connection.rb, line 38 def synchronize(*) activerecord_lock do yield activerecord_connection.raw_connection end end
Match database timezone with Active Record.
# File lib/sequel/extensions/activerecord_connection.rb, line 52 def timezone @timezone || ActiveRecord::Base.default_timezone end
Private Instance Methods
Synchronizes transaction state with ActiveRecord. Sequel
uses this information to know whether we're in a transaction, whether to create a savepoint, when to run transaction/savepoint hooks etc.
# File lib/sequel/extensions/activerecord_connection.rb, line 61 def _trans(conn) hash = super || { savepoints: [], activerecord: true } # add any ActiveRecord transactions/savepoints that have been opened # directly via ActiveRecord::Base.transaction while hash[:savepoints].length < activerecord_connection.open_transactions hash[:savepoints] << { activerecord: true } end # remove any ActiveRecord transactions/savepoints that have been closed # directly via ActiveRecord::Base.transaction while hash[:savepoints].length > activerecord_connection.open_transactions && hash[:savepoints].last[:activerecord] hash[:savepoints].pop end # sync knowledge about joinability of current ActiveRecord transaction/savepoint if activerecord_connection.transaction_open? && !activerecord_connection.current_transaction.joinable? hash[:savepoints].last[:auto_savepoint] = true end if hash[:savepoints].empty? && hash[:activerecord] Sequel.synchronize { @transactions.delete(conn) } else Sequel.synchronize { @transactions[conn] = hash } end super end
# File lib/sequel/extensions/activerecord_connection.rb, line 142 def activerecord_connection activerecord_model.connection end
Active Record doesn't guarantee that a single connection can only be used by one thread at a time, so we need to use locking, which is what Active Record does internally as well.
# File lib/sequel/extensions/activerecord_connection.rb, line 132 def activerecord_lock return yield if ActiveRecord.version < Gem::Version.new("5.1.0") activerecord_connection.lock.synchronize do ActiveSupport::Dependencies.interlock.permit_concurrent_loads do yield end end end
# File lib/sequel/extensions/activerecord_connection.rb, line 146 def activerecord_log(sql, &block) ActiveSupport::Notifications.instrument( "sql.active_record", sql: sql, name: "Sequel", connection: activerecord_connection, &block ) end
When Active Record holds the savepoint, we cannot use Sequel
hooks, because Sequel
doesn't have knowledge of when the savepoint is released. So in this case we register an Active Record hook using the after_commit_everywhere gem.
# File lib/sequel/extensions/activerecord_connection.rb, line 121 def add_savepoint_hook(conn, type, block) if _trans(conn)[:savepoints].last[:activerecord] ACTIVERECORD_CALLBACKS.public_send(type, &block) else super end end
When Active Record holds the transaction, we cannot use Sequel
hooks, because Sequel
doesn't have knowledge of when the transaction is committed. So in this case we register an Active Record hook using the after_commit_everywhere gem.
# File lib/sequel/extensions/activerecord_connection.rb, line 109 def add_transaction_hook(conn, type, block) if _trans(conn)[:activerecord] ACTIVERECORD_CALLBACKS.public_send(type, &block) else super end end
# File lib/sequel/extensions/activerecord_connection.rb, line 90 def begin_transaction(conn, opts = OPTS) isolation = TRANSACTION_ISOLATION_MAP.fetch(opts[:isolation]) if opts[:isolation] joinable = !opts[:auto_savepoint] activerecord_connection.begin_transaction(isolation: isolation, joinable: joinable) end
# File lib/sequel/extensions/activerecord_connection.rb, line 97 def commit_transaction(conn, opts = OPTS) activerecord_connection.commit_transaction end
# File lib/sequel/extensions/activerecord_connection.rb, line 101 def rollback_transaction(conn, opts = OPTS) activerecord_connection.rollback_transaction end