rails_lens 0.2.4 → 0.2.6

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.
@@ -85,8 +85,33 @@ module RailsLens
85
85
  end
86
86
  end
87
87
  else
88
- # Fall back to normal processing without connection management
89
- results = pipeline.process(model_class)
88
+ # Fallback: Use the model's connection pool with proper management
89
+ # This path is used when annotating individual models
90
+ warn "Using fallback connection management for #{model_class.name}" if RailsLens.config.verbose
91
+
92
+ # Force connection management even in fallback mode
93
+ results = { schema: nil, sections: [], notes: [] }
94
+
95
+ model_class.connection_pool.with_connection do |connection|
96
+ pipeline.instance_variable_get(:@providers).each do |provider|
97
+ next unless provider.applicable?(model_class)
98
+
99
+ begin
100
+ result = provider.process(model_class, connection)
101
+
102
+ case provider.type
103
+ when :schema
104
+ results[:schema] = result
105
+ when :section
106
+ results[:sections] << result if result
107
+ when :notes
108
+ results[:notes].concat(Array(result))
109
+ end
110
+ rescue StandardError => e
111
+ warn "Provider #{provider.class} error for #{model_class}: #{e.message}"
112
+ end
113
+ end
114
+ end
90
115
  end
91
116
 
92
117
  annotation = Annotation.new
@@ -117,7 +142,13 @@ module RailsLens
117
142
  end
118
143
 
119
144
  def self.annotate_all(options = {})
145
+ # Convert models option to include option for ModelDetector
146
+ if options[:models]
147
+ options[:include] = options[:models]
148
+ end
149
+
120
150
  models = ModelDetector.detect_models(options)
151
+ puts "Detected #{models.size} models for annotation" if options[:verbose]
121
152
 
122
153
  # Filter abstract classes based on options
123
154
  if options[:include_abstract]
@@ -133,13 +164,35 @@ module RailsLens
133
164
 
134
165
  # Group models by their connection pool to process each database separately
135
166
  models_by_connection_pool = models.group_by do |model|
136
- model.connection_pool
137
- rescue StandardError
138
- nil # Models without connection pools will be processed separately
167
+ pool = model.connection_pool
168
+ pool
169
+ rescue StandardError => e
170
+ puts "Model #{model.name} -> NO POOL (#{e.message})" if options[:verbose]
171
+ nil # Models without connection pools will use primary pool
139
172
  end
140
173
 
174
+ # Force models without connection pools to use the primary connection pool
175
+ if models_by_connection_pool[nil]&.any?
176
+ begin
177
+ primary_pool = ApplicationRecord.connection_pool
178
+ models_by_connection_pool[primary_pool] ||= []
179
+ models_by_connection_pool[primary_pool].concat(models_by_connection_pool[nil])
180
+ models_by_connection_pool.delete(nil)
181
+ rescue StandardError => e
182
+ puts "Failed to assign to primary pool: #{e.message}" if options[:verbose]
183
+ end
184
+ end
185
+
186
+ # Get all connection pools first
187
+ all_pools = get_all_connection_pools(models_by_connection_pool)
188
+
189
+ # Log initial connection status (removed verbose output)
190
+
141
191
  models_by_connection_pool.each do |connection_pool, pool_models|
142
192
  if connection_pool
193
+ # Disconnect all OTHER connection pools before processing this one
194
+ disconnect_other_pools(connection_pool, all_pools, options)
195
+
143
196
  # Process all models for this database using a single connection
144
197
  connection_pool.with_connection do |connection|
145
198
  pool_models.each do |model|
@@ -147,9 +200,20 @@ module RailsLens
147
200
  end
148
201
  end
149
202
  else
150
- # Process models without connection pools individually
151
- pool_models.each do |model|
152
- process_model_with_connection(model, nil, results, options)
203
+ # This should not happen anymore since we assign orphaned models to primary pool
204
+ # Use primary connection pool as fallback to avoid creating new connections
205
+ begin
206
+ primary_pool = ApplicationRecord.connection_pool
207
+ primary_pool.with_connection do |connection|
208
+ pool_models.each do |model|
209
+ process_model_with_connection(model, connection, results, options)
210
+ end
211
+ end
212
+ rescue StandardError => e
213
+ # Last resort: process without connection management (will create multiple connections)
214
+ pool_models.each do |model|
215
+ process_model_with_connection(model, nil, results, options)
216
+ end
153
217
  end
