brick 1.0.70 → 1.0.72
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 +114 -52
- data/lib/brick/frameworks/rails/engine.rb +5 -5
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +60 -2
- data/lib/generators/brick/migrations_generator.rb +42 -11
- 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: 06c3efed47ee7086f01a4f59c177f362343f376b1d6534f86e61ee38001b0d7b
|
4
|
+
data.tar.gz: 8b521c56eb5afc09b06aec0adf0085136cb9e94d2c1a93355a8a2fc79da0316b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1566ddaa89e45496b752b64ec218456e05dca50cc81a722d5a88e4d4bbe19b28ca981eeb253688f44e55a8ef9949d195bdc5a61f5ba7980b2507c315277b4d4d
|
7
|
+
data.tar.gz: 7643f78bf92721c89bdb0065f112592cba26023ea7745ba34551ce39d9378cd286f50b3ea1491c813e10f6d93521ef16c54fa70edc5dbcd4d70cb5ad6a859ad3
|
data/lib/brick/extensions.rb
CHANGED
@@ -68,13 +68,14 @@ module ActiveRecord
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def self._brick_primary_key(relation = nil)
|
71
|
-
return
|
71
|
+
return @_brick_primary_key if instance_variable_defined?(:@_brick_primary_key)
|
72
72
|
|
73
73
|
pk = begin
|
74
74
|
primary_key.is_a?(String) ? [primary_key] : primary_key || []
|
75
75
|
rescue
|
76
76
|
[]
|
77
77
|
end
|
78
|
+
pk.map! { |pk_part| pk_part =~ /^[A-Z0-9_]+$/ ? pk_part.downcase : pk_part } unless connection.adapter_name == 'MySQL2'
|
78
79
|
# Just return [] if we're missing any part of the primary key. (PK is usually just "id")
|
79
80
|
if relation && pk.present?
|
80
81
|
@_brick_primary_key ||= pk.any? { |pk_part| !relation[:cols].key?(pk_part) } ? [] : pk
|
@@ -99,13 +100,14 @@ module ActiveRecord
|
|
99
100
|
dsl
|
100
101
|
end
|
101
102
|
|
102
|
-
def self.brick_parse_dsl(build_array = nil, prefix = [], translations = {}, is_polymorphic = false)
|
103
|
+
def self.brick_parse_dsl(build_array = nil, prefix = [], translations = {}, emit_dsl = false, is_polymorphic = false)
|
103
104
|
build_array = ::Brick::JoinArray.new.tap { |ary| ary.replace([build_array]) } if build_array.is_a?(::Brick::JoinHash)
|
104
105
|
build_array = ::Brick::JoinArray.new unless build_array.nil? || build_array.is_a?(Array)
|
105
106
|
members = []
|
106
107
|
bracket_name = nil
|
107
108
|
prefix = [prefix] unless prefix.is_a?(Array)
|
108
109
|
if (dsl = ::Brick.config.model_descrips[name] || brick_get_dsl)
|
110
|
+
dsl2 = +''
|
109
111
|
klass = nil
|
110
112
|
dsl.each_char do |ch|
|
111
113
|
if bracket_name
|
@@ -125,7 +127,15 @@ module ActiveRecord
|
|
125
127
|
end
|
126
128
|
translations[parts[0..-2].join('.')] = klass
|
127
129
|
end
|
128
|
-
|
130
|
+
if klass.column_names.exclude?(parts.last) &&
|
131
|
+
(klass = (orig_class = klass).reflect_on_association(possible_dsl = parts.pop.to_sym)&.klass)
|
132
|
+
members2, dsl2a = klass.brick_parse_dsl(build_array, prefix + [possible_dsl], translations, true)
|
133
|
+
members += members2
|
134
|
+
dsl2 << dsl2a
|
135
|
+
else
|
136
|
+
dsl2 << "[#{bracket_name}]"
|
137
|
+
members << parts
|
138
|
+
end
|
129
139
|
bracket_name = nil
|
130
140
|
else
|
131
141
|
bracket_name << ch
|
@@ -133,13 +143,17 @@ module ActiveRecord
|
|
133
143
|
elsif ch == '['
|
134
144
|
bracket_name = +''
|
135
145
|
klass = self
|
146
|
+
else
|
147
|
+
dsl2 << ch
|
136
148
|
end
|
137
149
|
end
|
150
|
+
# Rewrite the DSL in case it's now different from having to expand it
|
151
|
+
::Brick.config.model_descrips[name] = dsl2 unless emit_dsl
|
138
152
|
else # With no DSL available, still put this prefix into the JoinArray so we can get primary key (ID) info from this table
|
139
153
|
x = prefix.each_with_object(build_array) { |v, s| s[v.to_sym] }
|
140
154
|
x[prefix.last] = nil unless prefix.empty? # Using []= will "hydrate" any missing part(s) in our whole series
|
141
155
|
end
|
142
|
-
members
|
156
|
+
emit_dsl ? [members, dsl2] : members
|
143
157
|
end
|
144
158
|
|
145
159
|
# If available, parse simple DSL attached to a model in order to provide a friendlier name.
|
@@ -169,9 +183,12 @@ module ActiveRecord
|
|
169
183
|
this_obj = caches.fetch(obj_name) { caches[obj_name] = this_obj&.send(part.to_sym) }
|
170
184
|
break if this_obj.nil?
|
171
185
|
end
|
186
|
+
if this_obj.is_a?(ActiveRecord::Base) && (obj_descrip = this_obj.class.brick_descrip(this_obj))
|
187
|
+
this_obj = obj_descrip
|
188
|
+
end
|
172
189
|
this_obj&.to_s || ''
|
173
190
|
end
|
174
|
-
is_brackets_have_content = true unless
|
191
|
+
is_brackets_have_content = true unless datum.blank?
|
175
192
|
output << (datum || '')
|
176
193
|
bracket_name = nil
|
177
194
|
else
|
@@ -244,7 +261,8 @@ module ActiveRecord
|
|
244
261
|
|
245
262
|
# join_array will receive this relation name when calling #brick_parse_dsl
|
246
263
|
_br_bt_descrip[bt.first] = if bt[1].is_a?(Array)
|
247
|
-
|
264
|
+
# Last two params here: "false" is for don't emit DSL, and "true" is for yes, we are polymorphic
|
265
|
+
bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations, false, true) }
|
248
266
|
else
|
249
267
|
{ bt.last => bt[1].brick_parse_dsl(join_array, bt.first, translations) }
|
250
268
|
end
|
@@ -303,7 +321,7 @@ module ActiveRecord
|
|
303
321
|
end
|
304
322
|
|
305
323
|
class Relation
|
306
|
-
attr_reader :_brick_chains
|
324
|
+
attr_reader :_brick_chains, :_arel_applied_aliases
|
307
325
|
|
308
326
|
# CLASS STUFF
|
309
327
|
def _recurse_arel(piece, prefix = '')
|
@@ -341,7 +359,7 @@ module ActiveRecord
|
|
341
359
|
# parent = piece.left == on.left.relation ? on.right.relation : on.left.relation
|
342
360
|
# binding.pry if piece.left.is_a?(Arel::Nodes::TableAlias)
|
343
361
|
if table.is_a?(Arel::Nodes::TableAlias)
|
344
|
-
alias_name = table.right
|
362
|
+
@_arel_applied_aliases << (alias_name = table.right)
|
345
363
|
table = table.left
|
346
364
|
end
|
347
365
|
(_brick_chains[table._arel_table_type] ||= []) << (alias_name || table.table_alias || table.name)
|
@@ -367,6 +385,7 @@ module ActiveRecord
|
|
367
385
|
|
368
386
|
# INSTANCE STUFF
|
369
387
|
def _arel_alias_names
|
388
|
+
@_arel_applied_aliases = []
|
370
389
|
# %%% If with Rails 3.1 and older you get "NoMethodError: undefined method `eq' for nil:NilClass"
|
371
390
|
# when trying to call relation.arel, then somewhere along the line while navigating a has_many
|
372
391
|
# relationship it can't find the proper foreign key.
|
@@ -414,10 +433,17 @@ module ActiveRecord
|
|
414
433
|
"`#{tbl_no_schema}`.`#{col_name}`#{col_alias}"
|
415
434
|
elsif is_postgres || is_mssql
|
416
435
|
# Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
|
417
|
-
cast_as_text = '::text' if is_distinct && Brick.relations[klass.table_name]&.[](:cols)&.[](
|
436
|
+
cast_as_text = '::text' if is_distinct && Brick.relations[klass.table_name]&.[](:cols)&.[](col_name)&.first&.start_with?('xml')
|
418
437
|
"\"#{tbl_no_schema}\".\"#{col_name}\"#{cast_as_text}#{col_alias}"
|
419
|
-
|
420
|
-
|
438
|
+
elsif col.type # Could be Sqlite or Oracle
|
439
|
+
if col_alias || !(/^[a-z0-9_]+$/ =~ col_name)
|
440
|
+
"#{tbl_no_schema}.\"#{col_name}\"#{col_alias}"
|
441
|
+
else
|
442
|
+
"#{tbl_no_schema}.#{col_name}"
|
443
|
+
end
|
444
|
+
else # Oracle with a custom data type
|
445
|
+
typ = col.sql_type
|
446
|
+
"'<#{typ.end_with?('_TYP') ? typ[0..-5] : typ}>' AS #{col.name}"
|
421
447
|
end
|
422
448
|
end
|
423
449
|
end
|
@@ -436,9 +462,14 @@ module ActiveRecord
|
|
436
462
|
next if chains[k1].nil?
|
437
463
|
|
438
464
|
tbl_name = (field_tbl_names[v.first][k1] ||= shift_or_first(chains[k1])).split('.').last
|
465
|
+
# If it's Oracle, quote any AREL aliases that had been applied
|
466
|
+
tbl_name = "\"#{tbl_name}\"" if ::Brick.is_oracle && rel_dupe._arel_applied_aliases.include?(tbl_name)
|
439
467
|
field_tbl_name = nil
|
440
468
|
v1.map { |x| [translations[x[0..-2].map(&:to_s).join('.')], x.last] }.each_with_index do |sel_col, idx|
|
469
|
+
# binding.pry if chains[sel_col.first].nil?
|
441
470
|
field_tbl_name = (field_tbl_names[v.first][sel_col.first] ||= shift_or_first(chains[sel_col.first])).split('.').last
|
471
|
+
# If it's Oracle, quote any AREL aliases that had been applied
|
472
|
+
field_tbl_name = "\"#{field_tbl_name}\"" if ::Brick.is_oracle && rel_dupe._arel_applied_aliases.include?(field_tbl_name)
|
442
473
|
|
443
474
|
# Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
|
444
475
|
is_xml = is_distinct && Brick.relations[sel_col.first.table_name]&.[](:cols)&.[](sel_col.last)&.first&.start_with?('xml')
|
@@ -709,9 +740,11 @@ Module.class_exec do
|
|
709
740
|
base_module == Object && # %%% This works for Person::Person -- but also limits us to not being able to allow more than one level of namespacing
|
710
741
|
(schema_name = [(singular_table_name = class_name.underscore),
|
711
742
|
(table_name = singular_table_name.pluralize),
|
712
|
-
class_name,
|
743
|
+
::Brick.is_oracle ? class_name.upcase : class_name,
|
713
744
|
(plural_class_name = class_name.pluralize)].find { |s| Brick.db_schemas.include?(s) }&.camelize ||
|
714
745
|
(::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}::") && class_name))
|
746
|
+
return self.const_get(schema_name) if self.const_defined?(schema_name)
|
747
|
+
|
715
748
|
# Build out a module for the schema if it's namespaced
|
716
749
|
# schema_name = schema_name.camelize
|
717
750
|
base_module.const_set(schema_name.to_sym, (built_module = Module.new))
|
@@ -1344,8 +1377,13 @@ class Object
|
|
1344
1377
|
[new_alt_name, true]
|
1345
1378
|
else
|
1346
1379
|
assoc_name = ::Brick.namify(hm_assoc[:inverse_table]).pluralize
|
1380
|
+
if (needs_class = assoc_name.include?('.')) # If there is a schema name present, use a downcased version for the :has_many
|
1381
|
+
assoc_parts = assoc_name.split('.')
|
1382
|
+
assoc_parts[0].downcase! if assoc_parts[0] =~ /^[A-Z0-9_]+$/
|
1383
|
+
assoc_name = assoc_parts.join('.')
|
1384
|
+
end
|
1347
1385
|
# hm_assoc[:assoc_name] = assoc_name
|
1348
|
-
[assoc_name,
|
1386
|
+
[assoc_name, needs_class]
|
1349
1387
|
end
|
1350
1388
|
end
|
1351
1389
|
end
|
@@ -1401,7 +1439,8 @@ module ActiveRecord::ConnectionHandling
|
|
1401
1439
|
row['table_schema']
|
1402
1440
|
end
|
1403
1441
|
# Remove any system schemas
|
1404
|
-
s[row] = nil unless ['information_schema', 'pg_catalog'
|
1442
|
+
s[row] = nil unless ['information_schema', 'pg_catalog',
|
1443
|
+
'INFORMATION_SCHEMA', 'sys'].include?(row)
|
1405
1444
|
end
|
1406
1445
|
if (is_multitenant = (multitenancy = ::Brick.config.schema_behavior[:multitenant]) &&
|
1407
1446
|
(sta = multitenancy[:schema_to_analyse]) != 'public') &&
|
@@ -1415,7 +1454,7 @@ module ActiveRecord::ConnectionHandling
|
|
1415
1454
|
# ActiveRecord::Base.connection.current_database will be something like "XEPDB1"
|
1416
1455
|
::Brick.default_schema = schema = ActiveRecord::Base.connection.raw_connection.username
|
1417
1456
|
::Brick.db_schemas = {}
|
1418
|
-
|
1457
|
+
ActiveRecord::Base.execute_sql("SELECT username FROM sys.all_users WHERE ORACLE_MAINTAINED != 'Y'").each { |s| ::Brick.db_schemas[s.first] = nil }
|
1419
1458
|
when 'SQLite'
|
1420
1459
|
sql = "SELECT m.name AS relation_name, UPPER(m.type) AS table_type,
|
1421
1460
|
p.name AS column_name, p.type AS data_type,
|
@@ -1445,6 +1484,7 @@ module ActiveRecord::ConnectionHandling
|
|
1445
1484
|
# ActiveRecord::Base.internal_metadata_table_name, ActiveRecord::Base.schema_migrations_table_name
|
1446
1485
|
# For if it's not SQLite -- so this is the Postgres and MySQL version
|
1447
1486
|
measures = []
|
1487
|
+
::Brick.is_oracle = true if ActiveRecord::Base.connection.adapter_name == 'OracleEnhanced'
|
1448
1488
|
case ActiveRecord::Base.connection.adapter_name
|
1449
1489
|
when 'PostgreSQL', 'SQLite' # These bring back a hash for each row because the query uses column aliases
|
1450
1490
|
# schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
@@ -1478,8 +1518,9 @@ module ActiveRecord::ConnectionHandling
|
|
1478
1518
|
schema_and_tables = case ActiveRecord::Base.connection.adapter_name
|
1479
1519
|
when 'OracleEnhanced'
|
1480
1520
|
sql =
|
1481
|
-
"SELECT c.owner AS schema, c.table_name AS relation_name,
|
1482
|
-
|
1521
|
+
"SELECT c.owner AS schema, c.table_name AS relation_name,
|
1522
|
+
CASE WHEN v.owner IS NULL THEN 'BASE_TABLE' ELSE 'VIEW' END AS table_type,
|
1523
|
+
c.column_name, c.data_type,
|
1483
1524
|
COALESCE(c.data_length, c.data_precision) AS max_length,
|
1484
1525
|
CASE ac.constraint_type WHEN 'P' THEN 'PRIMARY KEY' END AS const,
|
1485
1526
|
ac.constraint_name AS \"key\",
|
@@ -1487,21 +1528,29 @@ module ActiveRecord::ConnectionHandling
|
|
1487
1528
|
FROM all_tab_cols c
|
1488
1529
|
LEFT OUTER JOIN all_cons_columns acc ON acc.owner = c.owner AND acc.table_name = c.table_name AND acc.column_name = c.column_name
|
1489
1530
|
LEFT OUTER JOIN all_constraints ac ON ac.owner = acc.owner AND ac.table_name = acc.table_name AND ac.constraint_name = acc.constraint_name AND constraint_type = 'P'
|
1531
|
+
LEFT OUTER JOIN all_views v ON c.owner = v.owner AND c.table_name = v.view_name
|
1490
1532
|
WHERE c.owner IN (#{::Brick.db_schemas.keys.map { |s| "'#{s}'" }.join(', ')})
|
1533
|
+
AND c.table_name NOT IN (?, ?)
|
1491
1534
|
ORDER BY 1, 2, c.internal_column_id, acc.position"
|
1492
|
-
|
1535
|
+
ActiveRecord::Base.execute_sql(sql, *ar_tables)
|
1493
1536
|
else
|
1494
1537
|
ActiveRecord::Base.retrieve_schema_and_tables(sql)
|
1495
1538
|
end
|
1539
|
+
|
1496
1540
|
schema_and_tables.each do |r|
|
1497
1541
|
next if r[1].index('$') # Oracle can have goofy table names with $
|
1498
1542
|
|
1499
|
-
if (relation_name = r[1]) =~ /^[A-
|
1543
|
+
if (relation_name = r[1]) =~ /^[A-Z0-9_]+$/
|
1500
1544
|
relation_name.downcase!
|
1501
1545
|
end
|
1546
|
+
# Expect the default schema for SQL Server to be 'dbo'.
|
1547
|
+
if (::Brick.is_oracle && r[0] != schema) || (is_mssql && r[0] != 'dbo')
|
1548
|
+
relation_name = "#{r[0]}.#{relation_name}"
|
1549
|
+
end
|
1550
|
+
|
1502
1551
|
relation = relations[relation_name] # here relation represents a table or view from the database
|
1503
1552
|
relation[:isView] = true if r[2] == 'VIEW' # table_type
|
1504
|
-
col_name = r[3]
|
1553
|
+
col_name = ::Brick.is_oracle ? connection.send(:oracle_downcase, r[3]) : r[3]
|
1505
1554
|
key = case r[6] # constraint type
|
1506
1555
|
when 'PRIMARY KEY'
|
1507
1556
|
# key
|
@@ -1513,8 +1562,8 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1513
1562
|
end
|
1514
1563
|
key << col_name if key
|
1515
1564
|
cols = relation[:cols] # relation.fetch(:cols) { relation[:cols] = [] }
|
1516
|
-
# 'data_type', 'max_length'
|
1517
|
-
cols[col_name] = [r[4], r[5], measures&.include?(col_name)]
|
1565
|
+
# 'data_type', 'max_length', measure, 'is_nullable'
|
1566
|
+
cols[col_name] = [r[4], r[5], measures&.include?(col_name), r[8] == 'NO']
|
1518
1567
|
# puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
|
1519
1568
|
end
|
1520
1569
|
end
|
@@ -1567,10 +1616,10 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1567
1616
|
schemas = ::Brick.db_schemas.keys.map { |s| "'#{s}'" }.join(', ')
|
1568
1617
|
sql =
|
1569
1618
|
"SELECT -- fk
|
1570
|
-
ac.owner AS constraint_schema, acc_fk.table_name,
|
1619
|
+
ac.owner AS constraint_schema, acc_fk.table_name, acc_fk.column_name,
|
1571
1620
|
-- referenced pk
|
1572
1621
|
ac.r_owner AS primary_schema, acc_pk.table_name AS primary_table, acc_fk.constraint_name AS constraint_schema_fk
|
1573
|
-
-- ,
|
1622
|
+
-- , acc_pk.column_name
|
1574
1623
|
FROM all_cons_columns acc_fk
|
1575
1624
|
INNER JOIN all_constraints ac ON acc_fk.owner = ac.owner
|
1576
1625
|
AND acc_fk.constraint_name = ac.constraint_name
|
@@ -1579,7 +1628,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1579
1628
|
WHERE ac.constraint_type = 'R'
|
1580
1629
|
AND ac.owner IN (#{schemas})
|
1581
1630
|
AND ac.r_owner IN (#{schemas})"
|
1582
|
-
fk_references = ActiveRecord::Base.
|
1631
|
+
fk_references = ActiveRecord::Base.execute_sql(sql)
|
1583
1632
|
end
|
1584
1633
|
::Brick.is_oracle = true if ActiveRecord::Base.connection.adapter_name == 'OracleEnhanced'
|
1585
1634
|
# ::Brick.default_schema ||= schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
@@ -1588,49 +1637,71 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1588
1637
|
# Multitenancy makes things a little more general overall, except for non-tenanted tables
|
1589
1638
|
if apartment_excluded&.include?(::Brick.namify(fk[1]).singularize.camelize)
|
1590
1639
|
fk[0] = Apartment.default_schema
|
1591
|
-
elsif is_postgres && (fk[0] == 'public' || (is_multitenant && fk[0] == schema)) ||
|
1592
|
-
|
1640
|
+
elsif (is_postgres && (fk[0] == 'public' || (is_multitenant && fk[0] == schema))) ||
|
1641
|
+
(::Brick.is_oracle && fk[0] == schema) ||
|
1642
|
+
(is_mssql && fk[0] == 'dbo') ||
|
1643
|
+
(!is_postgres && !::Brick.is_oracle && !is_mssql && ['mysql', 'performance_schema', 'sys'].exclude?(fk[0]))
|
1593
1644
|
fk[0] = nil
|
1594
1645
|
end
|
1595
1646
|
if apartment_excluded&.include?(fk[4].singularize.camelize)
|
1596
1647
|
fk[3] = Apartment.default_schema
|
1597
|
-
elsif is_postgres && (fk[3] == 'public' || (is_multitenant && fk[3] == schema)) ||
|
1598
|
-
|
1648
|
+
elsif (is_postgres && (fk[3] == 'public' || (is_multitenant && fk[3] == schema))) ||
|
1649
|
+
(::Brick.is_oracle && fk[3] == schema) ||
|
1650
|
+
(is_mssql && fk[3] == 'dbo') ||
|
1651
|
+
(!is_postgres && !::Brick.is_oracle && !is_mssql && ['mysql', 'performance_schema', 'sys'].exclude?(fk[3]))
|
1599
1652
|
fk[3] = nil
|
1600
1653
|
end
|
1601
|
-
|
1602
|
-
|
1654
|
+
if ::Brick.is_oracle
|
1655
|
+
fk[1].downcase! if fk[1] =~ /^[A-Z0-9_]+$/
|
1656
|
+
fk[4].downcase! if fk[4] =~ /^[A-Z0-9_]+$/
|
1657
|
+
fk[2] = connection.send(:oracle_downcase, fk[2])
|
1658
|
+
end
|
1603
1659
|
::Brick._add_bt_and_hm(fk, relations)
|
1604
1660
|
end
|
1605
1661
|
end
|
1606
1662
|
|
1607
1663
|
tables = []
|
1608
1664
|
views = []
|
1665
|
+
table_class_length = 0
|
1666
|
+
view_class_length = 0
|
1609
1667
|
relations.each do |k, v|
|
1610
1668
|
name_parts = k.split('.')
|
1611
1669
|
idx = 1
|
1612
1670
|
name_parts = name_parts.map do |x|
|
1613
|
-
(
|
1671
|
+
(idx += 1) < name_parts.length ? x : x.singularize
|
1614
1672
|
end
|
1673
|
+
name_parts.shift if apartment && name_parts.length > 1 && name_parts.first == Apartment.default_schema
|
1674
|
+
class_name = name_parts.map(&:camelize).join('::')
|
1615
1675
|
if v.key?(:isView)
|
1676
|
+
view_class_length = class_name.length if class_name.length > view_class_length
|
1616
1677
|
views
|
1617
1678
|
else
|
1618
|
-
|
1679
|
+
table_class_length = class_name.length if class_name.length > table_class_length
|
1619
1680
|
tables
|
1620
|
-
end << name_parts
|
1681
|
+
end << [class_name, name_parts]
|
1621
1682
|
end
|
1622
|
-
|
1623
|
-
|
1624
|
-
|
1683
|
+
puts "\n" if tables.present? || views.present?
|
1684
|
+
if tables.present?
|
1685
|
+
puts "Classes that can be built from tables:"
|
1686
|
+
display_classes(tables, table_class_length)
|
1625
1687
|
end
|
1626
|
-
|
1627
|
-
puts "
|
1628
|
-
views
|
1688
|
+
if views.present?
|
1689
|
+
puts "Classes that can be built from views:"
|
1690
|
+
display_classes(views, view_class_length)
|
1629
1691
|
end
|
1630
1692
|
|
1631
1693
|
::Brick.load_additional_references if initializer_loaded
|
1632
1694
|
end
|
1633
1695
|
|
1696
|
+
def display_classes(rels, max_length)
|
1697
|
+
rels.sort.each do |rel|
|
1698
|
+
rel_link = rel.last.dup.map(&:underscore)
|
1699
|
+
rel_link[-1] = rel_link[-1].pluralize
|
1700
|
+
puts "#{rel.first}#{' ' * (max_length - rel.first.length)} /#{rel_link.join('/')}"
|
1701
|
+
end
|
1702
|
+
puts "\n"
|
1703
|
+
end
|
1704
|
+
|
1634
1705
|
def retrieve_schema_and_tables(sql = nil, is_postgres = nil, is_mssql = nil, schema = nil)
|
1635
1706
|
is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer' if is_mssql.nil?
|
1636
1707
|
sql ||= "SELECT t.table_schema AS \"schema\", t.table_name AS relation_name, t.table_type,#{"
|
@@ -1660,7 +1731,8 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1660
1731
|
-- AND kcu.position_in_unique_constraint IS NULL" unless is_mssql}
|
1661
1732
|
AND kcu.ordinal_position = c.ordinal_position
|
1662
1733
|
WHERE t.table_schema #{is_postgres || is_mssql ?
|
1663
|
-
"NOT IN ('information_schema', 'pg_catalog'
|
1734
|
+
"NOT IN ('information_schema', 'pg_catalog',
|
1735
|
+
'INFORMATION_SCHEMA', 'sys')"
|
1664
1736
|
:
|
1665
1737
|
"= '#{ActiveRecord::Base.connection.current_database.tr("'", "''")}'"}#{"
|
1666
1738
|
AND t.table_schema = COALESCE(current_setting('SEARCH_PATH'), 'public')" if is_postgres && schema }
|
@@ -1679,16 +1751,6 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1679
1751
|
ar_imtn = ActiveRecord.version >= ::Gem::Version.new('5.0') ? ActiveRecord::Base.internal_metadata_table_name : ''
|
1680
1752
|
[ar_smtn, ar_imtn]
|
1681
1753
|
end
|
1682
|
-
|
1683
|
-
def execute_oracle(*sql_args)
|
1684
|
-
cursor = ActiveRecord::Base.execute_sql(*sql_args)
|
1685
|
-
result = []
|
1686
|
-
while row = cursor.fetch()
|
1687
|
-
result << row
|
1688
|
-
end
|
1689
|
-
cursor.close
|
1690
|
-
result
|
1691
|
-
end
|
1692
1754
|
end
|
1693
1755
|
|
1694
1756
|
# ==========================================
|
@@ -1715,7 +1777,7 @@ module Brick
|
|
1715
1777
|
|
1716
1778
|
class << self
|
1717
1779
|
def _add_bt_and_hm(fk, relations, is_polymorphic = false, is_optional = false)
|
1718
|
-
bt_assoc_name = ::Brick.namify(fk[2])
|
1780
|
+
bt_assoc_name = ::Brick.namify(fk[2], true)
|
1719
1781
|
unless is_polymorphic
|
1720
1782
|
bt_assoc_name = if bt_assoc_name.underscore.end_with?('_id')
|
1721
1783
|
bt_assoc_name[-3] == '_' ? bt_assoc_name[0..-4] : bt_assoc_name[0..-3]
|
@@ -87,9 +87,9 @@ module Brick
|
|
87
87
|
|
88
88
|
def path_keys(hm_assoc, fk_name, obj_name, pk)
|
89
89
|
keys = if fk_name.is_a?(Array) && pk.is_a?(Array) # Composite keys?
|
90
|
-
fk_name.zip(pk.map { |
|
90
|
+
fk_name.zip(pk.map { |fk_part| "#{obj_name}.#{fk_part}" })
|
91
91
|
else
|
92
|
-
pk = pk.
|
92
|
+
pk = pk.map { |pk_part| "#{obj_name}.#{pk_part}" }
|
93
93
|
[[fk_name, pk.length == 1 ? pk.first : pk.inspect]]
|
94
94
|
end
|
95
95
|
keys << [hm_assoc.inverse_of.foreign_type, hm_assoc.active_record.name] if hm_assoc.options.key?(:as)
|
@@ -106,8 +106,8 @@ module Brick
|
|
106
106
|
return _brick_find_template(*args, **options)
|
107
107
|
end
|
108
108
|
|
109
|
-
|
110
|
-
pk = @_brick_model._brick_primary_key(::Brick.relations.fetch(
|
109
|
+
if @_brick_model
|
110
|
+
pk = @_brick_model._brick_primary_key(::Brick.relations.fetch(@_brick_model&.table_name, nil))
|
111
111
|
obj_name = model_name.split('::').last.underscore
|
112
112
|
path_obj_name = model_name.underscore.tr('/', '_')
|
113
113
|
table_name = obj_name.pluralize
|
@@ -1090,9 +1090,9 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
|
|
1090
1090
|
mermaid.initialize({
|
1091
1091
|
startOnLoad: true,
|
1092
1092
|
securityLevel: \"loose\",
|
1093
|
+
er: { useMaxWidth: false },
|
1093
1094
|
mermaid: {callback: function(objId) {
|
1094
1095
|
var svg = document.getElementById(objId);
|
1095
|
-
svg.removeAttribute(\"width\");
|
1096
1096
|
var cb;
|
1097
1097
|
for(cb in cbs) {
|
1098
1098
|
var gErd = svg.getElementById(cb);
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -152,7 +152,8 @@ module Brick
|
|
152
152
|
end
|
153
153
|
|
154
154
|
# Convert spaces to underscores if the second character and onwards is mixed case
|
155
|
-
def namify(name)
|
155
|
+
def namify(name, attempt_downcase = false)
|
156
|
+
name.downcase! if attempt_downcase && name =~ /^[A-Z0-9_]+$/
|
156
157
|
if name.include?(' ')
|
157
158
|
# All uppers or all lowers?
|
158
159
|
if name[1..-1] =~ /^[A-Z0-9_]+$/ || name[1..-1] =~ /^[a-z0-9_]+$/
|
@@ -560,7 +561,7 @@ ActiveSupport.on_load(:active_record) do
|
|
560
561
|
class << self
|
561
562
|
def execute_sql(sql, *param_array)
|
562
563
|
param_array = param_array.first if param_array.length == 1 && param_array.first.is_a?(Array)
|
563
|
-
if ActiveRecord::Base.connection.adapter_name
|
564
|
+
if ['OracleEnhanced', 'SQLServer'].include?(ActiveRecord::Base.connection.adapter_name)
|
564
565
|
connection.exec_query(send(:sanitize_sql_array, [sql] + param_array)).rows
|
565
566
|
else
|
566
567
|
connection.execute(send(:sanitize_sql_array, [sql] + param_array))
|
@@ -726,6 +727,63 @@ ActiveSupport.on_load(:active_record) do
|
|
726
727
|
end
|
727
728
|
end
|
728
729
|
end
|
730
|
+
|
731
|
+
# Migration stuff
|
732
|
+
module ConnectionAdapters
|
733
|
+
# Override the downcasing implementation from the OracleEnhanced gem as it has bad regex
|
734
|
+
if const_defined?(:OracleEnhanced)
|
735
|
+
module OracleEnhanced::Quoting
|
736
|
+
private
|
737
|
+
|
738
|
+
def oracle_downcase(column_name)
|
739
|
+
return nil if column_name.nil?
|
740
|
+
|
741
|
+
/^[A-Za-z0-9_]+$/ =~ column_name ? column_name.downcase : column_name
|
742
|
+
end
|
743
|
+
end
|
744
|
+
end
|
745
|
+
if const_defined?(:SQLServerAdapter)
|
746
|
+
class SQLServer::TableDefinition
|
747
|
+
alias _brick_new_column_definition new_column_definition
|
748
|
+
def new_column_definition(name, type, **options)
|
749
|
+
case type
|
750
|
+
when :serial
|
751
|
+
type = :integer
|
752
|
+
options[:is_identity] = true
|
753
|
+
when :bigserial
|
754
|
+
type = :bigint
|
755
|
+
options[:is_identity] = true
|
756
|
+
end
|
757
|
+
_brick_new_column_definition(name, type, **options)
|
758
|
+
end
|
759
|
+
def serial(*args)
|
760
|
+
options = args.extract_options!
|
761
|
+
options[:is_identity] = true
|
762
|
+
args.each { |name| column(name, 'integer', options) }
|
763
|
+
end
|
764
|
+
def bigserial(*args)
|
765
|
+
options = args.extract_options!
|
766
|
+
options[:is_identity] = true
|
767
|
+
args.each { |name| column(name, 'bigint', options) }
|
768
|
+
end
|
769
|
+
# Seems that geography gets used a fair bit in MSSQL
|
770
|
+
def geography(*args)
|
771
|
+
options = args.extract_options!
|
772
|
+
# options[:precision] ||= 8
|
773
|
+
# options[:scale] ||= 2
|
774
|
+
args.each { |name| column(name, 'geography', options) }
|
775
|
+
end
|
776
|
+
end
|
777
|
+
class SQLServerAdapter
|
778
|
+
unless respond_to?(:schema_exists?)
|
779
|
+
def schema_exists?(schema)
|
780
|
+
schema_sql = 'SELECT 1 FROM sys.schemas WHERE name = ?'
|
781
|
+
ActiveRecord::Base.execute_sql(schema_sql, schema).present?
|
782
|
+
end
|
783
|
+
end
|
784
|
+
end
|
785
|
+
end
|
786
|
+
end
|
729
787
|
end
|
730
788
|
# rubocop:enable Lint/ConstantDefinitionInBlock
|
731
789
|
|
@@ -23,6 +23,21 @@ 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
|
+
# # Oracle data types
|
27
|
+
'VARCHAR2' => 'string',
|
28
|
+
'CHAR' => 'string',
|
29
|
+
['NUMBER', 22] => 'integer',
|
30
|
+
/^INTERVAL / => 'string', # Time interval stuff like INTERVAL YEAR(2) TO MONTH, INTERVAL '999' DAY(3), etc
|
31
|
+
'XMLTYPE' => 'xml',
|
32
|
+
'RAW' => 'binary',
|
33
|
+
'SDO_GEOMETRY' => 'geometry',
|
34
|
+
# MSSQL data types
|
35
|
+
'int' => 'integer',
|
36
|
+
'nvarchar' => 'string',
|
37
|
+
'nchar' => 'string',
|
38
|
+
'datetime2' => 'timestamp',
|
39
|
+
'bit' => 'boolean',
|
40
|
+
'varbinary' => 'binary',
|
26
41
|
# Sqlite data types
|
27
42
|
'TEXT' => 'text',
|
28
43
|
'' => 'string',
|
@@ -122,7 +137,7 @@ module Brick
|
|
122
137
|
fringe.each do |tbl|
|
123
138
|
next unless (relation = ::Brick.relations.fetch(tbl, nil))&.fetch(:cols, nil)&.present?
|
124
139
|
|
125
|
-
pkey_cols = (rpk = relation[:pkey].values.flatten) & (arpk = [
|
140
|
+
pkey_cols = (rpk = relation[:pkey].values.flatten) & (arpk = [ApplicationRecord.primary_key].flatten.sort)
|
126
141
|
# In case things aren't as standard
|
127
142
|
if pkey_cols.empty?
|
128
143
|
pkey_cols = if rpk.empty? && relation[:cols][arpk.first]&.first == key_type
|
@@ -141,7 +156,7 @@ module Brick
|
|
141
156
|
end
|
142
157
|
unless schema.blank? || built_schemas.key?(schema)
|
143
158
|
mig = +" def change\n create_schema(:#{schema}) unless schema_exists?(:#{schema})\n end\n"
|
144
|
-
migration_file_write(mig_path, "create_db_schema_#{schema}", current_mig_time += 1.minute, ar_version, mig)
|
159
|
+
migration_file_write(mig_path, "create_db_schema_#{schema.underscore}", current_mig_time += 1.minute, ar_version, mig)
|
145
160
|
built_schemas[schema] = nil
|
146
161
|
end
|
147
162
|
|
@@ -151,11 +166,16 @@ module Brick
|
|
151
166
|
# a column definition which includes :primary_key -- %%% also using a data type of bigserial or serial
|
152
167
|
# if this one has come in as bigint or integer.
|
153
168
|
pk_is_also_fk = fkey_cols.any? { |assoc| pkey_cols&.first == assoc[:fk] } ? pkey_cols&.first : nil
|
154
|
-
# Support missing primary key (by adding: ,id: false)
|
169
|
+
# Support missing primary key (by adding: , id: false)
|
155
170
|
id_option = if pk_is_also_fk || !pkey_cols&.present?
|
171
|
+
needs_serial_col = true
|
156
172
|
', id: false'
|
157
|
-
elsif ((pkey_col_first = relation[:cols][pkey_cols&.first]&.first) &&
|
158
|
-
(pkey_col_first = SQL_TYPES[pkey_col_first] ||
|
173
|
+
elsif ((pkey_col_first = (col_def = relation[:cols][pkey_cols&.first])&.first) &&
|
174
|
+
(pkey_col_first = SQL_TYPES[pkey_col_first] || SQL_TYPES[col_def&.[](0..1)] ||
|
175
|
+
SQL_TYPES.find { |r| r.first.is_a?(Regexp) && pkey_col_first =~ r.first }&.last ||
|
176
|
+
pkey_col_first
|
177
|
+
) != key_type
|
178
|
+
)
|
159
179
|
case pkey_col_first
|
160
180
|
when 'integer'
|
161
181
|
', id: :serial'
|
@@ -164,9 +184,14 @@ module Brick
|
|
164
184
|
else
|
165
185
|
", id: :#{pkey_col_first}" # Something like: id: :integer, primary_key: :businessentityid
|
166
186
|
end +
|
167
|
-
(pkey_cols.first ? ", primary_key: :#{pkey_cols.first}" : '')
|
168
|
-
(!is_4x_rails && (comment = relation&.fetch(:description, nil))&.present? ? ", comment: #{comment.inspect}" : '')
|
187
|
+
(pkey_cols.first ? ", primary_key: :#{pkey_cols.first}" : '')
|
169
188
|
end
|
189
|
+
if !id_option && pkey_cols.sort != arpk
|
190
|
+
id_option = ", primary_key: :#{pkey_cols.first}"
|
191
|
+
end
|
192
|
+
if !is_4x_rails && (comment = relation&.fetch(:description, nil))&.present?
|
193
|
+
(id_option ||= +'') << ", comment: #{comment.inspect}"
|
194
|
+
end
|
170
195
|
# Find the ActiveRecord class in order to see if the columns have comments
|
171
196
|
unless is_4x_rails
|
172
197
|
klass = begin
|
@@ -187,18 +212,24 @@ module Brick
|
|
187
212
|
possible_ts = [] # Track possible generic timestamps
|
188
213
|
add_fks = [] # Track foreign keys to add after table creation
|
189
214
|
relation[:cols].each do |col, col_type|
|
190
|
-
sql_type = SQL_TYPES[col_type.first] || col_type
|
191
|
-
|
215
|
+
sql_type = SQL_TYPES[col_type.first] || SQL_TYPES[col_type[0..1]] ||
|
216
|
+
SQL_TYPES.find { |r| r.first.is_a?(Regexp) && col_type.first =~ r.first }&.last ||
|
217
|
+
col_type.first
|
218
|
+
suffix = col_type[3] || pkey_cols&.include?(col) ? +', null: false' : +''
|
192
219
|
if !is_4x_rails && klass && (comment = klass.columns_hash.fetch(col, nil)&.comment)&.present?
|
193
220
|
suffix << ", comment: #{comment.inspect}"
|
194
221
|
end
|
195
222
|
# Determine if this column is used as part of a foreign key
|
196
|
-
if fk = fkey_cols.find { |assoc| col == assoc[:fk] }
|
223
|
+
if (fk = fkey_cols.find { |assoc| col == assoc[:fk] })
|
197
224
|
to_table = fk[:inverse_table].split('.')
|
198
225
|
to_table = to_table.length == 1 ? ":#{to_table.first}" : "'#{fk[:inverse_table]}'"
|
226
|
+
if needs_serial_col && pkey_cols&.include?(col) && (new_serial_type = {'integer' => 'serial', 'bigint' => 'bigserial'}[sql_type])
|
227
|
+
sql_type = new_serial_type
|
228
|
+
needs_serial_col = false
|
229
|
+
end
|
199
230
|
if fk[:fk] != "#{fk[:assoc_name].singularize}_id" # Need to do our own foreign_key tricks, not use references?
|
200
231
|
column = fk[:fk]
|
201
|
-
mig <<
|
232
|
+
mig << emit_column(sql_type, column, suffix)
|
202
233
|
add_fks << [to_table, column, ::Brick.relations[fk[:inverse_table]]]
|
203
234
|
else
|
204
235
|
suffix << ", type: :#{sql_type}" unless sql_type == key_type
|
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.72
|
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-09-
|
11
|
+
date: 2022-09-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|