rails_console_pro 0.1.2 → 0.1.4

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec_status +288 -240
  3. data/CHANGELOG.md +7 -0
  4. data/QUICK_START.md +17 -0
  5. data/README.md +43 -0
  6. data/docs/FORMATTING.md +5 -0
  7. data/docs/MODEL_INTROSPECTION.md +371 -0
  8. data/docs/MODEL_STATISTICS.md +4 -0
  9. data/docs/OBJECT_DIFFING.md +6 -0
  10. data/docs/PROFILING.md +91 -0
  11. data/docs/QUERY_BUILDER.md +385 -0
  12. data/docs/QUEUE_INSIGHTS.md +82 -0
  13. data/docs/SCHEMA_INSPECTION.md +5 -0
  14. data/docs/SNIPPETS.md +71 -0
  15. data/lib/rails_console_pro/commands/compare_command.rb +151 -0
  16. data/lib/rails_console_pro/commands/introspect_command.rb +220 -0
  17. data/lib/rails_console_pro/commands/jobs_command.rb +212 -0
  18. data/lib/rails_console_pro/commands/profile_command.rb +84 -0
  19. data/lib/rails_console_pro/commands/query_builder_command.rb +43 -0
  20. data/lib/rails_console_pro/commands/snippets_command.rb +141 -0
  21. data/lib/rails_console_pro/commands.rb +30 -0
  22. data/lib/rails_console_pro/compare_result.rb +81 -0
  23. data/lib/rails_console_pro/configuration.rb +51 -0
  24. data/lib/rails_console_pro/format_exporter.rb +32 -0
  25. data/lib/rails_console_pro/global_methods.rb +24 -0
  26. data/lib/rails_console_pro/initializer.rb +41 -1
  27. data/lib/rails_console_pro/introspect_result.rb +101 -0
  28. data/lib/rails_console_pro/model_validator.rb +1 -1
  29. data/lib/rails_console_pro/printers/compare_printer.rb +138 -0
  30. data/lib/rails_console_pro/printers/introspect_printer.rb +282 -0
  31. data/lib/rails_console_pro/printers/profile_printer.rb +180 -0
  32. data/lib/rails_console_pro/printers/query_builder_printer.rb +81 -0
  33. data/lib/rails_console_pro/printers/queue_insights_printer.rb +150 -0
  34. data/lib/rails_console_pro/printers/snippet_collection_printer.rb +68 -0
  35. data/lib/rails_console_pro/printers/snippet_printer.rb +64 -0
  36. data/lib/rails_console_pro/profile_result.rb +109 -0
  37. data/lib/rails_console_pro/pry_commands.rb +106 -0
  38. data/lib/rails_console_pro/query_builder.rb +197 -0
  39. data/lib/rails_console_pro/query_builder_result.rb +66 -0
  40. data/lib/rails_console_pro/queue_insights_result.rb +110 -0
  41. data/lib/rails_console_pro/serializers/compare_serializer.rb +66 -0
  42. data/lib/rails_console_pro/serializers/introspect_serializer.rb +99 -0
  43. data/lib/rails_console_pro/serializers/profile_serializer.rb +73 -0
  44. data/lib/rails_console_pro/serializers/query_builder_serializer.rb +35 -0
  45. data/lib/rails_console_pro/services/introspection_collector.rb +420 -0
  46. data/lib/rails_console_pro/services/profile_collector.rb +245 -0
  47. data/lib/rails_console_pro/services/queue_action_service.rb +176 -0
  48. data/lib/rails_console_pro/services/queue_insight_fetcher.rb +600 -0
  49. data/lib/rails_console_pro/services/snippet_repository.rb +191 -0
  50. data/lib/rails_console_pro/snippets/collection_result.rb +45 -0
  51. data/lib/rails_console_pro/snippets/single_result.rb +30 -0
  52. data/lib/rails_console_pro/snippets/snippet.rb +112 -0
  53. data/lib/rails_console_pro/snippets.rb +13 -0
  54. data/lib/rails_console_pro/version.rb +1 -1
  55. data/rails_console_pro.gemspec +1 -1
  56. metadata +42 -8
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ class QueueInsightsResult
5
+ JobSummary = Struct.new(
6
+ :id,
7
+ :job_class,
8
+ :queue,
9
+ :args,
10
+ :enqueued_at,
11
+ :scheduled_at,
12
+ :attempts,
13
+ :error,
14
+ :metadata,
15
+ keyword_init: true
16
+ )
17
+
18
+ ExecutionSummary = Struct.new(
19
+ :id,
20
+ :job_class,
21
+ :queue,
22
+ :started_at,
23
+ :runtime_ms,
24
+ :worker,
25
+ :hostname,
26
+ :metadata,
27
+ keyword_init: true
28
+ )
29
+
30
+ attr_reader :adapter_name,
31
+ :adapter_type,
32
+ :enqueued_jobs,
33
+ :retry_jobs,
34
+ :recent_executions,
35
+ :meta,
36
+ :warnings,
37
+ :captured_at
38
+
39
+ def initialize(adapter_name:, adapter_type:, enqueued_jobs:, retry_jobs:, recent_executions:, meta: {}, warnings: [], captured_at: Time.current)
40
+ @adapter_name = adapter_name
41
+ @adapter_type = adapter_type
42
+ @enqueued_jobs = Array(enqueued_jobs)
43
+ @retry_jobs = Array(retry_jobs)
44
+ @recent_executions = Array(recent_executions)
45
+ @meta = meta || {}
46
+ @warnings = Array(warnings).compact
47
+ @captured_at = captured_at || Time.current
48
+ end
49
+
50
+ def adapter_label
51
+ [adapter_name, adapter_type].compact.uniq.join(" ")
52
+ end
53
+
54
+ def has_enqueued?
55
+ enqueued_jobs.any?
56
+ end
57
+
58
+ def has_retry_jobs?
59
+ retry_jobs.any?
60
+ end
61
+
62
+ def has_recent_executions?
63
+ recent_executions.any?
64
+ end
65
+
66
+ def empty?
67
+ !has_enqueued? && !has_retry_jobs? && !has_recent_executions?
68
+ end
69
+
70
+ def warnings?
71
+ warnings.any?
72
+ end
73
+
74
+ def total_enqueued
75
+ enqueued_jobs.size
76
+ end
77
+
78
+ def total_retry
79
+ retry_jobs.size
80
+ end
81
+
82
+ def total_recent
83
+ recent_executions.size
84
+ end
85
+
86
+ def totals
87
+ {
88
+ enqueued: total_enqueued,
89
+ retry: total_retry,
90
+ recent: total_recent
91
+ }
92
+ end
93
+
94
+ def with_overrides(overrides = {})
95
+ self.class.new(
96
+ adapter_name: overrides.fetch(:adapter_name, adapter_name),
97
+ adapter_type: overrides.fetch(:adapter_type, adapter_type),
98
+ enqueued_jobs: overrides.fetch(:enqueued_jobs, enqueued_jobs),
99
+ retry_jobs: overrides.fetch(:retry_jobs, retry_jobs),
100
+ recent_executions: overrides.fetch(:recent_executions, recent_executions),
101
+ meta: overrides.fetch(:meta, meta),
102
+ warnings: overrides.fetch(:warnings, warnings),
103
+ captured_at: overrides.fetch(:captured_at, captured_at)
104
+ )
105
+ end
106
+ end
107
+ end
108
+
109
+
110
+
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ module Serializers
5
+ # Serializer for CompareResult objects
6
+ class CompareSerializer < BaseSerializer
7
+ def serialize(compare_result)
8
+ result = {
9
+ timestamp: compare_result.timestamp&.iso8601,
10
+ total_strategies: compare_result.comparisons.size,
11
+ fastest: compare_result.fastest_name,
12
+ slowest: compare_result.slowest_name,
13
+ performance_ratio: compare_result.performance_ratio,
14
+ has_errors: compare_result.has_errors?,
15
+ error_count: compare_result.error_count,
16
+ total_queries: compare_result.total_queries,
17
+ comparisons: serialize_comparisons(compare_result.comparisons)
18
+ }
19
+ result[:winner] = serialize_comparison(compare_result.winner) if compare_result.winner
20
+ result
21
+ end
22
+
23
+ private
24
+
25
+ def serialize_comparisons(comparisons)
26
+ Array(comparisons).map { |c| serialize_comparison(c) }
27
+ end
28
+
29
+ def serialize_comparison(comparison)
30
+ return nil unless comparison
31
+
32
+ {
33
+ name: comparison.name,
34
+ duration_ms: comparison.duration_ms,
35
+ query_count: comparison.query_count,
36
+ memory_usage_kb: comparison.memory_usage_kb,
37
+ error: serialize_error(comparison.error),
38
+ sql_queries: serialize_sql_queries(comparison.sql_queries),
39
+ result: serialize_data(comparison.result)
40
+ }
41
+ end
42
+
43
+ def serialize_sql_queries(queries)
44
+ Array(queries).map do |query|
45
+ {
46
+ sql: query[:sql],
47
+ duration_ms: query[:duration_ms],
48
+ name: query[:name],
49
+ cached: query[:cached] || false
50
+ }
51
+ end
52
+ end
53
+
54
+ def serialize_error(error)
55
+ return nil unless error
56
+
57
+ {
58
+ class: error.class.name,
59
+ message: error.message,
60
+ backtrace: Array(error.backtrace).first(10)
61
+ }
62
+ end
63
+ end
64
+ end
65
+ end
66
+
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ module Serializers
5
+ # Serializer for introspection results
6
+ class IntrospectSerializer < BaseSerializer
7
+ def serialize(result)
8
+ {
9
+ 'type' => 'introspection',
10
+ 'model' => result.model.name,
11
+ 'callbacks' => serialize_callbacks(result.callbacks),
12
+ 'enums' => serialize_enums(result.enums),
13
+ 'concerns' => serialize_concerns(result.concerns),
14
+ 'scopes' => serialize_scopes(result.scopes),
15
+ 'validations' => serialize_validations(result.validations),
16
+ 'lifecycle_hooks' => serialize_data(result.lifecycle_hooks),
17
+ 'timestamp' => result.timestamp.iso8601,
18
+ 'has_callbacks' => result.has_callbacks?,
19
+ 'has_enums' => result.has_enums?,
20
+ 'has_concerns' => result.has_concerns?,
21
+ 'has_scopes' => result.has_scopes?,
22
+ 'has_validations' => result.has_validations?
23
+ }
24
+ end
25
+
26
+ private
27
+
28
+ def serialize_callbacks(callbacks)
29
+ callbacks.transform_values do |chain|
30
+ chain.map do |callback|
31
+ {
32
+ 'name' => callback[:name].to_s,
33
+ 'kind' => callback[:kind].to_s,
34
+ 'if' => serialize_condition(callback[:if]),
35
+ 'unless' => serialize_condition(callback[:unless])
36
+ }.compact
37
+ end
38
+ end
39
+ end
40
+
41
+ def serialize_enums(enums)
42
+ enums.transform_values do |data|
43
+ {
44
+ 'mapping' => data[:mapping],
45
+ 'values' => data[:values],
46
+ 'type' => data[:type].to_s
47
+ }
48
+ end
49
+ end
50
+
51
+ def serialize_concerns(concerns)
52
+ concerns.map do |concern|
53
+ {
54
+ 'name' => concern[:name],
55
+ 'type' => concern[:type].to_s,
56
+ 'location' => serialize_location(concern[:location])
57
+ }.compact
58
+ end
59
+ end
60
+
61
+ def serialize_scopes(scopes)
62
+ scopes.transform_keys(&:to_s).transform_values do |data|
63
+ {
64
+ 'sql' => data[:sql],
65
+ 'values' => serialize_data(data[:values]),
66
+ 'conditions' => data[:conditions]
67
+ }
68
+ end
69
+ end
70
+
71
+ def serialize_validations(validations)
72
+ validations.transform_keys(&:to_s).transform_values do |validators|
73
+ validators.map do |validator|
74
+ {
75
+ 'type' => validator[:type],
76
+ 'attributes' => validator[:attributes].map(&:to_s),
77
+ 'options' => serialize_data(validator[:options]),
78
+ 'conditions' => serialize_data(validator[:conditions])
79
+ }
80
+ end
81
+ end
82
+ end
83
+
84
+ def serialize_condition(condition)
85
+ return nil if condition.nil? || condition.empty?
86
+ condition.map(&:to_s)
87
+ end
88
+
89
+ def serialize_location(location)
90
+ return nil if location.nil?
91
+ {
92
+ 'file' => location[:file],
93
+ 'line' => location[:line]
94
+ }
95
+ end
96
+ end
97
+ end
98
+ end
99
+
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ module Serializers
5
+ # Serializer for ProfileResult objects
6
+ class ProfileSerializer < BaseSerializer
7
+ def serialize(profile)
8
+ {
9
+ label: profile.label,
10
+ duration_ms: profile.duration_ms,
11
+ total_sql_duration_ms: profile.total_sql_duration_ms,
12
+ query_count: profile.query_count,
13
+ read_query_count: profile.read_query_count,
14
+ write_query_count: profile.write_query_count,
15
+ cached_query_count: profile.cached_query_count,
16
+ instantiation_count: profile.instantiation_count,
17
+ cache_stats: serialize_cache(profile),
18
+ slow_queries: serialize_queries(profile.slow_queries),
19
+ duplicate_queries: serialize_duplicates(profile.duplicate_queries),
20
+ query_samples: serialize_queries(profile.query_samples),
21
+ error: serialize_error(profile.error),
22
+ started_at: profile.started_at&.iso8601,
23
+ finished_at: profile.finished_at&.iso8601,
24
+ result: serialize_data(profile.result)
25
+ }
26
+ end
27
+
28
+ private
29
+
30
+ def serialize_cache(profile)
31
+ {
32
+ hits: profile.cache_hits,
33
+ misses: profile.cache_misses,
34
+ writes: profile.cache_writes
35
+ }
36
+ end
37
+
38
+ def serialize_queries(queries)
39
+ Array(queries).map do |query|
40
+ {
41
+ sql: query.sql,
42
+ duration_ms: query.duration_ms,
43
+ cached: query.cached,
44
+ name: query.name,
45
+ binds: query.binds
46
+ }
47
+ end
48
+ end
49
+
50
+ def serialize_duplicates(duplicates)
51
+ Array(duplicates).map do |duplicate|
52
+ {
53
+ fingerprint: duplicate.fingerprint,
54
+ sql: duplicate.sql,
55
+ count: duplicate.count,
56
+ total_duration_ms: duplicate.total_duration_ms
57
+ }
58
+ end
59
+ end
60
+
61
+ def serialize_error(error)
62
+ return nil unless error
63
+
64
+ {
65
+ class: error.class.name,
66
+ message: error.message,
67
+ backtrace: Array(error.backtrace).first(10)
68
+ }
69
+ end
70
+ end
71
+ end
72
+ end
73
+
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsConsolePro
4
+ module Serializers
5
+ # Serializer for QueryBuilderResult objects
6
+ class QueryBuilderSerializer < BaseSerializer
7
+ def serialize(query_builder_result)
8
+ {
9
+ model_class: query_builder_result.model_class.name,
10
+ sql: query_builder_result.sql,
11
+ statistics: query_builder_result.statistics,
12
+ explain_result: serialize_explain(query_builder_result.explain_result)
13
+ }
14
+ end
15
+
16
+ private
17
+
18
+ def serialize_explain(explain_result)
19
+ return nil unless explain_result
20
+
21
+ if defined?(Serializers::ExplainSerializer)
22
+ Serializers::ExplainSerializer.serialize(explain_result, exporter)
23
+ else
24
+ {
25
+ sql: explain_result.sql,
26
+ execution_time: explain_result.execution_time,
27
+ indexes_used: explain_result.indexes_used,
28
+ recommendations: explain_result.recommendations
29
+ }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+