154
218
  end
155
219
  end
@@ -167,7 +231,6 @@ module RailsLens
167
231
  # Skip models without tables or with missing tables (but not abstract classes)
168
232
  unless model.abstract_class? || model.table_exists?
169
233
  results[:skipped] << model.name
170
- warn "Skipping #{model.name} - table does not exist" if options[:verbose]
171
234
  return
172
235
  end
173
236
 
@@ -191,10 +254,9 @@ module RailsLens
191
254
  else
192
255
  results[:skipped] << model.name
193
256
  end
194
- rescue ActiveRecord::StatementInvalid => e
257
+ rescue ActiveRecord::StatementInvalid
195
258
  # Handle database-related errors (missing tables, schemas, etc.)
196
259
  results[:skipped] << model.name
197
- warn "Skipping #{model.name} - database error: #{e.message}" if options[:verbose]
198
260
  rescue StandardError => e
199
261
  model_name = if model.is_a?(Class) && model.respond_to?(:name)
200
262
  model.name
@@ -222,6 +284,45 @@ module RailsLens
222
284
  results
223
285
  end
224
286
 
287
+ def self.disconnect_other_pools(current_pool, all_pools, options = {})
288
+ all_pools.each do |pool|
289
+ next if pool == current_pool || pool.nil?
290
+
291
+ begin
292
+ if pool.connected?
293
+ pool.disconnect!
294
+ end
295
+ rescue StandardError => e
296
+ warn "Failed to disconnect pool: #{e.message}"
297
+ end
298
+ end
299
+ end
300
+
301
+ def self.get_all_connection_pools(models_by_pool)
302
+ models_by_pool.keys.compact
303
+ end
304
+
305
+ def self.log_connection_status(all_pools, options = {})
306
+ return unless options[:verbose]
307
+
308
+ puts "\n=== Connection Pool Status ==="
309
+ all_pools.each do |pool|
310
+ next unless pool
311
+
312
+ begin
313
+ name = pool.db_config&.name || 'unknown'
314
+ connected = pool.connected?
315
+ size = pool.size
316
+ checked_out = pool.stat[:busy]
317
+
318
+ puts "Pool #{name}: connected=#{connected}, size=#{size}, busy=#{checked_out}"
319
+ rescue StandardError => e
320
+ puts "Pool status error: #{e.message}"
321
+ end
322
+ end
323
+ puts "================================\n"
324
+ end
325
+
225
326
  private
226
327
 
227
328
  def add_annotation(content, _file_path = nil)
@@ -9,7 +9,14 @@ namespace :rails_lens do
9
9
  options = {}
10
10
  options[:include_abstract] = true if ENV['INCLUDE_ABSTRACT'] == 'true'
11
11
 
12
- puts 'Annotating models with schema information...'
12
+ # Support model filtering via environment variable
13
+ if ENV['MODELS']
14
+ model_list = ENV['MODELS'].split(',').map(&:strip)
15
+ options[:models] = model_list
16
+ puts "Filtering to specific models: #{model_list.join(', ')}"
17
+ end
18
+
19
+ options[:verbose] = true # Force verbose mode to see connection management
13
20
  results = RailsLens.annotate_models(options)
14
21
 
15
22
  if results[:annotated].any?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsLens
4
- VERSION = '0.2.4'
4
+ VERSION = '0.2.6'
5
5
  end
data/lib/rails_lens.rb CHANGED
@@ -26,92 +26,31 @@ loader.setup
26
26
 
27
27
  require_relative 'rails_lens/errors'
28
28
  require_relative 'rails_lens/cli'
29
+ require_relative 'rails_lens/configuration'
29
30
 
30
31
  module RailsLens
