rails_console_pro 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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +12 -0
  3. data/.rspec +4 -0
  4. data/.rspec_status +240 -0
  5. data/.rubocop.yml +26 -0
  6. data/CHANGELOG.md +24 -0
  7. data/CONTRIBUTING.md +76 -0
  8. data/LICENSE.txt +22 -0
  9. data/QUICK_START.md +112 -0
  10. data/README.md +124 -0
  11. data/Rakefile +13 -0
  12. data/config/database.yml +3 -0
  13. data/docs/ASSOCIATION_NAVIGATION.md +85 -0
  14. data/docs/EXPORT.md +95 -0
  15. data/docs/FORMATTING.md +86 -0
  16. data/docs/MODEL_STATISTICS.md +72 -0
  17. data/docs/OBJECT_DIFFING.md +87 -0
  18. data/docs/SCHEMA_INSPECTION.md +60 -0
  19. data/docs/SQL_EXPLAIN.md +70 -0
  20. data/lib/generators/rails_console_pro/install_generator.rb +16 -0
  21. data/lib/generators/rails_console_pro/templates/rails_console_pro.rb +44 -0
  22. data/lib/rails_console_pro/active_record_extensions.rb +113 -0
  23. data/lib/rails_console_pro/association_navigator.rb +273 -0
  24. data/lib/rails_console_pro/base_printer.rb +74 -0
  25. data/lib/rails_console_pro/color_helper.rb +36 -0
  26. data/lib/rails_console_pro/commands/base_command.rb +17 -0
  27. data/lib/rails_console_pro/commands/diff_command.rb +135 -0
  28. data/lib/rails_console_pro/commands/explain_command.rb +118 -0
  29. data/lib/rails_console_pro/commands/export_command.rb +16 -0
  30. data/lib/rails_console_pro/commands/schema_command.rb +20 -0
  31. data/lib/rails_console_pro/commands/stats_command.rb +93 -0
  32. data/lib/rails_console_pro/commands.rb +34 -0
  33. data/lib/rails_console_pro/configuration.rb +219 -0
  34. data/lib/rails_console_pro/diff_result.rb +56 -0
  35. data/lib/rails_console_pro/error_handler.rb +60 -0
  36. data/lib/rails_console_pro/explain_result.rb +47 -0
  37. data/lib/rails_console_pro/format_exporter.rb +403 -0
  38. data/lib/rails_console_pro/global_methods.rb +42 -0
  39. data/lib/rails_console_pro/initializer.rb +176 -0
  40. data/lib/rails_console_pro/model_validator.rb +219 -0
  41. data/lib/rails_console_pro/paginator.rb +204 -0
  42. data/lib/rails_console_pro/printers/active_record_printer.rb +30 -0
  43. data/lib/rails_console_pro/printers/collection_printer.rb +34 -0
  44. data/lib/rails_console_pro/printers/diff_printer.rb +97 -0
  45. data/lib/rails_console_pro/printers/explain_printer.rb +151 -0
  46. data/lib/rails_console_pro/printers/relation_printer.rb +25 -0
  47. data/lib/rails_console_pro/printers/schema_printer.rb +188 -0
  48. data/lib/rails_console_pro/printers/stats_printer.rb +129 -0
  49. data/lib/rails_console_pro/pry_commands.rb +241 -0
  50. data/lib/rails_console_pro/pry_integration.rb +9 -0
  51. data/lib/rails_console_pro/railtie.rb +29 -0
  52. data/lib/rails_console_pro/schema_inspector_result.rb +43 -0
  53. data/lib/rails_console_pro/serializers/active_record_serializer.rb +18 -0
  54. data/lib/rails_console_pro/serializers/array_serializer.rb +31 -0
  55. data/lib/rails_console_pro/serializers/base_serializer.rb +25 -0
  56. data/lib/rails_console_pro/serializers/diff_serializer.rb +24 -0
  57. data/lib/rails_console_pro/serializers/explain_serializer.rb +35 -0
  58. data/lib/rails_console_pro/serializers/relation_serializer.rb +25 -0
  59. data/lib/rails_console_pro/serializers/schema_serializer.rb +121 -0
  60. data/lib/rails_console_pro/serializers/stats_serializer.rb +24 -0
  61. data/lib/rails_console_pro/services/column_stats_calculator.rb +64 -0
  62. data/lib/rails_console_pro/services/index_analyzer.rb +110 -0
  63. data/lib/rails_console_pro/services/stats_calculator.rb +40 -0
  64. data/lib/rails_console_pro/services/table_size_calculator.rb +43 -0
  65. data/lib/rails_console_pro/stats_result.rb +66 -0
  66. data/lib/rails_console_pro/version.rb +6 -0
  67. data/lib/rails_console_pro.rb +14 -0
  68. data/lib/tasks/rails_console_pro.rake +10 -0
  69. data/rails_console_pro.gemspec +60 -0
  70. metadata +240 -0
