rails_lens 0.2.0 → 0.2.3
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/CHANGELOG.md +27 -0
- data/README.md +70 -0
- data/lib/rails_lens/analyzers/delegated_types.rb +6 -1
- data/lib/rails_lens/analyzers/notes.rb +114 -6
- data/lib/rails_lens/annotation_pipeline.rb +26 -21
- data/lib/rails_lens/cli.rb +6 -0
- data/lib/rails_lens/connection.rb +4 -4
- data/lib/rails_lens/erd/visualizer.rb +86 -11
- data/lib/rails_lens/model_detector.rb +89 -5
- data/lib/rails_lens/providers/base.rb +1 -1
- data/lib/rails_lens/providers/extension_notes_provider.rb +3 -2
- data/lib/rails_lens/providers/extensions_provider.rb +1 -1
- data/lib/rails_lens/providers/index_notes_provider.rb +3 -2
- data/lib/rails_lens/providers/inheritance_provider.rb +1 -1
- data/lib/rails_lens/providers/notes_provider_base.rb +3 -2
- data/lib/rails_lens/providers/schema_provider.rb +28 -13
- data/lib/rails_lens/providers/section_provider_base.rb +1 -1
- data/lib/rails_lens/providers/view_notes_provider.rb +22 -0
- data/lib/rails_lens/providers/view_provider.rb +67 -0
- data/lib/rails_lens/schema/adapters/mysql.rb +119 -7
- data/lib/rails_lens/schema/adapters/postgresql.rb +163 -1
- data/lib/rails_lens/schema/adapters/sqlite3.rb +114 -1
- data/lib/rails_lens/schema/annotation_manager.rb +2 -2
- data/lib/rails_lens/version.rb +1 -1
- data/lib/rails_lens/view_metadata.rb +98 -0
- metadata +4 -1
@@ -8,7 +8,15 @@ module RailsLens
|
|
8
8
|
'SQLite'
|
9
9
|
end
|
10
10
|
|
11
|
-
def generate_annotation(
|
11
|
+
def generate_annotation(model_class)
|
12
|
+
if model_class && ModelDetector.view_exists?(model_class)
|
13
|
+
generate_view_annotation(model_class)
|
14
|
+
else
|
15
|
+
generate_table_annotation(model_class)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def generate_table_annotation(_model_class)
|
12
20
|
lines = []
|
13
21
|
lines << "table = \"#{table_name}\""
|
14
22
|
lines << "database_dialect = \"#{database_dialect}\""
|
@@ -22,6 +30,27 @@ module RailsLens
|
|
22
30
|
lines.join("\n")
|
23
31
|
end
|
24
32
|
|
33
|
+
def generate_view_annotation(model_class)
|
34
|
+
lines = []
|
35
|
+
lines << "view = \"#{table_name}\""
|
36
|
+
lines << "database_dialect = \"#{database_dialect}\""
|
37
|
+
|
38
|
+
# Fetch all view metadata in a single query
|
39
|
+
view_info = fetch_view_metadata
|
40
|
+
|
41
|
+
if view_info
|
42
|
+
lines << "view_type = \"#{view_info[:type]}\"" if view_info[:type]
|
43
|
+
lines << "updatable = #{view_info[:updatable]}"
|
44
|
+
end
|
45
|
+
|
46
|
+
lines << ''
|
47
|
+
|
48
|
+
add_columns_toml(lines)
|
49
|
+
add_view_dependencies_toml(lines, view_info)
|
50
|
+
|
51
|
+
lines.join("\n")
|
52
|
+
end
|
53
|
+
|
25
54
|
protected
|
26
55
|
|
27
56
|
def format_column(column)
|
@@ -90,6 +119,90 @@ module RailsLens
|
|
90
119
|
Rails.logger.debug { "SQLite error fetching pragmas: #{e.message}" }
|
91
120
|
end
|
92
121
|
end
|
122
|
+
|
123
|
+
def add_view_dependencies_toml(lines, view_info)
|
124
|
+
return unless view_info && view_info[:dependencies]
|
125
|
+
|
126
|
+
dependencies = view_info[:dependencies]
|
127
|
+
return if dependencies.empty?
|
128
|
+
|
129
|
+
lines << ''
|
130
|
+
lines << "view_dependencies = [#{dependencies.map { |d| "\"#{d}\"" }.join(', ')}]"
|
131
|
+
end
|
132
|
+
|
133
|
+
# SQLite-specific view methods
|
134
|
+
public
|
135
|
+
|
136
|
+
# Fetch all view metadata in a single consolidated query
|
137
|
+
def fetch_view_metadata
|
138
|
+
result = connection.exec_query(<<~SQL.squish, 'SQLite View Metadata')
|
139
|
+
SELECT sql FROM sqlite_master#{' '}
|
140
|
+
WHERE type = 'view' AND name = '#{connection.quote_string(table_name)}'
|
141
|
+
LIMIT 1
|
142
|
+
SQL
|
143
|
+
|
144
|
+
return nil if result.rows.empty?
|
145
|
+
|
146
|
+
definition = result.rows.first&.first&.strip
|
147
|
+
return nil unless definition
|
148
|
+
|
149
|
+
# Parse dependencies from the SQL definition
|
150
|
+
tables = []
|
151
|
+
definition.scan(/(?:FROM|JOIN)\s+(\w+)/i) do |match|
|
152
|
+
table_name_match = match[0]
|
153
|
+
# Exclude the view itself and common SQL keywords
|
154
|
+
if !table_name_match.downcase.in?(%w[select where order group having limit offset]) &&
|
155
|
+
tables.exclude?(table_name_match) &&
|
156
|
+
table_name_match != table_name
|
157
|
+
tables << table_name_match
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
{
|
162
|
+
type: 'regular', # SQLite only supports regular views
|
163
|
+
updatable: false, # SQLite views are generally read-only
|
164
|
+
dependencies: tables.sort
|
165
|
+
}
|
166
|
+
rescue ActiveRecord::StatementInvalid, SQLite3::Exception => e
|
167
|
+
Rails.logger.debug { "Failed to fetch view metadata for #{table_name}: #{e.message}" }
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
|
171
|
+
# Legacy methods - kept for backward compatibility but now use consolidated query
|
172
|
+
def view_type
|
173
|
+
@view_metadata ||= fetch_view_metadata
|
174
|
+
@view_metadata&.dig(:type)
|
175
|
+
end
|
176
|
+
|
177
|
+
def view_updatable?
|
178
|
+
@view_metadata ||= fetch_view_metadata
|
179
|
+
@view_metadata&.dig(:updatable) || false
|
180
|
+
end
|
181
|
+
|
182
|
+
def view_dependencies
|
183
|
+
@view_metadata ||= fetch_view_metadata
|
184
|
+
@view_metadata&.dig(:dependencies) || []
|
185
|
+
end
|
186
|
+
|
187
|
+
def view_definition
|
188
|
+
result = connection.exec_query(<<~SQL.squish, 'SQLite View Definition')
|
189
|
+
SELECT sql FROM sqlite_master#{' '}
|
190
|
+
WHERE type = 'view' AND name = '#{connection.quote_string(table_name)}'
|
191
|
+
LIMIT 1
|
192
|
+
SQL
|
193
|
+
|
194
|
+
result.rows.first&.first&.strip
|
195
|
+
rescue ActiveRecord::StatementInvalid, SQLite3::Exception
|
196
|
+
nil
|
197
|
+
end
|
198
|
+
|
199
|
+
def view_refresh_strategy
|
200
|
+
nil # SQLite doesn't have materialized views
|
201
|
+
end
|
202
|
+
|
203
|
+
def view_last_refreshed
|
204
|
+
nil # SQLite doesn't have materialized views
|
205
|
+
end
|
93
206
|
end
|
94
207
|
end
|
95
208
|
end
|
@@ -112,8 +112,8 @@ module RailsLens
|
|
112
112
|
next
|
113
113
|
end
|
114
114
|
|
115
|
-
# Skip models without tables or with missing tables
|
116
|
-
unless model.table_exists?
|
115
|
+
# Skip models without tables or with missing tables (but not abstract classes)
|
116
|
+
unless model.abstract_class? || model.table_exists?
|
117
117
|
results[:skipped] << model.name
|
118
118
|
warn "Skipping #{model.name} - table does not exist" if options[:verbose]
|
119
119
|
next
|
data/lib/rails_lens/version.rb
CHANGED
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsLens
|
4
|
+
# Extracts and manages metadata for database views and materialized views
|
5
|
+
class ViewMetadata
|
6
|
+
attr_reader :model_class, :connection, :table_name
|
7
|
+
|
8
|
+
def initialize(model_class)
|
9
|
+
@model_class = model_class
|
10
|
+
@connection = model_class.connection
|
11
|
+
@table_name = model_class.table_name
|
12
|
+
@adapter_name = connection.adapter_name.downcase
|
13
|
+
@adapter = create_adapter
|
14
|
+
end
|
15
|
+
|
16
|
+
def view_type
|
17
|
+
return nil unless view_exists?
|
18
|
+
|
19
|
+
@adapter&.view_type
|
20
|
+
end
|
21
|
+
|
22
|
+
def view_exists?
|
23
|
+
ModelDetector.view_exists?(model_class)
|
24
|
+
end
|
25
|
+
|
26
|
+
def materialized_view?
|
27
|
+
view_type == 'materialized'
|
28
|
+
end
|
29
|
+
|
30
|
+
def regular_view?
|
31
|
+
view_type == 'regular'
|
32
|
+
end
|
33
|
+
|
34
|
+
def updatable?
|
35
|
+
return false unless view_exists?
|
36
|
+
|
37
|
+
@adapter&.view_updatable? || false
|
38
|
+
rescue ActiveRecord::StatementInvalid, ActiveRecord::ConnectionNotDefined
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
def dependencies
|
43
|
+
return [] unless view_exists?
|
44
|
+
|
45
|
+
@adapter&.view_dependencies || []
|
46
|
+
rescue ActiveRecord::StatementInvalid, ActiveRecord::ConnectionNotDefined
|
47
|
+
[]
|
48
|
+
end
|
49
|
+
|
50
|
+
def refresh_strategy
|
51
|
+
return nil unless materialized_view?
|
52
|
+
|
53
|
+
@adapter&.view_refresh_strategy
|
54
|
+
end
|
55
|
+
|
56
|
+
def last_refreshed
|
57
|
+
return nil unless materialized_view?
|
58
|
+
|
59
|
+
@adapter&.view_last_refreshed
|
60
|
+
rescue ActiveRecord::StatementInvalid, ActiveRecord::ConnectionNotDefined
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def view_definition
|
65
|
+
return nil unless view_exists?
|
66
|
+
|
67
|
+
@adapter&.view_definition
|
68
|
+
rescue ActiveRecord::StatementInvalid, ActiveRecord::ConnectionNotDefined
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_h
|
73
|
+
{
|
74
|
+
view_type: view_type,
|
75
|
+
updatable: updatable?,
|
76
|
+
dependencies: dependencies,
|
77
|
+
refresh_strategy: refresh_strategy,
|
78
|
+
last_refreshed: last_refreshed,
|
79
|
+
view_definition: view_definition
|
80
|
+
}.compact
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def create_adapter
|
86
|
+
case @adapter_name
|
87
|
+
when 'postgresql'
|
88
|
+
Schema::Adapters::Postgresql.new(connection, table_name)
|
89
|
+
when 'mysql', 'mysql2'
|
90
|
+
Schema::Adapters::Mysql.new(connection, table_name)
|
91
|
+
when 'sqlite', 'sqlite3'
|
92
|
+
Schema::Adapters::Sqlite3.new(connection, table_name)
|
93
|
+
else
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_lens
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Abdelkader Boudih
|
@@ -219,6 +219,8 @@ files:
|
|
219
219
|
- lib/rails_lens/providers/performance_notes_provider.rb
|
220
220
|
- lib/rails_lens/providers/schema_provider.rb
|
221
221
|
- lib/rails_lens/providers/section_provider_base.rb
|
222
|
+
- lib/rails_lens/providers/view_notes_provider.rb
|
223
|
+
- lib/rails_lens/providers/view_provider.rb
|
222
224
|
- lib/rails_lens/railtie.rb
|
223
225
|
- lib/rails_lens/rake_bootstrapper.rb
|
224
226
|
- lib/rails_lens/route/annotator.rb
|
@@ -237,6 +239,7 @@ files:
|
|
237
239
|
- lib/rails_lens/tasks/routes.rake
|
238
240
|
- lib/rails_lens/tasks/schema.rake
|
239
241
|
- lib/rails_lens/version.rb
|
242
|
+
- lib/rails_lens/view_metadata.rb
|
240
243
|
homepage: https://github.com/seuros/rails_lens
|
241
244
|
licenses:
|
242
245
|
- MIT
|