sequel-activerecord_connection 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b3007b81d8906ff8ddbac1e5e5adadbc555eddacf6dbd7ef92d9062a72d3a751
4
+ data.tar.gz: 9e1ee7db2f4ee482abe17ee893d9f222325ec95f24e6a3ada63b1f5c10f94634
5
+ SHA512:
6
+ metadata.gz: d73aba0aa9bd41b9a66fab9d3da6db349bf0b2ae39fb3354243146485b5b647f00c55d89887d026752dc5b007fc2504f2d69a01e86f1f712d0494277cb8b4af1
7
+ data.tar.gz: cfbb41ff961d3c5b83d0ab21cb90706ff5a472e666dc469fbf1f9be4dd4b6d8407b5e93f89cdc6d2778e1fc3a6c0fd99a205e51a49851af129f0455efdb0a1fa
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Janko Marohnić
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,203 @@
1
+ # Sequel::ActiveRecordConnection
2
+
3
+ This is an extension for [Sequel] that allows it to reuse an existing
4
+ ActiveRecord connection for database interaction. It works on ActiveRecord 4.2
5
+ or higher, and supports the built-in `postgresql`, `mysql2` and `sqlite3`
6
+ adapters.
7
+
8
+ This can be useful if you're using a library that uses Sequel for database
9
+ interaction (e.g. [Rodauth]), but you want to avoid creating a separate
10
+ database connection. Or if you're transitioning from ActiveRecord to Sequel,
11
+ and want the database connection to be shared.
12
+
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.
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem "sequel-activerecord_connection"
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ ```sh
28
+ $ bundle install
29
+ ```
30
+
31
+ Or install it yourself as:
32
+
33
+ ```sh
34
+ $ gem install sequel-activerecord_connection
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ Assuming you've configured your ActiveRecord connection, you can initialize the
40
+ appropriate Sequel adapter and load the `activerecord_connection` extension:
41
+
42
+ ```rb
43
+ require "sequel"
44
+
45
+ DB = Sequel.postgres(test: false) # avoid creating a connection
46
+ DB.extension :activerecord_connection
47
+ ```
48
+
49
+ Now any Sequel operations that you make will internaly be done using the
50
+ ActiveRecord connection, so you should see the queries in your ActiveRecord
51
+ logs.
52
+
53
+ ```rb
54
+ DB.create_table :posts do
55
+ primary_key :id
56
+ String :title, null: false
57
+ Stirng :body, null: false
58
+ end
59
+
60
+ DB[:posts].insert(
61
+ title: "Sequel::ActiveRecordConnection",
62
+ body: "Allows Sequel to reuse ActiveRecord's connection",
63
+ )
64
+ #=> 1
65
+
66
+ DB[:posts].all
67
+ #=> [{ title: "Sequel::ActiveRecordConnection", body: "Allows Sequel to reuse ActiveRecord's connection" }]
68
+
69
+ DB[:posts].update(title: "sequel-activerecord_connection")
70
+ #=> 1
71
+ ```
72
+
73
+ The database extension supports `postgresql`, `mysql2` and `sqlite3`
74
+ ActiveRecord adapters, just make sure to initialize the corresponding Sequel
75
+ adapter before loading the extension.
76
+
77
+ ```rb
78
+ DB = Sequel.postgres(test: false) # for "postgresql" adapter
79
+ # or
80
+ DB = Sequel.mysql2(test: false) # for "mysql2" adapter
81
+ # or
82
+ DB = Sequel.sqlite(test: false) # for "sqlite3" adapter
83
+ ```
84
+
85
+ ### Transactions
86
+
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.
91
+
92
+ ```rb
93
+ DB.transaction do
94
+ ActiveRecord::Base.transaction do
95
+ # this all works
96
+ end
97
+ end
98
+ ```
99
+
100
+ The following Sequel transaction options are currently supported:
101
+
102
+ * `:savepoint`
103
+ * `:auto_savepoint`
104
+ * `:rollback`
105
+
106
+ ```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
112
+ end
113
+ end
114
+ ```
115
+
116
+ The `#in_transaction?` method is supported as well:
117
+
118
+ ```rb
119
+ ActiveRecord::Base.transaction do
120
+ DB.in_transaction? #=> true
121
+ end
122
+ ```
123
+
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).
127
+
128
+ ### Exceptions
129
+
130
+ To ensure Sequel compatibility, any `ActiveRecord::StatementInvalid` exceptions
131
+ will be translated into Sequel exceptions:
132
+
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
142
+ ```
143
+
144
+ ### Model
145
+
146
+ By default, the connection configuration will be read from `ActiveRecord::Base`.
147
+ If you want to use connection configuration from a different model, you can
148
+ can assign it to the database object after loading the extension:
149
+
150
+ ```rb
151
+ class MyModel < ActiveRecord::Base
152
+ connects_to database: { writing: :animals, reading: :animals_replica }
153
+ end
154
+ ```
155
+ ```rb
156
+ DB.activerecord_model = MyModel
157
+ ```
158
+
159
+ ### Timezone
160
+
161
+ Sequel's database timezone will be automatically set to ActiveRecord's default
162
+ timezone (`:utc` by default) when the extension is loaded.
163
+
164
+ If you happen to be changing ActiveRecord's default timezone after you've
165
+ loaded the extension, make sure to reflect that in your Sequel database object,
166
+ for example:
167
+
168
+ ```rb
169
+ DB.timezone = :local
170
+ ```
171
+
172
+ ## Tests
173
+
174
+ You'll first want to run the rake tasks for setting up databases and users:
175
+
176
+ ```sh
177
+ $ rake db_setup_postgres
178
+ $ rake db_setup_mysql
179
+ ```
180
+
181
+ Then you can run the tests:
182
+
183
+ ```sh
184
+ $ rake test
185
+ ```
186
+
187
+ When you're done, you can delete the created databases and users:
188
+
189
+ ```sh
190
+ $ rake db_teardown_postgres
191
+ $ rake db_teardown_mysql
192
+ ```
193
+
194
+ ## License
195
+
196
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
197
+
198
+ ## Code of Conduct
199
+
200
+ Everyone interacting in this project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/janko/sequel-activerecord-adapter/blob/master/CODE_OF_CONDUCT.md).
201
+
202
+ [Sequel]: https://github.com/jeremyevans/sequel
203
+ [Rodauth]: https://github.com/jeremyevans/rodauth
@@ -0,0 +1,89 @@
1
+ module Sequel
2
+ module ActiveRecordConnection
3
+ Error = Class.new(Sequel::Error)
4
+
5
+ def self.extended(db)
6
+ db.activerecord_model = ActiveRecord::Base
7
+ db.timezone = ActiveRecord::Base.default_timezone
8
+
9
+ begin
10
+ require "sequel/extensions/activerecord_connection/#{db.adapter_scheme}"
11
+ db.extend Sequel::ActiveRecordConnection.const_get(db.adapter_scheme.capitalize)
12
+ rescue LoadError
13
+ fail Error, "unsupported adapter: #{db.adapter_scheme}"
14
+ end
15
+ end
16
+
17
+ attr_accessor :activerecord_model
18
+
19
+ # Ensure Sequel is not creating its own connection anywhere.
20
+ def connect(*)
21
+ raise Error, "creating a Sequel connection is not allowed"
22
+ end
23
+
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]
29
+ else
30
+ requires_new = true
31
+ end
32
+
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
+ end
43
+
44
+ raise ActiveRecord::Rollback if rollback == :always
45
+ end
46
+ end
47
+
48
+ def in_transaction?(*)
49
+ activerecord_connection.transaction_open?
50
+ end
51
+
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
56
+ end
57
+
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
67
+ end
68
+
69
+ private
70
+
71
+ def activerecord_raw_connection
72
+ activerecord_connection.raw_connection
73
+ end
74
+
75
+ def activerecord_connection
76
+ activerecord_model.connection
77
+ end
78
+
79
+ def activesupport_interlock(&block)
80
+ if ActiveSupport::Dependencies.respond_to?(:interlock)
81
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads(&block)
82
+ else
83
+ yield
84
+ end
85
+ end
86
+ end
87
+
88
+ Database.register_extension(:activerecord_connection, ActiveRecordConnection)
89
+ end
@@ -0,0 +1,35 @@
1
+ module Sequel
2
+ module ActiveRecordConnection
3
+ module Mysql2
4
+ def execute(sql, opts=OPTS)
5
+ original_query_options = activerecord_raw_connection.query_options.dup
6
+
7
+ activerecord_raw_connection.query_options.merge!(
8
+ as: :hash,
9
+ symbolize_keys: true,
10
+ cache_rows: false,
11
+ )
12
+
13
+ result = activerecord_connection.execute(sql)
14
+
15
+ if opts[:type] == :select
16
+ if block_given?
17
+ yield result
18
+ else
19
+ result
20
+ end
21
+ elsif block_given?
22
+ yield activerecord_raw_connection
23
+ end
24
+ rescue ActiveRecord::StatementInvalid => exception
25
+ if exception.cause.is_a?(::Mysql2::Error)
26
+ raise_error(exception.cause)
27
+ else
28
+ raise
29
+ end
30
+ ensure
31
+ activerecord_raw_connection.query_options.replace(original_query_options)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,19 @@
1
+ module Sequel
2
+ module ActiveRecordConnection
3
+ module Postgres
4
+ def execute(sql, opts=OPTS)
5
+ result = activerecord_connection.execute(sql)
6
+
7
+ if block_given?
8
+ yield result
9
+ else
10
+ result.cmd_tuples
11
+ end
12
+ rescue ActiveRecord::StatementInvalid => exception
13
+ raise_error(exception.cause, classes: database_error_classes)
14
+ ensure
15
+ result.clear if result
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,44 @@
1
+ module Sequel
2
+ module ActiveRecordConnection
3
+ module Sqlite
4
+ def execute_ddl(sql, opts=OPTS)
5
+ execute(sql, opts)
6
+ end
7
+
8
+ private
9
+
10
+ # ActiveRecord doesn't send SQLite methods Sequel expects, so we need to
11
+ # try to replicate what ActiveRecord does around connection excecution.
12
+ def _execute(type, sql, opts, &block)
13
+ if activerecord_raw_connection.respond_to?(:extended_result_codes=)
14
+ activerecord_raw_connection.extended_result_codes = true
15
+ end
16
+
17
+ if ActiveRecord::VERSION::MAJOR >= 6
18
+ activerecord_connection.materialize_transactions
19
+ end
20
+
21
+ activerecord_connection.send(:log, sql) do
22
+ activesupport_interlock do
23
+ case type
24
+ when :select
25
+ activerecord_raw_connection.query(sql, &block)
26
+ when :insert
27
+ activerecord_raw_connection.execute(sql)
28
+ activerecord_raw_connection.last_insert_row_id
29
+ when :update
30
+ activerecord_raw_connection.execute_batch(sql)
31
+ activerecord_raw_connection.changes
32
+ end
33
+ end
34
+ end
35
+ rescue ActiveRecord::StatementInvalid => exception
36
+ if exception.cause.is_a?(SQLite3::Exception)
37
+ raise_error(exception.cause)
38
+ else
39
+ raise exception
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "sequel-activerecord_connection"
3
+ spec.version = "0.2.2"
4
+ spec.authors = ["Janko Marohnić"]
5
+ spec.email = ["janko.marohnic@gmail.com"]
6
+
7
+ spec.summary = %q{Allows Sequel to use ActiveRecord connection for database interaction.}
8
+ spec.description = %q{Allows Sequel to use ActiveRecord connection for database interaction.}
9
+ spec.homepage = "https://github.com/janko/sequel-activerecord_connection"
10
+ spec.license = "MIT"
11
+
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.2.0")
13
+
14
+ spec.add_dependency "sequel", ">= 4.0", "< 6"
15
+ spec.add_dependency "activerecord", ">= 4.2", "< 7"
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
+ spec.add_development_dependency "minitest"
21
+
22
+ spec.files = Dir["README.md", "LICENSE.txt", "CHANGELOG.md", "lib/**/*.rb", "*.gemspec"]
23
+ spec.require_paths = ["lib"]
24
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sequel-activerecord_connection
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.2
5
+ platform: ruby
6
+ authors:
7
+ - Janko Marohnić
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sequel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '6'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '6'
33
+ - !ruby/object:Gem::Dependency
34
+ name: activerecord
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '4.2'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '7'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '4.2'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
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
+ - !ruby/object:Gem::Dependency
96
+ name: minitest
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ description: Allows Sequel to use ActiveRecord connection for database interaction.
110
+ email:
111
+ - janko.marohnic@gmail.com
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files: []
115
+ files:
116
+ - LICENSE.txt
117
+ - README.md
118
+ - lib/sequel/extensions/activerecord_connection.rb
119
+ - lib/sequel/extensions/activerecord_connection/mysql2.rb
120
+ - lib/sequel/extensions/activerecord_connection/postgres.rb
121
+ - lib/sequel/extensions/activerecord_connection/sqlite.rb
122
+ - sequel-activerecord_connection.gemspec
123
+ homepage: https://github.com/janko/sequel-activerecord_connection
124
+ licenses:
125
+ - MIT
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: 2.2.0
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubygems_version: 3.1.1
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Allows Sequel to use ActiveRecord connection for database interaction.
146
+ test_files: []