sequel-activerecord-adapter 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ab2f715820608ebeb5599461592f548e15ee736e887eab787019fa491bb96d97
4
+ data.tar.gz: 60e410e9cde19d52d9338674235e62c0e8f9cfe0839674de0c220fe70b465abd
5
+ SHA512:
6
+ metadata.gz: 9de6d6449ca1ea8a7f7f471d157efefa844de7e42d3b8e0d5c8f87028aeb1160fdee30b6185447ec5e921b3c28127cbcb43720fe85933adf9778c50039feceac
7
+ data.tar.gz: eb7ef32a99ce269285e1677b0a6cd421638e7e7920dd4d5af0829d3c633e55322273a950b74d21826412272d2829c8139e6926f91baa91c101d65d2ce5165c31
@@ -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,108 @@
1
+ # Sequel ActiveRecord Adapter
2
+
3
+ This gem allows the [Sequel] library to reuse an existing ActiveRecord connection.
4
+ It supports `postgresql`, `mysql2` and `sqlite3` adapters.
5
+
6
+ This can be useful if you're using a library that uses Sequel for database
7
+ interaction (e.g. [Rodauth]), but you want to avoid creating a separate
8
+ database connection. Or if you're transitioning from ActiveRecord to Sequel,
9
+ and want the database connection to be reused.
10
+
11
+ Note that this is a best-effort implementation, so some discrepancies are still
12
+ possible. However, it's worth mentioning that this gem passes [Rodauth]'s test
13
+ suite for all adapters, which has some pretty advanced Sequel usage.
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem "sequel-activerecord-adapter"
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ ```sh
26
+ $ bundle install
27
+ ```
28
+
29
+ Or install it yourself as:
30
+
31
+ ```sh
32
+ $ gem install sequel-activerecord-adapter
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ Given you've configured your ActiveRecord connection, you can create a Sequel
38
+ database and start making queries:
39
+
40
+ ```rb
41
+ require "sequel-activerecord-adapter"
42
+
43
+ DB = Sequel.activerecord
44
+ DB.create_table :posts do
45
+ primary_key :id
46
+ String :title, null: false
47
+ Stirng :body, null: false
48
+ end
49
+
50
+ DB[:posts].insert(
51
+ title: "Sequel ActiveRecord Adapter",
52
+ body: "Allows Sequel to reuse ActiveRecord's connection",
53
+ )
54
+ #=> 1
55
+
56
+ DB[:posts].all
57
+ #=> [{ title: "Sequel ActiveRecord Adapter", body: "Allows Sequel to reuse ActiveRecord's connection" }]
58
+
59
+ DB[:posts].update(title: "Sequel Active Record Adapter")
60
+ #=> 1
61
+ ```
62
+
63
+ Since Sequel is using ActiveRecord connection object to make queries, any SQL
64
+ queries will be logged to the ActiveRecord logger.
65
+
66
+ ### Configuration
67
+
68
+ By default, the connection configuration will be read from `ActiveRecord::Base`.
69
+ If you want to use connection configuration from a different model, you can
70
+ pass the model class:
71
+
72
+ ```rb
73
+ class MyModel < ActiveRecord::Base
74
+ connects_to database: { writing: :animals, reading: :animals_replica }
75
+ end
76
+ ```
77
+ ```rb
78
+ Sequel.activerecord(MyModel)
79
+ ```
80
+
81
+ If the correct adapter cannot be inferred from ActiveRecord configuration at
82
+ the time of initialization, you can always specify it explicitly:
83
+
84
+ ```rb
85
+ Sequel.activerecord(adapter: "postgresql")
86
+ ```
87
+
88
+ ## Tests
89
+
90
+ The Rakefile has rake tasks for setting up and tearing down different
91
+ databases, which you need to run first.
92
+
93
+ Then you can run the tests:
94
+
95
+ ```sh
96
+ $ rake test
97
+ ```
98
+
99
+ ## License
100
+
101
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
102
+
103
+ ## Code of Conduct
104
+
105
+ Everyone interacting in the Sequel::Activerecord::Adapter 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).
106
+
107
+ [Sequel]: https://github.com/jeremyevans/sequel
108
+ [Rodauth]: https://github.com/jeremyevans/rodauth
@@ -0,0 +1,43 @@
1
+ require "sequel/core"
2
+ require "active_record"
3
+
4
+ require "sequel/adapters/activerecord"
5
+
6
+ module Sequel
7
+ def self.activerecord(model = nil, **options)
8
+ model ||= ::ActiveRecord::Base
9
+
10
+ activerecord_adapter = model.connection_config.fetch(:adapter)
11
+
12
+ case activerecord_adapter
13
+ when "postgresql" then adapter ||= :postgres
14
+ when "mysql2" then adapter ||= :mysql2
15
+ when "sqlite3" then adapter ||= :sqlite
16
+ else
17
+ raise Sequel::ActiveRecord::Error, "unsupported adapter: #{activerecord_adapter}"
18
+ end
19
+
20
+ db = connect(
21
+ adapter: adapter,
22
+ activerecord_model: model,
23
+ pool_class: Sequel::ConnectionPool, # fake connection pool
24
+ test: false, # don't force ActiveRecord connection
25
+ **options,
26
+ )
27
+
28
+ # general database extensions
29
+ db.extend Sequel::ActiveRecord::DatabaseMethods
30
+
31
+ # adapter-specific database extensions
32
+ Kernel.require "sequel/adapters/activerecord/#{activerecord_adapter}"
33
+ adapter_module = Sequel::ActiveRecord.const_get(activerecord_adapter.capitalize)
34
+ db.extend adapter_module::DatabaseMethods
35
+
36
+ db
37
+ end
38
+
39
+ module ActiveRecord
40
+ class Error < Sequel::Error
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,77 @@
1
+ require "active_record"
2
+
3
+ module Sequel
4
+ module ActiveRecord
5
+ module DatabaseMethods
6
+ # Ensure Sequel is not creating its own connection anywhere.
7
+ def connect(*)
8
+ raise Sequel::ActiveRecord::Error, "creating a Sequel connection is not allowed"
9
+ end
10
+
11
+ def transaction(savepoint: nil, rollback: nil, auto_savepoint: nil, server: nil, **options)
12
+ activerecord_not_supported!("#{options} transaction options") if options.any?
13
+
14
+ if in_transaction?
15
+ requires_new = savepoint || Thread.current[:sequel_activerecord_auto_savepoint]
16
+ else
17
+ requires_new = true
18
+ end
19
+
20
+ activerecord_model.transaction(requires_new: requires_new) do
21
+ begin
22
+ Thread.current[:sequel_activerecord_auto_savepoint] = true if auto_savepoint
23
+ yield
24
+ rescue Sequel::Rollback => exception
25
+ raise if rollback == :reraise
26
+ raise ::ActiveRecord::Rollback, exception.message, exception.backtrace
27
+ ensure
28
+ Thread.current[:sequel_activerecord_auto_savepoint] = nil if auto_savepoint
29
+ end
30
+
31
+ raise ::ActiveRecord::Rollback if rollback == :always
32
+ end
33
+ end
34
+
35
+ def in_transaction?(*)
36
+ activerecord_connection.transaction_open?
37
+ end
38
+
39
+ %i[after_commit after_rollback rollback_on_exit rollback_checker].each do |meth|
40
+ define_method(meth) { |*| activerecord_not_supported!("Database##{meth}") }
41
+ end
42
+
43
+ def synchronize(*)
44
+ activerecord_connection.lock.synchronize do
45
+ yield activerecord_raw_connection
46
+ end
47
+ end
48
+
49
+ def timezone
50
+ @timezone || activerecord_model.default_timezone || Sequel.database_timezone
51
+ end
52
+
53
+ private
54
+
55
+ # We won't be needing a real connection pool.
56
+ def connection_pool_default_options
57
+ { pool_class: Sequel::ConnectionPool }
58
+ end
59
+
60
+ def activerecord_raw_connection
61
+ activerecord_connection.raw_connection
62
+ end
63
+
64
+ def activerecord_connection
65
+ activerecord_model.connection
66
+ end
67
+
68
+ def activerecord_model
69
+ opts[:activerecord_model]
70
+ end
71
+
72
+ def activerecord_not_supported!(feature)
73
+ fail Sequel::ActiveRecord::Error, "#{feature} is currently not supported by ActiveRecord adapter"
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,37 @@
1
+ module Sequel
2
+ module ActiveRecord
3
+ module Mysql2
4
+ module DatabaseMethods
5
+ def execute(sql, opts=OPTS)
6
+ original_query_options = activerecord_raw_connection.query_options.dup
7
+
8
+ activerecord_raw_connection.query_options.merge!(
9
+ as: :hash,
10
+ symbolize_keys: true,
11
+ cache_rows: false,
12
+ )
13
+
14
+ result = activerecord_connection.execute(sql)
15
+
16
+ if opts[:type] == :select
17
+ if block_given?
18
+ yield result
19
+ else
20
+ result
21
+ end
22
+ elsif block_given?
23
+ yield activerecord_raw_connection
24
+ end
25
+ rescue ::ActiveRecord::StatementInvalid => exception
26
+ if exception.cause.is_a?(::Mysql2::Error)
27
+ raise_error(exception.cause)
28
+ else
29
+ raise
30
+ end
31
+ ensure
32
+ activerecord_raw_connection.query_options.replace(original_query_options)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ module Sequel
2
+ module ActiveRecord
3
+ module Postgresql
4
+ module DatabaseMethods
5
+ def execute(sql, opts=OPTS)
6
+ result = activerecord_connection.execute(sql)
7
+
8
+ if block_given?
9
+ yield result
10
+ else
11
+ result.cmd_tuples
12
+ end
13
+ rescue ::ActiveRecord::StatementInvalid => exception
14
+ raise_error(exception.cause, classes: database_error_classes)
15
+ ensure
16
+ result.clear if result
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,64 @@
1
+ module Sequel
2
+ module ActiveRecord
3
+ module Sqlite3
4
+ module DatabaseMethods
5
+ def execute_ddl(sql, opts=OPTS)
6
+ execute(sql, opts)
7
+ end
8
+
9
+ private
10
+
11
+ def _execute(type, sql, opts, &block)
12
+ original_results_as_hash = activerecord_raw_connection.results_as_hash
13
+ activerecord_raw_connection.results_as_hash = false
14
+
15
+ case type
16
+ when :select
17
+ activerecord_connection.send(:log, sql) do
18
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
19
+ activerecord_raw_connection.query(sql, &block)
20
+ end
21
+ end
22
+ when :insert
23
+ activerecord_connection.execute(sql)
24
+ activerecord_raw_connection.last_insert_row_id
25
+ when :update
26
+ activerecord_connection.send(:execute_batch, sql)
27
+ activerecord_raw_connection.changes
28
+ end
29
+ rescue ::ActiveRecord::RecordNotUnique => exception
30
+ raise Sequel::UniqueConstraintViolation, exception.cause.message, exception.cause.backtrace
31
+ rescue ::ActiveRecord::StatementInvalid => exception
32
+ if exception.cause.is_a?(SQLite3::Exception)
33
+ raise_error(exception.cause)
34
+ else
35
+ raise
36
+ end
37
+ ensure
38
+ activerecord_raw_connection.results_as_hash = original_results_as_hash
39
+ end
40
+ end
41
+
42
+ class Result
43
+ def initialize(array)
44
+ binding.irb
45
+ @array = array
46
+ end
47
+
48
+ def types
49
+ return [] if @array.empty?
50
+ @array[0].types
51
+ end
52
+
53
+ def columns
54
+ return [] if @array.empty?
55
+ @array[0].fields
56
+ end
57
+
58
+ def each(&block)
59
+ @array.each(&block)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "sequel-activerecord-adapter"
3
+ spec.version = "0.1.0"
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-adapter"
10
+ spec.license = "MIT"
11
+
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.2.0")
13
+
14
+ spec.add_dependency "sequel", "~> 5.0"
15
+ spec.add_dependency "activerecord", ">= 5.0", "< 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.4"
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,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sequel-activerecord-adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Janko Marohnić
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-04-27 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: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '7'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '5.0'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '7'
47
+ - !ruby/object:Gem::Dependency
48
+ name: pg
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: mysql2
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.5'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.5'
75
+ - !ruby/object:Gem::Dependency
76
+ name: sqlite3
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.4'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.4'
89
+ - !ruby/object:Gem::Dependency
90
+ name: minitest
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ description: Allows Sequel to use ActiveRecord connection for database interaction.
104
+ email:
105
+ - janko.marohnic@gmail.com
106
+ executables: []
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - LICENSE.txt
111
+ - README.md
112
+ - lib/sequel-activerecord-adapter.rb
113
+ - lib/sequel/adapters/activerecord.rb
114
+ - lib/sequel/adapters/activerecord/mysql2.rb
115
+ - lib/sequel/adapters/activerecord/postgresql.rb
116
+ - lib/sequel/adapters/activerecord/sqlite3.rb
117
+ - sequel-activerecord-adapter.gemspec
118
+ homepage: https://github.com/janko/sequel-activerecord-adapter
119
+ licenses:
120
+ - MIT
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: 2.2.0
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubygems_version: 3.1.1
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Allows Sequel to use ActiveRecord connection for database interaction.
141
+ test_files: []