31
32
  include ActiveSupport::Configurable
33
+ include Configuration
32
34
 
33
- # Add configuration for error handling
34
- config_accessor :verbose, default: false
35
- config_accessor :debug, default: false
36
- config_accessor :raise_on_error, default: false
37
-
38
- # Configuration using ActiveSupport::Configurable
39
- config_accessor :annotations do
40
- {
41
- position: :before,
42
- format: :rdoc
43
- }
44
- end
45
-
46
- config_accessor :erd do
47
- {
48
- output_dir: 'doc/erd',
49
- orientation: 'TB',
50
- theme: true,
51
- default_colors: %w[
52
- lightblue
53
- lightcoral
54
- lightgreen
55
- lightyellow
56
- plum
57
- lightcyan
58
- lightgray
59
- ]
60
- }
61
- end
62
-
63
- config_accessor :schema do
64
- {
65
- adapter: :auto,
66
- include_notes: true,
67
- exclude_tables: %w[schema_migrations ar_internal_metadata],
68
- format_options: {
69
- show_defaults: true,
70
- show_comments: true,
71
- show_foreign_keys: true,
72
- show_indexes: true,
73
- show_check_constraints: true
74
- }
75
- }
76
- end
77
-
78
- config_accessor :extensions do
79
- {
80
- enabled: true,
81
- autoload: true,
82
- interface_version: '1.0',
83
- ignore: [],
84
- custom_paths: [],
85
- error_reporting: :warn, # :silent, :warn, :verbose
86
- fail_safe_mode: true, # Continue processing if extensions fail
87
- track_health: false # Track extension success/failure rates
88
- }
89
- end
35
+ class << self
36
+ def logger
37
+ @logger ||= config.logger || default_logger
38
+ end
90
39
 
91
- config_accessor :routes do
92
- {
93
- enabled: true,
94
- include_defaults: true,
95
- include_constraints: true,
96
- pattern: '**/*_controller.rb',
97
- exclusion_pattern: 'vendor/**/*_controller.rb'
98
- }
99
- end
40
+ def logger=(new_logger)
41
+ @logger = new_logger
42
+ config.logger = new_logger
43
+ end
100
44
 
101
- config_accessor :mailers do
102
- {
103
- enabled: true,
104
- include_templates: true,
105
- include_delivery_methods: true,
106
- include_variables: true,
107
- include_locales: true,
108
- include_defaults: true,
109
- pattern: '**/*_mailer.rb',
110
- exclusion_pattern: 'vendor/**/*_mailer.rb'
111
- }
112
- end
45
+ def default_logger
46
+ if defined?(Rails.logger) && Rails.logger
47
+ Rails.logger
48
+ else
49
+ require 'logger'
50
+ Logger.new($stdout)
51
+ end
52
+ end
113
53
 
114
- class << self
115
54
  def load_config_file(path = '.rails-lens.yml')
116
55
  return unless File.exist?(path)
117
56
 
@@ -130,6 +69,17 @@ module RailsLens
130
69
  end
131
70
  end
132
71
 
72
+ # Get tables to exclude
73
+ def excluded_tables
74
+ custom_excludes = config.schema[:exclude_tables]
75
+ if custom_excludes.nil?
76
+ # Use ActiveRecord's default ignore tables
77
+ ActiveRecord::SchemaDumper.ignore_tables.to_a
78
+ else
79
+ Array(custom_excludes)
80
+ end
81
+ end
82
+
133
83
  # Schema annotation methods
134
84
  def annotate_models(options = {})
135
85
  Schema::AnnotationManager.annotate_all(options)
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.2.4
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -182,6 +182,7 @@ files:
182
182
  - lib/rails_lens/cli.rb
183
183
  - lib/rails_lens/cli_error_handler.rb
184
184
  - lib/rails_lens/commands.rb
185
+ - lib/rails_lens/configuration.rb
185
186
  - lib/rails_lens/connection.rb
186
187
  - lib/rails_lens/erd/column_type_formatter.rb
187
188
  - lib/rails_lens/erd/domain_color_mapper.rb