@@ -0,0 +1,403 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'yaml'
5
+
6
+ module RailsConsolePro
7
+ # Format exporter for converting data to JSON, YAML, and HTML
8
+ module FormatExporter
9
+ extend self
10
+
11
+ # Export to JSON format
12
+ # Similar to awesome_print philosophy: convert objects to JSON-serializable structures
13
+ # Supports both pretty-printed and compact formats
14
+ def to_json(data, pretty: true, options: {})
15
+ json_data = serialize_data(data)
16
+
17
+ # Apply awesome_print-style options if provided
18
+ if pretty
19
+ # Ruby's JSON.pretty_generate uses 2-space indentation by default
20
+ JSON.pretty_generate(json_data)
21
+ else
22
+ JSON.generate(json_data)
23
+ end
24
+ rescue JSON::GeneratorError => e
25
+ # Fallback for complex objects that can't be serialized
26
+ { error: "Could not serialize to JSON", message: e.message, type: data.class.name }.to_json
27
+ end
28
+
29
+ # Export to YAML format
30
+ def to_yaml(data)
31
+ yaml_data = serialize_data(data)
32
+ # Convert symbols to strings for YAML.safe_load compatibility
33
+ yaml_data = convert_symbols_to_strings(yaml_data)
34
+ yaml_data.to_yaml
35
+ end
36
+
37
+ # Export to HTML format
38
+ def to_html(data, title: nil, style: :default)
39
+ html_data = serialize_data(data)
40
+ generate_html(html_data, title: title || infer_title(data), style: style)
41
+ end
42
+
43
+ # Export to file
44
+ def export_to_file(data, file_path, format: nil)
45
+ return nil if data.nil?
46
+
47
+ format ||= infer_format_from_path(file_path)
48
+ content = case format.to_s.downcase
49
+ when 'json'
50
+ to_json(data)
51
+ when 'yaml', 'yml'
52
+ to_yaml(data)
53
+ when 'html', 'htm'
54
+ to_html(data, title: infer_title(data))
55
+ else
56
+ raise ArgumentError, "Unsupported format: #{format}. Supported: json, yaml, html"
57
+ end
58
+
59
+ File.write(file_path, content)
60
+ file_path
61
+ rescue ArgumentError, Errno::ENOENT, Errno::EACCES, Errno::ENOSPC => e
62
+ # Handle file system errors gracefully
63
+ nil
64
+ rescue => e
65
+ # Handle any other errors
66
+ nil
67
+ end
68
+
69
+ private
70
+
71
+ # Serialize data to a hash structure
72
+ # Similar to awesome_print's approach: handle native types directly,
73
+ # convert complex objects to structured data
74
+ def serialize_data(data)
75
+ case data
76
+ when SchemaInspectorResult
77
+ serialize_schema_result(data)
78
+ when StatsResult
79
+ serialize_stats_result(data)
80
+ when DiffResult
81
+ serialize_diff_result(data)
82
+ when ExplainResult
83
+ serialize_explain_result(data)
84
+ when ActiveRecord::Base
85
+ serialize_active_record(data)
86
+ when ActiveRecord::Relation
87
+ serialize_relation(data)
88
+ when Array
89
+ serialize_array(data)
90
+ when Hash
91
+ # Recursively serialize hash values (awesome_print-style)
92
+ serialize_hash(data)
93
+ when String, Numeric, TrueClass, FalseClass, NilClass
94
+ # Native JSON types - return as-is
95
+ data
96
+ when Symbol
97
+ # Convert symbols to strings for JSON/YAML compatibility
98
+ data.to_s
99
+ when Regexp
100
+ # Convert regexp to string for JSON/YAML compatibility
101
+ data.to_s
102
+ when Time, Date, DateTime, ActiveSupport::TimeWithZone
103
+ # Convert time objects to ISO8601 strings (JSON-friendly)
104
+ data.iso8601
105
+ else
106
+ # Fallback: try to_json if available, otherwise use inspect
107
+ if data.respond_to?(:to_json)
108
+ JSON.parse(data.to_json)
109
+ elsif data.respond_to?(:attributes)
110
+ # Object with attributes (like ActiveRecord but not ActiveRecord::Base)
111
+ serialize_object_with_attributes(data)
112
+ else
113
+ { type: data.class.name, value: data.inspect }
114
+ end
115
+ end
116
+ end
117
+
118
+ def serialize_schema_result(result)
119
+ Serializers::SchemaSerializer.serialize(result, self)
120
+ end
121
+
122
+ def serialize_explain_result(result)
123
+ Serializers::ExplainSerializer.serialize(result, self)
124
+ end
125
+
126
+ def serialize_stats_result(result)
127
+ Serializers::StatsSerializer.serialize(result, self)
128
+ end
129
+
130
+ def serialize_diff_result(result)
131
+ Serializers::DiffSerializer.serialize(result, self)
132
+ end
133
+
134
+ def serialize_active_record(record)
135
+ Serializers::ActiveRecordSerializer.serialize(record, self)
136
+ end
137
+
138
+ def serialize_relation(relation)
139
+ Serializers::RelationSerializer.serialize(relation, self)
140
+ end
141
+
142
+ def serialize_array(array)
143
+ Serializers::ArraySerializer.serialize(array, self)
144
+ end
145
+
146
+ def serialize_hash(hash)
147
+ # Recursively serialize hash values (awesome_print-style deep conversion)
148
+ # Convert symbol keys to strings for YAML compatibility
149
+ hash.each_with_object({}) do |(key, value), result|
150
+ string_key = key.is_a?(Symbol) ? key.to_s : key
151
+ result[string_key] = serialize_data(value)
152
+ end
153
+ end
154
+
155
+ def serialize_object_with_attributes(obj)
156
+ # Handle objects with attributes method (similar to awesome_print's approach)
157
+ attrs = obj.attributes rescue {}
158
+ {
159
+ _type: obj.class.name,
160
+ **attrs.transform_values { |v| serialize_data(v) }
161
+ }
162
+ end
163
+
164
+
165
+ def generate_html(data, title:, style:)
166
+ html_title = escape_html(title)
167
+ html_content = generate_html_content(data)
168
+ css = generate_css(style)
169
+
170
+ <<~HTML
171
+ <!DOCTYPE html>
172
+ <html lang="en">
173
+ <head>
174
+ <meta charset="UTF-8">
175
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
176
+ <title>#{html_title}</title>
177
+ <style>#{css}</style>
178
+ </head>
179
+ <body>
180
+ <div class="container">
181
+ <h1 class="title">#{html_title}</h1>
182
+ <div class="content">
183
+ #{html_content}
184
+ </div>
185
+ <div class="footer">
186
+ <p>Generated by Enhanced Console Printer at #{Time.current.strftime('%Y-%m-%d %H:%M:%S')}</p>
187
+ </div>
188
+ </div>
189
+ </body>
190
+ </html>
191
+ HTML
192
+ end
193
+
194
+ def generate_html_content(data, depth: 0)
195
+ case data
196
+ when Hash
197
+ generate_hash_html(data, depth)
198
+ when Array
199
+ generate_array_html(data, depth)
200
+ when String, Numeric, TrueClass, FalseClass, NilClass
201
+ generate_value_html(data)
202
+ else
203
+ generate_value_html(data.inspect)
204
+ end
205
+ end
206
+
207
+ def generate_hash_html(hash, depth)
208
+ return '<span class="empty">Empty hash</span>' if hash.empty?
209
+
210
+ html = '<dl class="hash">'
211
+ hash.each do |key, value|
212
+ key_class = value.is_a?(Hash) || value.is_a?(Array) ? 'key-expandable' : 'key'
213
+ html += "<dt class=\"#{key_class}\">#{escape_html(key.to_s)}</dt>"
214
+ html += "<dd class=\"value\">#{generate_html_content(value, depth: depth + 1)}</dd>"
215
+ end
216
+ html + '</dl>'
217
+ end
218
+
219
+ def generate_array_html(array, depth)
220
+ return '<span class="empty">Empty array</span>' if array.empty?
221
+
222
+ html = '<ul class="array">'
223
+ array.each do |item|
224
+ html += "<li>#{generate_html_content(item, depth: depth + 1)}</li>"
225
+ end
226
+ html + '</ul>'
227
+ end
228
+
229
+ def generate_value_html(value)
230
+ case value
231
+ when NilClass
232
+ '<span class="nil">nil</span>'
233
+ when TrueClass
234
+ '<span class="boolean true">true</span>'
235
+ when FalseClass
236
+ '<span class="boolean false">false</span>'
237
+ when Numeric
238
+ "<span class=\"number\">#{escape_html(value.to_s)}</span>"
239
+ when String
240
+ "<span class=\"string\">#{escape_html(value)}</span>"
241
+ else
242
+ "<span class=\"other\">#{escape_html(value.inspect)}</span>"
243
+ end
244
+ end
245
+
246
+ def generate_css(style)
247
+ case style
248
+ when :default
249
+ default_css
250
+ when :minimal
251
+ minimal_css
252
+ else
253
+ default_css
254
+ end
255
+ end
256
+
257
+ def default_css
258
+ <<~CSS
259
+ * { margin: 0; padding: 0; box-sizing: border-box; }
260
+ body {
261
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
262
+ line-height: 1.6;
263
+ color: #333;
264
+ background: #f5f5f5;
265
+ padding: 20px;
266
+ }
267
+ .container {
268
+ max-width: 1200px;
269
+ margin: 0 auto;
270
+ background: white;
271
+ border-radius: 8px;
272
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
273
+ padding: 30px;
274
+ }
275
+ .title {
276
+ color: #2c3e50;
277
+ border-bottom: 3px solid #3498db;
278
+ padding-bottom: 10px;
279
+ margin-bottom: 20px;
280
+ }
281
+ .content {
282
+ margin-bottom: 30px;
283
+ }
284
+ dl.hash {
285
+ margin: 10px 0;
286
+ }
287
+ dt.key, dt.key-expandable {
288
+ font-weight: bold;
289
+ color: #2980b9;
290
+ margin-top: 10px;
291
+ margin-bottom: 5px;
292
+ }
293
+ dt.key-expandable {
294
+ cursor: pointer;
295
+ user-select: none;
296
+ }
297
+ dt.key-expandable:hover {
298
+ color: #1f5f8b;
299
+ }
300
+ dd.value {
301
+ margin-left: 20px;
302
+ margin-bottom: 10px;
303
+ }
304
+ ul.array {
305
+ margin-left: 20px;
306
+ list-style-type: disc;
307
+ }
308
+ ul.array li {
309
+ margin: 5px 0;
310
+ }
311
+ .nil { color: #95a5a6; font-style: italic; }
312
+ .boolean.true { color: #27ae60; font-weight: bold; }
313
+ .boolean.false { color: #e74c3c; font-weight: bold; }
314
+ .number { color: #3498db; font-weight: bold; }
315
+ .string { color: #2ecc71; }
316
+ .other { color: #7f8c8d; }
317
+ .empty { color: #95a5a6; font-style: italic; }
318
+ .footer {
319
+ margin-top: 30px;
320
+ padding-top: 20px;
321
+ border-top: 1px solid #ecf0f1;
322
+ text-align: center;
323
+ color: #95a5a6;
324
+ font-size: 0.9em;
325
+ }
326
+ @media print {
327
+ body { background: white; padding: 0; }
328
+ .container { box-shadow: none; }
329
+ }
330
+ CSS
331
+ end
332
+
333
+ def minimal_css
334
+ <<~CSS
335
+ body { font-family: monospace; padding: 20px; }
336
+ .container { max-width: 800px; margin: 0 auto; }
337
+ .title { border-bottom: 1px solid #ccc; padding-bottom: 10px; }
338
+ dt { font-weight: bold; margin-top: 10px; }
339
+ dd { margin-left: 20px; }
340
+ ul { margin-left: 20px; }
341
+ .nil { color: #999; }
342
+ .boolean.true { color: green; }
343
+ .boolean.false { color: red; }
344
+ .number { color: blue; }
345
+ .string { color: #333; }
346
+ CSS
347
+ end
348
+
349
+ def escape_html(text)
350
+ text.to_s
351
+ .gsub('&', '&amp;')
352
+ .gsub('<', '&lt;')
353
+ .gsub('>', '&gt;')
354
+ .gsub('"', '&quot;')
355
+ .gsub("'", '&#39;')
356
+ end
357
+
358
+ # Convert all symbols in data structure to strings for YAML.safe_load compatibility
359
+ def convert_symbols_to_strings(data)
360
+ case data
361
+ when Hash
362
+ data.each_with_object({}) do |(key, value), result|
363
+ string_key = key.is_a?(Symbol) ? key.to_s : key
364
+ result[string_key] = convert_symbols_to_strings(value)
365
+ end
366
+ when Array
367
+ data.map { |item| convert_symbols_to_strings(item) }
368
+ when Symbol
369
+ data.to_s
370
+ else
371
+ data
372
+ end
373
+ end
374
+
375
+ def infer_format_from_path(path)
376
+ ext = File.extname(path).downcase.delete('.')
377
+ return 'json' if ext.empty? # Default to JSON if no extension
378
+ ext
379
+ end
380
+
381
+ def infer_title(data)
382
+ case data
383
+ when SchemaInspectorResult
384
+ "Schema: #{data.model.name}"
385
+ when StatsResult
386
+ "Statistics: #{data.model.name}"
387
+ when DiffResult
388
+ "Diff Comparison: #{data.object1_type} vs #{data.object2_type}"
389
+ when ExplainResult
390
+ "SQL Explain Analysis"
391
+ when ActiveRecord::Base
392
+ "#{data.class.name} ##{data.id}"
393
+ when ActiveRecord::Relation
394
+ "#{data.klass.name} Collection (#{data.count} records)"
395
+ when Array
396
+ data.first.is_a?(ActiveRecord::Base) ? "#{data.first.class.name} Collection (#{data.size} records)" : "Array (#{data.size} items)"
397
+ else
398
+ "Export"
399
+ end
400
+ end
401
+ end
402
+ end
403
+
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Global helper methods available in console
4
+ def schema(model_class)
5
+ RailsConsolePro::Commands.schema(model_class)
6
+ end
7
+
8
+ def explain(relation_or_model, *args)
9
+ RailsConsolePro::Commands.explain(relation_or_model, *args)
10
+ end
11
+
12
+ def navigate(model_or_string)
13
+ pastel = RailsConsolePro::ColorHelper.pastel
14
+ if model_or_string.is_a?(String)
15
+ begin
16
+ model = model_or_string.constantize
17
+ rescue NameError
18
+ puts pastel.red("Error: Could not find model '#{model_or_string}'")
19
+ puts pastel.yellow("Make sure the model name is correct and loaded.")
20
+ return nil
21
+ end
22
+ else
23
+ model = model_or_string
24
+ end
25
+
26
+ unless RailsConsolePro::ModelValidator.valid_model?(model)
27
+ puts pastel.red("Error: #{model} is not an ActiveRecord model")
28
+ return nil
29
+ end
30
+
31
+ navigator = RailsConsolePro::AssociationNavigator.new(model)
32
+ navigator.start
33
+ end
34
+
35
+ def stats(model_class)
36
+ RailsConsolePro::Commands.stats(model_class)
37
+ end
38
+
39
+ def diff(object1, object2)
40
+ RailsConsolePro::Commands.diff(object1, object2)
41
+ end
42
+
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pastel'
4
+ require 'tty-color'
5
+
6
+ module RailsConsolePro
7
+ extend self
8
+
9
+ # Singleton Pastel instance
10
+ PASTEL = Pastel.new(enabled: TTY::Color.color?)
11
+
12
+ # Configuration instance
13
+ def config
14
+ @config ||= Configuration.new
15
+ end
16
+
17
+ # Configuration DSL
18
+ def configure
19
+ yield config if block_given?
20
+ config
21
+ end
22
+
23
+ # Autoload all components
24
+ autoload :Configuration, "rails_console_pro/configuration"
25
+ autoload :ColorHelper, "rails_console_pro/color_helper"
26
+ autoload :BasePrinter, "rails_console_pro/base_printer"
27
+ autoload :ModelValidator, "rails_console_pro/model_validator"
28
+ autoload :SchemaInspectorResult, "rails_console_pro/schema_inspector_result"
29
+ autoload :ExplainResult, "rails_console_pro/explain_result"
30
+ autoload :StatsResult, "rails_console_pro/stats_result"
31
+ autoload :DiffResult, "rails_console_pro/diff_result"
32
+ autoload :AssociationNavigator, "rails_console_pro/association_navigator"
33
+ autoload :Commands, "rails_console_pro/commands"
34
+ autoload :FormatExporter, "rails_console_pro/format_exporter"
35
+ autoload :ErrorHandler, "rails_console_pro/error_handler"
36
+ autoload :Paginator, "rails_console_pro/paginator"
37
+
38
+ module Printers
39
+ autoload :ActiveRecordPrinter, "rails_console_pro/printers/active_record_printer"
40
+ autoload :RelationPrinter, "rails_console_pro/printers/relation_printer"
41
+ autoload :CollectionPrinter, "rails_console_pro/printers/collection_printer"
42
+ autoload :SchemaPrinter, "rails_console_pro/printers/schema_printer"
43
+ autoload :ExplainPrinter, "rails_console_pro/printers/explain_printer"
44
+ autoload :StatsPrinter, "rails_console_pro/printers/stats_printer"
45
+ autoload :DiffPrinter, "rails_console_pro/printers/diff_printer"
46
+ end
47
+
48
+ # Main dispatcher - optimized with early returns
49
+ # Supports both Pry (with pry_instance) and IRB (without pry_instance)
50
+ def call(output, value, pry_instance = nil)
51
+ unless config.enabled
52
+ return default_print(output, value, pry_instance)
53
+ end
54
+
55
+ printer_class = printer_for(value)
56
+ printer_class.new(output, value, pry_instance).print
57
+ rescue => e
58
+ # Show error in development to help debug
59
+ if Rails.env.development? || ENV['RAILS_CONSOLE_PRO_DEBUG']
60
+ pastel = ColorHelper.pastel
61
+ output.puts pastel.red.bold("💥 RailsConsolePro Error: #{e.class}: #{e.message}")
62
+ output.puts pastel.dim(e.backtrace.first(5).join("\n"))
63
+ end
64
+ handle_error(output, e, value, pry_instance)
65
+ end
66
+
67
+ private
68
+
69
+ # Optimized printer selection with class hierarchy checks
70
+ PRINTER_MAP = {
71
+ ActiveRecord::Base => Printers::ActiveRecordPrinter,
72
+ ActiveRecord::Relation => Printers::RelationPrinter,
73
+ Array => Printers::CollectionPrinter
74
+ }.freeze
75
+
76
+ def printer_for(value)
77
+ # Check inheritance hierarchy (covers exact matches too)
78
+ PRINTER_MAP.each do |klass, printer|
79
+ if value.is_a?(klass) && printer_enabled?(printer)
80
+ return printer
81
+ end
82
+ end
83
+
84
+ # Check for result objects
85
+ return Printers::SchemaPrinter if value.is_a?(SchemaInspectorResult)
86
+ return Printers::ExplainPrinter if value.is_a?(ExplainResult)
87
+ return Printers::StatsPrinter if value.is_a?(StatsResult)
88
+ return Printers::DiffPrinter if value.is_a?(DiffResult)
89
+
90
+ # Fallback to base printer
91
+ BasePrinter
92
+ end
93
+
94
+ def printer_enabled?(printer_class)
95
+ case printer_class
96
+ when Printers::ActiveRecordPrinter
97
+ config.active_record_printer_enabled
98
+ when Printers::RelationPrinter
99
+ config.relation_printer_enabled
100
+ when Printers::CollectionPrinter
101
+ config.collection_printer_enabled
102
+ else
103
+ true
104
+ end
105
+ end
106
+
107
+ def handle_error(output, error, value, pry_instance)
108
+ pastel = ColorHelper.pastel
109
+ output.puts pastel.red.bold("💥 #{error.class}: #{error.message}")
110
+ output.puts pastel.dim(error.backtrace.first(3).join("\n"))
111
+ default_print(output, value, pry_instance)
112
+ end
113
+
114
+ # Default print method that works for both Pry and IRB
115
+ def default_print(output, value, pry_instance)
116
+ if defined?(Pry) && pry_instance
117
+ Pry::ColorPrinter.default(output, value, pry_instance)
118
+ else
119
+ # IRB fallback - use standard inspect
120
+ output.puts value.inspect
121
+ end
122
+ end
123
+ end
124
+
125
+ # Load Pry integration and commands
126
+ require_relative 'pry_integration'
127
+ require_relative 'pry_commands'
128
+
129
+ # Load global helper methods
130
+ require_relative 'global_methods'
131
+
132
+ # Load ErrorHandler (needed by Commands)
133
+ require_relative 'error_handler'
134
+
135
+ # Load service objects (needed by StatsCommand)
136
+ require_relative 'services/stats_calculator'
137
+ require_relative 'services/table_size_calculator'
138
+ require_relative 'services/index_analyzer'
139
+ require_relative 'services/column_stats_calculator'
140
+
141
+ # Load command classes (needed by Commands module)
142
+ require_relative 'commands/base_command'
143
+ require_relative 'commands/schema_command'
144
+ require_relative 'commands/explain_command'
145
+ require_relative 'commands/stats_command'
146
+ require_relative 'commands/diff_command'
147
+ require_relative 'commands/export_command'
148
+
149
+ # Load Commands module (uses command classes)
150
+ require_relative 'commands'
151
+
152
+ # Load serializers (needed by FormatExporter)
153
+ require_relative 'serializers/base_serializer'
154
+ require_relative 'serializers/schema_serializer'
155
+ require_relative 'serializers/stats_serializer'
156
+ require_relative 'serializers/explain_serializer'
157
+ require_relative 'serializers/diff_serializer'
158
+ require_relative 'serializers/active_record_serializer'
159
+ require_relative 'serializers/relation_serializer'
160
+ require_relative 'serializers/array_serializer'
161
+
162
+ # Load FormatExporter (uses serializers)
163
+ require_relative 'format_exporter'
164
+
165
+ # Load ActiveRecord extensions (only if ActiveRecord is available)
166
+ if defined?(ActiveRecord::Base)
167
+ require_relative 'active_record_extensions'
168
+ end
169
+
170
+ # Print welcome message if enabled (only for Pry)
171
+ if RailsConsolePro.config.show_welcome_message && defined?(Pry)
172
+ pastel = ColorHelper.pastel
173
+ puts pastel.bright_green("🚀 Rails Console Pro Loaded!")
174
+ puts pastel.cyan("📊 Use `schema ModelName`, `explain Query`, `stats ModelName`, `diff obj1, obj2`, or `navigate ModelName`")
175
+ puts pastel.dim("💾 Export support: Use `.to_json`, `.to_yaml`, `.to_html`, or `.export_to_file` on any result")
176
+ end