torque-postgresql 3.2.2 → 3.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: 1948838632756aeab31c22f59995a8a41ce1900671e3cc9e1f8d802d898a4fe6
4
- data.tar.gz: ca24eb91ead1a52927d1b0069e8408419600ba7884744a31de3909f45b59bb4d
3
+ metadata.gz: 2ea2d3fe5e64c12ea8cf8b77ee39a08092b23c5f15802442d5bc92464e248111
4
+ data.tar.gz: 17541c4f9cbd22c940b5082be168c0fd62e9736abe77efc79340c73058342db4
5
5
  SHA512:
6
- metadata.gz: 41071ecb22d7730bdb0debe558cf4b14db7ca648637d8f59cc43de31d0ca847b0ce7df2447b2e75be507b7ccd03f668c0dc7fa7232a38217889f5172f923a243
7
- data.tar.gz: 74be2a0828976d3f521e3c9ff0c49708cc767dd019c8a22b6e98080bbda036da4297675c8852f124d01de3588f04b390ae7f5138549a3e85a6f6ce0ad1e50ccb
6
+ metadata.gz: 39f36019706145c9b6ff5c4e407ee95b7965746d194338a52d1ee3d67876008b62f673996fb41bc0eb257184454ba74cbe3ffc2cb60805bcaf2d366a3a8ad54f
7
+ data.tar.gz: 8c77b20d700a66a002eecf369d3d7e2f458d88b280e182e93c4e25bcd8ddbe10f03f77d578d50f13a7234683721ed3cc99d49eac9db7f3de1f0ade9c058f5e53
@@ -176,11 +176,11 @@ module Torque
176
176
  # Build the query for allowed schemas
177
177
  def user_defined_schemas_sql
178
178
  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("', '")}'])
181
181
  SQL
182
182
 
183
- conditions << <<-SQL if schemas_whitelist.any?
183
+ conditions << <<-SQL.squish if schemas_whitelist.any?
184
184
  nspname LIKE ANY (ARRAY['#{schemas_whitelist.join("', '")}'])
185
185
  SQL
186
186
 
@@ -16,7 +16,7 @@ module Torque
16
16
  statements.concat(o.indexes.map { |c, o| index_in_create(o.name, c, o) })
17
17
  end
18
18
 
19
- if supports_foreign_keys?
19
+ if @conn.supports_foreign_keys?
20
20
  statements.concat(o.foreign_keys.map { |fk| accept fk })
21
21
  end
22
22
 
@@ -122,6 +122,12 @@ module Torque
122
122
  super.sub('SELECT c.relname FROM', "SELECT n.nspname || '.' || c.relname FROM")
123
123
  end
124
124
 
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
+
125
131
  private
126
132
 
127
133
  # Remove the schema from the sequence name
@@ -4,6 +4,9 @@ module Torque
4
4
  module PostgreSQL
5
5
  include ActiveSupport::Configurable
6
6
 
7
+ # Stores a version check for compatibility purposes
8
+ AR710 = (ActiveRecord.gem_version >= Gem::Version.new('7.1.0'))
9
+
7
10
  # Use the same logger as the Active Record one
8
11
  def self.logger
9
12
  ActiveRecord::Base.logger
@@ -17,8 +20,8 @@ module Torque
17
20
  send("#{name}=", klass)
18
21
  end
19
22
 
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
22
25
  # same configuration is set to true
23
26
  config.eager_load = false
24
27
 
@@ -17,7 +17,7 @@ module Torque
17
17
  private
18
18
 
19
19
  # Check if the foreign key should be pluralized
20
- def derive_foreign_key
20
+ def derive_foreign_key(*, **)
21
21
  result = super
22
22
  result = ActiveSupport::Inflector.pluralize(result) \
23
23
  if collection? && connected_through_array?
