brick 1.0.59 → 1.0.60

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: db41c2288f8bba8f3cb9c5d2d87bacd4806d1f2401ca9709f85de1886b0db377
4
- data.tar.gz: 42b0a7b285a3328ce922daa442e15bf994a382ad0bfc5ed8fe4370ae6b779fbb
3
+ metadata.gz: 741384ed503092151ed20f360abd8819fdf3b25fc4042e5aa953dae928acab6d
4
+ data.tar.gz: 11e88b4b7bdc51b9237e095fdaae1297741e1c315737e18d7194c3c84e937797
5
5
  SHA512:
6
- metadata.gz: b8baacad02f8ba5c0d9bfe11182d8297c817ba46bef250e172f55a0ff949591abcadda790aecc2614515468a4da468415a8a0e62fd2a60232177dcd3cd63ca03
7
- data.tar.gz: e84d2573ec0f95b01dfebf681cec409ec66a1e6052abbb59ebd3130728cad71015c51087f484c55e8e30f3b9a3736d3d9262b7d8cdebf23e5ab702035c0dcbfa
6
+ metadata.gz: daa2950ea3ff1354c642949797a4ed7e7ed5c14cfcba4e5560572e997e4df43b537ecb12e84dc2d710b3e067870a91c69519ee41735ea7e9ada0d110ae2bd906
7
+ data.tar.gz: 3e74b2d21d72d84ea7286a7b8959b0abb293c50844b5e3ba4fce73cc4f8b5dd68fa7a50dc9753901f0b82b6a3196ab3fd282c4e709d0ae82af37e5a4e68488a9
@@ -373,6 +373,7 @@ module ActiveRecord
373
373
  end
374
374
 
375
375
  def brick_select(params, selects = nil, order_by = nil, translations = {}, join_array = ::Brick::JoinArray.new)
376
+ is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2'
376
377
  is_distinct = nil
377
378
  wheres = {}
378
379
  params.each do |k, v|
@@ -397,12 +398,14 @@ module ActiveRecord
397
398
  if selects&.empty? # Default to all columns
398
399
  tbl_no_schema = table.name.split('.').last
399
400
  columns.each do |col|
400
- if (col_name = col.name) == 'class'
401
- col_alias = ' AS _class'
402
- end
403
- # Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
404
- cast_as_text = '::text' if is_distinct && Brick.relations[klass.table_name]&.[](:cols)&.[](col.name)&.first&.start_with?('xml')
405
- selects << "\"#{tbl_no_schema}\".\"#{col_name}\"#{cast_as_text}#{col_alias}"
401
+ col_alias = ' AS _class' if (col_name = col.name) == 'class'
402
+ selects << if is_mysql
403
+ "`#{tbl_no_schema}`.`#{col_name}`#{col_alias}"
404
+ else
405
+ # Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
406
+ cast_as_text = '::text' if is_distinct && Brick.relations[klass.table_name]&.[](:cols)&.[](col.name)&.first&.start_with?('xml')
407
+ "\"#{tbl_no_schema}\".\"#{col_name}\"#{cast_as_text}#{col_alias}"
408
+ end
406
409
  end
407
410
  end
408
411
 
@@ -430,7 +433,11 @@ module ActiveRecord
430
433
  if used_col_aliases.key?(col_alias = "_brfk_#{v.first}__#{sel_col.last}")
431
434
  col_alias = "_brfk_#{v.first}__#{v1[idx][-2..-1].map(&:to_s).join('__')}"
432
435
  end
433
- selects << "\"#{field_tbl_name}\".\"#{sel_col.last}\"#{'::text' if is_xml} AS \"#{col_alias}\""
436
+ selects << if is_mysql
437
+ "`#{field_tbl_name}`.`#{sel_col.last}` AS `#{col_alias}`"
438
+ else
439
+ "\"#{field_tbl_name}\".\"#{sel_col.last}\"#{'::text' if is_xml} AS \"#{col_alias}\""
440
+ end
434
441
  used_col_aliases[col_alias] = nil
435
442
  v1[idx] << col_alias
436
443
  end
@@ -439,7 +446,11 @@ module ActiveRecord
439
446
  # Accommodate composite primary key by allowing id_col to come in as an array
440
447
  ((id_col = k1.primary_key).is_a?(Array) ? id_col : [id_col]).each do |id_part|
441
448
  id_for_tables[v.first] << if id_part
