brick 1.0.69 → 1.0.71
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 +111 -59
- data/lib/brick/frameworks/rails/engine.rb +9 -6
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +21 -2
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e50dce8dd2821ec196836393569138d4e18c53a9ef1e3e910ddffa6bc3788824
|
4
|
+
data.tar.gz: 982b26f7ed3a14f69589b6c422b37a5ec42b210c095cf85ef0eeab151ea80c59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c0e76b3e092d35f57045f3dd106ae9b8c90b266df0a7c0c973824ed20c1ac41560f124420d164e4e9d170b9a386345f424f19bce01bf041c9ebb553c842e553
|
7
|
+
data.tar.gz: 6d257bbec4c364456be5167b55c2eb377d94cdd0485e56dfb699daf14f3dce7a46c94c91534ecbe9c8f090b564e8752de0c3b88cf7560e01ca9d8388a3ec9930
|
data/lib/brick/extensions.rb
CHANGED
@@ -99,13 +99,14 @@ module ActiveRecord
|
|
99
99
|
dsl
|
100
100
|
end
|
101
101
|
|
102
|
-
def self.brick_parse_dsl(build_array = nil, prefix = [], translations = {}, is_polymorphic = false)
|
102
|
+
def self.brick_parse_dsl(build_array = nil, prefix = [], translations = {}, emit_dsl = false, is_polymorphic = false)
|
103
103
|
build_array = ::Brick::JoinArray.new.tap { |ary| ary.replace([build_array]) } if build_array.is_a?(::Brick::JoinHash)
|
104
104
|
build_array = ::Brick::JoinArray.new unless build_array.nil? || build_array.is_a?(Array)
|
105
105
|
members = []
|
106
106
|
bracket_name = nil
|
107
107
|
prefix = [prefix] unless prefix.is_a?(Array)
|
108
108
|
if (dsl = ::Brick.config.model_descrips[name] || brick_get_dsl)
|
109
|
+
dsl2 = +''
|
109
110
|
klass = nil
|
110
111
|
dsl.each_char do |ch|
|
111
112
|
if bracket_name
|
@@ -125,7 +126,15 @@ module ActiveRecord
|
|
125
126
|
end
|
126
127
|
translations[parts[0..-2].join('.')] = klass
|
127
128
|
end
|
128
|
-
|
129
|
+
if klass.column_names.exclude?(parts.last) &&
|
130
|
+
(klass = (orig_class = klass).reflect_on_association(possible_dsl = parts.pop.to_sym)&.klass)
|
131
|
+
members2, dsl2a = klass.brick_parse_dsl(build_array, prefix + [possible_dsl], translations, true)
|
132
|
+
members += members2
|
133
|
+
dsl2 << dsl2a
|
134
|
+
else
|
135
|
+
dsl2 << "[#{bracket_name}]"
|
136
|
+
members << parts
|
137
|
+
end
|
129
138
|
bracket_name = nil
|
130
139
|
else
|
131
140
|
bracket_name << ch
|
@@ -133,13 +142,20 @@ module ActiveRecord
|
|
133
142
|
elsif ch == '['
|
134
143
|
bracket_name = +''
|
135
144
|
klass = self
|
145
|
+
else
|
146
|
+
dsl2 << ch
|
136
147
|
end
|
137
148
|
end
|
149
|
+
# Rewrite the DSL just in case it's different because we had to expand it
|
150
|
+
unless emit_dsl
|
151
|
+
# puts "Compare:\n #{dsl}\n #{dsl2}"
|
152
|
+
::Brick.config.model_descrips[name] = dsl2
|
153
|
+
end
|
138
154
|
else # With no DSL available, still put this prefix into the JoinArray so we can get primary key (ID) info from this table
|
139
155
|
x = prefix.each_with_object(build_array) { |v, s| s[v.to_sym] }
|
140
156
|
x[prefix.last] = nil unless prefix.empty? # Using []= will "hydrate" any missing part(s) in our whole series
|
141
157
|
end
|
142
|
-
members
|
158
|
+
emit_dsl ? [members, dsl2] : members
|
143
159
|
end
|
144
160
|
|
145
161
|
# If available, parse simple DSL attached to a model in order to provide a friendlier name.
|
@@ -169,9 +185,12 @@ module ActiveRecord
|
|
169
185
|
this_obj = caches.fetch(obj_name) { caches[obj_name] = this_obj&.send(part.to_sym) }
|
170
186
|
break if this_obj.nil?
|
171
187
|
end
|
188
|
+
if this_obj.is_a?(ActiveRecord::Base) && (obj_descrip = this_obj.class.brick_descrip(this_obj))
|
189
|
+
this_obj = obj_descrip
|
190
|
+
end
|
172
191
|
this_obj&.to_s || ''
|
173
192
|
end
|
174
|
-
is_brackets_have_content = true unless
|
193
|
+
is_brackets_have_content = true unless datum.blank?
|
175
194
|
output << (datum || '')
|
176
195
|
bracket_name = nil
|
177
196
|
else
|
@@ -244,7 +263,8 @@ module ActiveRecord
|
|
244
263
|
|
245
264
|
# join_array will receive this relation name when calling #brick_parse_dsl
|
246
265
|
_br_bt_descrip[bt.first] = if bt[1].is_a?(Array)
|
247
|
-
|
266
|
+
# Last two params here: "false" is for don't emit DSL, and "true" is for yes, we are polymorphic
|
267
|
+
bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations, false, true) }
|
248
268
|
else
|
249
269
|
{ bt.last => bt[1].brick_parse_dsl(join_array, bt.first, translations) }
|
250
270
|
end
|
@@ -384,6 +404,7 @@ module ActiveRecord
|
|
384
404
|
def brick_select(params, selects = nil, order_by = nil, translations = {}, join_array = ::Brick::JoinArray.new)
|
385
405
|
is_postgres = ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
386
406
|
is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2'
|
407
|
+
is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer'
|
387
408
|
is_distinct = nil
|
388
409
|
wheres = {}
|
389
410
|
params.each do |k, v|
|
@@ -408,15 +429,37 @@ module ActiveRecord
|
|
408
429
|
if selects&.empty? # Default to all columns
|
409
430
|
tbl_no_schema = table.name.split('.').last
|
410
431
|
columns.each do |col|
|
411
|
-
|
432
|
+
if (col_name = col.name) == 'class'
|
433
|
+
col_alias = " AS #{col.name}_"
|
434
|
+
else
|
435
|
+
alias_name = nil
|
436
|
+
idx = 0
|
437
|
+
col_name.each_char do |c|
|
438
|
+
unless (c >= 'a' && c <= 'z') ||
|
439
|
+
c == '_' ||
|
440
|
+
(c >= 'A' && c <= 'Z') ||
|
441
|
+
(c >= '0' && c <= '9')
|
442
|
+
(alias_name ||= col_name.dup)[idx] = 'x'
|
443
|
+
end
|
444
|
+
++idx
|
445
|
+
end
|
446
|
+
col_alias = " AS #{alias_name}" if alias_name
|
447
|
+
end
|
412
448
|
selects << if is_mysql
|
413
449
|
"`#{tbl_no_schema}`.`#{col_name}`#{col_alias}"
|
414
|
-
elsif is_postgres
|
450
|
+
elsif is_postgres || is_mssql
|
415
451
|
# Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
|
416
|
-
cast_as_text = '::text' if is_distinct && Brick.relations[klass.table_name]&.[](:cols)&.[](
|
452
|
+
cast_as_text = '::text' if is_distinct && Brick.relations[klass.table_name]&.[](:cols)&.[](col_name)&.first&.start_with?('xml')
|
417
453
|
"\"#{tbl_no_schema}\".\"#{col_name}\"#{cast_as_text}#{col_alias}"
|
418
|
-
|
419
|
-
|
454
|
+
elsif col.type # Could be Sqlite or Oracle
|
455
|
+
if col_alias
|
456
|
+
"#{tbl_no_schema}.#{col_name}#{col_alias}"
|
457
|
+
else
|
458
|
+
"#{tbl_no_schema}.#{col_name}"
|
459
|
+
end
|
460
|
+
else # Oracle with a custom data type
|
461
|
+
typ = col.sql_type
|
462
|
+
"'<#{typ.end_with?('_TYP') ? typ[0..-5] : typ}>' AS #{col.name}"
|
420
463
|
end
|
421
464
|
end
|
422
465
|
end
|
@@ -449,6 +492,8 @@ module ActiveRecord
|
|
449
492
|
"`#{field_tbl_name}`.`#{sel_col.last}` AS `#{col_alias}`"
|
450
493
|
elsif is_postgres
|
451
494
|
"\"#{field_tbl_name}\".\"#{sel_col.last}\"#{'::text' if is_xml} AS \"#{col_alias}\""
|
495
|
+
elsif is_mssql
|
496
|
+
"\"#{field_tbl_name}\".\"#{sel_col.last}\" AS \"#{col_alias}\""
|
452
497
|
else
|
453
498
|
"#{field_tbl_name}.#{sel_col.last} AS \"#{col_alias}\""
|
454
499
|
end
|
@@ -462,7 +507,7 @@ module ActiveRecord
|
|
462
507
|
id_for_tables[v.first] << if id_part
|
463
508
|
selects << if is_mysql
|
464
509
|
"#{"`#{tbl_name}`.`#{id_part}`"} AS `#{(id_alias = "br_fk_#{v.first}__#{id_part}")}`"
|
465
|
-
elsif is_postgres
|
510
|
+
elsif is_postgres || is_mssql
|
466
511
|
"#{"\"#{tbl_name}\".\"#{id_part}\""} AS \"#{(id_alias = "br_fk_#{v.first}__#{id_part}")}\""
|
467
512
|
else
|
468
513
|
"#{"#{tbl_name}.#{id_part}"} AS \"#{(id_alias = "br_fk_#{v.first}__#{id_part}")}\""
|
@@ -506,7 +551,7 @@ module ActiveRecord
|
|
506
551
|
pri_tbl_name = is_mysql ? "`#{pri_tbl.table_name}`" : "\"#{pri_tbl.table_name.gsub('.', '"."')}\""
|
507
552
|
pri_tbl_name = if is_mysql
|
508
553
|
"`#{pri_tbl.table_name}`"
|
509
|
-
elsif is_postgres
|
554
|
+
elsif is_postgres || is_mssql
|
510
555
|
"\"#{pri_tbl.table_name.gsub('.', '"."')}\""
|
511
556
|
else
|
512
557
|
pri_tbl.table_name
|
@@ -525,12 +570,12 @@ module ActiveRecord
|
|
525
570
|
end
|
526
571
|
hm_table_name = if is_mysql
|
527
572
|
"`#{associative&.table_name || hm.klass.table_name}`"
|
528
|
-
elsif is_postgres
|
573
|
+
elsif is_postgres || is_mssql
|
529
574
|
"\"#{(associative&.table_name || hm.klass.table_name).gsub('.', '"."')}\""
|
530
575
|
else
|
531
576
|
associative&.table_name || hm.klass.table_name
|
532
577
|
end
|
533
|
-
group_bys = ::Brick.is_oracle ? selects : (1..selects.length).to_a
|
578
|
+
group_bys = ::Brick.is_oracle || is_mssql ? selects : (1..selects.length).to_a
|
534
579
|
join_clause = "LEFT OUTER
|
535
580
|
JOIN (SELECT #{selects.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}#{count_column
|
536
581
|
}) AS c_t_ FROM #{hm_table_name} GROUP BY #{group_bys.join(', ')}) #{tbl_alias}"
|
@@ -661,7 +706,7 @@ Module.class_exec do
|
|
661
706
|
)
|
662
707
|
return possible
|
663
708
|
end
|
664
|
-
class_name = args.first.to_s
|
709
|
+
class_name = ::Brick.namify(args.first.to_s)
|
665
710
|
# self.name is nil when a model name is requested in an .erb file
|
666
711
|
base_module = (self < ActiveRecord::Migration || !self.name) ? Object : self
|
667
712
|
# See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
@@ -773,7 +818,7 @@ class Object
|
|
773
818
|
schema_name = Apartment.default_schema
|
774
819
|
end
|
775
820
|
# Maybe, just maybe there's a database table that will satisfy this need
|
776
|
-
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(schema_name ? "#{schema_name}.#{m}" : m) })
|
821
|
+
if (matching = [table_name, singular_table_name, plural_class_name, model_name, table_name.titleize].find { |m| relations.key?(schema_name ? "#{schema_name}.#{m}" : m) })
|
777
822
|
build_model_worker(schema_name, inheritable_name, model_name, singular_table_name, table_name, relations, matching)
|
778
823
|
end
|
779
824
|
end
|
@@ -1340,7 +1385,7 @@ class Object
|
|
1340
1385
|
# hm_assoc[:assoc_name] = new_alt_name
|
1341
1386
|
[new_alt_name, true]
|
1342
1387
|
else
|
1343
|
-
assoc_name = hm_assoc[:inverse_table].pluralize
|
1388
|
+
assoc_name = ::Brick.namify(hm_assoc[:inverse_table]).pluralize
|
1344
1389
|
# hm_assoc[:assoc_name] = assoc_name
|
1345
1390
|
[assoc_name, assoc_name.include?('.')]
|
1346
1391
|
end
|
@@ -1383,12 +1428,20 @@ module ActiveRecord::ConnectionHandling
|
|
1383
1428
|
# puts ActiveRecord::Base.execute_sql("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
1384
1429
|
|
1385
1430
|
is_postgres = nil
|
1431
|
+
is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer'
|
1386
1432
|
case ActiveRecord::Base.connection.adapter_name
|
1387
|
-
when 'PostgreSQL'
|
1388
|
-
is_postgres =
|
1433
|
+
when 'PostgreSQL', 'SQLServer'
|
1434
|
+
is_postgres = !is_mssql
|
1389
1435
|
db_schemas = ActiveRecord::Base.execute_sql('SELECT DISTINCT table_schema FROM INFORMATION_SCHEMA.tables;')
|
1390
1436
|
::Brick.db_schemas = db_schemas.each_with_object({}) do |row, s|
|
1391
|
-
row =
|
1437
|
+
row = case row
|
1438
|
+
when String
|
1439
|
+
row
|
1440
|
+
when Array
|
1441
|
+
row.first
|
1442
|
+
else
|
1443
|
+
row['table_schema']
|
1444
|
+
end
|
1392
1445
|
# Remove any system schemas
|
1393
1446
|
s[row] = nil unless ['information_schema', 'pg_catalog'].include?(row)
|
1394
1447
|
end
|
@@ -1404,7 +1457,7 @@ module ActiveRecord::ConnectionHandling
|
|
1404
1457
|
# ActiveRecord::Base.connection.current_database will be something like "XEPDB1"
|
1405
1458
|
::Brick.default_schema = schema = ActiveRecord::Base.connection.raw_connection.username
|
1406
1459
|
::Brick.db_schemas = {}
|
1407
|
-
|
1460
|
+
ActiveRecord::Base.execute_sql("SELECT username FROM sys.all_users WHERE ORACLE_MAINTAINED != 'Y'").each { |s| ::Brick.db_schemas[s.first] = nil }
|
1408
1461
|
when 'SQLite'
|
1409
1462
|
sql = "SELECT m.name AS relation_name, UPPER(m.type) AS table_type,
|
1410
1463
|
p.name AS column_name, p.type AS data_type,
|
@@ -1437,7 +1490,7 @@ module ActiveRecord::ConnectionHandling
|
|
1437
1490
|
case ActiveRecord::Base.connection.adapter_name
|
1438
1491
|
when 'PostgreSQL', 'SQLite' # These bring back a hash for each row because the query uses column aliases
|
1439
1492
|
# schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
1440
|
-
ActiveRecord::Base.retrieve_schema_and_tables(sql, is_postgres, schema).each do |r|
|
1493
|
+
ActiveRecord::Base.retrieve_schema_and_tables(sql, is_postgres, is_mssql, schema).each do |r|
|
1441
1494
|
# If Apartment gem lists the table as being associated with a non-tenanted model then use whatever it thinks
|
1442
1495
|
# is the default schema, usually 'public'.
|
1443
1496
|
schema_name = if ::Brick.config.schema_behavior[:multitenant]
|
@@ -1464,10 +1517,12 @@ module ActiveRecord::ConnectionHandling
|
|
1464
1517
|
# puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
|
1465
1518
|
end
|
1466
1519
|
else # MySQL2 and OracleEnhanced act a little differently, bringing back an array for each row
|
1467
|
-
schema_and_tables =
|
1520
|
+
schema_and_tables = case ActiveRecord::Base.connection.adapter_name
|
1521
|
+
when 'OracleEnhanced'
|
1468
1522
|
sql =
|
1469
|
-
"SELECT c.owner AS schema, c.table_name AS relation_name,
|
1470
|
-
|
1523
|
+
"SELECT c.owner AS schema, c.table_name AS relation_name,
|
1524
|
+
CASE WHEN v.owner IS NULL THEN 'BASE_TABLE' ELSE 'VIEW' END AS table_type,
|
1525
|
+
c.column_name, c.data_type,
|
1471
1526
|
COALESCE(c.data_length, c.data_precision) AS max_length,
|
1472
1527
|
CASE ac.constraint_type WHEN 'P' THEN 'PRIMARY KEY' END AS const,
|
1473
1528
|
ac.constraint_name AS \"key\",
|
@@ -1475,9 +1530,11 @@ module ActiveRecord::ConnectionHandling
|
|
1475
1530
|
FROM all_tab_cols c
|
1476
1531
|
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
|
1477
1532
|
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'
|
1533
|
+
LEFT OUTER JOIN all_views v ON c.owner = v.owner AND c.table_name = v.view_name
|
1478
1534
|
WHERE c.owner IN (#{::Brick.db_schemas.keys.map { |s| "'#{s}'" }.join(', ')})
|
1535
|
+
AND c.table_name NOT IN (?, ?)
|
1479
1536
|
ORDER BY 1, 2, c.internal_column_id, acc.position"
|
1480
|
-
|
1537
|
+
ActiveRecord::Base.execute_sql(sql, *ar_tables)
|
1481
1538
|
else
|
1482
1539
|
ActiveRecord::Base.retrieve_schema_and_tables(sql)
|
1483
1540
|
end
|
@@ -1527,9 +1584,9 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1527
1584
|
# end
|
1528
1585
|
# schema = ::Brick.default_schema # Reset back for this next round of fun
|
1529
1586
|
case ActiveRecord::Base.connection.adapter_name
|
1530
|
-
when 'PostgreSQL', 'Mysql2'
|
1587
|
+
when 'PostgreSQL', 'Mysql2', 'SQLServer'
|
1531
1588
|
sql = "SELECT kcu1.CONSTRAINT_SCHEMA, kcu1.TABLE_NAME, kcu1.COLUMN_NAME,
|
1532
|
-
|
1589
|
+
kcu2.CONSTRAINT_SCHEMA AS primary_schema, kcu2.TABLE_NAME AS primary_table, kcu1.CONSTRAINT_NAME AS CONSTRAINT_SCHEMA_FK
|
1533
1590
|
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS rc
|
1534
1591
|
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu1
|
1535
1592
|
ON kcu1.CONSTRAINT_CATALOG = rc.CONSTRAINT_CATALOG
|
@@ -1540,7 +1597,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1540
1597
|
AND kcu2.CONSTRAINT_SCHEMA = rc.UNIQUE_CONSTRAINT_SCHEMA
|
1541
1598
|
AND kcu2.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME#{"
|
1542
1599
|
AND kcu2.TABLE_NAME = kcu1.REFERENCED_TABLE_NAME
|
1543
|
-
AND kcu2.COLUMN_NAME = kcu1.REFERENCED_COLUMN_NAME" unless is_postgres }
|
1600
|
+
AND kcu2.COLUMN_NAME = kcu1.REFERENCED_COLUMN_NAME" unless is_postgres || is_mssql }
|
1544
1601
|
AND kcu2.ORDINAL_POSITION = kcu1.ORDINAL_POSITION#{"
|
1545
1602
|
WHERE kcu1.CONSTRAINT_SCHEMA = COALESCE(current_setting('SEARCH_PATH'), 'public')" if is_postgres && schema }"
|
1546
1603
|
# AND kcu2.TABLE_NAME = ?;", Apartment::Tenant.current, table_name
|
@@ -1567,16 +1624,14 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1567
1624
|
WHERE ac.constraint_type = 'R'
|
1568
1625
|
AND ac.owner IN (#{schemas})
|
1569
1626
|
AND ac.r_owner IN (#{schemas})"
|
1570
|
-
fk_references = ActiveRecord::Base.
|
1571
|
-
else
|
1572
|
-
binding.pry
|
1627
|
+
fk_references = ActiveRecord::Base.execute_sql(sql)
|
1573
1628
|
end
|
1574
1629
|
::Brick.is_oracle = true if ActiveRecord::Base.connection.adapter_name == 'OracleEnhanced'
|
1575
1630
|
# ::Brick.default_schema ||= schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
1576
1631
|
fk_references&.each do |fk|
|
1577
1632
|
fk = fk.values unless fk.is_a?(Array)
|
1578
1633
|
# Multitenancy makes things a little more general overall, except for non-tenanted tables
|
1579
|
-
if apartment_excluded&.include?(fk[1].singularize.camelize)
|
1634
|
+
if apartment_excluded&.include?(::Brick.namify(fk[1]).singularize.camelize)
|
1580
1635
|
fk[0] = Apartment.default_schema
|
1581
1636
|
elsif is_postgres && (fk[0] == 'public' || (is_multitenant && fk[0] == schema)) ||
|
1582
1637
|
!is_postgres && ['mysql', 'performance_schema', 'sys'].exclude?(fk[0])
|
@@ -1621,29 +1676,35 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1621
1676
|
::Brick.load_additional_references if initializer_loaded
|
1622
1677
|
end
|
1623
1678
|
|
1624
|
-
def retrieve_schema_and_tables(sql = nil, is_postgres = nil, schema = nil)
|
1679
|
+
def retrieve_schema_and_tables(sql = nil, is_postgres = nil, is_mssql = nil, schema = nil)
|
1680
|
+
is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer' if is_mssql.nil?
|
1625
1681
|
sql ||= "SELECT t.table_schema AS \"schema\", t.table_name AS relation_name, t.table_type,#{"
|
1626
1682
|
pg_catalog.obj_description(
|
1627
1683
|
('\"' || t.table_schema || '\".\"' || t.table_name || '\"')::regclass, 'pg_class'
|
1628
1684
|
) AS table_description," if is_postgres}
|
1629
1685
|
c.column_name, c.data_type,
|
1630
1686
|
COALESCE(c.character_maximum_length, c.numeric_precision) AS max_length,
|
1631
|
-
|
1687
|
+
kcu.constraint_type AS const, kcu.constraint_name AS \"key\",
|
1632
1688
|
c.is_nullable
|
1633
1689
|
FROM INFORMATION_SCHEMA.tables AS t
|
1634
1690
|
LEFT OUTER JOIN INFORMATION_SCHEMA.columns AS c ON t.table_schema = c.table_schema
|
1635
1691
|
AND t.table_name = c.table_name
|
1636
|
-
|
1637
|
-
|
1692
|
+
LEFT OUTER JOIN
|
1693
|
+
(SELECT kcu1.constraint_schema, kcu1.table_name, kcu1.ordinal_position,
|
1694
|
+
tc.constraint_type, kcu1.constraint_name
|
1695
|
+
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu1
|
1696
|
+
INNER JOIN INFORMATION_SCHEMA.table_constraints AS tc
|
1697
|
+
ON kcu1.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
|
1698
|
+
AND kcu1.TABLE_NAME = tc.TABLE_NAME
|
1699
|
+
AND kcu1.CONSTRAINT_NAME = tc.constraint_name
|
1700
|
+
AND tc.constraint_type != 'FOREIGN KEY' -- For MSSQL
|
1701
|
+
) AS kcu ON
|
1702
|
+
-- kcu.CONSTRAINT_CATALOG = t.table_catalog AND
|
1638
1703
|
kcu.CONSTRAINT_SCHEMA = c.table_schema
|
1639
|
-
AND kcu.TABLE_NAME = c.table_name
|
1640
|
-
|
1704
|
+
AND kcu.TABLE_NAME = c.table_name#{"
|
1705
|
+
-- AND kcu.position_in_unique_constraint IS NULL" unless is_mssql}
|
1641
1706
|
AND kcu.ordinal_position = c.ordinal_position
|
1642
|
-
|
1643
|
-
ON kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
|
1644
|
-
AND kcu.TABLE_NAME = tc.TABLE_NAME
|
1645
|
-
AND kcu.CONSTRAINT_NAME = tc.constraint_name
|
1646
|
-
WHERE t.table_schema #{is_postgres ?
|
1707
|
+
WHERE t.table_schema #{is_postgres || is_mssql ?
|
1647
1708
|
"NOT IN ('information_schema', 'pg_catalog')"
|
1648
1709
|
:
|
1649
1710
|
"= '#{ActiveRecord::Base.connection.current_database.tr("'", "''")}'"}#{"
|
@@ -1663,16 +1724,6 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1663
1724
|
ar_imtn = ActiveRecord.version >= ::Gem::Version.new('5.0') ? ActiveRecord::Base.internal_metadata_table_name : ''
|
1664
1725
|
[ar_smtn, ar_imtn]
|
1665
1726
|
end
|
1666
|
-
|
1667
|
-
def execute_oracle(*sql_args)
|
1668
|
-
cursor = ActiveRecord::Base.execute_sql(*sql_args)
|
1669
|
-
result = []
|
1670
|
-
while row = cursor.fetch()
|
1671
|
-
result << row
|
1672
|
-
end
|
1673
|
-
cursor.close
|
1674
|
-
result
|
1675
|
-
end
|
1676
1727
|
end
|
1677
1728
|
|
1678
1729
|
# ==========================================
|
@@ -1699,22 +1750,23 @@ module Brick
|
|
1699
1750
|
|
1700
1751
|
class << self
|
1701
1752
|
def _add_bt_and_hm(fk, relations, is_polymorphic = false, is_optional = false)
|
1702
|
-
bt_assoc_name = fk[2]
|
1753
|
+
bt_assoc_name = ::Brick.namify(fk[2])
|
1703
1754
|
unless is_polymorphic
|
1704
1755
|
bt_assoc_name = if bt_assoc_name.underscore.end_with?('_id')
|
1705
|
-
bt_assoc_name[0..-4]
|
1756
|
+
bt_assoc_name[-3] == '_' ? bt_assoc_name[0..-4] : bt_assoc_name[0..-3]
|
1706
1757
|
elsif bt_assoc_name.downcase.end_with?('id') && bt_assoc_name.exclude?('_')
|
1707
1758
|
bt_assoc_name[0..-3] # Make the bold assumption that we can just peel off any final ID part
|
1708
1759
|
else
|
1709
1760
|
"#{bt_assoc_name}_bt"
|
1710
1761
|
end
|
1711
1762
|
end
|
1712
|
-
bt_assoc_name = "
|
1763
|
+
bt_assoc_name = "#{bt_assoc_name}_" if bt_assoc_name == 'attribute'
|
1713
1764
|
|
1714
1765
|
# %%% Temporary schema patch
|
1715
1766
|
for_tbl = fk[1]
|
1767
|
+
fk_namified = ::Brick.namify(fk[1])
|
1716
1768
|
apartment = Object.const_defined?('Apartment') && Apartment
|
1717
|
-
fk[0] = Apartment.default_schema if apartment && apartment.excluded_models.include?(
|
1769
|
+
fk[0] = Apartment.default_schema if apartment && apartment.excluded_models.include?(fk_namified.singularize.camelize)
|
1718
1770
|
fk[1] = "#{fk[0]}.#{fk[1]}" if fk[0] # && fk[0] != ::Brick.default_schema
|
1719
1771
|
bts = (relation = relations.fetch(fk[1], nil))&.fetch(:fks) { relation[:fks] = {} }
|
1720
1772
|
|
@@ -1813,7 +1865,7 @@ module Brick
|
|
1813
1865
|
else
|
1814
1866
|
fk[1]
|
1815
1867
|
end
|
1816
|
-
assoc_hm = hms[hm_cnstr_name] = { is_bt: false, fk: fk[2], assoc_name:
|
1868
|
+
assoc_hm = hms[hm_cnstr_name] = { is_bt: false, fk: fk[2], assoc_name: fk_namified.pluralize, alternate_name: bt_assoc_name,
|
1817
1869
|
inverse_table: inv_tbl, inverse: assoc_bt }
|
1818
1870
|
assoc_hm[:polymorphic] = true if is_polymorphic
|
1819
1871
|
hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
|
@@ -49,8 +49,10 @@ module Brick
|
|
49
49
|
# After we're initialized and before running the rest of stuff, put our configuration in place
|
50
50
|
ActiveSupport.on_load(:after_initialize) do |app|
|
51
51
|
assets_path = File.expand_path("#{__dir__}/../../../../vendor/assets")
|
52
|
-
(app.config.assets
|
53
|
-
|
52
|
+
if (app_config = app.config).respond_to?(:assets)
|
53
|
+
(app_config.assets.precompile ||= []) << "#{assets_path}/images/brick_erd.png"
|
54
|
+
(app.config.assets.paths ||= []) << assets_path
|
55
|
+
end
|
54
56
|
# ====================================
|
55
57
|
# Dynamically create generic templates
|
56
58
|
# ====================================
|
@@ -565,7 +567,7 @@ erDiagram
|
|
565
567
|
end %>
|
566
568
|
<% end
|
567
569
|
def dt_lookup(dt)
|
568
|
-
{ 'integer' => 'int', }[dt] || dt
|
570
|
+
{ 'integer' => 'int', }[dt] || dt&.tr(' ', '_') || 'int'
|
569
571
|
end
|
570
572
|
callbacks.merge({model_short_name => #{@_brick_model.name}}).each do |cb_k, cb_class|
|
571
573
|
cb_relation = ::Brick.relations[cb_class.table_name]
|
@@ -581,8 +583,9 @@ erDiagram
|
|
581
583
|
fk.each do |fk_part| %>
|
582
584
|
<%= \"#\{dt_lookup(cols[fk_part].first)} #\{fk_part} \\\" fk\\\"\".html_safe unless pkeys&.include?(fk_part) %><%
|
583
585
|
end
|
584
|
-
else
|
585
|
-
|
586
|
+
else # %%% Does not yet accommodate polymorphic BTs
|
587
|
+
%>
|
588
|
+
<%= \"#\{dt_lookup(cols[fk]&.first)} #\{fk} \\\" fk\\\"\".html_safe unless pkeys&.include?(fk) %><%
|
586
589
|
end
|
587
590
|
end %>
|
588
591
|
}
|
@@ -1087,9 +1090,9 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
|
|
1087
1090
|
mermaid.initialize({
|
1088
1091
|
startOnLoad: true,
|
1089
1092
|
securityLevel: \"loose\",
|
1093
|
+
er: { useMaxWidth: false },
|
1090
1094
|
mermaid: {callback: function(objId) {
|
1091
1095
|
var svg = document.getElementById(objId);
|
1092
|
-
svg.removeAttribute(\"width\");
|
1093
1096
|
var cb;
|
1094
1097
|
for(cb in cbs) {
|
1095
1098
|
var gErd = svg.getElementById(cb);
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -151,6 +151,20 @@ module Brick
|
|
151
151
|
@pending_models ||= {}
|
152
152
|
end
|
153
153
|
|
154
|
+
# Convert spaces to underscores if the second character and onwards is mixed case
|
155
|
+
def namify(name)
|
156
|
+
if name.include?(' ')
|
157
|
+
# All uppers or all lowers?
|
158
|
+
if name[1..-1] =~ /^[A-Z0-9_]+$/ || name[1..-1] =~ /^[a-z0-9_]+$/
|
159
|
+
name.titleize.tr(' ', '_')
|
160
|
+
else # Mixed uppers and lowers -- just remove existing spaces
|
161
|
+
name.tr(' ', '')
|
162
|
+
end
|
163
|
+
else
|
164
|
+
name
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
154
168
|
def get_bts_and_hms(model)
|
155
169
|
bts, hms = model.reflect_on_all_associations.each_with_object([{}, {}]) do |a, s|
|
156
170
|
next if !const_defined?(a.name.to_s.singularize.camelize) && ::Brick.config.exclude_tables.include?(a.plural_name)
|
@@ -469,9 +483,10 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
469
483
|
# %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
|
470
484
|
# If auto-controllers and auto-models are both enabled then this makes sense:
|
471
485
|
::Brick.relations.each do |rel_name, v|
|
472
|
-
rel_name = rel_name.split('.').map(
|
486
|
+
rel_name = rel_name.split('.').map { |x| ::Brick.namify(x).underscore }
|
473
487
|
schema_names = rel_name[0..-2]
|
474
488
|
schema_names.shift if ::Brick.apartment_multitenant && schema_names.first == Apartment.default_schema
|
489
|
+
# %%% If more than one schema has the same table name, will need to add a schema name prefix to have uniqueness
|
475
490
|
k = rel_name.last
|
476
491
|
unless existing_controllers.key?(controller_name = k.pluralize)
|
477
492
|
options = {}
|
@@ -545,7 +560,11 @@ ActiveSupport.on_load(:active_record) do
|
|
545
560
|
class << self
|
546
561
|
def execute_sql(sql, *param_array)
|
547
562
|
param_array = param_array.first if param_array.length == 1 && param_array.first.is_a?(Array)
|
548
|
-
|
563
|
+
if ['OracleEnhanced', 'SQLServer'].include?(ActiveRecord::Base.connection.adapter_name)
|
564
|
+
connection.exec_query(send(:sanitize_sql_array, [sql] + param_array)).rows
|
565
|
+
else
|
566
|
+
connection.execute(send(:sanitize_sql_array, [sql] + param_array))
|
567
|
+
end
|
549
568
|
end
|
550
569
|
end
|
551
570
|
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.71
|
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-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '3.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '3.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: fancy_gets
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -264,5 +264,5 @@ requirements: []
|
|
264
264
|
rubygems_version: 3.1.6
|
265
265
|
signing_key:
|
266
266
|
specification_version: 4
|
267
|
-
summary:
|
267
|
+
summary: Create a Rails app from data alone
|
268
268
|
test_files: []
|