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.
@@ -8,7 +8,15 @@ module RailsLens
8
8
  'SQLite'
9
9
  end
10
10
 
11
- def generate_annotation(_model_class)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsLens
4
- VERSION = '0.2.0'
4
+ VERSION = '0.2.3'
5
5
  end
@@ -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.0
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