rails_lens 0.5.1 → 0.5.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1bd96547bfc5ae90c3971f787bbb03cad57e8912ed25c4ea0005ab3652401408
4
- data.tar.gz: 95f1bcfa75b796a22c1fd5fb861c79e41e048608648388d1d9b255b4a3466157
3
+ metadata.gz: 792f89c06b8c11b77fb6892aa7488397d068104e2f30b7c343e38039660b0a2f
4
+ data.tar.gz: d808455520cffa29f877e43ca8492a1e6dae238e231c641fe8ddd36663ba3d07
5
5
  SHA512:
6
- metadata.gz: 960195b484708f9904f1d96d5f52e5fca70580431deffdc9a5cb56c99ab57af3509c33ad88eb7cdfa84c75067ea04f1d542568152cc60ab6dbcb162b036d1b61
7
- data.tar.gz: da71bed238e3fa5742039fa0f931d4fc625f52a6d1df3cc831678bd070b65b38e6f3ea4b01b998c7e163409764db0aebfaab83af9a10733081d99513f6a80893
6
+ metadata.gz: a754d8353f7a75e11283af18fb2c128bdedec40bf673b5c2cae042d48c14d98dd3c7f26c118d81e0af09a836e0624a4ad2dbd5d092b7b64e4c8664aa9540df0f
7
+ data.tar.gz: 821f26b9307bfd92914e9c1e6266c3bad0db90c16593e98eb91c1878f9662b3409485a0f32d03d8a007fd1d73bb9f114faec367ce533c41699f01cc2b0b12dc9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.3](https://github.com/seuros/rails_lens/compare/rails_lens/v0.5.2...rails_lens/v0.5.3) (2026-02-17)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * handle edge cases table naming. ([dec95af](https://github.com/seuros/rails_lens/commit/dec95af7866a9ed3c5430974a1492749ca0a5f8e))
9
+
10
+ ## [0.5.2](https://github.com/seuros/rails_lens/compare/rails_lens/v0.5.1...rails_lens/v0.5.2) (2026-02-16)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * reduce INVERSE_OF false positives for vanilla associations ([#39](https://github.com/seuros/rails_lens/issues/39)) ([b209c2a](https://github.com/seuros/rails_lens/commit/b209c2a6b2a894193561e9aa30985c3043b40ac3))
16
+
3
17
  ## [0.5.1](https://github.com/seuros/rails_lens/compare/rails_lens/v0.5.0...rails_lens/v0.5.1) (2025-12-07)
4
18
 
5
19
 
@@ -42,7 +42,7 @@ module RailsLens
42
42
  def associations_needing_inverse
43
43
  all_associations.select do |association|
44
44
  association.options[:inverse_of].nil? &&
45
- !association.options[:through] &&
45
+ needs_explicit_inverse_of?(association) &&
46
46
  !association.polymorphic? &&
47
47
  bidirectional_association?(association)
48
48
  end
@@ -71,6 +71,16 @@ module RailsLens
71
71
  false
72
72
  end
73
73
 
74
+ def needs_explicit_inverse_of?(association)
75
+ # Rails can auto-infer inverse_of for vanilla associations
76
+ # Only require explicit inverse_of when using custom options
77
+ association.options[:class_name].present? ||
78
+ association.options[:foreign_key].present? ||
79
+ association.options[:as].present? ||
80
+ association.options[:source].present? ||
81
+ association.options[:through].present?
82
+ end
83
+
74
84
  def should_have_counter_cache?(association)
75
85
  return false unless association.macro == :belongs_to
76
86
 
@@ -116,23 +116,23 @@ module RailsLens
116
116
  case filter
117
117
  when Symbol
118
118
  # Check if method is defined in the model class itself
119
- return true if model_class.instance_methods(false).include?(filter)
120
- return true if model_class.private_instance_methods(false).include?(filter)
119
+ return true if model_class.method_defined?(filter, false)
120
+ return true if model_class.private_method_defined?(filter, false)
121
121
 
122
122
  # Check if defined in included concerns (non-Rails modules)
123
123
  model_class.included_modules.each do |mod|
124
124
  next if mod.name.nil?
125
125
  next if mod.name.start_with?('ActiveRecord', 'ActiveModel', 'ActiveSupport')
126
126
 
127
- return true if mod.instance_methods(false).include?(filter)
128
- return true if mod.private_instance_methods(false).include?(filter)
127
+ return true if mod.method_defined?(filter, false)
128
+ return true if mod.private_method_defined?(filter, false)
129
129
  end
130
130
 
131
131
  # For STI: check parent classes up to (but not including) ActiveRecord::Base
132
132
  klass = model_class.superclass
133
133
  while klass && klass < ActiveRecord::Base
134
- return true if klass.instance_methods(false).include?(filter)
135
- return true if klass.private_instance_methods(false).include?(filter)
134
+ return true if klass.method_defined?(filter, false)
135
+ return true if klass.private_method_defined?(filter, false)
136
136
 
137
137
  klass = klass.superclass
138
138
  end
@@ -35,7 +35,7 @@ module RailsLens
35
35
 
36
36
  def detect_composite_primary_key_from_db
37
37
  # Query PostgreSQL system catalogs to find composite primary keys
38
- sql = <<-SQL.squish
38
+ sql = <<~SQL.squish
39
39
  SELECT a.attname
40
40
  FROM pg_index i
41
41
  JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
@@ -22,7 +22,7 @@ module RailsLens
22
22
 
23
23
  def detect_generated_columns
24
24
  # PostgreSQL system query to find generated columns
25
- sql = <<-SQL.squish
25
+ sql = <<~SQL.squish
26
26
  SELECT
27
27
  a.attname AS column_name,
28
28
  pg_get_expr(d.adbin, d.adrelid) AS generation_expression
@@ -312,8 +312,7 @@ module RailsLens
312
312
  def associations_needing_inverse
313
313
  associations.select do |association|
314
314
  association.options[:inverse_of].nil? &&
315
- !association.options[:through] &&
316
- !association.options[:as] &&
315
+ needs_explicit_inverse_of?(association) &&
317
316
  association.macro != :has_and_belongs_to_many
318
317
  end
319
318
  end
@@ -418,6 +417,16 @@ module RailsLens
418
417
  RailsLens.logger.debug { "Error checking view existence for #{view_name}: #{e.message}" }
419
418
  false
420
419
  end
420
+
421
+ def needs_explicit_inverse_of?(association)
422
+ # Rails can auto-infer inverse_of for vanilla associations
423
+ # Only require explicit inverse_of when using custom options
424
+ association.options[:class_name].present? ||
425
+ association.options[:foreign_key].present? ||
426
+ association.options[:as].present? ||
427
+ association.options[:source].present? ||
428
+ association.options[:through].present?
429
+ end
421
430
  end
422
431
  end
423
432
  end
@@ -78,13 +78,38 @@ module RailsLens
78
78
 
79
79
  # rubocop:disable Naming/PredicateMethod
80
80
  def check_postgresql_view(connection, table_name)
81
+ # Handle schema-qualified table names (e.g., 'audit.audit_logs')
82
+ if table_name.include?('.')
83
+ schema_name, unqualified_name = table_name.split('.', 2)
84
+ schema_filter = "'#{connection.quote_string(schema_name)}'"
85
+ table_to_check = unqualified_name
86
+ else
87
+ # Extract search_path schemas for unqualified table names
88
+ search_path = connection.schema_search_path
89
+ .split(',')
90
+ .map(&:strip)
91
+ .reject { |s| s == '"$user"' || s.empty? }
92
+
93
+ # Default to 'public' if search_path is empty
94
+ search_path = ['public'] if search_path.empty?
95
+
96
+ schema_filter = search_path.map { |s| "'#{connection.quote_string(s)}'" }.join(', ')
97
+ table_to_check = table_name
98
+ end
99
+
81
100
  # Check both regular views and materialized views
101
+ # Exclude system schemas to prevent false positives when user table names
102
+ # match PostgreSQL system view names (e.g., 'triggers', 'tables', 'columns')
82
103
  result = connection.exec_query(<<~SQL.squish, 'Check PostgreSQL View')
83
104
  SELECT 1 FROM information_schema.views
84
- WHERE table_name = '#{connection.quote_string(table_name)}'
105
+ WHERE table_name = '#{connection.quote_string(table_to_check)}'
106
+ AND table_schema IN (#{schema_filter})
107
+ AND table_schema NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
85
108
  UNION ALL
86
109
  SELECT 1 FROM pg_matviews
87
- WHERE matviewname = '#{connection.quote_string(table_name)}'
110
+ WHERE matviewname = '#{connection.quote_string(table_to_check)}'
111
+ AND schemaname IN (#{schema_filter})
112
+ AND schemaname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
88
113
  LIMIT 1
89
114
  SQL
90
115
  result.rows.any?
@@ -84,7 +84,7 @@ module RailsLens
84
84
  def extensions
85
85
  return [] unless adapter_name == 'PostgreSQL'
86
86
 
87
- connection.select_all(<<-SQL.squish).to_a
87
+ connection.select_all(<<~SQL.squish).to_a
88
88
  SELECT extname as name, extversion as version
89
89
  FROM pg_extension
90
90
  WHERE extname NOT IN ('plpgsql')
@@ -97,7 +97,7 @@ module RailsLens
97
97
  def schemas
98
98
  return [] unless adapter_name == 'PostgreSQL'
99
99
 
100
- connection.select_values(<<-SQL.squish)
100
+ connection.select_values(<<~SQL.squish)
101
101
  SELECT schema_name
102
102
  FROM information_schema.schemata
103
103
  WHERE schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
@@ -211,12 +211,12 @@ module RailsLens
211
211
  def has_partitions?
212
212
  return false unless connection.respond_to?(:execute)
213
213
 
214
- result = connection.execute(<<-SQL.squish)
215
- SELECT COUNT(*) as count
216
- FROM information_schema.partitions
217
- WHERE table_schema = DATABASE()
218
- AND table_name = '#{table_name}'
219
- AND partition_name IS NOT NULL
214
+ result = connection.execute(<<~SQL.squish)
215
+ SELECT COUNT(*) as count
216
+ FROM information_schema.partitions
217
+ WHERE table_schema = DATABASE()
218
+ AND table_name = '#{table_name}'
219
+ AND partition_name IS NOT NULL
220
220
  SQL
221
221
 
222
222
  count = if result.first.is_a?(Hash)
@@ -241,13 +241,13 @@ module RailsLens
241
241
  def add_partitions(lines)
242
242
  return unless connection.respond_to?(:execute)
243
243
 
244
- partitions = connection.execute(<<-SQL.squish)
245
- SELECT partition_name, partition_expression, partition_description
246
- FROM information_schema.partitions
247
- WHERE table_schema = DATABASE()
248
- AND table_name = '#{table_name}'
249
- AND partition_name IS NOT NULL
250
- ORDER BY partition_ordinal_position
244
+ partitions = connection.execute(<<~SQL.squish)
245
+ SELECT partition_name, partition_expression, partition_description
246
+ FROM information_schema.partitions
247
+ WHERE table_schema = DATABASE()
248
+ AND table_name = '#{table_name}'
249
+ AND partition_name IS NOT NULL
250
+ ORDER BY partition_ordinal_position
251
251
  SQL
252
252
 
253
253
  return if partitions.none?
@@ -269,13 +269,13 @@ module RailsLens
269
269
  def add_partitions_toml(lines)
270
270
  return unless connection.respond_to?(:execute)
271
271
 
272
- partitions = connection.execute(<<-SQL.squish)
273
- SELECT partition_name, partition_expression, partition_description
274
- FROM information_schema.partitions
275
- WHERE table_schema = DATABASE()
276
- AND table_name = '#{table_name}'
277
- AND partition_name IS NOT NULL
278
- ORDER BY partition_ordinal_position
272
+ partitions = connection.execute(<<~SQL.squish)
273
+ SELECT partition_name, partition_expression, partition_description
274
+ FROM information_schema.partitions
275
+ WHERE table_schema = DATABASE()
276
+ AND table_name = '#{table_name}'
277
+ AND partition_name IS NOT NULL
278
+ ORDER BY partition_ordinal_position
279
279
  SQL
280
280
 
281
281
  return if partitions.none?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsLens
4
- VERSION = '0.5.1'
4
+ VERSION = '0.5.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_lens
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -270,7 +270,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
270
270
  - !ruby/object:Gem::Version
271
271
  version: '0'
272
272
  requirements: []
273
- rubygems_version: 3.6.9
273
+ rubygems_version: 4.0.3
274
274
  specification_version: 4
275
275
  summary: Comprehensive Rails application visualization and annotation
276
276
  test_files: []