Skip to content

Commit bdeb93a

Browse files
author
Carlos
authored
Add Rails 7.1 support (#86)
* Add Rails 7.1 support * Small code simplification
1 parent 356a190 commit bdeb93a

17 files changed

+244
-125
lines changed

.circleci/config.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ references:
4040
matrix:
4141
parameters:
4242
ruby-version: ['2.7', '3.0', '3.1', '3.2']
43-
bundle-version: ['Gemfile.rails-7.0']
43+
bundle-version: ['Gemfile.rails-7.0', 'Gemfile.rails-7.1']
4444

4545
workflows:
46-
commit:
47-
jobs:
48-
- <<: *matrix_build
46+
commit:
47+
jobs:
48+
- <<: *matrix_build

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ To install torque-postgresql you need to add the following to your Gemfile:
2222
```ruby
2323
gem 'torque-postgresql', '~> 2.0' # For Rails >= 6.0 < 6.1
2424
gem 'torque-postgresql', '~> 2.0.4' # For Rails >= 6.1
25-
gem 'torque-postgresql', '~> 3.0' # For Rails >= 7.0
25+
gem 'torque-postgresql', '~> 3.0' # For Rails >= 7.0 < 7.1
26+
gem 'torque-postgresql', '~> 3.3' # For Rails >= 7.1
2627
```
2728

2829
Also, run:

gemfiles/Gemfile.rails-7.0

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
source 'https://rubygems.org'
22

3-
gem 'rails', '~> 7.0'
3+
gem 'rails', '~> 7.0', '< 7.1'
44
gem 'pg', '~> 1.4.0'
55
gem "byebug"
66

gemfiles/Gemfile.rails-7.1

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
source 'https://rubygems.org'
2+
3+
gem 'rails', '~> 7.1'
4+
gem 'pg', '~> 1.4.0'
5+
gem "byebug"
6+
7+
gemspec path: "../"

lib/torque/postgresql/adapter/database_statements.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,11 @@ def user_defined_schemas
176176
# Build the query for allowed schemas
177177
def user_defined_schemas_sql
178178
conditions = []
179-
conditions << <<-SQL if schemas_blacklist.any?
180-
nspname NOT LIKE ANY (ARRAY['#{schemas_blacklist.join("', '")}'])
179+
conditions << <<-SQL.squish if schemas_blacklist.any?
180+
nspname NOT LIKE ALL (ARRAY['#{schemas_blacklist.join("', '")}'])
181181
SQL
182182

183-
conditions << <<-SQL if schemas_whitelist.any?
183+
conditions << <<-SQL.squish if schemas_whitelist.any?
184184
nspname LIKE ANY (ARRAY['#{schemas_whitelist.join("', '")}'])
185185
SQL
186186

lib/torque/postgresql/adapter/schema_creation.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def visit_TableDefinition(o)
1616
statements.concat(o.indexes.map { |c, o| index_in_create(o.name, c, o) })
1717
end
1818

19-
if supports_foreign_keys?
19+
if @conn.supports_foreign_keys?
2020
statements.concat(o.foreign_keys.map { |fk| accept fk })
2121
end
2222

lib/torque/postgresql/adapter/schema_statements.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ def data_source_sql(name = nil, type: nil)
122122
super.sub('SELECT c.relname FROM', "SELECT n.nspname || '.' || c.relname FROM")
123123
end
124124

125+
# Add schema and inherits as one of the valid options for table
126+
# definition
127+
def valid_table_definition_options
128+
super + [:schema, :inherits]
129+
end
130+
125131
private
126132

127133
# Remove the schema from the sequence name

lib/torque/postgresql/config.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ module Torque
44
module PostgreSQL
55
include ActiveSupport::Configurable
66

7+
# Stores a version check for compatibility purposes
8+
AR710 = (ActiveRecord.gem_version >= Gem::Version.new('7.1.0'))
9+
710
# Use the same logger as the Active Record one
811
def self.logger
912
ActiveRecord::Base.logger
@@ -17,8 +20,8 @@ def self.logger
1720
send("#{name}=", klass)
1821
end
1922

20-
# Set if any information that requires querying and searching or collectiong
21-
# information shuld be eager loaded. This automatically changes when rails
23+
# Set if any information that requires querying and searching or collecting
24+
# information should be eager loaded. This automatically changes when rails
2225
# same configuration is set to true
2326
config.eager_load = false
2427

lib/torque/postgresql/reflection/association_reflection.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def initialize(name, scope, options, active_record)
1717
private
1818

1919
# Check if the foreign key should be pluralized
20-
def derive_foreign_key
20+
def derive_foreign_key(*, **)
2121
result = super
2222
result = ActiveSupport::Inflector.pluralize(result) \
2323
if collection? && connected_through_array?

lib/torque/postgresql/schema_cache.rb

Lines changed: 28 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
# frozen_string_literal: true
22

3+
require 'torque/postgresql/schema_cache/inheritance'
4+
5+
if Torque::PostgreSQL::AR710
6+
require 'torque/postgresql/schema_cache/schema_reflection'
7+
require 'torque/postgresql/schema_cache/bound_schema_reflection'
8+
end
9+
310
module Torque
411
module PostgreSQL
512
LookupError = Class.new(ArgumentError)
613

714
# :TODO: Create the +add+ to load inheritance info
815
module SchemaCache
16+
include Torque::PostgreSQL::SchemaCache::Inheritance
917

1018
def initialize(*) # :nodoc:
1119
super
@@ -37,7 +45,7 @@ def init_with(coder) # :nodoc:
3745
@inheritance_associations = coder['inheritance_associations']
3846
end
3947

40-
def add(table_name, *) # :nodoc:
48+
def add(connection_or_table_name, table_name = connection_or_table_name, *) # :nodoc:
4149
super
4250

4351
# Reset inheritance information when a table is added
@@ -64,8 +72,8 @@ def size # :nodoc:
6472
].map(&:size).inject(:+)
6573
end
6674