@@ -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,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Torque
4
+ module PostgreSQL
5
+ module SchemaCache
6
+ module Inheritance
7
+
8
+ # Try to find a model based on a given table
9
+ def lookup_model(table_name, scoped_class = '', source_to_model:)
10
+ scoped_class = scoped_class.name if scoped_class.is_a?(Class)
11
+ return source_to_model[table_name] if source_to_model.key?(table_name)
12
+
13
+ # Get all the possible scopes
14
+ scopes = scoped_class.scan(/(?:::)?[A-Z][a-z]+/)
15
+ scopes.unshift('Object::')
16
+
17
+ # Check if the table name comes with a schema
18
+ if table_name.include?('.')
19
+ schema, table_name = table_name.split('.')
20
+ scopes.insert(1, schema.camelize) if schema != 'public'
21
+ end
22
+
23
+ # Consider the maximum namespaced possible model name
24
+ max_name = table_name.tr('_', '/').camelize.split(/(::)/)
25
+ max_name[-1] = max_name[-1].singularize
26
+
27
+ # Test all the possible names against all the possible scopes
28
+ until scopes.size == 0
29
+ scope = scopes.join.chomp('::').safe_constantize
30
+ model = find_model(max_name, table_name, scope) unless scope.nil?
31
+ return source_to_model[table_name] = model unless model.nil?
32
+ scopes.pop
33
+ end
34
+
35
+ # If this part is reach, no model name was found
36
+ raise LookupError.new(<<~MSG.squish)
37
+ Unable to find a valid model that is associated with the
38
+ '#{table_name}' table. Please, check if they correctly inherit from
39
+ ActiveRecord::Base
40
+ MSG
41
+ end
42
+
43
+ protected
44
+
45
+ # Find a model by a given max namespaced class name that matches the
46
+ # given table name
47
+ def find_model(max_name, table_name, scope = Object)
48
+ pieces = max_name.is_a?(::Array) ? max_name : max_name.split(/(::)/)
49
+ ns_places = (1..(max_name.size - 1)).step(2).to_a
50
+
51
+ # Generate all possible combinations
52
+ conditions = []
53
+ range = Torque::PostgreSQL.config.inheritance.inverse_lookup \
54
+ ? 0.upto(ns_places.size) \
55
+ : ns_places.size.downto(0)
56
+ range.each do |size|
57
+ conditions.concat(ns_places.combination(size).to_a)
58
+ end
59
+
60
+ # Now iterate over
61
+ while (condition = conditions.shift)
62
+ ns_places.each do |i|
63
+ pieces[i] = condition.include?(i) ? '::' : ''
64
+ end
65
+
66
+ candidate = pieces.join
67
+ candidate.prepend("#{scope.name}::") unless scope === Object
68
+
69
+ klass = candidate.safe_constantize
70
+ next if klass.nil?
71
+
72
+ # Check if the class match the table name
73
+ return klass if klass < ::ActiveRecord::Base &&
74
+ klass.table_name == table_name
75
+ end
76
+ end
77
+
78
+ # Calculates the inverted dependency (association), where even indirect
79
+ # inheritance comes up in the list
80
+ def generate_associations(inheritance_dependencies)
81
+ return {} if inheritance_dependencies.empty?
82
+
83
+ result = Hash.new{ |h, k| h[k] = [] }
84
+ masters = inheritance_dependencies.values.flatten.uniq
85
+
86
+ # Add direct associations
87
+ masters.map do |master|
88
+ inheritance_dependencies.each do |(dependent, associations)|
89
+ result[master] << dependent if associations.include?(master)
90
+ end
91
+ end
92
+
93
+ # Add indirect associations
94
+ result.each do |master, children|
95
+ children.each do |child|
96
+ children.concat(result[child]).uniq! if result.key?(child)
97
+ end
98
+ end
99
+
100
+ # Remove the default proc that would create new entries
101
+ result.default_proc = nil
102
+ result
103
+ end
104
+
105
+ # Parse the Torque config into the proper hash of irregular models.
106
+ # This is smart enough to only load necessary models
107
+ def prepare_irregular_models(data_sources)
108
+ entries = Torque::PostgreSQL.config.irregular_models
109
+ entries.slice(*data_sources).each_with_object({}) do |(table, model), hash|
110
+ hash[table] = model.is_a?(Class) ? model : model.constantize
111
+ end
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Torque
4
+ module PostgreSQL
5
+ module SchemaReflection
6
+ def add_model_name(connection, table_name, model)
7
+ cache(connection).add_model_name(table_name, model)
8
+ end
9
+
10
+ def dependencies(connection, table_name)
11
+ cache(connection).dependencies(connection, table_name)
12
+ end
13
+
14
+ def associations(connection, table_name)
15
+ cache(connection).associations(connection, table_name)
16
+ end
17
+
18
+ def lookup_model(connection, table_name, scoped_class)
19
+ cache(connection).lookup_model(table_name, scoped_class)
20
+ end
21
+ end
22
+
23
+ ActiveRecord::ConnectionAdapters::SchemaReflection.prepend SchemaReflection
24
+ end
25
+ end
@@ -1,11 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
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
+
3
10
  module Torque
