sequel-activerecord_connection 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3007b81d8906ff8ddbac1e5e5adadbc555eddacf6dbd7ef92d9062a72d3a751
4
- data.tar.gz: 9e1ee7db2f4ee482abe17ee893d9f222325ec95f24e6a3ada63b1f5c10f94634
3
+ metadata.gz: d4078a41f54331cbb97fc266abdbe24cf8c297be14bb7f92b3bf605bf9b549bd
4
+ data.tar.gz: af466cd9b062279a7781106136a05151f02339830469136cdf9b58d629397b98
5
5
  SHA512:
6
- metadata.gz: d73aba0aa9bd41b9a66fab9d3da6db349bf0b2ae39fb3354243146485b5b647f00c55d89887d026752dc5b007fc2504f2d69a01e86f1f712d0494277cb8b4af1
7
- data.tar.gz: cfbb41ff961d3c5b83d0ab21cb90706ff5a472e666dc469fbf1f9be4dd4b6d8407b5e93f89cdc6d2778e1fc3a6c0fd99a205e51a49851af129f0455efdb0a1fa
6
+ metadata.gz: e28881de46188d6dc143952a473e00254b0742c497e7888e8d334ff33b627d3d90d9174675809cc3248b15cfcc800908bc83dda5a8da3fee93c4048c44282424
7
+ data.tar.gz: 7c0a46d18a45e76ca4a7af11d467b957035dd4b62adbdcaf64d8e9cafe1bfb406fa40c3d4feadff10c823d2535cab978111203ade32ea519f991e828ed1df033
@@ -0,0 +1,37 @@
1
+ ## 0.3.0 (2020-07-24)
2
+
3
+ * Fully support Sequel transaction API (all transaction options, transaction/savepoint hooks etc.) (@janko)
4
+
5
+ ## 0.2.6 (2020-07-19)
6
+
7
+ * Return block result in `Sequel::Database#transaction` (@zabolotnov87, @janko)
8
+
9
+ * Fix `Sequel::Model#save_changes` or `#save` with additional options not executing (@zabolotnov87, @janko)
10
+
11
+ ## 0.2.5 (2020-06-04)
12
+
13
+ * Use `#current_timestamp_utc` for the JDBC SQLite adapter as well (@HoneyryderChuck)
14
+
15
+ ## 0.2.4 (2020-06-03)
16
+
17
+ * Add JRuby support for ActiveRecord 6.0 and 5.2 (@HoneyryderChuck)
18
+
19
+ * Use `#current_timestamp_utc` setting for SQLite adapter on Sequel >= 5.33 (@HoneyryderChuck)
20
+
21
+ ## 0.2.3 (2020-05-25)
22
+
23
+ * Fix Ruby 2.7 kwargs warnings in `#transaction` (@HoneyryderChuck)
24
+
25
+ ## 0.2.2 (2020-05-02)
26
+
27
+ * Add support for ActiveRecord 4.2 (@janko)
28
+
29
+ ## 0.2.1 (2020-05-02)
30
+
31
+ * Add support for Active Record 5.0, 5.1 and 5.2 (@janko)
32
+
33
+ * Allow Sequel 4.x (@janko)
34
+
35
+ ## 0.2.0 (2020-04-29)
36
+
37
+ * Rename to `sequel-activerecord_connection` and make it a Sequel extension (@janko)
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  This is an extension for [Sequel] that allows it to reuse an existing
4
4
  ActiveRecord connection for database interaction. It works on ActiveRecord 4.2
5
5
  or higher, and supports the built-in `postgresql`, `mysql2` and `sqlite3`
6
- adapters.
6
+ adapters, as well as JDBC adapter for JRuby.
7
7
 
8
8
  This can be useful if you're using a library that uses Sequel for database
9
9
  interaction (e.g. [Rodauth]), but you want to avoid creating a separate
