brick 1.0.245 → 1.0.247
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/lib/brick/extensions.rb +128 -97
- data/lib/brick/rails/engine.rb +43 -38
- data/lib/brick/rails/form_builder.rb +18 -16
- data/lib/brick/rails/form_tags.rb +78 -36
- data/lib/brick/rails.rb +2 -2
- data/lib/brick/reflect_tables.rb +12 -7
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +1 -1
- data/lib/generators/brick/airtable_api_caller.rb +21 -7
- data/lib/generators/brick/airtable_migrations_generator.rb +3 -2
- data/lib/generators/brick/airtable_seeds_generator.rb +1 -1
- data/lib/generators/brick/install_generator.rb +1 -1
- data/lib/generators/brick/migrations_builder.rb +25 -12
- data/lib/generators/brick/seeds_builder.rb +75 -33
- metadata +3 -6
|
@@ -119,7 +119,8 @@ module Brick
|
|
|
119
119
|
stuck = {}
|
|
120
120
|
indexes = {} # Track index names to make sure things are unique
|
|
121
121
|
built_schemas = {} # Track all built schemas so we can place an appropriate drop_schema command only in the first
|
|
122
|
-
|
|
122
|
+
# migration in which that schema is referenced, thereby allowing rollbacks to function properly.
|
|
123
|
+
is_airtable = nil
|
|
123
124
|
versions_to_create = [] # Resulting versions to be used when updating the schema_migrations table
|
|
124
125
|
# Start by making migrations for fringe tables (those with no foreign keys).
|
|
125
126
|
# Continue layer by layer, creating migrations for tables that reference ones already done, until
|
|
@@ -148,8 +149,9 @@ module Brick
|
|
|
148
149
|
mig = gen_migration_columns(relations, tbl, (tbl_parts = tbl.split('.')), (add_fks = []), built_schemas, mig_path, current_mig_time,
|
|
149
150
|
key_type, is_4x_rails, ar_version, do_fks_last, versions_to_create)
|
|
150
151
|
after_fks.concat(add_fks) if do_fks_last
|
|
151
|
-
current_mig_time
|
|
152
|
-
|
|
152
|
+
increment_time(mig_path, current_mig_time)
|
|
153
|
+
is_airtable ||= (relation = relations[tbl])&.fetch(:airtable_table, nil)
|
|
154
|
+
versions_to_create << migration_file_write(mig_path, "create_#{::Brick._brick_index(tbl, nil, separator, relation, true)}", current_mig_time, ar_version, mig)
|
|
153
155
|
end
|
|
154
156
|
done.concat(fringe)
|
|
155
157
|
chosen -= done
|
|
@@ -161,15 +163,15 @@ module Brick
|
|
|
161
163
|
mig = gen_migration_columns(relations, tbl, (tbl_parts = tbl.split('.')), (add_fks = []), built_schemas, mig_path, current_mig_time,
|
|
162
164
|
key_type, is_4x_rails, ar_version, do_fks_last, versions_to_create)
|
|
163
165
|
after_fks.concat(add_fks)
|
|
164
|
-
current_mig_time
|
|
166
|
+
increment_time(mig_path, current_mig_time)
|
|
165
167
|
versions_to_create << migration_file_write(mig_path, "create_#{
|
|
166
|
-
::Brick._brick_index(tbl, :migration, separator, relations[tbl])
|
|
168
|
+
::Brick._brick_index(tbl, :migration, separator, relations[tbl], true)
|
|
167
169
|
}", current_mig_time, ar_version, mig)
|
|
168
170
|
end
|
|
169
171
|
done.concat(chosen)
|
|
170
172
|
chosen.clear
|
|
171
173
|
|
|
172
|
-
case do_fks_last
|
|
174
|
+
case after_fks.present? && do_fks_last
|
|
173
175
|
when 'Separate' # Add a final migration to create all the foreign keys
|
|
174
176
|
mig = +" def change\n"
|
|
175
177
|
after_fks.each do |add_fk|
|
|
@@ -201,9 +203,10 @@ module Brick
|
|
|
201
203
|
'rb'
|
|
202
204
|
end
|
|
203
205
|
mig << +" end\n"
|
|
204
|
-
current_mig_time
|
|
205
|
-
|
|
206
|
-
|
|
206
|
+
increment_time(mig_path, current_mig_time)
|
|
207
|
+
base_name = "_#{relations[:base_name]&.tr(' ', '')&.underscore}" if relations[:base_name]
|
|
208
|
+
versions_to_create << migration_file_write(mig_path, "create_brick_fks#{base_name}.#{fks_extension}", current_mig_time, ar_version, mig)
|
|
209
|
+
puts "Have written out a final migration called 'create_brick_fks#{base_name}.#{fks_extension}' which creates #{after_fks.length} foreign keys."
|
|
207
210
|
if fks_extension == 'rbx'
|
|
208
211
|
puts " This file extension (.rbx) will cause it not to run yet when you do a 'rails db:migrate'.
|
|
209
212
|
The idea here is to do all data loading first, and then rename that migration file back
|
|
@@ -247,7 +250,7 @@ module Brick
|
|
|
247
250
|
". Here's the top 5 blockers" if stuck_sorted.length > 5
|
|
248
251
|
}:"
|
|
249
252
|
pp stuck_sorted[0..4]
|
|
250
|
-
elsif do_schema_migrations # Successful, and now we can update the schema_migrations table accordingly
|
|
253
|
+
elsif do_schema_migrations && !is_airtable # Successful, and now we can update the schema_migrations table accordingly
|
|
251
254
|
smtn = ::Brick._schema_migrations_table_name
|
|
252
255
|
if ActiveRecord::Migration.table_exists?(smtn)
|
|
253
256
|
# Remove to_delete - to_create
|
|
@@ -298,7 +301,7 @@ module Brick
|
|
|
298
301
|
end
|
|
299
302
|
unless schema.blank? || built_schemas.key?(schema)
|
|
300
303
|
mig = +" def change\n create_schema(:#{schema}) unless schema_exists?(:#{schema})\n end\n"
|
|
301
|
-
current_mig_time
|
|
304
|
+
increment_time(mig_path, current_mig_time)
|
|
302
305
|
versions_to_create << migration_file_write(mig_path, "create_db_schema_#{schema.underscore}", current_mig_time, ar_version, mig)
|
|
303
306
|
built_schemas[schema] = nil
|
|
304
307
|
end
|
|
@@ -352,7 +355,8 @@ module Brick
|
|
|
352
355
|
mig = +" def change\n return unless reverting? || !table_exists?(#{tbl_code})\n\n"
|
|
353
356
|
mig << " create_table #{tbl_code}#{id_option} do |t|\n"
|
|
354
357
|
possible_ts = [] # Track possible generic timestamps
|
|
355
|
-
relation[:
|
|
358
|
+
(relation.key?(:col_order) && relation[:col_order].present? && relation[:col_order].map { |col_name| [col_name, relation[:cols][col_name]] } ||
|
|
359
|
+
relation[:cols]).each do |col, col_type|
|
|
356
360
|
sql_type = SQL_TYPES[col_type.first] || SQL_TYPES[col_type[0..1]] ||
|
|
357
361
|
SQL_TYPES.find { |r| r.first.is_a?(Regexp) && col_type.first =~ r.first }&.last ||
|
|
358
362
|
col_type.first
|
|
@@ -449,7 +453,16 @@ module Brick
|
|
|
449
453
|
" t.#{type.start_with?('numeric') ? 'decimal' : type} :#{name}#{suffix}\n"
|
|
450
454
|
end
|
|
451
455
|
|
|
456
|
+
def increment_time(mig_path, current_mig_time)
|
|
457
|
+
loop do
|
|
458
|
+
current_mig_time[0] += 1.minute
|
|
459
|
+
break if Dir.glob("#{mig_path}/#{current_mig_time.first.strftime('%Y%m%d%H%M00')}_*.*").empty?
|
|
460
|
+
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
|
|
452
464
|
def migration_file_write(mig_path, name, current_mig_time, ar_version, mig)
|
|
465
|
+
name.gsub!(/[^a-zA-Z0-9_\.\p{L}\p{N}]/, 'x') # Strip out any weird unicode characters
|
|
453
466
|
File.open("#{mig_path}/#{version = current_mig_time.first.strftime('%Y%m%d%H%M00')}_#{name}#{'.rb' unless name.index('.')}", "w") do |f|
|
|
454
467
|
f.write "class #{name.split('.').first.camelize} < ActiveRecord::Migration#{ar_version}\n"
|
|
455
468
|
f.write mig
|
|
@@ -13,9 +13,10 @@ module Brick
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def generate_seeds(relations = nil)
|
|
16
|
+
action = 'Overwrite'
|
|
16
17
|
if File.exist?((seed_file_path = "#{::Rails.root}/db/seed") + 's.rb')
|
|
17
|
-
puts "WARNING: seeds file #{seed_file_path}s.rb appears to already be present.\
|
|
18
|
-
return
|
|
18
|
+
puts "WARNING: seeds file #{seed_file_path}s.rb appears to already be present.\nAppend or Overwrite?"
|
|
19
|
+
return if (action = gets_list(list: ['Cancel', 'Append', 'Overwrite'])) == 'Cancel'
|
|
19
20
|
|
|
20
21
|
puts "\n"
|
|
21
22
|
end
|
|
@@ -24,7 +25,7 @@ module Brick
|
|
|
24
25
|
is_airtable = true # So far the only thing that feeds us relations is Airtable
|
|
25
26
|
require 'generators/brick/airtable_api_caller'
|
|
26
27
|
# include ::Brick::MigrationsBuilder
|
|
27
|
-
chosen = relations.
|
|
28
|
+
chosen = relations.each_with_object([]) { |v, s| s << SeedModel.new(v.first, nil, false, v.last[:airtable_table]) unless v.first.is_a?(Symbol) }
|
|
28
29
|
else
|
|
29
30
|
::Brick.mode = :on
|
|
30
31
|
ActiveRecord::Base.establish_connection
|
|
@@ -57,11 +58,14 @@ module Brick
|
|
|
57
58
|
end
|
|
58
59
|
end
|
|
59
60
|
batch_num = 1
|
|
60
|
-
seeds_file = File.open("#{seed_file_path}s.rb", 'w')
|
|
61
|
+
seeds_file = File.open("#{seed_file_path}s.rb", action == 'Overwrite' ? 'w' : 'a+')
|
|
62
|
+
seeds_file.write("\n") if action == 'Append'
|
|
61
63
|
seeds_len = lines_len = 0
|
|
62
64
|
seeds_file.write('# Seeds file for ')
|
|
63
65
|
seed_rows = nil
|
|
64
|
-
if
|
|
66
|
+
if is_airtable
|
|
67
|
+
seeds_file.write("Airtable Base \"#{relations[:base_name]}\":\n")
|
|
68
|
+
elsif (arbc = ActiveRecord::Base.connection).respond_to?(:current_database) # SQLite3 can't do this!
|
|
65
69
|
seeds_file.write("#{arbc.current_database}:\n")
|
|
66
70
|
elsif (filename = arbc.instance_variable_get(:@connection_parameters)&.fetch(:database, nil))
|
|
67
71
|
seeds_file.write("#{filename}:\n")
|
|
@@ -146,19 +150,38 @@ module Brick
|
|
|
146
150
|
|
|
147
151
|
has_rows = false
|
|
148
152
|
is_empty = true
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
153
|
+
collection = nil
|
|
154
|
+
klass_name = if is_airtable
|
|
155
|
+
airtable_table = relation[:airtable_table]
|
|
156
|
+
::Brick.config.table_name_prefixes&.fetch(tbl, nil) ||
|
|
157
|
+
tbl.singularize.camelize
|
|
158
|
+
else
|
|
159
|
+
# Must convert to a symbol when doing ORDER BY due to this ActiveRecord nuance:
|
|
160
|
+
# https://github.com/rails/rails/issues/2601
|
|
161
|
+
collection = klass.order(*pkey_cols.map(&:to_sym)).enum_for unless relation[:pkey].empty?
|
|
162
|
+
klass.name
|
|
163
|
+
end
|
|
160
164
|
seed_rows = +''
|
|
161
|
-
|
|
165
|
+
airtable_idx = offset = nil
|
|
166
|
+
while collection || airtable_table do
|
|
167
|
+
obj = if is_airtable
|
|
168
|
+
if collection.nil? || (airtable_idx += 1) >= collection.length
|
|
169
|
+
break if collection && !offset
|
|
170
|
+
|
|
171
|
+
offset_param = "?offset=#{offset}" if offset
|
|
172
|
+
result = ::Brick::AirtableApiCaller.https_get("https://api.airtable.com/v0/#{airtable_table.base_id}/#{airtable_table.id}#{offset_param}")
|
|
173
|
+
offset = result.fetch('offset', nil)
|
|
174
|
+
collection = result.fetch('records', nil)
|
|
175
|
+
airtable_idx = 0
|
|
176
|
+
end
|
|
177
|
+
collection[airtable_idx]
|
|
178
|
+
else
|
|
179
|
+
begin
|
|
180
|
+
collection.next
|
|
181
|
+
rescue StopIteration
|
|
182
|
+
break
|
|
183
|
+
end
|
|
184
|
+
end
|
|
162
185
|
if is_airtable
|
|
163
186
|
fields = obj['fields'].each_with_object({}) do |field, s|
|
|
164
187
|
if relation[:cols].keys.include?(col_name = ::Brick::AirtableApiCaller.sane_name(field.first))
|
|
@@ -178,7 +201,7 @@ module Brick
|
|
|
178
201
|
end
|
|
179
202
|
end
|
|
180
203
|
end
|
|
181
|
-
objects =
|
|
204
|
+
objects = airtable_table.objects
|
|
182
205
|
obj = objects[airtable_id = obj['id']] = AirtableObject.new(seed_model, obj['fields'], obj['createdTime'])
|
|
183
206
|
end
|
|
184
207
|
unless has_rows
|
|
@@ -204,7 +227,12 @@ module Brick
|
|
|
204
227
|
# Used to be: obj.send(col)
|
|
205
228
|
# (and with that it was possible to raise ActiveRecord::Encryption::Errors::Configuration...)
|
|
206
229
|
# %%% should test further and see if that is possible with this code!)
|
|
207
|
-
|
|
230
|
+
val = if is_airtable && col == 'airtable_id'
|
|
231
|
+
pk_val
|
|
232
|
+
else
|
|
233
|
+
obj.attributes_before_type_cast[col]
|
|
234
|
+
end
|
|
235
|
+
if val
|
|
208
236
|
if (val.is_a?(Time) || val.is_a?(Date))
|
|
209
237
|
val = val.to_s
|
|
210
238
|
elsif val.is_a?(Numeric)
|
|
@@ -283,28 +311,42 @@ end\n"
|
|
|
283
311
|
end
|
|
284
312
|
seed_rows = +''
|
|
285
313
|
end
|
|
286
|
-
unless has_rows ||
|
|
314
|
+
unless has_rows || (is_airtable && !airtable_table)
|
|
287
315
|
seeds_file.write(" # (Skipping #{klass_name} as it has no rows)\n")
|
|
288
316
|
end
|
|
289
317
|
end
|
|
290
318
|
done.concat(fringe)
|
|
291
319
|
chosen -= done
|
|
292
320
|
end
|
|
293
|
-
|
|
294
|
-
airtable_assoc_recids.
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
321
|
+
|
|
322
|
+
if airtable_assoc_recids.present?
|
|
323
|
+
seed_rows << "\n# Seeding #{airtable_assoc_recids.count} sets of Airtable associations:\n"
|
|
324
|
+
airtable_assocs_done = {}
|
|
325
|
+
dupe_list = []
|
|
326
|
+
airtable_assoc_recids.each do |table_name, assoc_pairs| # N:M links
|
|
327
|
+
# Make note of any other tables which have exactly duplicated data
|
|
328
|
+
dupes = airtable_assocs_done.select do |dupe_table, dupe_pairs|
|
|
329
|
+
dupe_table != table_name && assoc_pairs.length > 0 && assoc_pairs.length == dupe_pairs.length &&
|
|
330
|
+
dupe_pairs.all? { |pair_name, _v3| assoc_pairs.keys.include?(pair_name) }
|
|
331
|
+
end
|
|
332
|
+
seed_rows << " if ActiveRecord::Migration.table_exists?('#{table_name}')\n"
|
|
333
|
+
unless dupes.empty?
|
|
334
|
+
seed_rows << " # Duplicate data found in: #{dupes.keys.join(', ')}\n"
|
|
335
|
+
dupes.keys.each { |k| dupe_list << "#{table_name} | #{k}" }
|
|
336
|
+
end
|
|
337
|
+
seed_rows << " puts 'Airtable associations for #{table_name.camelize.singularize}'\n"
|
|
338
|
+
assoc_pairs.each do |_k2, pair_values|
|
|
339
|
+
seed_rows << "#{table_name.singularize.camelize}.create(#{pair_values})\n"
|
|
340
|
+
end
|
|
341
|
+
seed_rows << " end\n"
|
|
342
|
+
airtable_assocs_done[table_name] = assoc_pairs
|
|
299
343
|
end
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
344
|
+
|
|
345
|
+
if dupe_list.present?
|
|
346
|
+
puts "\nAssociative tables with duplicate data:"
|
|
347
|
+
dupe_list.each { |entry| puts " #{entry}" }
|
|
348
|
+
puts '(You can safely remove one of the two tables in each of the pairs listed above.)'
|
|
305
349
|
end
|
|
306
|
-
seed_rows << " end\n"
|
|
307
|
-
airtable_assocs_done[table_name] = assoc_pairs
|
|
308
350
|
end
|
|
309
351
|
seeds_file.write(seed_rows) unless seed_rows.blank?
|
|
310
352
|
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
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.247
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lorin Thwaits
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: activerecord
|
|
@@ -246,7 +245,6 @@ homepage: https://github.com/lorint/brick
|
|
|
246
245
|
licenses:
|
|
247
246
|
- MIT
|
|
248
247
|
metadata: {}
|
|
249
|
-
post_install_message:
|
|
250
248
|
rdoc_options: []
|
|
251
249
|
require_paths:
|
|
252
250
|
- lib
|
|
@@ -261,8 +259,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
261
259
|
- !ruby/object:Gem::Version
|
|
262
260
|
version: 1.3.6
|
|
263
261
|
requirements: []
|
|
264
|
-
rubygems_version:
|
|
265
|
-
signing_key:
|
|
262
|
+
rubygems_version: 4.0.18
|
|
266
263
|
specification_version: 4
|
|
267
264
|
summary: Create a Rails app from data alone
|
|
268
265
|
test_files: []
|