4
11
  module PostgreSQL
5
12
  LookupError = Class.new(ArgumentError)
6
13
 
7
14
  # :TODO: Create the +add+ to load inheritance info
8
15
  module SchemaCache
16
+ include Torque::PostgreSQL::SchemaCache::Inheritance
9
17
 
10
18
  def initialize(*) # :nodoc:
11
19
  super
@@ -37,7 +45,7 @@ module Torque
37
45
  @inheritance_associations = coder['inheritance_associations']
38
46
  end
39
47
 
40
- def add(table_name, *) # :nodoc:
48
+ def add(connection_or_table_name, table_name = connection_or_table_name, *) # :nodoc:
41
49
  super
42
50
 
43
51
  # Reset inheritance information when a table is added
@@ -64,8 +72,8 @@ module Torque
64
72
  ].map(&:size).inject(:+)
65
73
  end
66
74
 
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)
69
77
  @data_sources_model_names.delete name
70
78
  @inheritance_dependencies.delete name
71
79
  @inheritance_associations.delete name
@@ -95,88 +103,29 @@ module Torque
95
103
  end
96
104
 
97
105
  # 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)
100
108
  @inheritance_dependencies[table_name]
101
109
  end
102
110
 
103
111
  # Get the list of all tables that are associated (direct or indirect
104
112
  # 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)
107
115
  @inheritance_associations[table_name]
108
116
  end
109
117
 
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)
143
122
  end
144
123
 
145
124
  private
146
125
 
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
-
177
126
  # 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)
180
129
  return if @inheritance_loaded
181
130
  @inheritance_dependencies = connection.inherited_tables
182
131
  @inheritance_associations = generate_associations
@@ -186,38 +135,15 @@ module Torque
186
135
  # Calculates the inverted dependency (association), where even indirect
187
136
  # inheritance comes up in the list
188
137
  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)
211
139
  end
212
140
 
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)
221
147
  end
222
148
 
223
149
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Torque
4
4
  module PostgreSQL
5
- VERSION = '3.2.2'
5
+ VERSION = '3.3.0'
6
6
  end
7
7
  end
@@ -4,8 +4,8 @@ RSpec.describe 'Schema' do
4
4
  let(:connection) { ActiveRecord::Base.connection }
5
5
 
6
6
  before do
7
- connection.instance_variable_set(:@schmeas_blacklist, nil)
8
- connection.instance_variable_set(:@schmeas_whitelist, nil)
7
+ connection.instance_variable_set(:@schemas_blacklist, nil)
8
+ connection.instance_variable_set(:@schemas_whitelist, nil)
9
9
  end
10
10
 
11
11
  context 'on migration' do
@@ -104,7 +104,12 @@ RSpec.describe 'TableInheritance' do
104
104
  end
105
105
 
106
106
  context 'on schema cache' do
107
- subject { ActiveRecord::Base.connection.schema_cache }
107
+ let(:schema_cache) { ActiveRecord::Base.connection.schema_cache }
108
+ let(:schema_cache_connection) { schema_cache.instance_variable_get(:@connection) }
109
+ let(:schema_cache_reflection) { schema_cache.instance_variable_get(:@schema_reflection) }
110
+ let(:new_schema_cache) { schema_cache_reflection.send(:cache, schema_cache_connection) }
111
+
112
+ subject { Torque::PostgreSQL::AR710 ? new_schema_cache : schema_cache }
108
113
 
109
114
  it 'correctly defines the associations' do