67-
def clear_data_source_cache!(name) # :nodoc:
68-
super
75+
def clear_data_source_cache!(connection_or_name, name = connection_or_name) # :nodoc:
76+
Torque::PostgreSQL::AR710 ? super : super(name)
6977
@data_sources_model_names.delete name
7078
@inheritance_dependencies.delete name
7179
@inheritance_associations.delete name
@@ -95,88 +103,29 @@ def add_model_name(table_name, model)
95103
end
96104

97105
# Get all the tables that the given one inherits from
98-
def dependencies(table_name)
99-
reload_inheritance_data!
106+
def dependencies(conn, table_name = conn)
107+
reload_inheritance_data!(conn == table_name ? connection : conn)
100108
@inheritance_dependencies[table_name]
101109
end
102110

103111
# Get the list of all tables that are associated (direct or indirect
104112
# inheritance) with the provided one
105-
def associations(table_name)
106-
reload_inheritance_data!
113+
def associations(conn, table_name = conn)
114+
reload_inheritance_data!(conn == table_name ? connection : conn)
107115
@inheritance_associations[table_name]
108116
end
109117

110-
# Try to find a model based on a given table
111-
def lookup_model(table_name, scoped_class = '')
112-
scoped_class = scoped_class.name if scoped_class.is_a?(Class)
113-
return @data_sources_model_names[table_name] \
114-
if @data_sources_model_names.key?(table_name)
115-
116-
# Get all the possible scopes
117-
scopes = scoped_class.scan(/(?:::)?[A-Z][a-z]+/)
118-
scopes.unshift('Object::')
119-
120-
# Check if the table name comes with a schema
121-
if table_name.include?('.')
122-
schema, table_name = table_name.split('.')
123-
scopes.insert(1, schema.camelize) if schema != 'public'
124-
end
125-
126-
# Consider the maximum namespaced possible model name
127-
max_name = table_name.tr('_', '/').camelize.split(/(::)/)
128-
max_name[-1] = max_name[-1].singularize
129-
130-
# Test all the possible names against all the possible scopes
131-
until scopes.size == 0
132-
scope = scopes.join.chomp('::').safe_constantize
133-
model = find_model(max_name, table_name, scope) unless scope.nil?
134-
return @data_sources_model_names[table_name] = model unless model.nil?
135-
scopes.pop
136-
end
137-
138-
# If this part is reach, no model name was found
139-
raise LookupError.new(<<~MSG.squish)
140-
Unable to find a valid model that is associated with the '#{table_name}' table.
141-
Please, check if they correctly inherit from ActiveRecord::Base
142-
MSG
118+
# Override the inheritance implementation to pass over the proper cache of
119+
# the existing association between data sources and model names
120+
def lookup_model(*args, **xargs)
121+
super(*args, **xargs, source_to_model: @data_sources_model_names)
143122
end
144123

