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,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Pry command definitions
4
+ if defined?(Pry)
5
+ Pry::Commands.create_command "schema" do
6
+ description "Inspect database schema for a model"
7
+
8
+ def process
9
+ pastel = RailsConsolePro::ColorHelper.pastel
10
+ unless RailsConsolePro.config.schema_command_enabled
11
+ output.puts pastel.yellow("Schema command is disabled. Enable it with: RailsConsolePro.configure { |c| c.schema_command_enabled = true }")
12
+ return
13
+ end
14
+
15
+ model_name = args.first
16
+ if model_name.nil?
17
+ output.puts pastel.red("Usage: schema ModelName")
18
+ output.puts pastel.yellow("Example: schema User")
19
+ return
20
+ end
21
+
22
+ begin
23
+ model_class = model_name.constantize
24
+ result = RailsConsolePro::Commands.schema(model_class)
25
+ RailsConsolePro.call(output, result, pry_instance) if result
26
+ rescue NameError => e
27
+ output.puts pastel.red("Error: Could not find model '#{model_name}'")
28
+ output.puts pastel.yellow("Make sure the model name is correct and loaded.")
29
+ rescue => e
30
+ output.puts pastel.red("Error: #{e.message}")
31
+ end
32
+ end
33
+ end
34
+
35
+ Pry::Commands.create_command "explain" do
36
+ description "Analyze SQL query execution plan"
37
+
38
+ def process
39
+ pastel = RailsConsolePro::ColorHelper.pastel
40
+ unless RailsConsolePro.config.explain_command_enabled
41
+ output.puts pastel.yellow("Explain command is disabled. Enable it with: RailsConsolePro.configure { |c| c.explain_command_enabled = true }")
42
+ return
43
+ end
44
+
45
+ if args.empty?
46
+ output.puts pastel.red("Usage: explain Model.where(...) or explain(Model, conditions)")
47
+ return
48
+ end
49
+
50
+ begin
51
+ relation = eval(args.join(' '), target)
52
+ result = RailsConsolePro::Commands.explain(relation)
53
+ output.puts result if result
54
+ rescue => e
55
+ output.puts pastel.red("Error: #{e.message}")
56
+ output.puts pastel.yellow("💡 Use: explain(Model.where(...)) or explain(Model, key: value)")
57
+ end
58
+ end
59
+ end
60
+
61
+ Pry::Commands.create_command "navigate" do
62
+ description "Navigate through model associations interactively"
63
+
64
+ def process
65
+ pastel = RailsConsolePro::ColorHelper.pastel
66
+ unless RailsConsolePro.config.navigate_command_enabled
67
+ output.puts pastel.yellow("Navigate command is disabled. Enable it with: RailsConsolePro.configure { |c| c.navigate_command_enabled = true }")
68
+ return
69
+ end
70
+
71
+ model_name = args.first
72
+ if model_name.nil?
73
+ output.puts pastel.red("Usage: navigate ModelName")
74
+ output.puts pastel.yellow("Example: navigate User")
75
+ return
76
+ end
77
+
78
+ begin
79
+ model = model_name.constantize
80
+ navigator = RailsConsolePro::AssociationNavigator.new(model)
81
+ navigator.start
82
+ rescue ArgumentError => e
83
+ output.puts pastel.red("Error: #{e.message}")
84
+ end
85
+ end
86
+ end
87
+
88
+ Pry::Commands.create_command "export" do
89
+ description "Export data to JSON, YAML, or HTML file"
90
+
91
+ def process
92
+ pastel = RailsConsolePro::ColorHelper.pastel
93
+ unless RailsConsolePro.config.export_enabled
94
+ output.puts pastel.yellow("Export command is disabled. Enable it with: RailsConsolePro.configure { |c| c.export_enabled = true }")
95
+ return
96
+ end
97
+
98
+ if args.empty?
99
+ show_usage
100
+ return
101
+ end
102
+
103
+ begin
104
+ if args.size < 2
105
+ show_usage
106
+ return
107
+ end
108
+
109
+ # Simple parsing: export <data> <file_path> [format]
110
+ file_path = args[-1].gsub(/^['"]|['"]$/, '')
111
+ data_expr = args[0..-2].join(' ')
112
+
113
+ format_keywords = %w[json yaml yml html htm]
114
+ if args.size >= 3 && format_keywords.include?(args[-2].downcase)
115
+ format = args[-2].downcase
116
+ data_expr = args[0..-3].join(' ')
117
+ else
118
+ format = nil
119
+ end
120
+
121
+ data = eval(data_expr, target)
122
+ result = RailsConsolePro::Commands.export(data, file_path, format: format)
123
+
124
+ if result
125
+ output.puts pastel.green("✅ Exported to: #{File.expand_path(result)}")
126
+ else
127
+ output.puts pastel.red("❌ Export failed")
128
+ end
129
+ rescue SyntaxError => e
130
+ output.puts pastel.red("Syntax Error: #{e.message}")
131
+ show_usage
132
+ rescue => e
133
+ output.puts pastel.red("Error: #{e.message}")
134
+ output.puts pastel.yellow("💡 Tip: Make sure the data is exportable (schema result, explain result, ActiveRecord object, etc.)")
135
+ show_usage
136
+ end
137
+ end
138
+
139
+ private
140
+
141
+ def show_usage
142
+ pastel = RailsConsolePro::ColorHelper.pastel
143
+ output.puts pastel.red("Usage: export <data> <file_path> [format]")
144
+ output.puts pastel.yellow("")
145
+ output.puts pastel.yellow("Examples:")
146
+ output.puts pastel.cyan(" export schema(User) user_schema.json")
147
+ output.puts pastel.cyan(" export User.first user.html html")
148
+ output.puts pastel.cyan(" export explain(User.where(active: true)) query.json")
149
+ output.puts pastel.cyan(" export User.all users.json")
150
+ output.puts pastel.yellow("")
151
+ output.puts pastel.yellow("Formats: json, yaml, html (auto-detected from file extension if not specified)")
152
+ output.puts pastel.yellow("")
153
+ output.puts pastel.dim("💡 Tip: You can also use methods directly:")
154
+ output.puts pastel.dim(" schema(User).export_to_file('user.json')")
155
+ output.puts pastel.dim(" User.first.export_to_file('user.html', format: 'html')")
156
+ end
157
+ end
158
+
159
+ Pry::Commands.create_command "stats" do
160
+ description "Show model statistics (record count, growth rate, table size, index usage)"
161
+
162
+ def process
163
+ pastel = RailsConsolePro::ColorHelper.pastel
164
+ unless RailsConsolePro.config.stats_command_enabled
165
+ output.puts pastel.yellow("Stats command is disabled. Enable it with: RailsConsolePro.configure { |c| c.stats_command_enabled = true }")
166
+ return
167
+ end
168
+
169
+ model_name = args.first
170
+ if model_name.nil?
171
+ output.puts pastel.red("Usage: stats ModelName")
172
+ output.puts pastel.yellow("Example: stats User")
173
+ return
174
+ end
175
+
176
+ begin
177
+ model_class = model_name.constantize
178
+ result = RailsConsolePro::Commands.stats(model_class)
179
+ RailsConsolePro.call(output, result, pry_instance) if result
180
+ rescue NameError => e
181
+ output.puts pastel.red("Error: Could not find model '#{model_name}'")
182
+ output.puts pastel.yellow("Make sure the model name is correct and loaded.")
183
+ rescue => e
184
+ output.puts pastel.red("Error: #{e.message}")
185
+ end
186
+ end
187
+ end
188
+
189
+ Pry::Commands.create_command "diff" do
190
+ description "Compare two objects and highlight differences"
191
+
192
+ def process
193
+ pastel = RailsConsolePro::ColorHelper.pastel
194
+ unless RailsConsolePro.config.diff_command_enabled
195
+ output.puts pastel.yellow("Diff command is disabled. Enable it with: RailsConsolePro.configure { |c| c.diff_command_enabled = true }")
196
+ return
197
+ end
198
+
199
+ if args.empty?
200
+ output.puts pastel.red("Usage: diff object1, object2")
201
+ output.puts pastel.yellow("Examples:")
202
+ output.puts pastel.cyan(" diff User.first, User.last")
203
+ output.puts pastel.cyan(" diff user1, user2")
204
+ output.puts pastel.cyan(" diff {a: 1}, {a: 2}")
205
+ return
206
+ end
207
+
208
+ begin
209
+ all_args = args.join(' ')
210
+ parts = all_args.split(',').map(&:strip)
211
+
212
+ if parts.size < 2
213
+ output.puts pastel.red("Error: Need two objects to compare (separated by comma)")
214
+ output.puts pastel.yellow("Usage: diff object1, object2")
215
+ return
216
+ end
217
+
218
+ object1 = eval(parts[0], target)
219
+ object2 = eval(parts[1], target)
220
+
221
+ result = RailsConsolePro::Commands.diff(object1, object2)
222
+ RailsConsolePro.call(output, result, pry_instance) if result
223
+ rescue SyntaxError => e
224
+ output.puts pastel.red("Syntax Error: #{e.message}")
225
+ output.puts pastel.yellow("💡 Make sure to separate objects with a comma: diff object1, object2")
226
+ rescue => e
227
+ output.puts pastel.red("Error: #{e.message}")
228
+ output.puts pastel.yellow("💡 Use: diff object1, object2")
229
+ end
230
+ end
231
+ end
232
+
233
+ # Add aliases
234
+ begin
235
+ Pry.commands.alias_command 'nav', 'navigate'
236
+ Pry.commands.alias_command 'n', 'navigate'
237
+ rescue => e
238
+ # Silently fail if aliases can't be registered
239
+ end
240
+ end
241
+
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Pry integration - sets up Pry hooks
4
+ if defined?(Pry)
5
+ Pry.config.print = proc do |output, value, pry_instance|
6
+ RailsConsolePro.call(output, value, pry_instance)
7
+ end
8
+ end
9
+
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ # Rails integration via Railtie
5
+ class Railtie < Rails::Railtie
6
+ # Auto-load Rails Console Pro when Rails starts
7
+ # The initializer.rb file handles the actual setup
8
+ config.after_initialize do
9
+ # Ensure Pry hook is set (in case Pry loads after the gem)
10
+ if defined?(Pry)
11
+ # Wrap in a proc that handles errors gracefully
12
+ Pry.config.print = proc do |output, value, pry_instance|
13
+ begin
14
+ RailsConsolePro.call(output, value, pry_instance)
15
+ rescue => e
16
+ # Fallback to default Pry printing if our printer fails
17
+ if Rails.env.development? || ENV['RAILS_CONSOLE_PRO_DEBUG']
18
+ pastel = RailsConsolePro::ColorHelper.pastel
19
+ output.puts pastel.red.bold("💥 RailsConsolePro Error in Pry hook: #{e.class}: #{e.message}")
20
+ output.puts pastel.dim(e.backtrace.first(3).join("\n"))
21
+ end
22
+ Pry::ColorPrinter.default(output, value, pry_instance)
23
+ end
24
+ end
25
+ Rails.logger&.debug("Rails Console Pro: Pry integration active")
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ # Value object for schema inspection results
5
+ class SchemaInspectorResult
6
+ attr_reader :model
7
+
8
+ def initialize(model)
9
+ @model = model
10
+ validate_model!
11
+ end
12
+
13
+ def ==(other)
14
+ other.is_a?(self.class) && other.model == model
15
+ end
16
+
17
+ # Export to JSON
18
+ def to_json(pretty: true)
19
+ FormatExporter.to_json(self, pretty: pretty)
20
+ end
21
+
22
+ # Export to YAML
23
+ def to_yaml
24
+ FormatExporter.to_yaml(self)
25
+ end
26
+
27
+ # Export to HTML
28
+ def to_html(style: :default)
29
+ FormatExporter.to_html(self, title: "Schema: #{model.name}", style: style)
30
+ end
31
+
32
+ # Export to file
33
+ def export_to_file(file_path, format: nil)
34
+ FormatExporter.export_to_file(self, file_path, format: format)
35
+ end
36
+
37
+ private
38
+
39
+ def validate_model!
40
+ ModelValidator.validate_model_for_schema!(model)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ module Serializers
5
+ class ActiveRecordSerializer < BaseSerializer
6
+ def serialize(record)
7
+ {
8
+ 'type' => 'active_record',
9
+ 'class' => record.class.name,
10
+ 'id' => record.id,
11
+ 'attributes' => record.attributes,
12
+ 'errors' => record.errors.any? ? record.errors.full_messages : nil
13
+ }.compact
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ module Serializers
5
+ class ArraySerializer < BaseSerializer
6
+ def serialize(array)
7
+ if array.all? { |item| item.is_a?(ActiveRecord::Base) }
8
+ {
9
+ 'type' => 'active_record_collection',
10
+ 'model' => array.first.class.name,
11
+ 'count' => array.size,
12
+ 'records' => array.map { |r| serialize_active_record(r) }
13
+ }
14
+ else
15
+ {
16
+ 'type' => 'array',
17
+ 'count' => array.size,
18
+ 'items' => array.map { |item| serialize_data(item) }
19
+ }
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def serialize_active_record(record)
26
+ ActiveRecordSerializer.serialize(record, exporter)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ module Serializers
5
+ # Base serializer class
6
+ class BaseSerializer
7
+ def self.serialize(data, exporter)
8
+ new(exporter).serialize(data)
9
+ end
10
+
11
+ def initialize(exporter)
12
+ @exporter = exporter
13
+ end
14
+
15
+ protected
16
+
17
+ attr_reader :exporter
18
+
19
+ def serialize_data(data)
20
+ exporter.send(:serialize_data, data)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ module Serializers
5
+ class DiffSerializer < BaseSerializer
6
+ def serialize(result)
7
+ {
8
+ 'type' => 'diff_comparison',
9
+ 'object1_type' => result.object1_type,
10
+ 'object2_type' => result.object2_type,
11
+ 'identical' => result.identical,
12
+ 'different_types' => result.different_types?,
13
+ 'diff_count' => result.diff_count,
14
+ 'has_differences' => result.has_differences?,
15
+ 'differences' => serialize_data(result.differences),
16
+ 'object1' => serialize_data(result.object1),
17
+ 'object2' => serialize_data(result.object2),
18
+ 'timestamp' => result.timestamp.iso8601
19
+ }
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ module Serializers
5
+ class ExplainSerializer < BaseSerializer
6
+ def serialize(result)
7
+ {
8
+ 'type' => 'sql_explain',
9
+ 'sql' => result.sql,
10
+ 'execution_time' => result.execution_time,
11
+ 'explain_output' => format_explain_output(result.explain_output),
12
+ 'indexes_used' => result.indexes_used,
13
+ 'recommendations' => result.recommendations,
14
+ 'statistics' => result.statistics,
15
+ 'slow_query' => result.slow_query?,
16
+ 'has_indexes' => result.has_indexes?
17
+ }
18
+ end
19
+
20
+ private
21
+
22
+ def format_explain_output(explain_output)
23
+ case explain_output
24
+ when String
25
+ explain_output
26
+ when Array
27
+ explain_output.map { |row| row.is_a?(Hash) ? row : row.to_s }
28
+ else
29
+ explain_output.inspect
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ module Serializers
5
+ class RelationSerializer < BaseSerializer
6
+ def serialize(relation)
7
+ records = relation.to_a
8
+ {
9
+ 'type' => 'active_record_relation',
10
+ 'model' => relation.klass.name,
11
+ 'count' => records.size,
12
+ 'records' => records.map { |r| serialize_active_record(r) },
13
+ 'sql' => relation.to_sql
14
+ }
15
+ end
16
+
17
+ private
18
+
19
+ def serialize_active_record(record)
20
+ ActiveRecordSerializer.serialize(record, exporter)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ module Serializers
5
+ class SchemaSerializer < BaseSerializer
6
+ def serialize(result)
7
+ model = result.model
8
+ {
9
+ 'type' => 'schema_inspection',
10
+ 'model' => model.name,
11
+ 'table_name' => model.table_name,
12
+ 'columns' => serialize_columns(model),
13
+ 'indexes' => serialize_indexes(model),
14
+ 'associations' => serialize_associations(model),
15
+ 'validations' => serialize_validations(model),
16
+ 'scopes' => serialize_scopes(model),
17
+ 'database' => serialize_database_info(model)
18
+ }
19
+ end
20
+
21
+ private
22
+
23
+ def serialize_columns(model)
24
+ model.columns.map do |column|
25
+ {
26
+ 'name' => column.name.to_s,
27
+ 'type' => column.type.to_s,
28
+ 'null' => column.null,
29
+ 'default' => column.default,
30
+ 'limit' => column.limit,
31
+ 'precision' => column.precision,
32
+ 'scale' => column.scale
33
+ }.compact
34
+ end
35
+ end
36
+
37
+ def serialize_indexes(model)
38
+ model.connection.indexes(model.table_name).map do |index|
39
+ where_clause = if index.where.is_a?(Regexp)
40
+ index.where.to_s
41
+ else
42
+ index.where
43
+ end
44
+ {
45
+ 'name' => index.name.to_s,
46
+ 'columns' => index.columns.map(&:to_s),
47
+ 'unique' => index.unique,
48
+ 'where' => where_clause
49
+ }.compact
50
+ end
51
+ end
52
+
53
+ def serialize_associations(model)
54
+ associations = {}
55
+ %i[belongs_to has_one has_many has_and_belongs_to_many].each do |macro|
56
+ assocs = model.reflect_on_all_associations(macro)
57
+ next if assocs.empty?
58
+
59
+ associations[macro.to_s] = assocs.map do |assoc|
60
+ {
61
+ 'name' => assoc.name.to_s,
62
+ 'class_name' => assoc.class_name,
63
+ 'foreign_key' => assoc.respond_to?(:foreign_key) ? assoc.foreign_key : nil,
64
+ 'dependent' => assoc.options[:dependent]&.to_s,
65
+ 'optional' => assoc.options[:optional],
66
+ 'through' => assoc.options[:through]&.to_s,
67
+ 'join_table' => assoc.respond_to?(:join_table) ? assoc.join_table : nil
68
+ }.compact
69
+ end
70
+ end
71
+ associations
72
+ end
73
+
74
+ def serialize_validations(model)
75
+ validators_by_attr = model.validators.each_with_object({}) do |validator, hash|
76
+ validator.attributes.each do |attr|
77
+ attr_key = attr.is_a?(Symbol) ? attr.to_s : attr
78
+ (hash[attr_key] ||= []) << {
79
+ type: validator.class.name.split('::').last.gsub('Validator', ''),
80
+ options: serialize_options(validator.options)
81
+ }
82
+ end
83
+ end
84
+ validators_by_attr
85
+ end
86
+
87
+ def serialize_options(options)
88
+ return nil if options.nil?
89
+ return options if options.empty?
90
+
91
+ options.each_with_object({}) do |(key, value), result|
92
+ string_key = key.is_a?(Symbol) ? key.to_s : key
93
+ result[string_key] = case value
94
+ when Symbol
95
+ value.to_s
96
+ when Regexp
97
+ value.to_s
98
+ when Array
99
+ value.map { |v| v.is_a?(Symbol) || v.is_a?(Regexp) ? v.to_s : serialize_data(v) }
100
+ else
101
+ serialize_data(value)
102
+ end
103
+ end
104
+ end
105
+
106
+ def serialize_scopes(model)
107
+ return [] unless model.respond_to?(:scopes) && model.scopes.any?
108
+ model.scopes.keys.map(&:to_s)
109
+ end
110
+
111
+ def serialize_database_info(model)
112
+ connection = model.connection
113
+ {
114
+ adapter: connection.adapter_name,
115
+ database: connection.respond_to?(:current_database) ? connection.current_database : nil
116
+ }.compact
117
+ end
118
+ end
119
+ end
120
+ end
121
+
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ module Serializers
5
+ class StatsSerializer < BaseSerializer
6
+ def serialize(result)
7
+ {
8
+ 'type' => 'statistics',
9
+ 'model' => result.model.name,
10
+ 'record_count' => result.record_count,
11
+ 'growth_rate' => result.growth_rate,
12
+ 'table_size' => result.table_size,
13
+ 'index_usage' => serialize_data(result.index_usage),
14
+ 'column_stats' => serialize_data(result.column_stats),
15
+ 'timestamp' => result.timestamp.iso8601,
16
+ 'has_growth_data' => result.has_growth_data?,
17
+ 'has_table_size' => result.has_table_size?,
18
+ 'has_index_data' => result.has_index_data?
19
+ }
20
+ end
21
+ end
22
+ end
23
+ end
24
+