442
- selects << "#{"\"#{tbl_name}\".\"#{id_part}\""} AS \"#{(id_alias = "_brfk_#{v.first}__#{id_part}")}\""
449
+ selects << if is_mysql
450
+ "#{"`#{tbl_name}`.`#{id_part}`"} AS `#{(id_alias = "_brfk_#{v.first}__#{id_part}")}`"
451
+ else
452
+ "#{"\"#{tbl_name}\".\"#{id_part}\""} AS \"#{(id_alias = "_brfk_#{v.first}__#{id_part}")}\""
453
+ end
443
454
  id_alias
444
455
  end
445
456
  end
@@ -990,6 +1001,17 @@ class Object
990
1001
  self.send(macro, assoc_name, **options)
991
1002
  end
992
1003
 
1004
+ def default_ordering(table_name, pk)
1005
+ case (order_tbl = ::Brick.config.order[table_name]) && (order_default = order_tbl[:_brick_default])
1006
+ when Array
1007
+ order_default.map { |od_part| order_tbl[od_part] || od_part }
1008
+ when Symbol
1009
+ order_tbl[order_default] || order_default
1010
+ else
1011
+ pk.map(&:to_sym) # If it's not a custom ORDER BY, just use the key
1012
+ end
1013
+ end
1014
+
993
1015
  def build_controller(namespace, class_name, plural_class_name, model, relations)
994
1016
  table_name = ActiveSupport::Inflector.underscore(plural_class_name)
995
1017
  singular_table_name = ActiveSupport::Inflector.singularize(table_name)
@@ -1077,27 +1099,9 @@ class Object
1077
1099
  # Normal (non-swagger) request
1078
1100
 
1079
1101
  # %%% Allow params to define which columns to use for order_by
1080
- ordering = if (order_tbl = ::Brick.config.order[table_name])
1081
- case (order_default = order_tbl[:_brick_default])
1082
- when Array
1083
- order_default.map { |od_part| order_tbl[od_part] || od_part }
1084
- when Symbol
1085
- order_tbl[order_default] || order_default
1086
- else
1087
- pk
1088
- end
1089
- else
1090
- pk # If it's not a custom ORDER BY, just use the key
1091
- end
1092
- order_by, order_by_txt = model._brick_calculate_ordering(ordering)
1093
- if (order_params = params['_brick_order']&.split(',')&.map(&:to_sym)) # Overriding the default by providing a querystring param?
1094
- order_by, _ = model._brick_calculate_ordering(order_params, true) # Don't do the txt part
1095
- end
1096
-
1097
- code << " def index\n"
1098
- code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{order_by_txt.join(', ')})" : '.all'}\n"
1099
- code << " @#{table_name}.brick_select(params)\n"
1100
- code << " end\n"
1102
+ # Overriding the default by providing a querystring param?
1103
+ ordering = params['_brick_order']&.split(',')&.map(&:to_sym) || Object.send(:default_ordering, table_name, pk)
1104
+ order_by, _ = model._brick_calculate_ordering(ordering, true) # Don't do the txt part
1101
1105
 
1102
1106
  ::Brick.set_db_schema(params)
1103
1107
  if request.format == :csv # Asking for a template?
@@ -1130,6 +1134,12 @@ class Object
1130
1134
  @_brick_join_array = join_array
1131
1135
  end
1132
1136
 
1137
+ _, order_by_txt = model._brick_calculate_ordering(default_ordering(table_name, pk))
1138
+ code << " def index\n"
1139
+ code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{order_by_txt.join(', ')})" : '.all'}\n"
1140
+ code << " @#{table_name}.brick_select(params)\n"
1141
+ code << " end\n"
1142
+
1133
1143
  is_pk_string = nil
1134
1144
  if (pk_col = model&.primary_key)
1135
1145
  code << " def show\n"
@@ -1316,7 +1326,7 @@ module ActiveRecord::ConnectionHandling
1316
1326
  load apartment_initializer
1317
1327
  apartment_excluded = Apartment.excluded_models
1318
1328
  end
1319
- # Only for Postgres? (Doesn't work in sqlite3)
1329
+ # Only for Postgres (Doesn't work in sqlite3 or MySQL)
1320
1330
  # puts ActiveRecord::Base.execute_sql("SELECT current_setting('SEARCH_PATH')").to_a.inspect
1321
1331
 
1322
1332
  is_postgres = nil
@@ -1351,7 +1361,7 @@ module ActiveRecord::ConnectionHandling
1351
1361
  puts "Unfamiliar with connection adapter #{ActiveRecord::Base.connection.adapter_name}"
1352
1362
  end
1353
1363
 
1354
- ::Brick.db_schemas ||= []
1364
+ ::Brick.db_schemas ||= {}
1355
1365
 