145124
private
146125

147-
# Find a model by a given max namespaced class name thath matches the
148-
# given table name
149-
def find_model(max_name, table_name, scope = Object)
150-
pieces = max_name.is_a?(::Array) ? max_name : max_name.split(/(::)/)
151-
ns_places = (1..(max_name.size - 1)).step(2).to_a
152-
153-
# Generate all possible combinarions
154-
conditions = []
155-
range = Torque::PostgreSQL.config.inheritance.inverse_lookup \
156-
? 0.upto(ns_places.size) \
157-
: ns_places.size.downto(0)
158-
range.each do |size|
159-
conditions.concat(ns_places.combination(size).to_a)
160-
end
161-
162-
# Now iterate over
163-
while (condition = conditions.shift)
164-
ns_places.each{ |i| pieces[i] = condition.include?(i) ? '::' : '' }
165-
166-
candidate = pieces.join
167-
candidate.prepend("#{scope.name}::") unless scope === Object
168-
169-
klass = candidate.safe_constantize
170-
next if klass.nil?
171-
172-
# Check if the class match the table name
173-
return klass if klass < ::ActiveRecord::Base && klass.table_name == table_name
174-
end
175-
end
176-
177126
# Reload information about tables inheritance and dependencies, uses a
178-
# cache to not perform additional checkes
179-
def reload_inheritance_data!
127+
# cache to not perform additional checks
128+
def reload_inheritance_data!(connection)
180129
return if @inheritance_loaded
181130
@inheritance_dependencies = connection.inherited_tables
182131
@inheritance_associations = generate_associations
@@ -186,38 +135,15 @@ def reload_inheritance_data!
186135
# Calculates the inverted dependency (association), where even indirect
187136
# inheritance comes up in the list
188137
def generate_associations
189-
return {} if @inheritance_dependencies.empty?
190-
191-
result = Hash.new{ |h, k| h[k] = [] }
192-
masters = @inheritance_dependencies.values.flatten.uniq
193-
194-
# Add direct associations
195-
masters.map do |master|
196-
@inheritance_dependencies.each do |(dependent, associations)|
197-
result[master] << dependent if associations.include?(master)
198-
end
199-
end
200-
201-
# Add indirect associations
202-
result.each do |master, children|
203-
children.each do |child|
204-
children.concat(result[child]).uniq! if result.key?(child)
205-
end
206-
end
207-
208-
# Remove the default proc that would create new entries
209-
result.default_proc = nil
210-
result
138+
super(@inheritance_dependencies)
211139
end
212140

213-
# Use this method to also load any irregular model name. This is smart
214-
# enought to only load the sources present on this instance
215-
def prepare_data_sources
216-
super
217-
@data_sources_model_names = Torque::PostgreSQL.config
218-
.irregular_models.slice(*@data_sources.keys).map do |table_name, model_name|
219-
[table_name, (model_name.is_a?(Class) ? model_name : model_name.constantize)]
220-
end.to_h
141+
# Use this method to also load any irregular model name
142+
def prepare_data_sources(connection = nil)
143+
Torque::PostgreSQL::AR710 ? super : super()
144+
145+
sources = connection.present? ? tables_to_cache(connection) : @data_sources.keys
146+
@data_sources_model_names = prepare_irregular_models(sources)
221147
end
222148

223149
end
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# frozen_string_literal: true
2+
3+
module Torque
4+
module PostgreSQL
5+
module BoundSchemaReflection
6+
def add_model_name(table_name, model)
7+
@schema_reflection.add_model_name(@connection, table_name, model)
8+
end
9+
10+
def dependencies(table_name)
11+
@schema_reflection.dependencies(@connection, table_name)
12+
end
13+
14+
def associations(table_name)
15+
@schema_reflection.associations(@connection, table_name)
16+
end
17+
18+
def lookup_model(table_name, scoped_class = '')
19+
@schema_reflection.lookup_model(@connection, table_name, scoped_class)
20+
end
21+
end
22+
23+
ActiveRecord::ConnectionAdapters::BoundSchemaReflection.prepend BoundSchemaReflection
24+
end
25+
end

0 commit comments

Comments
 (0)