brick 1.0.154 → 1.0.156
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/config.rb +8 -0
- data/lib/brick/extensions.rb +4 -1
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +7 -2
- data/lib/generators/brick/migrations_generator.rb +19 -10
- data/lib/generators/brick/seeds_generator.rb +156 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e001ce24602c8fef817c24a0eb88e014e990d98828474acd66d868b50dd68692
|
|
4
|
+
data.tar.gz: 8378593754641ffc86059672fe6148ba767d91a867361b21ec8377a82903af6b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 84fa7d9831dfca26b9b08ef46bd852c3d4f96e1f8420ff086526f97964090b3f95d71b5d66d9ff7ba47ec5f74e50938c9714731d21d0c59ac2365305ab8826bc
|
|
7
|
+
data.tar.gz: cf790bbb66852142fe07cb1619517043dc3ef8c47b0f020b22bc635cd902fface9b52631507b2fdd9520b604c98eae00b58a1bad29ace185c8d342e79e99a062
|
data/lib/brick/config.rb
CHANGED
|
@@ -380,6 +380,14 @@ module Brick
|
|
|
380
380
|
@mutex.synchronize { @always_load_fields = field_set }
|
|
381
381
|
end
|
|
382
382
|
|
|
383
|
+
def ignore_migration_fks
|
|
384
|
+
@mutex.synchronize { @ignore_migration_fks || [] }
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def ignore_migration_fks=(relations)
|
|
388
|
+
@mutex.synchronize { @ignore_migration_fks = relations }
|
|
389
|
+
end
|
|
390
|
+
|
|
383
391
|
# Add status page showing all resources and what files have been built out for them
|
|
384
392
|
def add_status
|
|
385
393
|
true
|
data/lib/brick/extensions.rb
CHANGED
|
@@ -1464,7 +1464,6 @@ class Object
|
|
|
1464
1464
|
rescue StandardError => ex
|
|
1465
1465
|
::ActiveRecord::Base
|
|
1466
1466
|
end))
|
|
1467
|
-
|
|
1468
1467
|
end
|
|
1469
1468
|
hmts = nil
|
|
1470
1469
|
code = +"class #{full_name} < #{base_model.name}\n"
|
|
@@ -1489,6 +1488,10 @@ class Object
|
|
|
1489
1488
|
end
|
|
1490
1489
|
# Accommodate singular or camel-cased table names such as "order_detail" or "OrderDetails"
|
|
1491
1490
|
code << " self.table_name = '#{self.table_name = matching}'\n" if inheritable_name || self.table_name != matching
|
|
1491
|
+
if (inh_col = ::Brick.config.sti_type_column.find { |_k, v| v.include?(matching) }&.first)
|
|
1492
|
+
self.inheritance_column = inh_col
|
|
1493
|
+
code << " self.inheritance_column = '#{inh_col}'\n"
|
|
1494
|
+
end
|
|
1492
1495
|
|
|
1493
1496
|
# Override models backed by a view so they return true for #is_view?
|
|
1494
1497
|
# (Dynamically-created controllers and view templates for such models will then act in a read-only way)
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
|
@@ -1159,11 +1159,16 @@ ActiveSupport.on_load(:active_record) do
|
|
|
1159
1159
|
# :singleton-method:
|
|
1160
1160
|
# Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
|
|
1161
1161
|
# dates and times from the database. This is set to :utc by default.
|
|
1162
|
-
unless respond_to?(:default_timezone)
|
|
1163
|
-
puts "ADDING!!! 4.w"
|
|
1162
|
+
unless ::ActiveRecord.respond_to?(:default_timezone) || respond_to?(:default_timezone)
|
|
1164
1163
|
mattr_accessor :default_timezone, instance_writer: false
|
|
1165
1164
|
self.default_timezone = :utc
|
|
1166
1165
|
end
|
|
1166
|
+
|
|
1167
|
+
unless respond_to?(:primary_abstract_class)
|
|
1168
|
+
def self.primary_abstract_class
|
|
1169
|
+
self.abstract_class = true
|
|
1170
|
+
end
|
|
1171
|
+
end
|
|
1167
1172
|
end
|
|
1168
1173
|
|
|
1169
1174
|
# Rails < 4.0 cannot do #find_by, #find_or_create_by, or do #pluck on multiple columns, so here are the patches:
|
|
@@ -23,7 +23,8 @@ module Brick
|
|
|
23
23
|
'time with time zone' => 'time',
|
|
24
24
|
'double precision' => 'float',
|
|
25
25
|
'smallint' => 'integer', # %%% Need to put in "limit: 2"
|
|
26
|
-
|
|
26
|
+
'ARRAY' => 'string', # Note that we'll also add ", array: true"
|
|
27
|
+
# Oracle data types
|
|
27
28
|
'VARCHAR2' => 'string',
|
|
28
29
|
'CHAR' => 'string',
|
|
29
30
|
['NUMBER', 22] => 'integer',
|
|
@@ -123,21 +124,27 @@ module Brick
|
|
|
123
124
|
built_schemas = {} # Track all built schemas so we can place an appropriate drop_schema command only in the first
|
|
124
125
|
# migration in which that schema is referenced, thereby allowing rollbacks to function properly.
|
|
125
126
|
versions_to_create = [] # Resulting versions to be used when updating the schema_migrations table
|
|
127
|
+
ar_base = Object.const_defined?(:ApplicationRecord) ? ApplicationRecord : Class.new(ActiveRecord::Base)
|
|
126
128
|
# Start by making migrations for fringe tables (those with no foreign keys).
|
|
127
129
|
# Continue layer by layer, creating migrations for tables that reference ones already done, until
|
|
128
130
|
# no more migrations can be created. (At that point hopefully all tables are accounted for.)
|
|
129
131
|
while (fringe = chosen.reject do |tbl|
|
|
132
|
+
snag_fks = []
|
|
130
133
|
snags = ::Brick.relations.fetch(tbl, nil)&.fetch(:fks, nil)&.select do |_k, v|
|
|
131
134
|
v[:is_bt] && !v[:polymorphic] &&
|
|
132
135
|
tbl != v[:inverse_table] && # Ignore self-referencing associations (stuff like "parent_id")
|
|
133
|
-
!done.include?(v[:inverse_table])
|
|
136
|
+
!done.include?(v[:inverse_table]) &&
|
|
137
|
+
::Brick.config.ignore_migration_fks.exclude?(snag_fk = "#{tbl}.#{v[:fk]}") &&
|
|
138
|
+
snag_fks << snag_fk
|
|
139
|
+
end
|
|
140
|
+
if snags&.present?
|
|
141
|
+
# puts snag_fks.inspect
|
|
142
|
+
stuck[tbl] = snags
|
|
134
143
|
end
|
|
135
|
-
stuck[tbl] = snags if snags&.present?
|
|
136
144
|
end).present?
|
|
137
145
|
fringe.each do |tbl|
|
|
138
146
|
next unless (relation = ::Brick.relations.fetch(tbl, nil))&.fetch(:cols, nil)&.present?
|
|
139
147
|
|
|
140
|
-
ar_base = Object.const_defined?(:ApplicationRecord) ? ApplicationRecord : Class.new(ActiveRecord::Base)
|
|
141
148
|
pkey_cols = (rpk = relation[:pkey].values.flatten) & (arpk = [ar_base.primary_key].flatten.sort)
|
|
142
149
|
# In case things aren't as standard
|
|
143
150
|
if pkey_cols.empty?
|
|
@@ -170,7 +177,7 @@ module Brick
|
|
|
170
177
|
# Support missing primary key (by adding: , id: false)
|
|
171
178
|
id_option = if pk_is_also_fk || !pkey_cols&.present?
|
|
172
179
|
needs_serial_col = true
|
|
173
|
-
', id: false'
|
|
180
|
+
+', id: false'
|
|
174
181
|
elsif ((pkey_col_first = (col_def = relation[:cols][pkey_cols&.first])&.first) &&
|
|
175
182
|
(pkey_col_first = SQL_TYPES[pkey_col_first] || SQL_TYPES[col_def&.[](0..1)] ||
|
|
176
183
|
SQL_TYPES.find { |r| r.first.is_a?(Regexp) && pkey_col_first =~ r.first }&.last ||
|
|
@@ -179,16 +186,16 @@ module Brick
|
|
|
179
186
|
)
|
|
180
187
|
case pkey_col_first
|
|
181
188
|
when 'integer'
|
|
182
|
-
', id: :serial'
|
|
189
|
+
+', id: :serial'
|
|
183
190
|
when 'bigint'
|
|
184
|
-
', id: :bigserial'
|
|
191
|
+
+', id: :bigserial'
|
|
185
192
|
else
|
|
186
|
-
", id: :#{pkey_col_first}" # Something like: id: :integer, primary_key: :businessentityid
|
|
193
|
+
+", id: :#{pkey_col_first}" # Something like: id: :integer, primary_key: :businessentityid
|
|
187
194
|
end +
|
|
188
195
|
(pkey_cols.first ? ", primary_key: :#{pkey_cols.first}" : '')
|
|
189
196
|
end
|
|
190
197
|
if !id_option && pkey_cols.sort != arpk
|
|
191
|
-
id_option = ", primary_key: :#{pkey_cols.first}"
|
|
198
|
+
id_option = +", primary_key: :#{pkey_cols.first}"
|
|
192
199
|
end
|
|
193
200
|
if !is_4x_rails && (comment = relation&.fetch(:description, nil))&.present?
|
|
194
201
|
(id_option ||= +'') << ", comment: #{comment.inspect}"
|
|
@@ -217,6 +224,7 @@ module Brick
|
|
|
217
224
|
SQL_TYPES.find { |r| r.first.is_a?(Regexp) && col_type.first =~ r.first }&.last ||
|
|
218
225
|
col_type.first
|
|
219
226
|
suffix = col_type[3] || pkey_cols&.include?(col) ? +', null: false' : +''
|
|
227
|
+
suffix << ', array: true' if (col_type.first == 'ARRAY')
|
|
220
228
|
if !is_4x_rails && klass && (comment = klass.columns_hash.fetch(col, nil)&.comment)&.present?
|
|
221
229
|
suffix << ", comment: #{comment.inspect}"
|
|
222
230
|
end
|
|
@@ -246,7 +254,8 @@ module Brick
|
|
|
246
254
|
suffix << ", index: { name: '#{shorter || idx_name}' }"
|
|
247
255
|
indexes[shorter || idx_name] = nil
|
|
248
256
|
end
|
|
249
|
-
|
|
257
|
+
primary_key = ::Brick.relations[fk[:inverse_table]][:class_name]&.constantize&.primary_key
|
|
258
|
+
mig << " t.references :#{fk[:assoc_name]}#{suffix}, foreign_key: { to_table: #{to_table}#{", primary_key: :#{primary_key}" if primary_key != ar_base.primary_key} }\n"
|
|
250
259
|
end
|
|
251
260
|
else
|
|
252
261
|
next if !id_option&.end_with?('id: false') && pkey_cols&.include?(col)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators'
|
|
4
|
+
require 'fancy_gets'
|
|
5
|
+
|
|
6
|
+
module Brick
|
|
7
|
+
class SeedsGenerator < ::Rails::Generators::Base
|
|
8
|
+
include FancyGets
|
|
9
|
+
|
|
10
|
+
desc 'Auto-generates a seeds file from existing data.'
|
|
11
|
+
|
|
12
|
+
def brick_seeds
|
|
13
|
+
# %%% If Apartment is active and there's no schema_to_analyse, ask which schema they want
|
|
14
|
+
|
|
15
|
+
::Brick.mode = :on
|
|
16
|
+
ActiveRecord::Base.establish_connection
|
|
17
|
+
|
|
18
|
+
if (tables = ::Brick.relations.reject { |k, v| v.key?(:isView) && v[:isView] == true }.map(&:first).sort).empty?
|
|
19
|
+
puts "No tables found in database #{ActiveRecord::Base.connection.current_database}."
|
|
20
|
+
return
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if File.exist?(seed_file_path = "#{::Rails.root}/db/seeds.rb")
|
|
24
|
+
puts "WARNING: seeds file #{seed_file_path} appears to already be present."
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Generate a list of tables that can be chosen
|
|
28
|
+
chosen = gets_list(list: tables, chosen: tables.dup)
|
|
29
|
+
schemas = chosen.each_with_object({}) do |v, s|
|
|
30
|
+
if (v_parts = v.split('.')).length > 1
|
|
31
|
+
s[v_parts.first] = nil unless [::Brick.default_schema, 'public'].include?(v_parts.first)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
seeds = +"# Seeds file for #{ActiveRecord::Base.connection.current_database}:\n"
|
|
35
|
+
done = []
|
|
36
|
+
fks = {}
|
|
37
|
+
stuck = {}
|
|
38
|
+
indexes = {} # Track index names to make sure things are unique
|
|
39
|
+
ar_base = Object.const_defined?(:ApplicationRecord) ? ApplicationRecord : Class.new(ActiveRecord::Base)
|
|
40
|
+
# Start by making entries for fringe models (those with no foreign keys).
|
|
41
|
+
# Continue layer by layer, creating entries for models that reference ones already done, until
|
|
42
|
+
# no more entries can be created. (At that point hopefully all models are accounted for.)
|
|
43
|
+
while (fringe = chosen.reject do |tbl|
|
|
44
|
+
snag_fks = []
|
|
45
|
+
snags = ::Brick.relations.fetch(tbl, nil)&.fetch(:fks, nil)&.select do |_k, v|
|
|
46
|
+
v[:is_bt] && !v[:polymorphic] &&
|
|
47
|
+
tbl != v[:inverse_table] && # Ignore self-referencing associations (stuff like "parent_id")
|
|
48
|
+
!done.include?(v[:inverse_table]) &&
|
|
49
|
+
::Brick.config.ignore_migration_fks.exclude?(snag_fk = "#{tbl}.#{v[:fk]}") &&
|
|
50
|
+
snag_fks << snag_fk
|
|
51
|
+
end
|
|
52
|
+
if snags&.present?
|
|
53
|
+
# puts snag_fks.inspect
|
|
54
|
+
stuck[tbl] = snags
|
|
55
|
+
end
|
|
56
|
+
end).present?
|
|
57
|
+
seeds << "\n"
|
|
58
|
+
fringe.each do |tbl|
|
|
59
|
+
next unless ::Brick.config.exclude_tables.exclude?(tbl) &&
|
|
60
|
+
(relation = ::Brick.relations.fetch(tbl, nil))&.fetch(:cols, nil)&.present? &&
|
|
61
|
+
(klass = Object.const_get(class_name = relation[:class_name])).table_exists?
|
|
62
|
+
|
|
63
|
+
pkey_cols = (rpk = relation[:pkey].values.flatten) & (arpk = [ar_base.primary_key].flatten.sort)
|
|
64
|
+
# In case things aren't as standard
|
|
65
|
+
if pkey_cols.empty?
|
|
66
|
+
pkey_cols = if rpk.empty? # && relation[:cols][arpk.first]&.first == key_type
|
|
67
|
+
arpk
|
|
68
|
+
elsif rpk.first
|
|
69
|
+
rpk
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
schema = if (tbl_parts = tbl.split('.')).length > 1
|
|
73
|
+
if tbl_parts.first == (::Brick.default_schema || 'public')
|
|
74
|
+
tbl_parts.shift
|
|
75
|
+
nil
|
|
76
|
+
else
|
|
77
|
+
tbl_parts.first
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# %%% For the moment we're skipping polymorphics
|
|
82
|
+
fkeys = relation[:fks].values.select { |assoc| assoc[:is_bt] && !assoc[:polymorphic] }
|
|
83
|
+
# Refer to this table name as a symbol or dotted string as appropriate
|
|
84
|
+
# tbl_code = tbl_parts.length == 1 ? ":#{tbl_parts.first}" : "'#{tbl}'"
|
|
85
|
+
|
|
86
|
+
has_rows = false
|
|
87
|
+
is_empty = true
|
|
88
|
+
klass.order(*pkey_cols).each do |obj|
|
|
89
|
+
unless has_rows
|
|
90
|
+
has_rows = true
|
|
91
|
+
seeds << " puts 'Seeding: #{class_name}'\n"
|
|
92
|
+
end
|
|
93
|
+
is_empty = false
|
|
94
|
+
pk_val = obj.send(pkey_cols.first)
|
|
95
|
+
fk_vals = []
|
|
96
|
+
data = []
|
|
97
|
+
relation[:cols].each do |col, col_type|
|
|
98
|
+
next if !(fk = fkeys.find { |assoc| col == assoc[:fk] }) &&
|
|
99
|
+
pkey_cols.include?(col)
|
|
100
|
+
|
|
101
|
+
if (val = obj.send(col)) && (val.is_a?(Time) || val.is_a?(Date))
|
|
102
|
+
val = val.to_s
|
|
103
|
+
end
|
|
104
|
+
if fk
|
|
105
|
+
inv_tbl = fk[:inverse_table].gsub('.', '__')
|
|
106
|
+
fk_vals << "#{fk[:assoc_name]}: #{inv_tbl}_#{brick_escape(val)}" if val
|
|
107
|
+
else
|
|
108
|
+
data << "#{col}: #{val.inspect}"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
seeds << "#{tbl.gsub('.', '__')}_#{brick_escape(pk_val)} = #{class_name}.create(#{(fk_vals + data).join(', ')})\n"
|
|
112
|
+
end
|
|
113
|
+
seeds << " # (Skipping #{class_name} as it has no rows)\n" unless has_rows
|
|
114
|
+
File.open(seed_file_path, "w") { |f| f.write seeds }
|
|
115
|
+
end
|
|
116
|
+
done.concat(fringe)
|
|
117
|
+
chosen -= done
|
|
118
|
+
end
|
|
119
|
+
stuck_counts = Hash.new { |h, k| h[k] = 0 }
|
|
120
|
+
chosen.each do |leftover|
|
|
121
|
+
puts "Can't do #{leftover} because:\n #{stuck[leftover].map do |snag|
|
|
122
|
+
stuck_counts[snag.last[:inverse_table]] += 1
|
|
123
|
+
snag.last[:assoc_name]
|
|
124
|
+
end.join(', ')}"
|
|
125
|
+
end
|
|
126
|
+
puts "\n*** Created seeds for #{done.length} models in db/seeds.rb ***"
|
|
127
|
+
if (stuck_sorted = stuck_counts.to_a.sort { |a, b| b.last <=> a.last }).length.positive?
|
|
128
|
+
puts "-----------------------------------------"
|
|
129
|
+
puts "Unable to create migrations for #{stuck_sorted.length} tables#{
|
|
130
|
+
". Here's the top 5 blockers" if stuck_sorted.length > 5
|
|
131
|
+
}:"
|
|
132
|
+
pp stuck_sorted[0..4]
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
def brick_escape(val)
|
|
139
|
+
val = val.to_s if val.is_a?(Date) || val.is_a?(Time) # Accommodate when for whatever reason a primary key is a date or time
|
|
140
|
+
case val
|
|
141
|
+
when String
|
|
142
|
+
ret = +''
|
|
143
|
+
val.each_char do |ch|
|
|
144
|
+
if ch < '0' || (ch > '9' && ch < 'A') || ch > 'Z'
|
|
145
|
+
ret << (ch == '_' ? ch : "x#{'K'.unpack('H*')[0]}")
|
|
146
|
+
else
|
|
147
|
+
ret << ch
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
ret
|
|
151
|
+
else
|
|
152
|
+
val
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
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.156
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lorin Thwaits
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-07-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -257,6 +257,7 @@ files:
|
|
|
257
257
|
- lib/generators/brick/install_generator.rb
|
|
258
258
|
- lib/generators/brick/migrations_generator.rb
|
|
259
259
|
- lib/generators/brick/models_generator.rb
|
|
260
|
+
- lib/generators/brick/seeds_generator.rb
|
|
260
261
|
- lib/generators/brick/templates/add_object_changes_to_versions.rb.erb
|
|
261
262
|
- lib/generators/brick/templates/create_versions.rb.erb
|
|
262
263
|
homepage: https://github.com/lorint/brick
|