transaction_isolation_continued 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +171 -0
  3. data/.gitignore +6 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.local +18 -0
  6. data/LICENSE +19 -0
  7. data/README.md +118 -0
  8. data/Rakefile +11 -0
  9. data/d +1 -0
  10. data/gemfiles/Gemfile.base +6 -0
  11. data/gemfiles/activerecord-5.2/Gemfile.base +4 -0
  12. data/gemfiles/activerecord-5.2/Gemfile.mysql2 +10 -0
  13. data/gemfiles/activerecord-5.2/Gemfile.postgresql +10 -0
  14. data/gemfiles/activerecord-5.2/Gemfile.sqlite3 +10 -0
  15. data/gemfiles/activerecord-6.0/Gemfile.base +4 -0
  16. data/gemfiles/activerecord-6.0/Gemfile.mysql2 +10 -0
  17. data/gemfiles/activerecord-6.0/Gemfile.postgresql +10 -0
  18. data/gemfiles/activerecord-6.0/Gemfile.sqlite3 +10 -0
  19. data/gemfiles/activerecord-6.1/Gemfile.base +4 -0
  20. data/gemfiles/activerecord-6.1/Gemfile.mysql2 +10 -0
  21. data/gemfiles/activerecord-6.1/Gemfile.postgresql +10 -0
  22. data/gemfiles/activerecord-6.1/Gemfile.sqlite3 +10 -0
  23. data/gemfiles/activerecord-7.0/Gemfile.base +4 -0
  24. data/gemfiles/activerecord-7.0/Gemfile.mysql2 +10 -0
  25. data/gemfiles/activerecord-7.0/Gemfile.postgresql +10 -0
  26. data/gemfiles/activerecord-7.0/Gemfile.sqlite3 +11 -0
  27. data/lib/transaction_isolation/active_record/base.rb +13 -0
  28. data/lib/transaction_isolation/active_record/connection_adapters/abstract_adapter.rb +32 -0
  29. data/lib/transaction_isolation/active_record/connection_adapters/mysql2_adapter.rb +83 -0
  30. data/lib/transaction_isolation/active_record/connection_adapters/postgresql_adapter.rb +81 -0
  31. data/lib/transaction_isolation/active_record/connection_adapters/sqlite3_adapter.rb +80 -0
  32. data/lib/transaction_isolation/active_record/errors.rb +10 -0
  33. data/lib/transaction_isolation/configuration.rb +19 -0
  34. data/lib/transaction_isolation/version.rb +3 -0
  35. data/lib/transaction_isolation.rb +51 -0
  36. data/test/db/all.rb +4 -0
  37. data/test/db/db.rb +37 -0
  38. data/test/db/migrations.rb +20 -0
  39. data/test/db/queued_job.rb +2 -0
  40. data/test/integration/active_record/base/isolation_level_test.rb +23 -0
  41. data/test/integration/active_record/connection_adapters/any_adapter/current_isolation_level_test.rb +33 -0
  42. data/test/integration/active_record/connection_adapters/any_adapter/current_vendor_isolation_level_test.rb +33 -0
  43. data/test/integration/active_record/connection_adapters/any_adapter/isolation_level_test.rb +69 -0
  44. data/test/integration/active_record/connection_adapters/any_adapter/supports_isolation_levels_test.rb +23 -0
  45. data/test/integration/active_record/connection_adapters/any_adapter/translate_exception_test.rb +46 -0
  46. data/test/library_setup.rb +25 -0
  47. data/test/log/.gitkeep +0 -0
  48. data/test/test_console.rb +11 -0
  49. data/test/test_helper.rb +12 -0
  50. data/test/test_runner.rb +4 -0
  51. data/tests +6 -0
  52. data/transaction_isolation_continued.gemspec +25 -0
  53. metadata +154 -0
