sequel-activerecord_connection 0.2.2 → 0.3.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/README.md +37 -40
- data/lib/sequel/extensions/activerecord_connection.rb +52 -32
- data/lib/sequel/extensions/activerecord_connection/jdbc.rb +38 -0
- data/lib/sequel/extensions/activerecord_connection/postgres.rb +10 -0
- data/lib/sequel/extensions/activerecord_connection/sqlite.rb +6 -0
- data/sequel-activerecord_connection.gemspec +1 -4
- metadata +4 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4078a41f54331cbb97fc266abdbe24cf8c297be14bb7f92b3bf605bf9b549bd
|
4
|
+
data.tar.gz: af466cd9b062279a7781106136a05151f02339830469136cdf9b58d629397b98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e28881de46188d6dc143952a473e00254b0742c497e7888e8d334ff33b627d3d90d9174675809cc3248b15cfcc800908bc83dda5a8da3fee93c4048c44282424
|
7
|
+
data.tar.gz: 7c0a46d18a45e76ca4a7af11d467b957035dd4b62adbdcaf64d8e9cafe1bfb406fa40c3d4feadff10c823d2535cab978111203ade32ea519f991e828ed1df033
|
data/CHANGELOG.md
ADDED
@@ -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
|
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
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
94
|
-
|
95
|
-
# this all works
|
96
|
-
end
|
95
|
+
ActiveRecord::Base.transaction do
|
96
|
+
DB.in_transaction? #=> true
|
97
97
|
end
|
98
98
|
```
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
* `:savepoint`
|
103
|
-
* `:auto_savepoint`
|
104
|
-
* `:rollback`
|
100
|
+
Sequel's transaction API is fully supported:
|
105
101
|
|
106
102
|
```rb
|
107
|
-
|
108
|
-
DB.
|
109
|
-
|
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
|
-
|
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
|
-
|
120
|
-
DB.
|
116
|
+
DB.transaction do
|
117
|
+
DB.after_commit { ... } # will get executed
|
121
118
|
end
|
122
|
-
```
|
123
119
|
|
124
|
-
|
125
|
-
|
126
|
-
|
120
|
+
ActiveRecord::Base.transaction do
|
121
|
+
DB.after_commit { ... } # won't get executed
|
122
|
+
end
|
127
123
|
|
128
|
-
|
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
|
-
|
131
|
-
will be translated into Sequel exceptions:
|
131
|
+
Savepoint hooks should still work, though:
|
132
132
|
|
133
133
|
```rb
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
38
|
+
yield activerecord_raw_connection
|
31
39
|
end
|
40
|
+
end
|
32
41
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
59
|
+
@transactions[conn] = result if result
|
60
|
+
result
|
45
61
|
end
|
46
62
|
end
|
47
63
|
|
48
|
-
|
49
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
59
|
-
|
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
|
-
|
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.
|
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.
|
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-
|
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
|