dbwatcher 1.1.4 → 1.1.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.
- checksums.yaml +4 -4
- data/README.md +80 -26
- data/app/assets/config/dbwatcher_manifest.js +9 -0
- data/app/assets/images/dbwatcher/README.md +24 -0
- data/app/assets/images/dbwatcher/apple-touch-icon.png +0 -0
- data/app/assets/images/dbwatcher/dbwatcher-tranparent_512x512.png +0 -0
- data/app/assets/images/dbwatcher/favicon-96x96.png +0 -0
- data/app/assets/images/dbwatcher/favicon.ico +0 -0
- data/app/assets/images/dbwatcher/site.webmanifest +21 -0
- data/app/assets/images/dbwatcher/unused-assets.zip +0 -0
- data/app/assets/images/dbwatcher/web-app-manifest-192x192.png +0 -0
- data/app/assets/images/dbwatcher/web-app-manifest-512x512.png +0 -0
- data/app/assets/stylesheets/dbwatcher/application.css +38 -4
- data/app/assets/stylesheets/dbwatcher/components/_tabulator.scss +57 -13
- data/app/controllers/dbwatcher/api/v1/system_info_controller.rb +1 -1
- data/app/controllers/dbwatcher/dashboard_controller.rb +1 -1
- data/app/views/dbwatcher/dashboard/_overview.html.erb +8 -7
- data/app/views/dbwatcher/sessions/index.html.erb +42 -59
- data/app/views/layouts/dbwatcher/application.html.erb +22 -6
- data/lib/dbwatcher/configuration.rb +49 -83
- data/lib/dbwatcher/logging.rb +2 -2
- data/lib/dbwatcher/services/diagram_analyzers/concerns/activerecord_introspection.rb +60 -0
- data/lib/dbwatcher/services/diagram_analyzers/concerns/association_scope_filtering.rb +60 -0
- data/lib/dbwatcher/services/diagram_analyzers/model_analysis/association_extractor.rb +224 -0
- data/lib/dbwatcher/services/diagram_analyzers/model_analysis/dataset_builder.rb +226 -0
- data/lib/dbwatcher/services/diagram_analyzers/model_analysis/model_discovery.rb +161 -0
- data/lib/dbwatcher/services/diagram_analyzers/model_association_analyzer.rb +27 -514
- data/lib/dbwatcher/services/diagram_data/attribute.rb +22 -83
- data/lib/dbwatcher/services/diagram_data/base.rb +129 -0
- data/lib/dbwatcher/services/diagram_data/entity.rb +23 -72
- data/lib/dbwatcher/services/diagram_data/relationship.rb +15 -66
- data/lib/dbwatcher/services/mermaid_syntax/erd_builder.rb +2 -2
- data/lib/dbwatcher/services/mermaid_syntax/sanitizer.rb +4 -14
- data/lib/dbwatcher/services/system_info/runtime_info_collector.rb +7 -7
- data/lib/dbwatcher/services/system_info/system_info_collector.rb +3 -3
- data/lib/dbwatcher/services/timeline_data_service/entry_builder.rb +23 -1
- data/lib/dbwatcher/storage/session_storage.rb +2 -2
- data/lib/dbwatcher/storage.rb +1 -1
- data/lib/dbwatcher/version.rb +1 -1
- metadata +17 -2
@@ -41,46 +41,39 @@
|
|
41
41
|
</div>
|
42
42
|
|
43
43
|
<!-- Content Area -->
|
44
|
-
<div class="flex-1 overflow-auto
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
</
|
53
|
-
<p class="text-xs">No tracking sessions yet</p>
|
54
|
-
<p class="text-xs text-gray-400">Start tracking with <code class="bg-gray-200 px-1 rounded">Dbwatcher.track { ... }</code></p>
|
44
|
+
<div class="flex-1 overflow-auto">
|
45
|
+
<% if @sessions.empty? %>
|
46
|
+
<div class="p-8 text-center text-gray-500">
|
47
|
+
<%= image_tag "dbwatcher/dbwatcher-tranparent_512x512.png",
|
48
|
+
alt: "DBWatcher Logo",
|
49
|
+
class: "mx-auto w-12 h-12 opacity-30 mb-3" %>
|
50
|
+
<p class="text-sm font-medium text-gray-600 mb-2">No tracking sessions yet</p>
|
51
|
+
<p class="text-xs text-gray-400">Start tracking with <code class="bg-gray-100 px-1 rounded text-xs">Dbwatcher.track { ... }</code></p>
|
52
|
+
<p class="text-xs text-gray-400 mt-1">or add <code class="bg-gray-100 px-1 rounded text-xs">?dbwatch=true</code> to any URL</p>
|
55
53
|
</div>
|
56
|
-
|
57
|
-
|
58
|
-
<div class="bg-white border border-gray-300 rounded shadow-sm">
|
59
|
-
<table class="compact-table sessions-table w-full">
|
54
|
+
<% else %>
|
55
|
+
<table class="compact-table w-full">
|
60
56
|
<thead>
|
61
57
|
<tr>
|
62
|
-
<th class="text-left" style="
|
63
|
-
<th class="text-left" style="
|
64
|
-
<th class="text-center" style="width:100px">Status</th>
|
65
|
-
<th class="text-center" style="width:100px">Changes</th>
|
66
|
-
<th class="text-right" style="width:120px">Started</th>
|
67
|
-
<th class="text-right" style="width:120px">Duration</th>
|
68
|
-
<th class="text-center" style="width:80px">Actions</th>
|
58
|
+
<th class="text-left" style="width: 18%; min-width: 180px">Session ID</th>
|
59
|
+
<th class="text-left" style="width: 22%; min-width: 160px">Name</th>
|
60
|
+
<th class="text-center" style="width: 100px">Status</th>
|
61
|
+
<th class="text-center" style="width: 100px">Changes</th>
|
62
|
+
<th class="text-right" style="width: 120px">Started</th>
|
63
|
+
<th class="text-right" style="width: 120px">Duration</th>
|
64
|
+
<th class="text-center" style="width: 80px">Actions</th>
|
69
65
|
</tr>
|
70
66
|
</thead>
|
71
67
|
<tbody>
|
72
68
|
<% @sessions.each do |session| %>
|
73
69
|
<tr class="session-row">
|
74
|
-
<td
|
75
|
-
|
76
|
-
<%= safe_value(session, :id) %>
|
77
|
-
</span>
|
70
|
+
<td class="font-mono text-xs">
|
71
|
+
<%= safe_value(session, :id) %>
|
78
72
|
</td>
|
79
|
-
<td
|
73
|
+
<td title="<%= safe_value(session, :name) %>">
|
80
74
|
<%= link_to display_session_name(safe_value(session, :name)),
|
81
75
|
session_path(safe_value(session, :id)),
|
82
|
-
class: "text-navy-dark hover:text-blue-medium
|
83
|
-
style: "max-width:260px; overflow-x:auto; display:inline-block;" %>
|
76
|
+
class: "text-navy-dark hover:text-blue-medium" %>
|
84
77
|
</td>
|
85
78
|
<td class="text-center">
|
86
79
|
<%= render 'dbwatcher/shared/badge',
|
@@ -106,44 +99,34 @@
|
|
106
99
|
) rescue 'N/A' %>
|
107
100
|
<% end %>
|
108
101
|
</td>
|
109
|
-
<td class="text-center
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
title: "View session details" do %>
|
114
|
-
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
115
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
116
|
-
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
117
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
118
|
-
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
119
|
-
</svg>
|
120
|
-
<% end %>
|
121
|
-
</div>
|
102
|
+
<td class="text-center">
|
103
|
+
<%= link_to "View", session_path(safe_value(session, :id)),
|
104
|
+
class: "compact-button bg-navy-dark text-white hover:bg-blue-medium",
|
105
|
+
title: "View session details" %>
|
122
106
|
</td>
|
123
107
|
</tr>
|
124
108
|
<% end %>
|
125
109
|
</tbody>
|
126
110
|
</table>
|
127
|
-
</div>
|
128
111
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
112
|
+
<!-- No Results Message -->
|
113
|
+
<div
|
114
|
+
x-show="filterText && document.querySelectorAll('.session-row:not(.hidden)').length === 0"
|
115
|
+
x-cloak
|
116
|
+
class="p-8 text-center text-gray-500 bg-gray-50">
|
117
|
+
<p class="mb-2">No sessions match your filter criteria</p>
|
118
|
+
<button
|
119
|
+
@click="filterText = ''"
|
120
|
+
class="compact-button bg-gray-500 text-white hover:bg-gray-600">
|
121
|
+
Clear filter
|
122
|
+
</button>
|
123
|
+
</div>
|
124
|
+
<% end %>
|
125
|
+
</div>
|
142
126
|
|
143
127
|
<!-- Status Bar -->
|
144
|
-
<div class="h-6 bg-gray-100 border-t border-gray-300 flex items-center px-4 text-xs text-gray-600
|
145
|
-
<span x-show="!filterText"><%= @sessions.count %> sessions total •
|
146
|
-
<%= @sessions.count { |s| session_active?(s) } %> active</span>
|
128
|
+
<div class="h-6 bg-gray-100 border-t border-gray-300 flex items-center px-4 text-xs text-gray-600">
|
129
|
+
<span x-show="!filterText"><%= @sessions.count %> sessions total • <%= @sessions.count { |s| session_active?(s) } %> active</span>
|
147
130
|
<span x-show="filterText" x-text="`${document.querySelectorAll('.session-row:not(.hidden)').length} of ${<%= @sessions.count %>} sessions shown`"></span>
|
148
131
|
<span class="ml-auto">Last updated: <%= Time.current.strftime("%H:%M:%S") %></span>
|
149
132
|
</div>
|
@@ -5,6 +5,14 @@
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
6
|
<%= csrf_meta_tags %>
|
7
7
|
|
8
|
+
<!-- Favicon -->
|
9
|
+
<%= favicon_link_tag asset_path('dbwatcher/favicon.ico') %>
|
10
|
+
<%= favicon_link_tag asset_path('dbwatcher/favicon-96x96.png'), sizes: '96x96', type: 'image/png' %>
|
11
|
+
<%= favicon_link_tag asset_path('dbwatcher/apple-touch-icon.png'), rel: 'apple-touch-icon', sizes: '180x180' %>
|
12
|
+
<%= favicon_link_tag asset_path('dbwatcher/web-app-manifest-192x192.png'), sizes: '192x192', type: 'image/png' %>
|
13
|
+
<%= favicon_link_tag asset_path('dbwatcher/web-app-manifest-512x512.png'), sizes: '512x512', type: 'image/png' %>
|
14
|
+
<link rel="manifest" href="<%= asset_path('dbwatcher/site.webmanifest') %>"
|
15
|
+
|
8
16
|
<!-- Alpine.js and Plugins - ensure plugins load BEFORE Alpine.js core -->
|
9
17
|
<script defer src="https://unpkg.com/@alpinejs/collapse@3.x.x/dist/cdn.min.js"></script>
|
10
18
|
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
@@ -119,12 +127,20 @@
|
|
119
127
|
:style="{ width: sidebarCollapsed ? '48px' : sidebarWidth + 'px' }">
|
120
128
|
<div class="flex flex-col h-full">
|
121
129
|
<!-- Logo -->
|
122
|
-
<div class="h-
|
123
|
-
<div class="flex items-center gap-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
<
|
130
|
+
<div class="h-12 flex items-center justify-between px-3 border-b border-gray-700 bg-gray-800">
|
131
|
+
<div class="flex items-center gap-3" x-show="!sidebarCollapsed">
|
132
|
+
<%= image_tag "dbwatcher/dbwatcher-tranparent_512x512.png",
|
133
|
+
alt: "DBWatcher Logo",
|
134
|
+
class: "w-10 h-10" %>
|
135
|
+
<div class="flex flex-col">
|
136
|
+
<span class="text-base font-bold text-white">dbwatcher</span>
|
137
|
+
</div>
|
138
|
+
</div>
|
139
|
+
<!-- Collapsed state logo -->
|
140
|
+
<div class="flex items-center justify-center w-full" x-show="sidebarCollapsed">
|
141
|
+
<%= image_tag "dbwatcher/dbwatcher-tranparent_512x512.png",
|
142
|
+
alt: "dbwatcher Logo",
|
143
|
+
class: "w-8 h-8" %>
|
128
144
|
</div>
|
129
145
|
<button @click="sidebarCollapsed = !sidebarCollapsed"
|
130
146
|
class="p-1 hover:bg-gray-800 rounded text-gray-400">
|
@@ -3,104 +3,84 @@
|
|
3
3
|
module Dbwatcher
|
4
4
|
# Configuration class for DBWatcher
|
5
5
|
#
|
6
|
-
#
|
7
|
-
# storage, tracking, and diagram visualization settings.
|
6
|
+
# Simplified configuration with logical groupings and sensible defaults
|
8
7
|
class Configuration
|
9
|
-
#
|
10
|
-
attr_accessor :
|
8
|
+
# Core settings - what users need most
|
9
|
+
attr_accessor :enabled, :storage_path
|
11
10
|
|
12
|
-
#
|
13
|
-
attr_accessor :
|
11
|
+
# Session management - how data is stored and cleaned
|
12
|
+
attr_accessor :max_sessions, :auto_clean_days
|
14
13
|
|
15
|
-
#
|
16
|
-
attr_accessor :
|
14
|
+
# Query tracking - performance monitoring
|
15
|
+
attr_accessor :track_queries
|
17
16
|
|
18
|
-
#
|
19
|
-
attr_accessor :
|
20
|
-
:diagram_max_attributes, :diagram_attribute_types, :diagram_relationship_labels,
|
21
|
-
:diagram_preserve_table_case, :diagram_direction, :diagram_cardinality_format,
|
22
|
-
:diagram_show_attribute_count, :diagram_show_method_count
|
17
|
+
# System info - debugging and monitoring
|
18
|
+
attr_accessor :system_info, :debug_mode
|
23
19
|
|
24
|
-
#
|
25
|
-
attr_accessor :
|
26
|
-
:
|
27
|
-
:
|
28
|
-
|
29
|
-
# Logging configuration
|
30
|
-
attr_accessor :debug_logging
|
20
|
+
# Advanced diagram options - available but not commonly needed
|
21
|
+
attr_accessor :diagram_show_methods, :diagram_max_attributes,
|
22
|
+
:diagram_attribute_types, :diagram_relationship_labels,
|
23
|
+
:diagram_show_attributes, :diagram_show_cardinality
|
31
24
|
|
32
25
|
# Initialize with default values
|
33
26
|
def initialize
|
34
|
-
#
|
35
|
-
@storage_path = default_storage_path
|
27
|
+
# Core settings
|
36
28
|
@enabled = true
|
29
|
+
@storage_path = default_storage_path
|
30
|
+
|
31
|
+
# Session management
|
37
32
|
@max_sessions = 50
|
38
|
-
@
|
33
|
+
@auto_clean_days = 7
|
39
34
|
|
40
|
-
# Query tracking
|
35
|
+
# Query tracking
|
41
36
|
@track_queries = false
|
42
|
-
@slow_query_threshold = 200 # milliseconds
|
43
|
-
@max_query_logs_per_day = 1000
|
44
|
-
|
45
|
-
# Routing configuration defaults
|
46
|
-
@mount_path = "/dbwatcher"
|
47
37
|
|
48
|
-
#
|
49
|
-
|
38
|
+
# System info
|
39
|
+
@system_info = true
|
40
|
+
@debug_mode = false
|
50
41
|
|
51
|
-
#
|
52
|
-
|
42
|
+
# Advanced diagram options - sensible defaults
|
43
|
+
@diagram_show_methods = false
|
44
|
+
@diagram_max_attributes = 10
|
45
|
+
@diagram_attribute_types = true
|
46
|
+
@diagram_relationship_labels = true
|
47
|
+
@diagram_show_attributes = true
|
48
|
+
@diagram_show_cardinality = true
|
49
|
+
end
|
53
50
|
|
54
|
-
|
55
|
-
|
51
|
+
# Fixed defaults for options that are still used in codebase but not configurable
|
52
|
+
def slow_query_threshold
|
53
|
+
200 # Fixed default value
|
56
54
|
end
|
57
55
|
|
58
|
-
|
59
|
-
|
60
|
-
@diagram_show_attributes = true
|
61
|
-
@diagram_show_methods = false # Hide methods by default
|
62
|
-
@diagram_show_cardinality = true
|
63
|
-
@diagram_max_attributes = 10
|
64
|
-
@diagram_attribute_types = true # Changed from array to boolean
|
65
|
-
@diagram_relationship_labels = true # Changed from symbol to boolean
|
66
|
-
@diagram_preserve_table_case = false # Changed from true to false
|
67
|
-
@diagram_direction = "LR" # Left to right by default
|
68
|
-
@diagram_cardinality_format = :simple # Use simpler 1:N format
|
69
|
-
@diagram_show_attribute_count = true
|
70
|
-
@diagram_show_method_count = true
|
56
|
+
def diagram_direction
|
57
|
+
"LR" # Fixed default value
|
71
58
|
end
|
72
59
|
|
73
|
-
#
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
60
|
+
# Fixed defaults for complex options that are still used in codebase but not configurable
|
61
|
+
def max_query_logs_per_day = 1000
|
62
|
+
def system_info_refresh_interval = 5 * 60
|
63
|
+
def system_info_cache_duration = 60 * 60
|
64
|
+
|
65
|
+
def collect_sensitive_env_vars?
|
66
|
+
false
|
80
67
|
end
|
81
68
|
|
82
|
-
|
83
|
-
|
84
|
-
@debug_logging = false
|
69
|
+
def system_info_include_performance_metrics?
|
70
|
+
true
|
85
71
|
end
|
86
72
|
|
87
73
|
# Validate configuration
|
88
|
-
#
|
89
|
-
# @return [Boolean] true if configuration is valid
|
90
74
|
def valid?
|
91
75
|
validate_storage_path
|
92
76
|
validate_max_sessions
|
93
|
-
|
94
|
-
validate_slow_query_threshold
|
95
|
-
validate_max_query_logs_per_day
|
77
|
+
validate_auto_clean_days
|
96
78
|
true
|
97
79
|
end
|
98
80
|
|
99
81
|
private
|
100
82
|
|
101
83
|
# Default storage path based on Rails or current directory
|
102
|
-
#
|
103
|
-
# @return [String] default storage path
|
104
84
|
def default_storage_path
|
105
85
|
if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
106
86
|
Rails.root.join("tmp", "dbwatcher").to_s
|
@@ -127,25 +107,11 @@ module Dbwatcher
|
|
127
107
|
raise ConfigurationError, "max_sessions must be a positive integer"
|
128
108
|
end
|
129
109
|
|
130
|
-
# Validate auto clean
|
131
|
-
def
|
132
|
-
return if
|
133
|
-
|
134
|
-
raise ConfigurationError, "auto_clean_after_days must be a positive integer"
|
135
|
-
end
|
136
|
-
|
137
|
-
# Validate slow query threshold
|
138
|
-
def validate_slow_query_threshold
|
139
|
-
return if slow_query_threshold.is_a?(Integer) && slow_query_threshold.positive?
|
140
|
-
|
141
|
-
raise ConfigurationError, "slow_query_threshold must be a positive integer"
|
142
|
-
end
|
143
|
-
|
144
|
-
# Validate max query logs per day
|
145
|
-
def validate_max_query_logs_per_day
|
146
|
-
return if max_query_logs_per_day.is_a?(Integer) && max_query_logs_per_day.positive?
|
110
|
+
# Validate auto clean days
|
111
|
+
def validate_auto_clean_days
|
112
|
+
return if auto_clean_days.is_a?(Integer) && auto_clean_days.positive?
|
147
113
|
|
148
|
-
raise ConfigurationError, "
|
114
|
+
raise ConfigurationError, "auto_clean_days must be a positive integer"
|
149
115
|
end
|
150
116
|
end
|
151
117
|
|
data/lib/dbwatcher/logging.rb
CHANGED
@@ -40,8 +40,8 @@ module Dbwatcher
|
|
40
40
|
# Check if debug logging is enabled
|
41
41
|
# @return [Boolean] true if debug logging is enabled
|
42
42
|
def debug_enabled?
|
43
|
-
return Dbwatcher.configuration.
|
44
|
-
|
43
|
+
return Dbwatcher.configuration.debug_mode if defined?(Dbwatcher.configuration) &&
|
44
|
+
Dbwatcher.configuration.respond_to?(:debug_mode)
|
45
45
|
return Rails.env.development? if defined?(Rails)
|
46
46
|
|
47
47
|
false
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dbwatcher
|
4
|
+
module Services
|
5
|
+
module DiagramAnalyzers
|
6
|
+
module Concerns
|
7
|
+
# Concern for ActiveRecord introspection utilities
|
8
|
+
#
|
9
|
+
# This module provides common methods for checking ActiveRecord availability
|
10
|
+
# and performing model introspection operations.
|
11
|
+
module ActiverecordIntrospection
|
12
|
+
extend ActiveSupport::Concern if defined?(ActiveSupport)
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# Check if ActiveRecord is available
|
17
|
+
#
|
18
|
+
# @return [Boolean]
|
19
|
+
def activerecord_available?
|
20
|
+
defined?(ActiveRecord::Base)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Check if models analysis is available
|
24
|
+
#
|
25
|
+
# @return [Boolean] true if models can be analyzed
|
26
|
+
def models_available?
|
27
|
+
unless activerecord_available?
|
28
|
+
Rails.logger.warn "#{self.class.name}: ActiveRecord not available"
|
29
|
+
return false
|
30
|
+
end
|
31
|
+
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
# Eagerly load all models including those from gems
|
36
|
+
#
|
37
|
+
# @return [void]
|
38
|
+
def eager_load_models
|
39
|
+
return unless defined?(Rails) && Rails.respond_to?(:application)
|
40
|
+
|
41
|
+
begin
|
42
|
+
# Force eager loading of application models
|
43
|
+
Rails.application.eager_load!
|
44
|
+
|
45
|
+
# Also load models from engines/gems if any are configured
|
46
|
+
Rails::Engine.descendants.each do |engine|
|
47
|
+
engine.eager_load! if engine.respond_to?(:eager_load!)
|
48
|
+
rescue StandardError => e
|
49
|
+
error_message = "#{self.class.name}: Could not eager load engine #{engine.class.name}: #{e.message}"
|
50
|
+
Rails.logger.debug error_message
|
51
|
+
end
|
52
|
+
rescue StandardError => e
|
53
|
+
Rails.logger.debug "#{self.class.name}: Could not eager load models: #{e.message}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dbwatcher
|
4
|
+
module Services
|
5
|
+
module DiagramAnalyzers
|
6
|
+
module Concerns
|
7
|
+
# Concern for filtering associations based on scope
|
8
|
+
#
|
9
|
+
# This module provides methods for determining which associations
|
10
|
+
# should be included in the analysis based on session context.
|
11
|
+
module AssociationScopeFiltering
|
12
|
+
extend ActiveSupport::Concern if defined?(ActiveSupport)
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# Check if target model is in analysis scope
|
17
|
+
#
|
18
|
+
# @param association [Object] association object
|
19
|
+
# @param session_tables [Array<String>] tables from session context
|
20
|
+
# @return [Boolean] true if target model should be included
|
21
|
+
def target_model_in_scope?(association, session_tables = [])
|
22
|
+
target_table = get_association_table_name(association)
|
23
|
+
|
24
|
+
# If analyzing session, both tables must be in session
|
25
|
+
# If analyzing globally, include all
|
26
|
+
return true if session_tables.empty?
|
27
|
+
|
28
|
+
# Skip if target table is not in session
|
29
|
+
return false if target_table && !session_tables.include?(target_table)
|
30
|
+
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get table name for association target
|
35
|
+
#
|
36
|
+
# @param association [Object] association object
|
37
|
+
# @return [String, nil] table name
|
38
|
+
def get_association_table_name(association)
|
39
|
+
association.table_name
|
40
|
+
rescue StandardError => e
|
41
|
+
Rails.logger.warn "#{self.class.name}: Could not get table name for #{association.name}: #{e.message}"
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
# Extract tables that were involved in the session
|
46
|
+
#
|
47
|
+
# @param session [Object] session object with changes
|
48
|
+
# @return [Array<String>] unique table names
|
49
|
+
def extract_session_tables(session)
|
50
|
+
return [] unless session&.changes
|
51
|
+
|
52
|
+
session.changes.map do |change|
|
53
|
+
change[:table_name] || change["table_name"]
|
54
|
+
end.compact.uniq
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|