1356
1366
  if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
1357
1367
  if (possible_schema = ::Brick.config.schema_behavior&.[](:multitenant)&.[](:schema_to_analyse))
@@ -1367,41 +1377,11 @@ module ActiveRecord::ConnectionHandling
1367
1377
  # %%% Retrieve internal ActiveRecord table names like this:
1368
1378
  # ActiveRecord::Base.internal_metadata_table_name, ActiveRecord::Base.schema_migrations_table_name
1369
1379
  # For if it's not SQLite -- so this is the Postgres and MySQL version
1370
- sql ||= "SELECT t.table_schema AS schema, t.table_name AS relation_name, t.table_type,#{"
1371
- pg_catalog.obj_description((t.table_schema || '.' || t.table_name)::regclass, 'pg_class') AS table_description," if is_postgres}
1372
- c.column_name, c.data_type,
1373
- COALESCE(c.character_maximum_length, c.numeric_precision) AS max_length,
1374
- tc.constraint_type AS const, kcu.constraint_name AS \"key\",
1375
- c.is_nullable
1376
- FROM INFORMATION_SCHEMA.tables AS t
1377
- LEFT OUTER JOIN INFORMATION_SCHEMA.columns AS c ON t.table_schema = c.table_schema
1378
- AND t.table_name = c.table_name
1379
- LEFT OUTER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu ON
1380
- -- ON kcu.CONSTRAINT_CATALOG = t.table_catalog AND
1381
- kcu.CONSTRAINT_SCHEMA = c.table_schema
1382
- AND kcu.TABLE_NAME = c.table_name
1383
- AND kcu.position_in_unique_constraint IS NULL
1384
- AND kcu.ordinal_position = c.ordinal_position
1385
- LEFT OUTER JOIN INFORMATION_SCHEMA.table_constraints AS tc
1386
- ON kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
1387
- AND kcu.TABLE_NAME = tc.TABLE_NAME
1388
- AND kcu.CONSTRAINT_NAME = tc.constraint_name
1389
- WHERE t.table_schema NOT IN ('information_schema', 'pg_catalog')#{"
1390
- AND t.table_schema = COALESCE(current_setting('SEARCH_PATH'), 'public')" if schema }
1391
- -- AND t.table_type IN ('VIEW') -- 'BASE TABLE', 'FOREIGN TABLE'
1392
- AND t.table_name NOT IN ('pg_stat_statements', ?, ?)
1393
- ORDER BY 1, t.table_type DESC, c.ordinal_position"
1394
1380
  measures = []
1395
1381
  case ActiveRecord::Base.connection.adapter_name
1396
1382
  when 'PostgreSQL', 'SQLite' # These bring back a hash for each row because the query uses column aliases
1397
1383
  # schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
1398
- ar_smtn = if ActiveRecord::Base.respond_to?(:schema_migrations_table_name)
1399
- ActiveRecord::Base.schema_migrations_table_name
1400
- else
1401
- 'schema_migrations'
1402
- end
1403
- ar_imtn = ActiveRecord.version >= ::Gem::Version.new('5.0') ? ActiveRecord::Base.internal_metadata_table_name : ''
1404
- ActiveRecord::Base.execute_sql(sql, ar_smtn, ar_imtn).each do |r|
1384
+ ActiveRecord::Base.retrieve_schema_and_tables(sql, is_postgres, schema).each do |r|
1405
1385
  # If Apartment gem lists the table as being associated with a non-tenanted model then use whatever it thinks
1406
1386
  # is the default schema, usually 'public'.
1407
1387
  schema_name = if ::Brick.config.schema_behavior[:multitenant]
@@ -1428,23 +1408,23 @@ module ActiveRecord::ConnectionHandling
1428
1408
  # puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
1429
1409
  end
1430
1410
  else # MySQL2 acts a little differently, bringing back an array for each row
1431
- ActiveRecord::Base.execute_sql(sql).each do |r|
1432
- relation = relations[(relation_name = r[0])] # here relation represents a table or view from the database
1433
- relation[:isView] = true if r[1] == 'VIEW' # table_type
1434
- col_name = r[2]
1435
- key = case r[5] # constraint type
1411
+ ActiveRecord::Base.retrieve_schema_and_tables(sql).each do |r|
1412
+ relation = relations[(relation_name = r[1])] # here relation represents a table or view from the database
1413
+ relation[:isView] = true if r[2] == 'VIEW' # table_type
1414
+ col_name = r[3]
1415
+ key = case r[6] # constraint type
1436
1416
  when 'PRIMARY KEY'
