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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7399b8731b349e5c2a053956c5e6aa9998eb5a2010b5a0e7c081e87f9cac843e
4
- data.tar.gz: bc3f65bb32230c05c63f7ef585a825de315ce2979d03b8db6efc6df0d0a5c143
3
+ metadata.gz: e50dce8dd2821ec196836393569138d4e18c53a9ef1e3e910ddffa6bc3788824
4
+ data.tar.gz: 982b26f7ed3a14f69589b6c422b37a5ec42b210c095cf85ef0eeab151ea80c59
5
5
  SHA512:
6
- metadata.gz: b8681ca78d035e6c727fcb6b60d17a5658841bb28e8a4f3dd7405563d5c7cb3e9739984cfe09ce741fd70d5334b949b786e929a1ce4232cf77568621c2813c9d
7
- data.tar.gz: 39a9586ea23107b66ab3d461e93eed2defc459231b26d4b25961a7e2e4df9b04167ff620ac570ad07040681e19ad6b313969e822d6fdd8b492b5f4c1fded4c1c
6
+ metadata.gz: 2c0e76b3e092d35f57045f3dd106ae9b8c90b266df0a7c0c973824ed20c1ac41560f124420d164e4e9d170b9a386345f424f19bce01bf041c9ebb553c842e553
7
+ data.tar.gz: 6d257bbec4c364456be5167b55c2eb377d94cdd0485e56dfb699daf14f3dce7a46c94c91534ecbe9c8f090b564e8752de0c3b88cf7560e01ca9d8388a3ec9930
@@ -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
- members << parts
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 (datum).blank?
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
- bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations, true) }
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
- col_alias = " AS _#{col.name}" if (col_name = col.name) == 'class'
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)&.[](col.name)&.first&.start_with?('xml')
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
- else # Sqlite or Oracle
419
- "#{tbl_no_schema}.#{col_name}#{col_alias}"
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 = true
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 = row.is_a?(String) ? row : row['table_schema']
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
- execute_oracle("SELECT username FROM sys.all_users WHERE ORACLE_MAINTAINED != 'Y'").each { |s| ::Brick.db_schemas[s.first] = nil }
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 = if ActiveRecord::Base.connection.adapter_name == 'OracleEnhanced'
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, 'BASE_TABLE' AS table_type,
1470
- LOWER(c.column_name) AS column_name, c.data_type,
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
- execute_oracle(sql, *ar_tables)
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
- kcu2.CONSTRAINT_SCHEMA AS primary_schema, kcu2.TABLE_NAME AS primary_table, kcu1.CONSTRAINT_NAME AS CONSTRAINT_SCHEMA_FK
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.execute_oracle(sql)
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
- tc.constraint_type AS const, kcu.constraint_name AS \"key\",
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
- LEFT OUTER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu ON
1637
- -- ON kcu.CONSTRAINT_CATALOG = t.table_catalog AND
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
- AND kcu.position_in_unique_constraint IS NULL
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
- LEFT OUTER JOIN INFORMATION_SCHEMA.table_constraints AS tc
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 = "_#{bt_assoc_name}" if bt_assoc_name == 'attribute'
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?(for_tbl.singularize.camelize)
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: for_tbl.pluralize, alternate_name: bt_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.precompile ||= []) << "#{assets_path}/images/brick_erd.png"
53
- (app.config.assets.paths ||= []) << assets_path
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.tr(' ', '_')
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} \\\"&nbsp;&nbsp;&nbsp;&nbsp;fk\\\"\".html_safe unless pkeys&.include?(fk_part) %><%
583
585
  end
584
- else %>
585
- <%= \"#\{dt_lookup(cols[fk].first)} #\{fk} \\\"&nbsp;&nbsp;&nbsp;&nbsp;fk\\\"\".html_safe unless pkeys&.include?(fk) %><%
586
+ else # %%% Does not yet accommodate polymorphic BTs
587
+ %>
588
+ <%= \"#\{dt_lookup(cols[fk]&.first)} #\{fk} \\\"&nbsp;&nbsp;&nbsp;&nbsp;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);
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 69
8
+ TINY = 71
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
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(&:underscore)
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
- connection.execute(send(:sanitize_sql_array, [sql] + param_array))
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.69
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-12 00:00:00.000000000 Z
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: '4.2'
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: '4.2'
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: Import and Export Data
267
+ summary: Create a Rails app from data alone
268
268
  test_files: []