@@ -11,15 +11,15 @@ database connection. Or if you're transitioning from ActiveRecord to Sequel,
11
11
  and want the database connection to be shared.
12
12
 
13
13
  Note that this is a best-effort implementation, so some discrepancies are still
14
- possible. That being said, this implementation passes [Rodauth]'s test suite
15
- (for all adapters), which has fairly advanced Sequel usage.
14
+ possible. That being said, this implementation passes Rodauth's test suite
15
+ (for all adapters), which has some fairly advanced Sequel usage.
16
16
 
17
17
  ## Installation
18
18
 
19
19
  Add this line to your application's Gemfile:
20
20
 
21
21
  ```ruby
22
- gem "sequel-activerecord_connection"
22
+ gem "sequel-activerecord_connection", "~> 0.3"
23
23
  ```
24
24
 
25
25
  And then execute:
@@ -80,65 +80,62 @@ DB = Sequel.postgres(test: false) # for "postgresql" adapter
80
80
  DB = Sequel.mysql2(test: false) # for "mysql2" adapter
81
81
  # or
82
82
  DB = Sequel.sqlite(test: false) # for "sqlite3" adapter
83
+ # or
84
+ DB = Sequel.jdbc(test: false) # for JDBC adapter
83
85
  ```
84
86
 
85
87
  ### Transactions
86
88
 
87
- The database extension overrides Sequel transactions to use ActiveRecord
88
- transcations, which allows using ActiveRecord inside Sequel transactions (and
89
- vice-versa), and have things like ActiveRecord's transactional callbacks still
90
- work correctly.
89
+ This database extension keeps the transaction state of Sequel and ActiveRecord
90
+ in sync, allowing you to use Sequel and ActiveRecord transactions
91
+ interchangeably (including nesting them), and have things like ActiveRecord's
92
+ and Sequel's transactional callbacks still work correctly.
91
93
 
92
94
  ```rb
93
- DB.transaction do
94
- ActiveRecord::Base.transaction do
95
- # this all works
96
- end
95
+ ActiveRecord::Base.transaction do
96
+ DB.in_transaction? #=> true
97
97
  end
98
98
  ```
99
99
 
100
- The following Sequel transaction options are currently supported:
101
-
102
- * `:savepoint`
103
- * `:auto_savepoint`
104
- * `:rollback`
100
+ Sequel's transaction API is fully supported:
105
101
 
106
102
  ```rb
107
- ActiveRecord::Base.transaction do
108
- DB.transaction(savepoint: true) do # will create a savepoint
109
- DB.transaction do # will not create a savepoint
110
- # ...
111
- end
103
+ DB.transaction(isolation: :serializable) do
104
+ DB.after_commit { ... } # call block after transaction commits
105
+ DB.transaction(savepoint: true) do # creates a savepoint
106
+ # ...
112
107
  end
113
108
  end
114
109
  ```
115
110
 
116
- The `#in_transaction?` method is supported as well:
111
+ One caveat to keep in mind is that Sequel's transaction hooks
112
+ (`after_commit`, `after_rollback`) will *not* run if ActiveRecord holds the
113
+ outer transaction:
117
114
 
118
115
  ```rb
119
- ActiveRecord::Base.transaction do
120
- DB.in_transaction? #=> true
116
+ DB.transaction do
117
+ DB.after_commit { ... } # will get executed
121
118
  end
122
- ```
123
119
 
124
- Other transaction-related Sequel methods (`#after_commit`, `#after_rollback`
125
- etc) are not supported, because ActiveRecord currently doesn't provide
126
- transactional callbacks on the connection level (only on the model level).
120
+ ActiveRecord::Base.transaction do
121
+ DB.after_commit { ... } # won't get executed
122
+ end
127
123
 