1437
1417
  # key
1438
- relation[:pkey][r[6] || relation_name] ||= []
1418
+ relation[:pkey][r[7] || relation_name] ||= []
1439
1419
  when 'UNIQUE'
1440
- relation[:ukeys][r[6] || "#{relation_name}.#{col_name}"] ||= []
1420
+ relation[:ukeys][r[7] || "#{relation_name}.#{col_name}"] ||= []
1441
1421
  # key = (relation[:ukeys] = Hash.new { |h, k| h[k] = [] }) if key.is_a?(Array)
1442
1422
  # key[r['key']]
1443
1423
  end
1444
1424
  key << col_name if key
1445
1425
  cols = relation[:cols] # relation.fetch(:cols) { relation[:cols] = [] }
1446
1426
  # 'data_type', 'max_length'
1447
- cols[col_name] = [r[3], r[4], measures&.include?(col_name)]
1427
+ cols[col_name] = [r[4], r[5], measures&.include?(col_name)]
1448
1428
  # puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
1449
1429
  end
1450
1430
  end
@@ -1482,7 +1462,7 @@ module ActiveRecord::ConnectionHandling
1482
1462
  AND kcu2.CONSTRAINT_SCHEMA = rc.UNIQUE_CONSTRAINT_SCHEMA
1483
1463
  AND kcu2.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
1484
1464
  AND kcu2.ORDINAL_POSITION = kcu1.ORDINAL_POSITION#{"
1485
- WHERE kcu1.CONSTRAINT_SCHEMA = COALESCE(current_setting('SEARCH_PATH'), 'public')" if schema }"
1465
+ WHERE kcu1.CONSTRAINT_SCHEMA = COALESCE(current_setting('SEARCH_PATH'), 'public')" if is_postgres && schema }"
1486
1466
  # AND kcu2.TABLE_NAME = ?;", Apartment::Tenant.current, table_name
1487
1467
  when 'SQLite'
1488
1468
  sql = "SELECT m.name, fkl.\"from\", fkl.\"table\", m.name || '_' || fkl.\"from\" AS constraint_name
@@ -1498,12 +1478,14 @@ module ActiveRecord::ConnectionHandling
1498
1478
  # Multitenancy makes things a little more general overall, except for non-tenanted tables
1499
1479
  if apartment_excluded&.include?(fk[1].singularize.camelize)
1500
1480
  fk[0] = Apartment.default_schema
1501
- elsif fk[0] == 'public' || (is_multitenant && fk[0] == schema)
1481
+ elsif is_postgres && (fk[0] == 'public' || (is_multitenant && fk[0] == schema)) ||
1482
+ !is_postgres && ['mysql', 'performance_schema', 'sys'].exclude?(fk[0])
1502
1483
  fk[0] = nil
1503
1484
  end
1504
1485
  if apartment_excluded&.include?(fk[4].singularize.camelize)
1505
1486
  fk[3] = Apartment.default_schema
1506
- elsif fk[3] == 'public' || (is_multitenant && fk[3] == schema)
1487
+ elsif is_postgres && (fk[3] == 'public' || (is_multitenant && fk[3] == schema)) ||
1488
+ !is_postgres && ['mysql', 'performance_schema', 'sys'].exclude?(fk[3])
1507
1489
  fk[3] = nil
1508
1490
  end
1509
1491
  ::Brick._add_bt_and_hm(fk, relations)
@@ -1537,6 +1519,41 @@ module ActiveRecord::ConnectionHandling
1537
1519
 
1538
1520
  ::Brick.load_additional_references if initializer_loaded
1539
1521
  end
1522
+
1523
+ def retrieve_schema_and_tables(sql = nil, is_postgres = nil, schema = nil)
1524
+ sql ||= "SELECT t.table_schema AS \"schema\", t.table_name AS relation_name, t.table_type,#{"
1525
+ pg_catalog.obj_description((t.table_schema || '.' || t.table_name)::regclass, 'pg_class') AS table_description," if is_postgres}
1526
+ c.column_name, c.data_type,
1527
+ COALESCE(c.character_maximum_length, c.numeric_precision) AS max_length,
1528
+ tc.constraint_type AS const, kcu.constraint_name AS \"key\",
1529
+ c.is_nullable
1530
+ FROM INFORMATION_SCHEMA.tables AS t
1531
+ LEFT OUTER JOIN INFORMATION_SCHEMA.columns AS c ON t.table_schema = c.table_schema
1532
+ AND t.table_name = c.table_name
1533
+ LEFT OUTER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu ON
1534
+ -- ON kcu.CONSTRAINT_CATALOG = t.table_catalog AND
1535
+ kcu.CONSTRAINT_SCHEMA = c.table_schema
1536
+ AND kcu.TABLE_NAME = c.table_name
1537
+ AND kcu.position_in_unique_constraint IS NULL
1538
+ AND kcu.ordinal_position = c.ordinal_position
1539
+ LEFT OUTER JOIN INFORMATION_SCHEMA.table_constraints AS tc
1540
+ ON kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
1541
+ AND kcu.TABLE_NAME = tc.TABLE_NAME
1542
+ AND kcu.CONSTRAINT_NAME = tc.constraint_name
1543
+ WHERE t.table_schema NOT IN ('information_schema', #{
1544
+ is_postgres ? "'pg_catalog'" : "'mysql', 'performance_schema', 'sys'"})#{"
1545
+ AND t.table_schema = COALESCE(current_setting('SEARCH_PATH'), 'public')" if is_postgres && schema }
1546
+ -- AND t.table_type IN ('VIEW') -- 'BASE TABLE', 'FOREIGN TABLE'
1547
+ AND t.table_name NOT IN ('pg_stat_statements', ?, ?)
1548
+ ORDER BY 1, t.table_type DESC, 2, c.ordinal_position"
1549
+ ar_smtn = if ActiveRecord::Base.respond_to?(:schema_migrations_table_name)
1550
+ ActiveRecord::Base.schema_migrations_table_name
1551
+ else
1552
+ 'schema_migrations'
1553
+ end
1554
+ ar_imtn = ActiveRecord.version >= ::Gem::Version.new('5.0') ? ActiveRecord::Base.internal_metadata_table_name : ''
1555
+ ActiveRecord::Base.execute_sql(sql, ar_smtn, ar_imtn)
1556
+ end
1540
1557
  end
1541
1558
 
1542
1559
  # ==========================================
@@ -150,7 +150,6 @@ module Brick
150
150
  ", nil, #{path_keys(hm_assoc, hm_fk_name, obj_name, pk)}"
151
151
  end
152
152
  hm_entry << ']'
153
- puts hm_entry
154
153
  hms_columns << hm_entry
155
154
  when 'show', 'update'
156
155
  hm_stuff << if hm_fk_name
@@ -615,8 +614,9 @@ if (headerTop) {
615
614
  });
616
615
  });
617
616
  </script>
618
- <% end %>
619
- <table id=\"headerTop\">
617
+ <% end
618
+
619
+ %><table id=\"headerTop\">
620
620
  <table id=\"#{table_name}\">
621
621
  <thead><tr>#{"<th x-order=\"#{pk.join(',')}\"></th>" if pk.present?}<%=
622
622
  # Consider getting the name from the association -- hm.first.name -- if a more \"friendly\" alias should be used for a screwy table name
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 59
8
+ TINY = 60
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
@@ -32,6 +32,7 @@ module Brick
32
32
  end
33
33
  models = ::Brick.relations.keys.map do |tbl|
34
34
  tbl_parts = tbl.split('.')
35
+ tbl_parts.shift if [::Brick.default_schema, 'public'].include?(tbl_parts.first)
35
36
  tbl_parts[-1] = tbl_parts[-1].singularize
36
37
  tbl_parts.join('/').camelize
37
38
  end - existing_models.map(&:name)
@@ -54,7 +55,7 @@ module Brick
54
55
  chosen = gets_list(list: models, chosen: models.dup)
55
56
  relations = ::Brick.relations
56
57
  chosen.each do |model_name|
57
- # If we're in a schema then make sure the module file exists
58
+ # %%% If we're in a schema then make sure the module file exists
58
59
  base_module = if (model_parts = model_name.split('::')).length > 1
59
60
  "::#{model_parts.first}".constantize
60
61
  else
@@ -68,7 +69,7 @@ module Brick
68
69
  dir << "/#{path_part}"
69
70
  Dir.mkdir(dir) unless Dir.exists?(dir)
70
71
  end
71
- File.open("#{dir}/#{path.last}.rb", 'w') { |f| f.write code }
72
+ File.open("#{dir}/#{path.last}.rb", 'w') { |f| f.write code } unless code.blank?
72
73
  end
73
74
  puts "\n*** Created #{chosen.length} model files under app/models ***"
74
75
  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.59
4
+ version: 1.0.60
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-08-14 00:00:00.000000000 Z
11
+ date: 2022-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord