brick 1.0.54 → 1.0.55
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/brick/extensions.rb +9 -13
- data/lib/brick/version_number.rb +1 -1
- data/lib/generators/brick/migrations_generator.rb +106 -38
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 430a2dfa5ef0caee9b99bc341ccd42a9d29e8f1d54071284aa2469e1baa0c659
|
4
|
+
data.tar.gz: b36e18de169bd9032e6c383548ec30cce06eeb97a35e8c3e4f4da935aff8f603
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c51491575b9ee56c3789619bfc74a2d9c0525750687224e94e120cbae42fad8db1abda9d82978115d9266c55fa0c6f914ce273a3613915657d8a0cf4e7fe6aeb
|
7
|
+
data.tar.gz: a0646cfdc9a2c844fd1149968d0175346c48835c6245d7fe92d0126d60708cf76514704e29dc3f506d2e06e546c8c995bf7103d6ef42be481bb931b4d2bfa73f
|
data/lib/brick/extensions.rb
CHANGED
@@ -1316,16 +1316,21 @@ module ActiveRecord::ConnectionHandling
|
|
1316
1316
|
# puts ActiveRecord::Base.execute_sql("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
1317
1317
|
|
1318
1318
|
is_postgres = nil
|
1319
|
-
schema_sql = 'SELECT NULL AS table_schema;'
|
1320
1319
|
case ActiveRecord::Base.connection.adapter_name
|
1321
1320
|
when 'PostgreSQL'
|
1322
1321
|
is_postgres = true
|
1322
|
+
db_schemas = ActiveRecord::Base.execute_sql('SELECT DISTINCT table_schema FROM INFORMATION_SCHEMA.tables;')
|
1323
|
+
::Brick.db_schemas = db_schemas.each_with_object({}) do |row, s|
|
1324
|
+
row = row.is_a?(String) ? row : row['table_schema']
|
1325
|
+
# Remove any system schemas
|
1326
|
+
s[row] = nil unless ['information_schema', 'pg_catalog'].include?(row)
|
1327
|
+
end
|
1323
1328
|
if (is_multitenant = (multitenancy = ::Brick.config.schema_behavior[:multitenant]) &&
|
1324
|
-
(sta = multitenancy[:schema_to_analyse]) != 'public')
|
1329
|
+
(sta = multitenancy[:schema_to_analyse]) != 'public') &&
|
1330
|
+
::Brick.db_schemas.include?(sta)
|
1325
1331
|
::Brick.default_schema = schema = sta
|
1326
1332
|
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", schema)
|
1327
1333
|
end
|
1328
|
-
schema_sql = 'SELECT DISTINCT table_schema FROM INFORMATION_SCHEMA.tables;'
|
1329
1334
|
when 'Mysql2'
|
1330
1335
|
::Brick.default_schema = schema = ActiveRecord::Base.connection.current_database
|
1331
1336
|
when 'SQLite'
|
@@ -1342,16 +1347,7 @@ module ActiveRecord::ConnectionHandling
|
|
1342
1347
|
puts "Unfamiliar with connection adapter #{ActiveRecord::Base.connection.adapter_name}"
|
1343
1348
|
end
|
1344
1349
|
|
1345
|
-
|
1346
|
-
db_schemas = db_schemas.to_a
|
1347
|
-
end
|
1348
|
-
unless db_schemas.empty?
|
1349
|
-
::Brick.db_schemas = db_schemas.each_with_object({}) do |row, s|
|
1350
|
-
row = row.is_a?(String) ? row : row['table_schema']
|
1351
|
-
# Remove any system schemas
|
1352
|
-
s[row] = nil unless ['information_schema', 'pg_catalog'].include?(row)
|
1353
|
-
end
|
1354
|
-
end
|
1350
|
+
::Brick.db_schemas ||= []
|
1355
1351
|
|
1356
1352
|
if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
1357
1353
|
if (possible_schema = ::Brick.config.schema_behavior&.[](:multitenant)&.[](:schema_to_analyse))
|
data/lib/brick/version_number.rb
CHANGED
@@ -10,11 +10,17 @@ module Brick
|
|
10
10
|
include FancyGets
|
11
11
|
# include ::Rails::Generators::Migration
|
12
12
|
|
13
|
-
# SQL types that are the same as their migration data type name: text, integer, bigint,
|
14
|
-
SQL_TYPES = { 'character varying' =>'string',
|
15
|
-
'
|
16
|
-
'
|
17
|
-
'
|
13
|
+
# SQL types that are the same as their migration data type name: text, integer, bigint, date, boolean, decimal, float
|
14
|
+
SQL_TYPES = { 'character varying' => 'string',
|
15
|
+
'character' => 'string', # %%% Need to put in "limit: 1"
|
16
|
+
'xml' => 'text',
|
17
|
+
'bytea' => 'binary',
|
18
|
+
'timestamp without time zone' => 'timestamp',
|
19
|
+
'timestamp with time zone' => 'timestamp',
|
20
|
+
'time without time zone' => 'time',
|
21
|
+
'time with time zone' => 'time',
|
22
|
+
'double precision' => 'float', # might work with 'double'
|
23
|
+
'smallint' => 'integer' } # %%% Need to put in "limit: 2"
|
18
24
|
# (Still need to find what "inet" and "json" data types map to.)
|
19
25
|
|
20
26
|
# # source_root File.expand_path('templates', __dir__)
|
@@ -38,24 +44,22 @@ module Brick
|
|
38
44
|
return
|
39
45
|
end
|
40
46
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
default_mig_path = (mig_path = ActiveRecord::Migrator.migrations_paths.first || "#{File.expand_path(__dir__)}/db/migrate")
|
47
|
+
key_type = (ActiveRecord.version < ::Gem::Version.new('5.1') ? 'integer' : 'bigint')
|
48
|
+
is_4x_rails = ActiveRecord.version < ::Gem::Version.new('5.0')
|
49
|
+
ar_version = "[#{ActiveRecord.version.segments[0..1].join('.')}]" unless is_4x_rails
|
50
|
+
default_mig_path = (mig_path = ActiveRecord::Migrator.migrations_paths.first || "#{::Rails.root}/db/migrate")
|
47
51
|
if Dir.exist?(mig_path)
|
48
52
|
if Dir["#{mig_path}/**/*.rb"].present?
|
49
53
|
puts "WARNING: migrations folder #{mig_path} appears to already have ruby files present."
|
50
|
-
mig_path2 = "#{
|
54
|
+
mig_path2 = "#{::Rails.root}/tmp/brick_migrations"
|
51
55
|
if Dir.exist?(mig_path2)
|
52
56
|
if Dir["#{mig_path2}/**/*.rb"].present?
|
53
57
|
puts "As well, temporary folder #{mig_path2} also has ruby files present."
|
54
58
|
puts "Choose a destination -- all existing .rb files will be removed:"
|
55
|
-
mig_path2 = gets_list(list: ['Cancel operation!', "
|
59
|
+
mig_path2 = gets_list(list: ['Cancel operation!', "Append migration files into #{mig_path} anyway", mig_path, mig_path2])
|
56
60
|
return if mig_path2.start_with?('Cancel')
|
57
61
|
|
58
|
-
if mig_path2.start_with?('
|
62
|
+
if mig_path2.start_with?('Append migration files into ')
|
59
63
|
mig_path2 = mig_path
|
60
64
|
else
|
61
65
|
Dir["#{mig_path2}/**/*.rb"].each { |rb| File.delete(rb) }
|
@@ -78,7 +82,7 @@ module Brick
|
|
78
82
|
|
79
83
|
# Generate a list of tables that can be chosen
|
80
84
|
chosen = gets_list(list: tables, chosen: tables.dup)
|
81
|
-
#
|
85
|
+
# Start the timestamps back the same number of minutes from now as expected number of migrations to create
|
82
86
|
current_mig_time = Time.now - chosen.length.minutes
|
83
87
|
done = []
|
84
88
|
fks = {}
|
@@ -87,48 +91,110 @@ module Brick
|
|
87
91
|
# Continue layer by layer, creating migrations for tables that reference ones already done, until
|
88
92
|
# no more migrations can be created. (At that point hopefully all tables are accounted for.)
|
89
93
|
while (fringe = chosen.reject do |tbl|
|
90
|
-
snags = ::Brick.relations
|
94
|
+
snags = ::Brick.relations.fetch(tbl, nil)&.fetch(:fks, nil)&.select do |_k, v|
|
91
95
|
v[:is_bt] && !v[:polymorphic] &&
|
92
96
|
tbl != v[:inverse_table] && # Ignore self-referencing associations (stuff like "parent_id")
|
93
97
|
!done.include?(v[:inverse_table])
|
94
98
|
end
|
95
|
-
stuck[tbl] = snags if snags
|
99
|
+
stuck[tbl] = snags if snags&.present?
|
96
100
|
end).present?
|
97
101
|
fringe.each do |tbl|
|
98
|
-
|
99
|
-
|
102
|
+
next unless (relation = ::Brick.relations.fetch(tbl, nil))&.fetch(:cols, nil)&.present?
|
103
|
+
|
104
|
+
pkey_cols = (rpk = relation[:pkey].values.flatten) & (arpk = [ActiveRecord::Base.primary_key].flatten)
|
105
|
+
# In case things aren't as standard
|
106
|
+
if pkey_cols.empty?
|
107
|
+
pkey_cols = if rpk.empty? && relation[:cols][arpk.first]&.first == key_type
|
108
|
+
arpk
|
109
|
+
elsif rpk.first
|
110
|
+
rpk
|
111
|
+
end
|
112
|
+
end
|
113
|
+
schema = if (tbl_parts = tbl.split('.')).length > 1
|
114
|
+
if tbl_parts.first == 'public'
|
115
|
+
tbl_parts.shift
|
116
|
+
nil
|
117
|
+
else
|
118
|
+
tbl_parts.first
|
119
|
+
end
|
120
|
+
end
|
100
121
|
# %%% For the moment we're skipping polymorphics
|
101
|
-
fkey_cols =
|
102
|
-
mig = +"class Create#{
|
103
|
-
#
|
104
|
-
# also
|
105
|
-
|
122
|
+
fkey_cols = relation[:fks].values.select { |assoc| assoc[:is_bt] && !assoc[:polymorphic] }
|
123
|
+
mig = +"class Create#{(full_table_name = tbl_parts.join('_')).camelize} < ActiveRecord::Migration#{ar_version}\n"
|
124
|
+
# Support missing primary key (by adding: ,id: false)
|
125
|
+
# also integer / uuid / other non-standard data types for primary key
|
126
|
+
id_option = unless (pkey_col_first = relation[:cols][pkey_cols&.first]&.first) == key_type
|
127
|
+
unless pkey_cols&.present?
|
128
|
+
', id: false'
|
129
|
+
else
|
130
|
+
case pkey_col_first
|
131
|
+
when 'integer'
|
132
|
+
', id: :serial'
|
133
|
+
when 'bigint'
|
134
|
+
', id: :bigserial'
|
135
|
+
else
|
136
|
+
", id: :#{SQL_TYPES[pkey_col_first] || pkey_col_first}" # Something like: id: :integer, primary_key: :businessentityid
|
137
|
+
end + (pkey_cols.first ? ", primary_key: :#{pkey_cols.first}" : '')
|
138
|
+
end
|
139
|
+
end
|
140
|
+
# Refer to this table name as a symbol or dotted string as appropriate
|
141
|
+
tbl = tbl_parts.length == 1 ? ":#{tbl_parts.first}" : "'#{tbl}'"
|
142
|
+
mig << " def change\n return unless reverting? || !table_exists?(#{tbl})\n\n"
|
143
|
+
mig << " create_schema :#{schema} unless schema_exists?(:#{schema})\n" if schema
|
144
|
+
mig << " create_table #{tbl}#{id_option} do |t|\n"
|
106
145
|
possible_ts = [] # Track possible generic timestamps
|
107
|
-
|
108
|
-
|
146
|
+
add_fks = [] # Track foreign keys to add after table creation
|
147
|
+
relation[:cols].each do |col, col_type|
|
148
|
+
next if !id_option&.end_with?('id: false') && pkey_cols.include?(col)
|
109
149
|
|
110
150
|
# See if there are generic timestamps
|
111
|
-
if (sql_type = SQL_TYPES[col_type.first]) == 'timestamp' &&
|
112
|
-
|
151
|
+
if (sql_type = SQL_TYPES[col_type.first]) == 'timestamp' &&
|
152
|
+
['created_at','updated_at'].include?(col)
|
153
|
+
possible_ts << [col, !col_type[3]]
|
113
154
|
next
|
114
155
|
end
|
115
156
|
|
116
157
|
sql_type ||= col_type.first
|
158
|
+
suffix = col_type[3] ? +', null: false' : +''
|
117
159
|
# Determine if this column is used as part of a foreign key
|
118
160
|
if fk = fkey_cols.find { |assoc| col == assoc[:fk] }
|
119
|
-
|
161
|
+
to_table = fk[:inverse_table].split('.')
|
162
|
+
to_table = to_table.length == 1 ? ":#{to_table.first}" : "'#{fk[:inverse_table]}'"
|
163
|
+
if fk[:fk] != "#{fk[:assoc_name].singularize}_id" # Need to do our own foreign_key tricks, not use references?
|
164
|
+
column = fk[:fk]
|
165
|
+
mig << " t.#{sql_type} :#{column}#{suffix}\n"
|
166
|
+
add_fks << [to_table, column, ::Brick.relations[fk[:inverse_table]]]
|
167
|
+
else
|
168
|
+
suffix << ", type: :#{sql_type}" unless sql_type == key_type
|
169
|
+
mig << " t.references :#{fk[:assoc_name]}#{suffix}, foreign_key: { to_table: #{to_table} }\n"
|
170
|
+
end
|
120
171
|
else
|
121
|
-
mig << emit_column(sql_type, col)
|
172
|
+
mig << emit_column(sql_type, col, suffix)
|
122
173
|
end
|
123
174
|
end
|
124
|
-
if possible_ts.length == 2 # Both created_at and updated_at
|
175
|
+
if possible_ts.length == 2 && # Both created_at and updated_at
|
176
|
+
# Rails 5 and later timestamps default to NOT NULL
|
177
|
+
(possible_ts.first.last == is_4x_rails && possible_ts.last.last == is_4x_rails)
|
125
178
|
mig << "\n t.timestamps\n"
|
126
|
-
else # Just one or the other
|
127
|
-
possible_ts.each { |ts| emit_column('timestamp', ts) }
|
179
|
+
else # Just one or the other, or a nullability mismatch
|
180
|
+
possible_ts.each { |ts| emit_column('timestamp', ts.first, nil) }
|
128
181
|
end
|
129
|
-
mig << " end\n
|
182
|
+
mig << " end\n"
|
183
|
+
add_fks.each do |add_fk|
|
184
|
+
is_commented = false
|
185
|
+
# add_fk[2] holds the inverse relation
|
186
|
+
unless (pk = add_fk[2][:pkey].values.flatten&.first)
|
187
|
+
is_commented = true
|
188
|
+
mig << " # (Unable to create relationship because primary key is missing on table #{add_fk[0]})\n"
|
189
|
+
# No official PK, but if coincidentally there's a column of the same name, take a chance on it
|
190
|
+
pk = (add_fk[2][:cols].key?(add_fk[1]) && add_fk[1]) || '???'
|
191
|
+
end
|
192
|
+
# to_table column
|
193
|
+
mig << " #{'# ' if is_commented}add_foreign_key #{tbl}, #{add_fk[0]}, column: :#{add_fk[1]}, primary_key: :#{pk}\n"
|
194
|
+
end
|
195
|
+
mig << " end\nend\n"
|
130
196
|
current_mig_time += 1.minute
|
131
|
-
File.open("#{mig_path}/#{current_mig_time.strftime('%Y%m%d%H%M00')}_create_#{
|
197
|
+
File.open("#{mig_path}/#{current_mig_time.strftime('%Y%m%d%H%M00')}_create_#{full_table_name}.rb", "w") { |f| f.write mig }
|
132
198
|
end
|
133
199
|
done.concat(fringe)
|
134
200
|
chosen -= done
|
@@ -140,7 +206,9 @@ module Brick
|
|
140
206
|
snag.last[:assoc_name]
|
141
207
|
end.join(', ')}"
|
142
208
|
end
|
143
|
-
|
209
|
+
if mig_path.start_with?(cur_path = ::Rails.root.to_s)
|
210
|
+
pretty_mig_path = mig_path[cur_path.length..-1]
|
211
|
+
end
|
144
212
|
puts "*** Created #{done.length} migration files under #{pretty_mig_path || mig_path} ***"
|
145
213
|
if (stuck_sorted = stuck_counts.to_a.sort { |a, b| b.last <=> a.last }).length.positive?
|
146
214
|
puts "-----------------------------------------"
|
@@ -153,8 +221,8 @@ module Brick
|
|
153
221
|
|
154
222
|
private
|
155
223
|
|
156
|
-
def emit_column(type, name)
|
157
|
-
" t.#{type.start_with?('numeric') ? 'decimal' : type} :#{name}\n"
|
224
|
+
def emit_column(type, name, suffix)
|
225
|
+
" t.#{type.start_with?('numeric') ? 'decimal' : type} :#{name}#{suffix}\n"
|
158
226
|
end
|
159
227
|
end
|
160
228
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.55
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lorin Thwaits
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08-
|
11
|
+
date: 2022-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|