128
- ### Exceptions
124
+ ActiveRecord::Base.transaction do
125
+ DB.transaction do
126
+ DB.after_commit { ... } # won't get executed
127
+ end
128
+ end
129
+ ```
129
130
 
130
- To ensure Sequel compatibility, any `ActiveRecord::StatementInvalid` exceptions
131
- will be translated into Sequel exceptions:
131
+ Savepoint hooks should still work, though:
132
132
 
133
133
  ```rb
134
- DB[:posts].multi_insert [{ id: 1 }, { id: 1 }]
135
- #~> Sequel::UniqueConstraintViolation
136
-
137
- DB[:posts].insert(title: nil)
138
- #~> Sequel::NotNullConstraintViolation
139
-
140
- DB[:posts].insert(author_id: 123)
141
- #~> Sequel::ForeignKeyConstraintViolation
134
+ ActiveRecord::Base.transaction do
135
+ DB.transaction(savepoint: true) do
136
+ DB.after_commit { ... } # will get executed after savepoint is released
137
+ end
138
+ end
142
139
  ```
143
140
 
144
141
  ### Model
@@ -2,6 +2,13 @@ module Sequel
2
2
  module ActiveRecordConnection
3
3
  Error = Class.new(Sequel::Error)
4
4
 
5
+ TRANSACTION_ISOLATION_MAP = {
6
+ uncommitted: :read_uncommitted,
7
+ committed: :read_committed,
8
+ repeatable: :repeatable_read,
9
+ serializable: :serializable,
10
+ }
11
+
5
12
  def self.extended(db)
6
13
  db.activerecord_model = ActiveRecord::Base
7
14
  db.timezone = ActiveRecord::Base.default_timezone
@@ -21,52 +28,65 @@ module Sequel
21
28
  raise Error, "creating a Sequel connection is not allowed"
22
29
  end
23
30
 
24
- def transaction(savepoint: nil, rollback: nil, auto_savepoint: nil, server: nil, **options)
25
- fail Error, "#{options} transaction options are currently not supported" unless options.empty?
26
-
27
- if in_transaction?
28
- requires_new = savepoint || Thread.current[:sequel_activerecord_auto_savepoint]
31
+ # Avoid calling Sequel's connection pool, instead use ActiveRecord.
32
+ def synchronize(*)
33
+ if ActiveRecord.version >= Gem::Version.new("5.1.0")
34
+ activerecord_connection.lock.synchronize do
35
+ yield activerecord_raw_connection
36
+ end
29
37
  else
30
- requires_new = true
38
+ yield activerecord_raw_connection
31
39
  end
40
+ end
32
41
 
33
- activerecord_model.transaction(requires_new: requires_new) do
34
- begin
35
- Thread.current[:sequel_activerecord_auto_savepoint] = true if auto_savepoint
36
- yield
37
- rescue Sequel::Rollback => exception
38
- raise if rollback == :reraise
39
- raise ActiveRecord::Rollback, exception.message, exception.backtrace
40
- ensure
41
- Thread.current[:sequel_activerecord_auto_savepoint] = nil if auto_savepoint
42
+ private
43
+
44
+ # Backfills any ActiveRecord transactions/savepoints that have been opened
45
+ # directly via ActiveRecord::Base.transaction. Sequel uses this information
46
+ # to know whether we're in a transaction, whether to create a savepoint,
47
+ # when to run transaction/savepoint hooks etc.
48
+ def _trans(conn)
49
+ Sequel.synchronize do
50
+ result = @transactions[conn]
51
+
52
+ if activerecord_connection.transaction_open?
53
+ result ||= { savepoints: [] }
54
+ while result[:savepoints].length < activerecord_connection.open_transactions
55
+ result[:savepoints].unshift({ activerecord: true })
56
+ end
42
57
  end
43
58
 
44
- raise ActiveRecord::Rollback if rollback == :always
59
+ @transactions[conn] = result if result
60
+ result
45
61
  end
46
62
  end
47
63
 
48
- def in_transaction?(*)
49
- activerecord_connection.transaction_open?
64
+ # First delete any transactions/savepoints opened directly via
65
+ # ActiveRecord::Base.transaction, so that Sequel can detect when the last
66
+ # Sequel transaction has been closed and clear transaction information.
67
+ def transaction_finished?(conn)
68
+ _trans(conn)[:savepoints].shift while _trans(conn)[:savepoints].first[:activerecord]
69
+ super
50
70
  end
51
71
 
52
- %i[after_commit after_rollback rollback_on_exit rollback_checker].each do |meth|
53
- define_method(meth) do |*|
54
- fail Error, "Database##{meth} is currently not supported"
55
- end
72
+ def begin_transaction(conn, opts = {})
73
+ isolation = TRANSACTION_ISOLATION_MAP.fetch(opts[:isolation]) if opts[:isolation]
74
+
75
+ activerecord_connection.begin_transaction(isolation: isolation)
56
76
  end
57
77
 
58
- # Avoid calling Sequel's connection pool, instead use ActiveRecord.
59
- def synchronize(*)
60
- if ActiveRecord.version >= Gem::Version.new("5.1.0")
61
- activerecord_connection.lock.synchronize do
62
- yield activerecord_raw_connection
63
- end
64
- else
65
- yield activerecord_raw_connection
66
- end
78
+ def commit_transaction(conn, opts = {})
79
+ activerecord_connection.commit_transaction
67
80
  end
68
81
 
69
- private
82
+ def rollback_transaction(conn, opts = {})
83
+ activerecord_connection.rollback_transaction
84
+ activerecord_connection.transaction_manager.send(:after_failure_actions, activerecord_connection.current_transaction, $!) if activerecord_connection.transaction_manager.respond_to?(:after_failure_actions)
85
+ end
86
+
87
+ def savepoint_level(conn)
88
+ activerecord_connection.open_transactions
89
+ end
70
90
 
71
91
  def activerecord_raw_connection
72
92
  activerecord_connection.raw_connection
@@ -0,0 +1,38 @@
1
+ module Sequel
2
+ module ActiveRecordConnection
3
+ module Jdbc
4
+ def self.extended(db)
5
+ if db.timezone == :utc && db.respond_to?(:current_timestamp_utc)
6
+ db.current_timestamp_utc = true
7
+ end
8
+ end
9
+
10
+ def statement(conn)
11
+ stmt = activerecord_raw_connection.connection.createStatement
12
+ yield stmt
13
+ rescue ActiveRecord::StatementInvalid => exception
14
+ raise_error(exception.cause, classes: database_error_classes)
15
+ rescue *database_error_classes => e
16
+ raise_error(e, classes: database_error_classes)
17
+ ensure
18
+ stmt.close if stmt
19
+ end
20
+
21
+ def execute(sql, opts=OPTS)
22
+ activerecord_connection.send(:log, sql) do
23
+ super
24
+ end
25
+ rescue ActiveRecord::StatementInvalid => exception
26
+ raise_error(exception.cause, classes: database_error_classes)
27
+ end
28
+
29
+ def execute_dui(sql, opts=OPTS)
30
+ activerecord_connection.send(:log, sql) do
31
+ super
32
+ end
33
+ rescue ActiveRecord::StatementInvalid => exception
34
+ raise_error(exception.cause, classes: database_error_classes)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -9,11 +9,21 @@ module Sequel
9
9
  else
10
10
  result.cmd_tuples
11
11
  end
12
+ rescue ActiveRecord::PreparedStatementCacheExpired
13
+ raise # ActiveRecord's transaction manager needs to handle this exception
12
14
  rescue ActiveRecord::StatementInvalid => exception
13
15
  raise_error(exception.cause, classes: database_error_classes)
14
16
  ensure
15
17
  result.clear if result
16
18
  end
19
+
20
+ def transaction(options = {})
21
+ %i[deferrable read_only synchronous].each do |key|
22
+ fail Error, "#{key.inspect} transaction option is currently not supported" if options.key?(key)
23
+ end
24
+
25
+ super
26
+ end
17
27
  end
18
28
  end
19
29
  end
@@ -1,6 +1,12 @@
1
1
  module Sequel
2
2
  module ActiveRecordConnection
3
3
  module Sqlite
4
+ def self.extended(db)
5
+ if db.timezone == :utc && db.respond_to?(:current_timestamp_utc)
6
+ db.current_timestamp_utc = true
7
+ end
8
+ end
9
+
4
10
  def execute_ddl(sql, opts=OPTS)
5
11
  execute(sql, opts)
6
12
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "sequel-activerecord_connection"
3
- spec.version = "0.2.2"
3
+ spec.version = "0.3.0"
4
4
  spec.authors = ["Janko Marohnić"]
5
5
  spec.email = ["janko.marohnic@gmail.com"]
6
6
 
@@ -14,9 +14,6 @@ Gem::Specification.new do |spec|
14
14
  spec.add_dependency "sequel", ">= 4.0", "< 6"
15
15
  spec.add_dependency "activerecord", ">= 4.2", "< 7"
16
16
 
17
- spec.add_development_dependency "pg", "~> 1.0"
18
- spec.add_development_dependency "mysql2", "~> 0.5"
19
- spec.add_development_dependency "sqlite3", "~> 1.3"
20
17
  spec.add_development_dependency "minitest"
21
18
 
22
19
  spec.files = Dir["README.md", "LICENSE.txt", "CHANGELOG.md", "lib/**/*.rb", "*.gemspec"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel-activerecord_connection
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janko Marohnić
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-02 00:00:00.000000000 Z
11
+ date: 2020-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -50,48 +50,6 @@ dependencies:
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
52
  version: '7'
53
- - !ruby/object:Gem::Dependency
54
- name: pg
55
- requirement: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - "~>"
58
- - !ruby/object:Gem::Version
59
- version: '1.0'
60
- type: :development
61
- prerelease: false
62
- version_requirements: !ruby/object:Gem::Requirement
63
- requirements:
64
- - - "~>"
65
- - !ruby/object:Gem::Version
66
- version: '1.0'
67
- - !ruby/object:Gem::Dependency
68
- name: mysql2
69
- requirement: !ruby/object:Gem::Requirement
70
- requirements:
71
- - - "~>"
72
- - !ruby/object:Gem::Version
73
- version: '0.5'
74
- type: :development
75
- prerelease: false
76
- version_requirements: !ruby/object:Gem::Requirement
77
- requirements:
78
- - - "~>"
79
- - !ruby/object:Gem::Version
80
- version: '0.5'
81
- - !ruby/object:Gem::Dependency
82
- name: sqlite3
83
- requirement: !ruby/object:Gem::Requirement
84
- requirements:
85
- - - "~>"
86
- - !ruby/object:Gem::Version
87
- version: '1.3'
88
- type: :development
89
- prerelease: false
90
- version_requirements: !ruby/object:Gem::Requirement
91
- requirements:
92
- - - "~>"
93
- - !ruby/object:Gem::Version
94
- version: '1.3'
95
53
  - !ruby/object:Gem::Dependency
96
54
  name: minitest
97
55
  requirement: !ruby/object:Gem::Requirement
@@ -113,9 +71,11 @@ executables: []
113
71
  extensions: []
114
72
  extra_rdoc_files: []
115
73
  files:
74
+ - CHANGELOG.md
116
75
  - LICENSE.txt
117
76
  - README.md
118
77
  - lib/sequel/extensions/activerecord_connection.rb
78
+ - lib/sequel/extensions/activerecord_connection/jdbc.rb
119
79
  - lib/sequel/extensions/activerecord_connection/mysql2.rb
120
80
  - lib/sequel/extensions/activerecord_connection/postgres.rb
121
81
  - lib/sequel/extensions/activerecord_connection/sqlite.rb