110
115
  scenario = {
@@ -131,6 +136,8 @@ RSpec.describe 'TableInheritance' do
131
136
  end
132
137
 
133
138
  context 'on looking up models' do
139
+ let(:prepare_arguments) { Torque::PostgreSQL::AR710 ? [schema_cache_connection] : nil }
140
+
134
141
  after(:all) do
135
142
  schema_cache = ActiveRecord::Base.connection.schema_cache
136
143
  schema_cache.instance_variable_set(:@data_sources, {})
@@ -139,13 +146,13 @@ RSpec.describe 'TableInheritance' do
139
146
 
140
147
  it 'respect irregular names' do
141
148
  Torque::PostgreSQL.config.irregular_models = {
142
- 'posts' => 'ActivityPost',
149
+ 'public.posts' => 'ActivityPost',
143
150
  }
144
151
 
145
- subject.send(:prepare_data_sources)
152
+ subject.send(:prepare_data_sources, *prepare_arguments)
146
153
  list = subject.instance_variable_get(:@data_sources_model_names)
147
- expect(list).to have_key('posts')
148
- expect(list['posts']).to eql(ActivityPost)
154
+ expect(list).to have_key('public.posts')
155
+ expect(list['public.posts']).to eql(ActivityPost)
149
156
  end
150
157
 
151
158
  it 'does not load irregular where the data source is not defined' do
@@ -153,7 +160,7 @@ RSpec.describe 'TableInheritance' do
153
160
  'products' => 'Product',
154
161
  }
155
162
 
156
- subject.send(:prepare_data_sources)
163
+ subject.send(:prepare_data_sources, *prepare_arguments)
157
164
  list = subject.instance_variable_get(:@data_sources_model_names)
158
165
  expect(list).to_not have_key('products')
159
166
  end
@@ -176,6 +183,8 @@ RSpec.describe 'TableInheritance' do
176
183
  let(:child2) { ActivityBook }
177
184
  let(:other) { AuthorJournalist }
178
185
 
186
+ before { ActiveRecord::Base.connection.schema_cache.clear! }
187
+
179
188
  it 'identifies mergeable attributes' do
180
189
  result_base = %w(id author_id title active kind created_at updated_at description url file post_id)
181
190
  expect(base.inheritance_mergeable_attributes.sort).to eql(result_base.sort)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: torque-postgresql
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.2
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos Silva
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-20 00:00:00.000000000 Z
11
+ date: 2023-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -64,20 +64,14 @@ dependencies:
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '1.5'
68
- - - ">="
69
- - !ruby/object:Gem::Version
70
- version: 1.5.3
67
+ version: '2.0'
71
68
  type: :development
72
69
  prerelease: false
73
70
  version_requirements: !ruby/object:Gem::Requirement
74
71
  requirements:
75
72
  - - "~>"
76
73
  - !ruby/object:Gem::Version
77
- version: '1.5'
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- version: 1.5.3
74
+ version: '2.0'
81
75
  - !ruby/object:Gem::Dependency
82
76
  name: dotenv
83
77
  requirement: !ruby/object:Gem::Requirement
@@ -233,6 +227,9 @@ files:
233
227
  - lib/torque/postgresql/relation/inheritance.rb
234
228
  - lib/torque/postgresql/relation/merger.rb
235
229
  - lib/torque/postgresql/schema_cache.rb
230
+ - lib/torque/postgresql/schema_cache/bound_schema_reflection.rb
231
+ - lib/torque/postgresql/schema_cache/inheritance.rb
232
+ - lib/torque/postgresql/schema_cache/schema_reflection.rb
236
233
  - lib/torque/postgresql/table_name.rb
237
234
  - lib/torque/postgresql/version.rb
238
235
  - spec/en.yml
@@ -292,7 +289,7 @@ licenses:
292
289
  metadata:
293
290
  source_code_uri: https://github.com/crashtech/torque-postgresql
294
291
  bug_tracker_uri: https://github.com/crashtech/torque-postgresql/issues
295
- post_install_message:
292
+ post_install_message:
296
293
  rdoc_options:
297
294
  - "--title"
298
295
  - Torque PostgreSQL
@@ -310,7 +307,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
310
307
  version: 1.8.11
311
308
  requirements: []
312
309
  rubygems_version: 3.2.15
313
- signing_key:
310
+ signing_key:
314
311
  specification_version: 4
315
312
  summary: ActiveRecord extension to access PostgreSQL advanced resources
316
313
  test_files: