rails_lens 0.0.0 → 0.2.2
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 +23 -0
- data/LICENSE.txt +2 -2
- data/README.md +463 -9
- data/exe/rails_lens +25 -0
- data/lib/rails_lens/analyzers/association_analyzer.rb +111 -0
- data/lib/rails_lens/analyzers/base.rb +35 -0
- data/lib/rails_lens/analyzers/best_practices_analyzer.rb +114 -0
- data/lib/rails_lens/analyzers/column_analyzer.rb +97 -0
- data/lib/rails_lens/analyzers/composite_keys.rb +62 -0
- data/lib/rails_lens/analyzers/database_constraints.rb +35 -0
- data/lib/rails_lens/analyzers/delegated_types.rb +129 -0
- data/lib/rails_lens/analyzers/enums.rb +34 -0
- data/lib/rails_lens/analyzers/error_handling.rb +66 -0
- data/lib/rails_lens/analyzers/foreign_key_analyzer.rb +47 -0
- data/lib/rails_lens/analyzers/generated_columns.rb +56 -0
- data/lib/rails_lens/analyzers/index_analyzer.rb +128 -0
- data/lib/rails_lens/analyzers/inheritance.rb +212 -0
- data/lib/rails_lens/analyzers/notes.rb +325 -0
- data/lib/rails_lens/analyzers/performance_analyzer.rb +110 -0
- data/lib/rails_lens/annotation_pipeline.rb +87 -0
- data/lib/rails_lens/cli.rb +176 -0
- data/lib/rails_lens/cli_error_handler.rb +86 -0
- data/lib/rails_lens/commands.rb +164 -0
- data/lib/rails_lens/connection.rb +133 -0
- data/lib/rails_lens/erd/column_type_formatter.rb +32 -0
- data/lib/rails_lens/erd/domain_color_mapper.rb +40 -0
- data/lib/rails_lens/erd/mysql_column_type_formatter.rb +19 -0
- data/lib/rails_lens/erd/postgresql_column_type_formatter.rb +19 -0
- data/lib/rails_lens/erd/visualizer.rb +329 -0
- data/lib/rails_lens/errors.rb +78 -0
- data/lib/rails_lens/extension_loader.rb +261 -0
- data/lib/rails_lens/extensions/base.rb +194 -0
- data/lib/rails_lens/extensions/closure_tree_ext.rb +157 -0
- data/lib/rails_lens/file_insertion_helper.rb +168 -0
- data/lib/rails_lens/mailer/annotator.rb +226 -0
- data/lib/rails_lens/mailer/extractor.rb +201 -0
- data/lib/rails_lens/model_detector.rb +252 -0
- data/lib/rails_lens/parsers/class_info.rb +46 -0
- data/lib/rails_lens/parsers/module_info.rb +33 -0
- data/lib/rails_lens/parsers/parser_result.rb +55 -0
- data/lib/rails_lens/parsers/prism_parser.rb +90 -0
- data/lib/rails_lens/parsers.rb +10 -0
- data/lib/rails_lens/providers/association_notes_provider.rb +11 -0
- data/lib/rails_lens/providers/base.rb +37 -0
- data/lib/rails_lens/providers/best_practices_notes_provider.rb +11 -0
- data/lib/rails_lens/providers/column_notes_provider.rb +11 -0
- data/lib/rails_lens/providers/composite_keys_provider.rb +11 -0
- data/lib/rails_lens/providers/database_constraints_provider.rb +11 -0
- data/lib/rails_lens/providers/delegated_types_provider.rb +11 -0
- data/lib/rails_lens/providers/enums_provider.rb +11 -0
- data/lib/rails_lens/providers/extension_notes_provider.rb +20 -0
- data/lib/rails_lens/providers/extensions_provider.rb +22 -0
- data/lib/rails_lens/providers/foreign_key_notes_provider.rb +11 -0
- data/lib/rails_lens/providers/generated_columns_provider.rb +11 -0
- data/lib/rails_lens/providers/index_notes_provider.rb +20 -0
- data/lib/rails_lens/providers/inheritance_provider.rb +23 -0
- data/lib/rails_lens/providers/notes_provider_base.rb +25 -0
- data/lib/rails_lens/providers/performance_notes_provider.rb +11 -0
- data/lib/rails_lens/providers/schema_provider.rb +61 -0
- data/lib/rails_lens/providers/section_provider_base.rb +28 -0
- data/lib/rails_lens/railtie.rb +17 -0
- data/lib/rails_lens/rake_bootstrapper.rb +18 -0
- data/lib/rails_lens/route/annotator.rb +268 -0
- data/lib/rails_lens/route/extractor.rb +133 -0
- data/lib/rails_lens/route/parser.rb +59 -0
- data/lib/rails_lens/schema/adapters/base.rb +345 -0
- data/lib/rails_lens/schema/adapters/database_info.rb +118 -0
- data/lib/rails_lens/schema/adapters/mysql.rb +279 -0
- data/lib/rails_lens/schema/adapters/postgresql.rb +197 -0
- data/lib/rails_lens/schema/adapters/sqlite3.rb +96 -0
- data/lib/rails_lens/schema/annotation.rb +144 -0
- data/lib/rails_lens/schema/annotation_manager.rb +202 -0
- data/lib/rails_lens/tasks/annotate.rake +35 -0
- data/lib/rails_lens/tasks/erd.rake +24 -0
- data/lib/rails_lens/tasks/mailers.rake +27 -0
- data/lib/rails_lens/tasks/routes.rake +27 -0
- data/lib/rails_lens/tasks/schema.rake +108 -0
- data/lib/rails_lens/version.rb +5 -0
- data/lib/rails_lens.rb +138 -5
- metadata +215 -11
@@ -0,0 +1,345 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsLens
|
4
|
+
module Schema
|
5
|
+
module Adapters
|
6
|
+
class Base
|
7
|
+
attr_reader :connection, :table_name
|
8
|
+
|
9
|
+
def initialize(connection, table_name)
|
10
|
+
@connection = connection
|
11
|
+
@table_name = table_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate_annotation(_model_class)
|
15
|
+
lines = []
|
16
|
+
lines << "table = \"#{table_name}\""
|
17
|
+
lines << "database_dialect = \"#{database_dialect}\""
|
18
|
+
lines << ''
|
19
|
+
|
20
|
+
add_columns_toml(lines)
|
21
|
+
add_indexes_toml(lines) if show_indexes?
|
22
|
+
add_foreign_keys_toml(lines) if show_foreign_keys?
|
23
|
+
add_check_constraints_toml(lines) if show_check_constraints?
|
24
|
+
|
25
|
+
lines.join("\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
delegate :adapter_name, to: :connection
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def database_dialect
|
33
|
+
RailsLens::Connection.database_dialect(connection)
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_columns(lines)
|
37
|
+
lines << 'Columns:'
|
38
|
+
columns.each do |column|
|
39
|
+
lines << format_column(column)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_indexes(lines)
|
44
|
+
indexes = fetch_indexes
|
45
|
+
return if indexes.empty?
|
46
|
+
|
47
|
+
lines << '' unless lines.last && lines.last.empty?
|
48
|
+
lines << 'Indexes:'
|
49
|
+
indexes.each do |index|
|
50
|
+
lines << format_index(index)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_foreign_keys(lines)
|
55
|
+
foreign_keys = fetch_foreign_keys
|
56
|
+
return if foreign_keys.empty?
|
57
|
+
|
58
|
+
lines << '' unless lines.last && lines.last.empty?
|
59
|
+
lines << 'Foreign Keys:'
|
60
|
+
foreign_keys.each do |fk|
|
61
|
+
lines << format_foreign_key(fk)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_check_constraints(lines)
|
66
|
+
constraints = fetch_check_constraints
|
67
|
+
return if constraints.empty?
|
68
|
+
|
69
|
+
lines << '' unless lines.last && lines.last.empty?
|
70
|
+
lines << 'Check Constraints:'
|
71
|
+
constraints.each do |constraint|
|
72
|
+
lines << format_check_constraint(constraint)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def columns
|
77
|
+
@columns ||= connection.columns(table_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
def fetch_indexes
|
81
|
+
connection.indexes(table_name)
|
82
|
+
end
|
83
|
+
|
84
|
+
def fetch_foreign_keys
|
85
|
+
if connection.supports_foreign_keys?
|
86
|
+
connection.foreign_keys(table_name)
|
87
|
+
else
|
88
|
+
[]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def fetch_check_constraints
|
93
|
+
# Override in database-specific adapters
|
94
|
+
[]
|
95
|
+
end
|
96
|
+
|
97
|
+
def format_column(column)
|
98
|
+
parts = []
|
99
|
+
parts << column.name.ljust(column_name_width)
|
100
|
+
parts << ":#{column.type.to_s.ljust(12)}"
|
101
|
+
|
102
|
+
attributes = []
|
103
|
+
attributes << 'not null' unless column.null
|
104
|
+
attributes << 'primary key' if primary_key?(column)
|
105
|
+
attributes << "default: #{format_default(column.default)}" if column.default && show_defaults?
|
106
|
+
|
107
|
+
parts << attributes.join(', ') unless attributes.empty?
|
108
|
+
|
109
|
+
" #{parts.join(' ')}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def format_index(index)
|
113
|
+
unique = index.unique ? ' UNIQUE' : ''
|
114
|
+
columns = Array(index.columns).join(', ')
|
115
|
+
" #{index.name} (#{columns})#{unique}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def format_foreign_key(fk)
|
119
|
+
" #{fk.name} (#{fk.column} => #{fk.to_table}.#{fk.primary_key})"
|
120
|
+
end
|
121
|
+
|
122
|
+
def format_check_constraint(constraint)
|
123
|
+
" #{constraint[:name]}: #{constraint[:expression]}"
|
124
|
+
end
|
125
|
+
|
126
|
+
def format_default(default)
|
127
|
+
case default
|
128
|
+
when String
|
129
|
+
%("#{default}")
|
130
|
+
when NilClass
|
131
|
+
'nil'
|
132
|
+
else
|
133
|
+
default.inspect
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def primary_key?(column)
|
138
|
+
column.name == primary_key_name
|
139
|
+
end
|
140
|
+
|
141
|
+
def primary_key_name
|
142
|
+
@primary_key_name ||= connection.primary_key(table_name)
|
143
|
+
end
|
144
|
+
|
145
|
+
def column_name_width
|
146
|
+
@column_name_width ||= columns.map { |c| c.name.length }.max || 0
|
147
|
+
end
|
148
|
+
|
149
|
+
def show_indexes?
|
150
|
+
RailsLens.config.schema[:format_options][:show_indexes]
|
151
|
+
end
|
152
|
+
|
153
|
+
def show_foreign_keys?
|
154
|
+
RailsLens.config.schema[:format_options][:show_foreign_keys] &&
|
155
|
+
connection.supports_foreign_keys?
|
156
|
+
end
|
157
|
+
|
158
|
+
def show_check_constraints?
|
159
|
+
RailsLens.config.schema[:format_options][:show_check_constraints] &&
|
160
|
+
connection.supports_check_constraints?
|
161
|
+
end
|
162
|
+
|
163
|
+
def show_defaults?
|
164
|
+
RailsLens.config.schema[:format_options][:show_defaults]
|
165
|
+
end
|
166
|
+
|
167
|
+
def show_comments?
|
168
|
+
RailsLens.config.schema[:format_options][:show_comments] &&
|
169
|
+
connection.supports_comments?
|
170
|
+
end
|
171
|
+
|
172
|
+
# Structured formatting methods
|
173
|
+
def add_columns_structured(lines)
|
174
|
+
lines << 'COLUMNS:'
|
175
|
+
columns.each do |column|
|
176
|
+
lines << format_column_structured(column)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def add_indexes_structured(lines)
|
181
|
+
indexes = fetch_indexes
|
182
|
+
return if indexes.empty?
|
183
|
+
|
184
|
+
lines << 'INDEXES:'
|
185
|
+
indexes.each do |index|
|
186
|
+
lines << format_index_structured(index)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def add_foreign_keys_structured(lines)
|
191
|
+
foreign_keys = fetch_foreign_keys
|
192
|
+
return if foreign_keys.empty?
|
193
|
+
|
194
|
+
lines << 'FOREIGN_KEYS:'
|
195
|
+
foreign_keys.each do |fk|
|
196
|
+
lines << format_foreign_key_structured(fk)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def add_check_constraints_structured(lines)
|
201
|
+
constraints = fetch_check_constraints
|
202
|
+
return if constraints.empty?
|
203
|
+
|
204
|
+
lines << 'CHECK_CONSTRAINTS:'
|
205
|
+
constraints.each do |constraint|
|
206
|
+
lines << format_check_constraint_structured(constraint)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def format_column_structured(column)
|
211
|
+
attributes = []
|
212
|
+
attributes << column.type.to_s
|
213
|
+
attributes << 'primary_key' if column.name == 'id' || (column.name.end_with?('_id') && column.type == :bigint)
|
214
|
+
attributes << (column.null ? 'nullable' : 'not_null')
|
215
|
+
attributes << "default: #{column.default}" if column.default && show_defaults?
|
216
|
+
|
217
|
+
" #{column.name}: #{attributes.join(', ')}"
|
218
|
+
end
|
219
|
+
|
220
|
+
def format_index_structured(index)
|
221
|
+
attributes = []
|
222
|
+
attributes << "columns: [#{index.columns.join(', ')}]"
|
223
|
+
attributes << 'unique' if index.unique
|
224
|
+
attributes << "type: #{index.type}" if index.respond_to?(:type) && index.type
|
225
|
+
|
226
|
+
" #{index.name}: #{attributes.join(', ')}"
|
227
|
+
end
|
228
|
+
|
229
|
+
def format_foreign_key_structured(fk)
|
230
|
+
" #{fk.column}: references #{fk.to_table}(#{fk.primary_key})"
|
231
|
+
end
|
232
|
+
|
233
|
+
def format_check_constraint_structured(constraint)
|
234
|
+
if constraint.is_a?(Hash)
|
235
|
+
" #{constraint[:name]}: #{constraint[:expression]}"
|
236
|
+
else
|
237
|
+
" #{constraint.name}: #{constraint.expression}"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# TOML formatting methods
|
242
|
+
def add_columns_toml(lines)
|
243
|
+
lines << 'columns = ['
|
244
|
+
columns.each_with_index do |column, index|
|
245
|
+
line = ' { '
|
246
|
+
attrs = []
|
247
|
+
attrs << "name = \"#{column.name}\""
|
248
|
+
attrs << "type = \"#{column.type}\""
|
249
|
+
attrs << 'primary_key = true' if primary_key?(column)
|
250
|
+
attrs << "nullable = #{column.null}"
|
251
|
+
attrs << "default = #{format_toml_value(column.default)}" if column.default && show_defaults?
|
252
|
+
line += attrs.join(', ')
|
253
|
+
line += ' }'
|
254
|
+
line += ',' if index < columns.length - 1
|
255
|
+
lines << line
|
256
|
+
end
|
257
|
+
lines << ']'
|
258
|
+
end
|
259
|
+
|
260
|
+
def add_indexes_toml(lines)
|
261
|
+
indexes = fetch_indexes
|
262
|
+
return if indexes.empty?
|
263
|
+
|
264
|
+
lines << ''
|
265
|
+
lines << 'indexes = ['
|
266
|
+
indexes.each_with_index do |index, i|
|
267
|
+
line = ' { '
|
268
|
+
attrs = []
|
269
|
+
attrs << "name = \"#{index.name}\""
|
270
|
+
attrs << "columns = [#{index.columns.map { |c| "\"#{c}\"" }.join(', ')}]"
|
271
|
+
attrs << 'unique = true' if index.unique
|
272
|
+
attrs << "type = \"#{index.type}\"" if index.respond_to?(:type) && index.type
|
273
|
+
line += attrs.join(', ')
|
274
|
+
line += ' }'
|
275
|
+
line += ',' if i < indexes.length - 1
|
276
|
+
lines << line
|
277
|
+
end
|
278
|
+
lines << ']'
|
279
|
+
end
|
280
|
+
|
281
|
+
def add_foreign_keys_toml(lines)
|
282
|
+
foreign_keys = fetch_foreign_keys
|
283
|
+
return if foreign_keys.empty?
|
284
|
+
|
285
|
+
lines << ''
|
286
|
+
lines << 'foreign_keys = ['
|
287
|
+
foreign_keys.each_with_index do |fk, i|
|
288
|
+
line = ' { '
|
289
|
+
attrs = []
|
290
|
+
attrs << "column = \"#{fk.column}\""
|
291
|
+
attrs << "references_table = \"#{fk.to_table}\""
|
292
|
+
attrs << "references_column = \"#{fk.primary_key}\""
|
293
|
+
attrs << "name = \"#{fk.name}\"" if fk.respond_to?(:name) && fk.name
|
294
|
+
attrs << "on_delete = \"#{fk.on_delete}\"" if fk.respond_to?(:on_delete) && fk.on_delete
|
295
|
+
attrs << "on_update = \"#{fk.on_update}\"" if fk.respond_to?(:on_update) && fk.on_update
|
296
|
+
line += attrs.join(', ')
|
297
|
+
line += ' }'
|
298
|
+
line += ',' if i < foreign_keys.length - 1
|
299
|
+
lines << line
|
300
|
+
end
|
301
|
+
lines << ']'
|
302
|
+
end
|
303
|
+
|
304
|
+
def add_check_constraints_toml(lines)
|
305
|
+
constraints = fetch_check_constraints
|
306
|
+
return if constraints.empty?
|
307
|
+
|
308
|
+
lines << ''
|
309
|
+
lines << 'check_constraints = ['
|
310
|
+
constraints.each_with_index do |constraint, i|
|
311
|
+
line = ' { '
|
312
|
+
attrs = []
|
313
|
+
if constraint.is_a?(Hash)
|
314
|
+
attrs << "name = \"#{constraint[:name]}\""
|
315
|
+
attrs << "expression = \"#{constraint[:expression]}\""
|
316
|
+
else
|
317
|
+
attrs << "name = \"#{constraint.name}\""
|
318
|
+
attrs << "expression = \"#{constraint.expression}\""
|
319
|
+
end
|
320
|
+
line += attrs.join(', ')
|
321
|
+
line += ' }'
|
322
|
+
line += ',' if i < constraints.length - 1
|
323
|
+
lines << line
|
324
|
+
end
|
325
|
+
lines << ']'
|
326
|
+
end
|
327
|
+
|
328
|
+
def format_toml_value(value)
|
329
|
+
case value
|
330
|
+
when String
|
331
|
+
"\"#{value.gsub('"', '\"')}\""
|
332
|
+
when NilClass
|
333
|
+
'null'
|
334
|
+
when TrueClass, FalseClass
|
335
|
+
value.to_s
|
336
|
+
when Numeric
|
337
|
+
value.to_s
|
338
|
+
else
|
339
|
+
"\"#{value}\""
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsLens
|
4
|
+
module Schema
|
5
|
+
module Adapters
|
6
|
+
class DatabaseInfo
|
7
|
+
attr_reader :connection, :adapter_name
|
8
|
+
|
9
|
+
def initialize(connection)
|
10
|
+
@connection = connection
|
11
|
+
@adapter_name = connection.adapter_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate_annotation
|
15
|
+
lines = []
|
16
|
+
lines << '== Database Information'
|
17
|
+
lines << "Adapter: #{adapter_name}"
|
18
|
+
lines << "Database: #{database_name}"
|
19
|
+
lines << "Version: #{database_version}"
|
20
|
+
lines << "Encoding: #{database_encoding}" if respond_to?(:database_encoding)
|
21
|
+
lines << "Collation: #{database_collation}" if respond_to?(:database_collation)
|
22
|
+
lines << ''
|
23
|
+
|
24
|
+
# Add extensions for PostgreSQL
|
25
|
+
if adapter_name == 'PostgreSQL' && extensions.any?
|
26
|
+
lines << 'Enabled Extensions:'
|
27
|
+
extensions.each do |ext|
|
28
|
+
lines << " - #{ext['name']} (#{ext['version']})"
|
29
|
+
end
|
30
|
+
lines << ''
|
31
|
+
end
|
32
|
+
|
33
|
+
# Add schemas for PostgreSQL
|
34
|
+
if adapter_name == 'PostgreSQL' && schemas.any?
|
35
|
+
lines << 'Database Schemas:'
|
36
|
+
schemas.each do |schema|
|
37
|
+
lines << " - #{schema}"
|
38
|
+
end
|
39
|
+
lines << ''
|
40
|
+
end
|
41
|
+
|
42
|
+
lines.join("\n")
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def database_name
|
48
|
+
connection.current_database
|
49
|
+
rescue StandardError
|
50
|
+
'N/A'
|
51
|
+
end
|
52
|
+
|
53
|
+
def database_version
|
54
|
+
case adapter_name
|
55
|
+
when 'PostgreSQL'
|
56
|
+
connection.select_value('SELECT version()').split[1]
|
57
|
+
when 'Mysql2'
|
58
|
+
connection.select_value('SELECT VERSION()')
|
59
|
+
when 'SQLite'
|
60
|
+
connection.select_value('SELECT sqlite_version()')
|
61
|
+
else
|
62
|
+
'Unknown'
|
63
|
+
end
|
64
|
+
rescue StandardError
|
65
|
+
'Unknown'
|
66
|
+
end
|
67
|
+
|
68
|
+
def database_encoding
|
69
|
+
case adapter_name
|
70
|
+
when 'PostgreSQL'
|
71
|
+
connection.select_value('SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()')
|
72
|
+
when 'Mysql2'
|
73
|
+
connection.select_value('SELECT DEFAULT_CHARACTER_SET_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = DATABASE()')
|
74
|
+
end
|
75
|
+
rescue StandardError
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def database_collation
|
80
|
+
case adapter_name
|
81
|
+
when 'PostgreSQL'
|
82
|
+
connection.select_value('SELECT datcollate FROM pg_database WHERE datname = current_database()')
|
83
|
+
when 'Mysql2'
|
84
|
+
connection.select_value('SELECT DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = DATABASE()')
|
85
|
+
end
|
86
|
+
rescue StandardError
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def extensions
|
91
|
+
return [] unless adapter_name == 'PostgreSQL'
|
92
|
+
|
93
|
+
connection.select_all(<<-SQL.squish).to_a
|
94
|
+
SELECT extname as name, extversion as version
|
95
|
+
FROM pg_extension
|
96
|
+
WHERE extname NOT IN ('plpgsql')
|
97
|
+
ORDER BY extname
|
98
|
+
SQL
|
99
|
+
rescue StandardError
|
100
|
+
[]
|
101
|
+
end
|
102
|
+
|
103
|
+
def schemas
|
104
|
+
return [] unless adapter_name == 'PostgreSQL'
|
105
|
+
|
106
|
+
connection.select_values(<<-SQL.squish)
|
107
|
+
SELECT schema_name#{' '}
|
108
|
+
FROM information_schema.schemata#{' '}
|
109
|
+
WHERE schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
|
110
|
+
ORDER BY schema_name
|
111
|
+
SQL
|
112
|
+
rescue StandardError
|
113
|
+
[]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|