@@ -0,0 +1,81 @@
1
+ if defined?( ActiveRecord::ConnectionAdapters::PostgreSQLAdapter )
2
+
3
+ module TransactionIsolation
4
+ module ActiveRecord
5
+ module ConnectionAdapters # :nodoc:
6
+ module PostgreSQLAdapter
7
+
8
+ def self.included( base )
9
+ base.class_eval do
10
+ alias_method :translate_exception_without_transaction_isolation_conflict, :translate_exception
11
+ alias_method :translate_exception, :translate_exception_with_transaction_isolation_conflict
12
+ end
13
+ end
14
+
15
+ def supports_isolation_levels?
16
+ true
17
+ end
18
+
19
+ VENDOR_ISOLATION_LEVEL = {
20
+ :read_uncommitted => 'READ UNCOMMITTED',
21
+ :read_committed => 'READ COMMITTED',
22
+ :repeatable_read => 'REPEATABLE READ',
23
+ :serializable => 'SERIALIZABLE'
24
+ }
25
+
26
+ ANSI_ISOLATION_LEVEL = {
27
+ 'READ UNCOMMITTED' => :read_uncommitted,
28
+ 'READ COMMITTED' => :read_committed,
29
+ 'REPEATABLE READ' => :repeatable_read,
30
+ 'SERIALIZABLE' => :serializable
31
+ }
32
+
33
+ def current_isolation_level
34
+ ANSI_ISOLATION_LEVEL[current_vendor_isolation_level]
35
+ end
36
+
37
+ def current_vendor_isolation_level
38
+ select_value( "SHOW TRANSACTION ISOLATION LEVEL" ).upcase
39
+ end
40
+
41
+ def isolation_level( level )
42
+ validate_isolation_level( level )
43
+
44
+ original_vendor_isolation_level = current_vendor_isolation_level if block_given?
45
+
46
+ execute "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL #{VENDOR_ISOLATION_LEVEL[level]}"
47
+
48
+ begin
49
+ yield
50
+ ensure
51
+ execute "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL #{original_vendor_isolation_level}"
52
+ end if block_given?
53
+ end
54
+
55
+ def translate_exception_with_transaction_isolation_conflict(*args)
56
+ exception = args.first
57
+
58
+ if isolation_conflict?(exception)
59
+ ::ActiveRecord::TransactionIsolationConflict.new("Transaction isolation conflict detected: #{exception.message}")
60
+ else
61
+ translate_exception_without_transaction_isolation_conflict(*args)
62
+ end
63
+ end
64
+
65
+ ruby2_keywords :translate_exception_with_transaction_isolation_conflict if respond_to?(:ruby2_keywords, true)
66
+
67
+ def isolation_conflict?( exception )
68
+ [ "deadlock detected",
69
+ "could not serialize access" ].any? do |error_message|
70
+ exception.message =~ /#{Regexp.escape( error_message )}/i
71
+ end
72
+ end
73
+
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send( :include, TransactionIsolation::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter )
80
+
81
+ end
@@ -0,0 +1,80 @@
1
+ if defined?( ActiveRecord::ConnectionAdapters::SQLite3Adapter )
2
+
3
+ module TransactionIsolation
4
+ module ActiveRecord
5
+ module ConnectionAdapters # :nodoc:
6
+ module SQLite3Adapter
7
+
8
+ def self.included( base )
9
+ base.class_eval do
10
+ alias_method :translate_exception_without_transaction_isolation_conflict, :translate_exception
11
+ alias_method :translate_exception, :translate_exception_with_transaction_isolation_conflict
12
+ end
13
+ end
14
+
15
+ def supports_isolation_levels?
16
+ true
17
+ end
18
+
19
+ VENDOR_ISOLATION_LEVEL = {
20
+ :read_uncommitted => 'read_uncommitted = 1',
21
+ :read_committed => 'read_uncommitted = 0',
22
+ :repeatable_read => 'read_uncommitted = 0',
23
+ :serializable => 'read_uncommitted = 0'
24
+ }
25
+
26
+ ANSI_ISOLATION_LEVEL = {
27
+ 'read_uncommitted = 1' => :read_uncommitted,
28
+ 'read_uncommitted = 0' => :serializable
29
+ }
30
+
31
+ def current_isolation_level
32
+ ANSI_ISOLATION_LEVEL[current_vendor_isolation_level]
33
+ end
34
+
35
+ def current_vendor_isolation_level
36
+ "read_uncommitted = #{select_value( "PRAGMA read_uncommitted" )}"
37
+ end
38
+
39
+ def isolation_level( level )
40
+ validate_isolation_level( level )
41
+
42
+ original_vendor_isolation_level = current_vendor_isolation_level if block_given?
43
+
44
+ execute "PRAGMA #{VENDOR_ISOLATION_LEVEL[level]}"
45
+
46
+ begin
47
+ yield
48
+ ensure
49
+ execute "PRAGMA #{original_vendor_isolation_level}"
50
+ end if block_given?
51
+ end
52
+
53
+ def translate_exception_with_transaction_isolation_conflict(*args)
54
+ exception = args.first
55
+
56
+ if isolation_conflict?(exception)
57
+ ::ActiveRecord::TransactionIsolationConflict.new("Transaction isolation conflict detected: #{exception.message}")
58
+ else
59
+ translate_exception_without_transaction_isolation_conflict(*args)
60
+ end
61
+ end
62
+
63
+ ruby2_keywords :translate_exception_with_transaction_isolation_conflict if respond_to?(:ruby2_keywords, true)
64
+
65
+ # http://www.sqlite.org/c3ref/c_abort.html
66
+ def isolation_conflict?( exception )
67
+ [ "The database file is locked",
68
+ "A table in the database is locked",
69
+ "Database lock protocol error"].any? do |error_message|
70
+ exception.message =~ /#{Regexp.escape( error_message )}/i
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.send( :include, TransactionIsolation::ActiveRecord::ConnectionAdapters::SQLite3Adapter )
79
+
80
+ end
@@ -0,0 +1,10 @@
1
+ require 'active_record/errors'
2
+
3
+ module ActiveRecord
4
+ # This exception represents both deadlocks and serialization conflicts.
5
+ # Deadlocks happen when db engine is using lock-based concurrency control.
6
+ # Serialization conflicts happen when db engine is using multi-version concurrency control.
7
+ # Often db engines combine both approaches and thus generate both types of errors.
8
+
9
+ class TransactionIsolationConflict < ::ActiveRecord::WrappedDatabaseException; end
10
+ end
@@ -0,0 +1,19 @@
1
+ module TransactionIsolation
2
+ class Configuration
3
+ attr_accessor :mysql_isolation_variable
4
+ attr_accessor :detect_mysql_isolation_variable
5
+
6
+ def initialize
7
+ @mysql_isolation_variable = 'tx_isolation'
8
+ @detect_mysql_isolation_variable = true
9
+ end
10
+
11
+ def mysql_isolation_variable=( value )
12
+ unless value.in? %w[transaction_isolation tx_isolation]
13
+ raise ArgumentError, "Invalid MySQL isolation variable '#{value}'. Supported variables include 'transaction_isolation' and 'tx_isolation'."
14
+ end
15
+
16
+ @mysql_isolation_variable = value
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module TransactionIsolation
2
+ VERSION = "1.0.5"
3
+ end
@@ -0,0 +1,51 @@
1
+ require "active_record"
2
+ require_relative 'transaction_isolation/version'
3
+ require_relative 'transaction_isolation/configuration'
4
+
5
+ module TransactionIsolation
6
+
7
+ # Must be called after ActiveRecord established a connection.
8
+ # Only then we know which connection adapter is actually loaded and can be enhanced.
9
+ # Please note ActiveRecord does not load unused adapters.
10
+ def self.apply_activerecord_patch
11
+ require_relative 'transaction_isolation/active_record/errors'
12
+ require_relative 'transaction_isolation/active_record/base'
13
+ require_relative 'transaction_isolation/active_record/connection_adapters/abstract_adapter'
14
+ require_relative 'transaction_isolation/active_record/connection_adapters/mysql2_adapter'
15
+ require_relative 'transaction_isolation/active_record/connection_adapters/postgresql_adapter'
16
+ require_relative 'transaction_isolation/active_record/connection_adapters/sqlite3_adapter'
17
+ end
18
+
19
+ def self.configuration
20
+ @configuration ||= Configuration.new
21
+ end
22
+
23
+ def self.configure
24
+ config = configuration
25
+ yield(config)
26
+ end
27
+
28
+ def self.config
29
+ config = configuration
30
+ yield(config) if block_given?
31
+ config
32
+ end
33
+
34
+ if defined?( ::Rails )
35
+ # Setup applying the patch after Rails is initialized.
36
+ class Railtie < ::Rails::Railtie
37
+ config.after_initialize do
38
+ if ActiveRecord::Base.connection.adapter_name == 'Mysql2' && TransactionIsolation.config.detect_mysql_isolation_variable
39
+ mysql_version = ActiveRecord::Base.connection.select_value('SELECT version()')
40
+ if mysql_version >= '8'
41
+ TransactionIsolation.config.mysql_isolation_variable = 'transaction_isolation'
42
+ else
43
+ TransactionIsolation.config.mysql_isolation_variable = 'tx_isolation'
44
+ end
45
+ end
46
+ TransactionIsolation.apply_activerecord_patch
47
+ end
48
+ end
49
+ end
50
+
51
+ end
data/test/db/all.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'active_record'
2
+ require_relative 'db'
3
+ require_relative 'migrations'
4
+ require_relative 'queued_job'
data/test/db/db.rb ADDED
@@ -0,0 +1,37 @@
1
+ require 'fileutils'
2
+
3
+ module TransactionIsolation
4
+ module Test
5
+ module Db
6
+
7
+ def self.connect_to_mysql2
8
+ ::ActiveRecord::Base.establish_connection(
9
+ :adapter => "mysql2",
10
+ :database => ENV['MYSQL_DB_NAME'],
11
+ :host => ENV['MYSQL_DB_HOST'],
12
+ :user => 'root',
13
+ :password => ENV['MYSQL_DB_PASS']
14
+ )
15
+ end
16
+
17
+ def self.connect_to_postgresql
18
+ ::ActiveRecord::Base.establish_connection(
19
+ :adapter => "postgresql",
20
+ :database => ENV['POSTGRESQL_DB_NAME'],
21
+ :host => ENV['POSTGRESQL_DB_HOST'],
22
+ :user => ENV['POSTGRESQL_DB_USER'],
23
+ :password => ENV['POSTGRESQL_DB_PASS']
24
+ )
25
+ end
26
+
27
+ def self.connect_to_sqlite3
28
+ ActiveRecord::Base.establish_connection(
29
+ :adapter => "sqlite3",
30
+ :database => ":memory:",
31
+ :verbosity => "silent"
32
+ )
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,20 @@
1
+ module TransactionIsolation
2
+ module Test
3
+ module Migrations
4
+
5
+ def self.run!
6
+ c = ::ActiveRecord::Base.connection
7
+
8
+ # Queued Jobs
9
+
10
+ c.create_table "queued_jobs", :force => true do |t|
11
+ t.text "job", :null => false
12
+ t.integer "status", :default => 0, :null => false
13
+ t.timestamps
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,2 @@
1
+ class QueuedJob < ActiveRecord::Base
2
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class ActiveRecordTest < Minitest::Test
6
+
7
+ class BaseTest < Minitest::Test
8
+
9
+ class IsolationLevelTest < Minitest::Test
10
+
11
+ def test_wraps_connection_isolation_level
12
+ ActiveRecord::Base.isolation_level( :serializable ) do
13
+ ActiveRecord::Base.transaction do
14
+ assert_equal( :serializable, ActiveRecord::Base.connection.current_isolation_level )
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,33 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class ActiveRecordTest < Minitest::Test
6
+
7
+ class ConnectionAdaptersTest < Minitest::Test
8
+
9
+ class AnyAdapterTest < Minitest::Test
10
+
11
+ class CurrentIsolationLevelTest < Minitest::Test
12
+
13
+ def test_returns_correct_default_isolation_level
14
+ if defined?( ActiveRecord::ConnectionAdapters::Mysql2Adapter )
15
+ assert_equal( :repeatable_read, ActiveRecord::Base.connection.current_isolation_level )
16
+ end
17
+
18
+ if defined?( ActiveRecord::ConnectionAdapters::PostgreSQLAdapter )
19
+ assert_equal( :read_committed, ActiveRecord::Base.connection.current_isolation_level )
20
+ end
21
+
22
+ if defined?( ActiveRecord::ConnectionAdapters::SQLite3Adapter )
23
+ assert_equal( :serializable, ActiveRecord::Base.connection.current_isolation_level )
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,33 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class ActiveRecordTest < Minitest::Test
6
+
7
+ class ConnectionAdaptersTest < Minitest::Test
8
+
9
+ class AnyAdapterTest < Minitest::Test
10
+
11
+ class CurrentVendorIsolationLevelTest < Minitest::Test
12
+
13
+ def test_returns_correct_default_vendor_isolation_level
14
+ if defined?( ActiveRecord::ConnectionAdapters::Mysql2Adapter )
15
+ assert_equal( 'REPEATABLE READ', ActiveRecord::Base.connection.current_vendor_isolation_level )
16
+ end
17
+
18
+ if defined?( ActiveRecord::ConnectionAdapters::PostgreSQLAdapter )
19
+ assert_equal( 'READ COMMITTED', ActiveRecord::Base.connection.current_vendor_isolation_level )
20
+ end
21
+
22
+ if defined?( ActiveRecord::ConnectionAdapters::SQLite3Adapter )
23
+ assert_equal( 'read_uncommitted = 0', ActiveRecord::Base.connection.current_vendor_isolation_level )
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,69 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class ActiveRecordTest < Minitest::Test
6
+
7
+ class ConnectionAdaptersTest < Minitest::Test
8
+
9
+ class AnyAdapterTest < Minitest::Test
10
+
11
+ class IsolationLevelTest < Minitest::Test
12
+
13
+ def test_without_a_block
14
+ original_isolation_level = ActiveRecord::Base.connection.current_isolation_level
15
+
16
+ ActiveRecord::Base.connection.isolation_level( :read_uncommitted )
17
+ assert_equal( :read_uncommitted, ActiveRecord::Base.connection.current_isolation_level )
18
+
19
+ ActiveRecord::Base.connection.isolation_level( original_isolation_level )
20
+ assert_equal( original_isolation_level, ActiveRecord::Base.connection.current_isolation_level )
21
+ end
22
+
23
+ def test_with_a_block
24
+ original_isolation_level = ActiveRecord::Base.connection.current_isolation_level
25
+ refute_equal( :read_uncommitted, original_isolation_level )
26
+
27
+ ActiveRecord::Base.connection.isolation_level( :read_uncommitted ) do
28
+ assert_equal( :read_uncommitted, ActiveRecord::Base.connection.current_isolation_level )
29
+ ActiveRecord::Base.transaction do
30
+ assert_equal( :read_uncommitted, ActiveRecord::Base.connection.current_isolation_level )
31
+ QueuedJob.count
32
+ QueuedJob.first
33
+ assert_equal( :read_uncommitted, ActiveRecord::Base.connection.current_isolation_level )
34
+ end
35
+ assert_equal( :read_uncommitted, ActiveRecord::Base.connection.current_isolation_level )
36
+ end
37
+
38
+ assert_equal( original_isolation_level, ActiveRecord::Base.connection.current_isolation_level )
39
+ end
40
+
41
+ def test_with_all_possible_ansi_levels
42
+ [:read_uncommitted, :read_committed, :repeatable_read, :serializable].each do |ansi_level|
43
+
44
+ QueuedJob.isolation_level( ansi_level ) do
45
+
46
+ # Some typical usage
47
+ QueuedJob.transaction do
48
+ QueuedJob.create!( :job => 'is fun' )
49
+ assert_equal( 1, QueuedJob.count )
50
+ raise ActiveRecord::Rollback
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+
57
+ def test_with_invalid_isolation_level
58
+ assert_raises( ArgumentError ) do
59
+ QueuedJob.isolation_level( :dupa )
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class ActiveRecordTest < Minitest::Test
6
+
7
+ class ConnectionAdaptersTest < Minitest::Test
8
+
9
+ class AnyAdapterTest < Minitest::Test
10
+
11
+ class SupportsIsolationLevelsTest < Minitest::Test
12
+
13
+ def test_returns_true
14
+ assert( ActiveRecord::Base.connection.supports_isolation_levels? )
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,46 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class ActiveRecordTest < Minitest::Test
6
+
7
+ class ConnectionAdaptersTest < Minitest::Test
8
+
9
+ class AnyAdapterTest < Minitest::Test
10
+
11
+ class TranslateExceptionTest < Minitest::Test
12
+
13
+ def test_does_not_break_existing_translation
14
+ assert_raises( ActiveRecord::StatementInvalid ) do
15
+ ActiveRecord::Base.connection.execute( "WE LIVE IN THE MOST EXCITING TIMES EVER" )
16
+ end
17
+ end
18
+
19
+ def test_translates_low_level_exceptions_to_transaction_isolation_level
20
+ if defined?( ActiveRecord::ConnectionAdapters::Mysql2Adapter )
21
+ message = "Deadlock found when trying to get lock"
22
+ translated_exception = ActiveRecord::Base.connection.send( :translate_exception, Mysql2::Error.new( message ), message: message, sql: nil, binds: nil )
23
+ assert_equal( ActiveRecord::TransactionIsolationConflict, translated_exception.class )
24
+ end
25
+
26
+ if defined?( ActiveRecord::ConnectionAdapters::PostgreSQLAdapter )
27
+ message = "deadlock detected"
28
+ translated_exception = ActiveRecord::Base.connection.send( :translate_exception, PG::Error.new( message ), message: message, sql: nil, binds: nil )
29
+ assert_equal( ActiveRecord::TransactionIsolationConflict, translated_exception.class )
30
+ end
31
+
32
+ if defined?( ActiveRecord::ConnectionAdapters::SQLite3Adapter )
33
+ message = "The database file is locked"
34
+ translated_exception = ActiveRecord::Base.connection.send( :translate_exception, StandardError.new( message ), message: message, sql: nil, binds: nil )
35
+ assert_equal( ActiveRecord::TransactionIsolationConflict, translated_exception.class )
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,25 @@
1
+ # Prepares application to be tested (requires files, connects to db, resets schema and data, applies patches, etc.)
2
+
3
+ # Initialize database
4
+ require 'db/all'
5
+
6
+ case ENV['db']
7
+ when 'mysql2'
8
+ TransactionIsolation::Test::Db.connect_to_mysql2
9
+ when 'postgresql'
10
+ TransactionIsolation::Test::Db.connect_to_postgresql
11
+ when 'sqlite3'
12
+ TransactionIsolation::Test::Db.connect_to_sqlite3
13
+ else
14
+ TransactionIsolation::Test::Db.connect_to_sqlite3
15
+ end
16
+
17
+ TransactionIsolation::Test::Migrations.run!
18
+
19
+ # Load the code that will be tested
20
+ require 'transaction_isolation'
21
+
22
+ require 'logger'
23
+ ActiveRecord::Base.logger = Logger.new( File.expand_path( "#{File.dirname( __FILE__ )}/log/test.log" ) )
24
+
25
+ TransactionIsolation.apply_activerecord_patch
data/test/log/.gitkeep ADDED
File without changes
@@ -0,0 +1,11 @@
1
+ # Ensure that LOAD_PATH is the same as when running "rake test"; normally rake takes care of that
2
+ $LOAD_PATH << File.expand_path( ".", File.dirname( __FILE__ ) )
3
+ $LOAD_PATH << File.expand_path( "./lib", File.dirname( __FILE__ ) )
4
+ $LOAD_PATH << File.expand_path( "./test", File.dirname( __FILE__ ) )
5
+
6
+ # Boot the app
7
+ require_relative 'library_setup'
8
+
9
+ # Fire the console
10
+ require 'pry'
11
+ binding.pry
@@ -0,0 +1,12 @@
1
+ # Load test coverage tool (must be loaded before any code)
2
+ #require 'simplecov'
3
+ #SimpleCov.start do
4
+ # add_filter '/test/'
5
+ # add_filter '/config/'
6
+ #end
7
+
8
+ # Load and initialize the application to be tested
9
+ require 'library_setup'
10
+
11
+ # Load test frameworks
12
+ require 'minitest/autorun'
@@ -0,0 +1,4 @@
1
+ require 'test_helper'
2
+
3
+ # Load all tests
4
+ Dir.glob( "./**/*_test.rb" ).each { |test_file| require test_file }
data/tests ADDED
@@ -0,0 +1,6 @@
1
+
2
+ db=mysql2 bundle exec rake
3
+
4
+ db=postgresql bundle exec rake
5
+
6
+ db=sqlite3 bundle exec rake
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "transaction_isolation/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "transaction_isolation_continued"
7
+ s.version = TransactionIsolation::VERSION
8
+ s.authors = ["Piotr 'Qertoip' Włodarek"]
9
+ s.email = ["qertoip@gmail.com"]
10
+ s.homepage = "https://github.com/qertoip/transaction_isolation"
11
+ s.summary = %q{Set transaction isolation level in the ActiveRecord in a database agnostic way.}
12
+ s.description = %q{Set transaction isolation level in the ActiveRecord in a database agnostic way.
13
+ Works with MySQL, PostgreSQL and SQLite as long as you are using new adapters mysql2, pg or sqlite3.
14
+ Supports all ANSI SQL isolation levels: :serializable, :repeatable_read, :read_committed, :read_uncommitted.}
15
+ s.required_ruby_version = '>= 1.9.2'
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_development_dependency 'rake', '~> 13.0'
23
+ s.add_development_dependency 'minitest', '5.3.4'
24
+ s.add_runtime_dependency "activerecord", ">= 5.2"
25
+ end