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.
- checksums.yaml +4 -4
- data/.rspec_status +288 -240
- data/CHANGELOG.md +7 -0
- data/QUICK_START.md +17 -0
- data/README.md +43 -0
- data/docs/FORMATTING.md +5 -0
- data/docs/MODEL_INTROSPECTION.md +371 -0
- data/docs/MODEL_STATISTICS.md +4 -0
- data/docs/OBJECT_DIFFING.md +6 -0
- data/docs/PROFILING.md +91 -0
- data/docs/QUERY_BUILDER.md +385 -0
- data/docs/QUEUE_INSIGHTS.md +82 -0
- data/docs/SCHEMA_INSPECTION.md +5 -0
- data/docs/SNIPPETS.md +71 -0
- data/lib/rails_console_pro/commands/compare_command.rb +151 -0
- data/lib/rails_console_pro/commands/introspect_command.rb +220 -0
- data/lib/rails_console_pro/commands/jobs_command.rb +212 -0
- data/lib/rails_console_pro/commands/profile_command.rb +84 -0
- data/lib/rails_console_pro/commands/query_builder_command.rb +43 -0
- data/lib/rails_console_pro/commands/snippets_command.rb +141 -0
- data/lib/rails_console_pro/commands.rb +30 -0
- data/lib/rails_console_pro/compare_result.rb +81 -0
- data/lib/rails_console_pro/configuration.rb +51 -0
- data/lib/rails_console_pro/format_exporter.rb +32 -0
- data/lib/rails_console_pro/global_methods.rb +24 -0
- data/lib/rails_console_pro/initializer.rb +41 -1
- data/lib/rails_console_pro/introspect_result.rb +101 -0
- data/lib/rails_console_pro/model_validator.rb +1 -1
- data/lib/rails_console_pro/printers/compare_printer.rb +138 -0
- data/lib/rails_console_pro/printers/introspect_printer.rb +282 -0
- data/lib/rails_console_pro/printers/profile_printer.rb +180 -0
- data/lib/rails_console_pro/printers/query_builder_printer.rb +81 -0
- data/lib/rails_console_pro/printers/queue_insights_printer.rb +150 -0
- data/lib/rails_console_pro/printers/snippet_collection_printer.rb +68 -0
- data/lib/rails_console_pro/printers/snippet_printer.rb +64 -0
- data/lib/rails_console_pro/profile_result.rb +109 -0
- data/lib/rails_console_pro/pry_commands.rb +106 -0
- data/lib/rails_console_pro/query_builder.rb +197 -0
- data/lib/rails_console_pro/query_builder_result.rb +66 -0
- data/lib/rails_console_pro/queue_insights_result.rb +110 -0
- data/lib/rails_console_pro/serializers/compare_serializer.rb +66 -0
- data/lib/rails_console_pro/serializers/introspect_serializer.rb +99 -0
- data/lib/rails_console_pro/serializers/profile_serializer.rb +73 -0
- data/lib/rails_console_pro/serializers/query_builder_serializer.rb +35 -0
- data/lib/rails_console_pro/services/introspection_collector.rb +420 -0
- data/lib/rails_console_pro/services/profile_collector.rb +245 -0
- data/lib/rails_console_pro/services/queue_action_service.rb +176 -0
- data/lib/rails_console_pro/services/queue_insight_fetcher.rb +600 -0
- data/lib/rails_console_pro/services/snippet_repository.rb +191 -0
- data/lib/rails_console_pro/snippets/collection_result.rb +45 -0
- data/lib/rails_console_pro/snippets/single_result.rb +30 -0
- data/lib/rails_console_pro/snippets/snippet.rb +112 -0
- data/lib/rails_console_pro/snippets.rb +13 -0
- data/lib/rails_console_pro/version.rb +1 -1
- data/rails_console_pro.gemspec +1 -1
- 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
|
+
|