brick 1.0.93 → 1.0.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/brick/config.rb +8 -0
- data/lib/brick/extensions.rb +90 -7
- data/lib/brick/frameworks/rails/crosstab.brk +0 -0
- data/lib/brick/frameworks/rails/engine.rb +36 -13
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +78 -8
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebb01aca7fcf3fa8cfbb699e48fa85601341df79645d4f3d6ebc5a43351c3d29
|
4
|
+
data.tar.gz: 2bca81242ee8eaa5bbfd477b39df8eb969f8999abe680329e2ae9b1b4ef928a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 566f2e6925b918fb8c7b3bf545dc8fd98f840b35fdf875e0f7812de3f127dae21e2fd4b9a2f3e9faba5255ec83d76b0651657e0051aa8e9706429354dee4d012
|
7
|
+
data.tar.gz: b07ce93b35464f516e3f03b1c8c98d215b3fa2db1051af5d6d41131313d694e483cc7cc40a063b6fd6b70e6d5eb2afb99fb9cc47914a81e6dc0a2392a5b2602c
|
data/lib/brick/config.rb
CHANGED
data/lib/brick/extensions.rb
CHANGED
@@ -245,12 +245,15 @@ module ActiveRecord
|
|
245
245
|
end
|
246
246
|
|
247
247
|
def self.bt_link(assoc_name)
|
248
|
-
|
248
|
+
assoc_html_name = unless (assoc_name = assoc_name.to_s).camelize == name
|
249
|
+
CGI.escapeHTML(assoc_name)
|
250
|
+
end
|
249
251
|
model_path = ::Rails.application.routes.url_helpers.send("#{_brick_index}_path".to_sym)
|
252
|
+
model_path << "?#{self.inheritance_column}=#{self.name}" if self != base_class
|
250
253
|
av_class = Class.new.extend(ActionView::Helpers::UrlHelper)
|
251
254
|
av_class.extend(ActionView::Helpers::TagHelper) if ActionView.version < ::Gem::Version.new('7')
|
252
|
-
link = av_class.link_to(name, model_path)
|
253
|
-
|
255
|
+
link = av_class.link_to(assoc_html_name ? name : assoc_name, model_path)
|
256
|
+
assoc_html_name ? "#{assoc_name}-#{link}".html_safe : link
|
254
257
|
end
|
255
258
|
|
256
259
|
def self._brick_index(mode = nil)
|
@@ -542,7 +545,7 @@ module ActiveRecord
|
|
542
545
|
field_tbl_name = "\"#{field_tbl_name}\"" if ::Brick.is_oracle && rel_dupe.brick_links.values.include?(field_tbl_name)
|
543
546
|
|
544
547
|
# Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
|
545
|
-
is_xml = is_distinct && Brick.relations[
|
548
|
+
is_xml = is_distinct && Brick.relations[k1.table_name]&.[](:cols)&.[](sel_col.last)&.first&.start_with?('xml')
|
546
549
|
# If it's not unique then also include the belongs_to association name before the column name
|
547
550
|
if used_col_aliases.key?(col_alias = "br_fk_#{v.first}__#{sel_col.last}")
|
548
551
|
col_alias = "br_fk_#{v.first}__#{v1[idx][-2..-1].map(&:to_s).join('__')}"
|
@@ -1413,6 +1416,85 @@ class Object
|
|
1413
1416
|
self.define_method :orphans do
|
1414
1417
|
instance_variable_set(:@orphans, ::Brick.find_orphans(::Brick.set_db_schema(params).first))
|
1415
1418
|
end
|
1419
|
+
self.define_method :crosstab do
|
1420
|
+
@relations = ::Brick.relations.each_with_object({}) do |r, s|
|
1421
|
+
cols = r.last[:cols].each_with_object([]) do |c, s2|
|
1422
|
+
s2 << [c.first] + c.last
|
1423
|
+
end
|
1424
|
+
s[r.first] = { cols: cols } if r.last.key?(:cols)
|
1425
|
+
end
|
1426
|
+
end
|
1427
|
+
|
1428
|
+
self.define_method :crosstab_data do
|
1429
|
+
# Bring in column names and use this to create an appropriate SELECT statement
|
1430
|
+
is_grouped = nil
|
1431
|
+
fields_in_sequence = params['fields'].split(',')
|
1432
|
+
relations = {}
|
1433
|
+
first_relation = nil
|
1434
|
+
fields = fields_in_sequence.each_with_object(Hash.new { |h, k| h[k] = {} }) do |f, s|
|
1435
|
+
relation, col = if (paren_index = f.index('(')) # Aggregate?
|
1436
|
+
aggregate = f[0..paren_index - 1]
|
1437
|
+
f_parts = f[(paren_index + 1)..-2].split(',')
|
1438
|
+
aggregate_options = f_parts[1..-1] # Options about aggregation
|
1439
|
+
f_parts.first.split('/') # Column being aggregated
|
1440
|
+
else
|
1441
|
+
f.split('/')
|
1442
|
+
end
|
1443
|
+
first_relation ||= relation
|
1444
|
+
# relation = if dts[(relation = relation.downcase)]
|
1445
|
+
# relation # Generally a common JOIN point, but who knows, maybe we'll add more
|
1446
|
+
# else
|
1447
|
+
# relation
|
1448
|
+
# end
|
1449
|
+
if col
|
1450
|
+
relations[relation] = nil
|
1451
|
+
s[f] = if aggregate
|
1452
|
+
is_grouped = true
|
1453
|
+
[aggregate, relation, col, aggregate_options]
|
1454
|
+
else
|
1455
|
+
[relation, col]
|
1456
|
+
end
|
1457
|
+
end
|
1458
|
+
s
|
1459
|
+
end
|
1460
|
+
# ver = params['ver']
|
1461
|
+
if fields.empty?
|
1462
|
+
render json: { data: [] } # [ver, []]
|
1463
|
+
return
|
1464
|
+
end
|
1465
|
+
|
1466
|
+
# Apartment::Tenant.switch!(params['schema'])
|
1467
|
+
# result = ActiveRecord::Base.connection.query("SELECT #{cols.join(', ')} FROM #{view}")
|
1468
|
+
col_num = 0
|
1469
|
+
grouping = []
|
1470
|
+
cols = fields_in_sequence.each_with_object([]) do |f, s|
|
1471
|
+
c = fields[f]
|
1472
|
+
col_num += 1
|
1473
|
+
col_def = if c.length > 2 # Aggregate?
|
1474
|
+
case c.first
|
1475
|
+
when 'COMMA_SEP'
|
1476
|
+
"STRING_AGG(DISTINCT #{c[1].downcase}.\"#{c[2]}\"::varchar, ',')" # Like STRING_AGG(DISTINCT v_tacos."price"::varchar, ',')
|
1477
|
+
when 'COUNT_DISTINCT'
|
1478
|
+
"COUNT(DISTINCT #{c[1].downcase}.\"#{c[2]}\")" # Like COUNT(DISTINCT v_tacos."price")
|
1479
|
+
when 'MODE'
|
1480
|
+
"MODE() WITHIN GROUP(ORDER BY #{c[1].downcase}.\"#{c[2]}\")" # Like MODE() WITHIN GROUP(ORDER BY v_tacos."price")
|
1481
|
+
when 'NUM_DAYS'
|
1482
|
+
"EXTRACT(DAYS FROM (MAX(#{c[1].downcase}.\"#{c[2]}\")::timestamp - MIN(#{c[1].downcase}.\"#{c[2]}\")::timestamp))" # Like EXTRACT(DAYS FROM (MAX(order."order_date") - MIN(order."order_date"))
|
1483
|
+
else
|
1484
|
+
"#{c.first}(#{c[1].downcase}.\"#{c[2]}\")" # Something like AVG(v_tacos."price")
|
1485
|
+
end
|
1486
|
+
else # Normal column, represented in an array having: [relation, column_name]
|
1487
|
+
grouping << col_num
|
1488
|
+
"#{c.first.downcase}.\"#{c.last}\"" # Like v_tacos."price"
|
1489
|
+
end
|
1490
|
+
s << "#{col_def} AS c#{col_num}"
|
1491
|
+
end
|
1492
|
+
sql = "SELECT #{cols.join(', ')} FROM #{first_relation.downcase}"
|
1493
|
+
sql << "\nGROUP BY #{grouping.map(&:to_s).join(',')}" if is_grouped && grouping.present?
|
1494
|
+
result = ActiveRecord::Base.connection.query(sql)
|
1495
|
+
render json: { data: result } # [ver, result]
|
1496
|
+
end
|
1497
|
+
|
1416
1498
|
return [new_controller_class, code + "end # BrickGem controller\n"]
|
1417
1499
|
when 'BrickOpenapi'
|
1418
1500
|
is_openapi = true
|
@@ -2310,7 +2392,7 @@ module Brick
|
|
2310
2392
|
rails_root = ::Rails.root.to_s
|
2311
2393
|
migrations = if Dir.exist?(mig_path = ActiveRecord::Migrator.migrations_paths.first || "#{rails_root}/db/migrate")
|
2312
2394
|
Dir["#{mig_path}/**/*.rb"].each_with_object(Hash.new { |h, k| h[k] = [] }) do |v, s|
|
2313
|
-
File.read(v).split("\n").
|
2395
|
+
File.read(v).split("\n").each_with_index do |line, line_idx|
|
2314
2396
|
# For all non-commented lines, look for any that have "create_table", "alter_table", or "drop_table"
|
2315
2397
|
if !line.lstrip.start_with?('#') &&
|
2316
2398
|
(idx = (line.index('create_table ') || line.index('create_table('))&.+(13)) ||
|
@@ -2318,8 +2400,9 @@ module Brick
|
|
2318
2400
|
(idx = (line.index('drop_table ') || line.index('drop_table('))&.+(11))
|
2319
2401
|
tbl = line[idx..-1].match(/([:'"\w\.]+)/)&.captures&.first
|
2320
2402
|
if tbl
|
2321
|
-
|
2322
|
-
|
2403
|
+
v = v[(rails_root.length)..-1] if v.start_with?(rails_root)
|
2404
|
+
v = v[1..-1] if v.start_with?('/')
|
2405
|
+
s[tbl.tr(':\'"', '').pluralize] << [v, line_idx + 1]
|
2323
2406
|
end
|
2324
2407
|
end
|
2325
2408
|
end
|
Binary file
|
@@ -71,6 +71,7 @@ module Brick
|
|
71
71
|
def template_exists?(*args, **options)
|
72
72
|
(::Brick.config.add_status && args.first == 'status') ||
|
73
73
|
(::Brick.config.add_orphans && args.first == 'orphans') ||
|
74
|
+
(args.first == 'crosstab') ||
|
74
75
|
_brick_template_exists?(*args, **options) ||
|
75
76
|
# Do not auto-create a template when it's searching for an application.html.erb, which comes in like: ["edit", ["games", "application"]]
|
76
77
|
((args[1].length == 1 || args[1][-1] != 'application') &&
|
@@ -107,7 +108,8 @@ module Brick
|
|
107
108
|
def find_template(*args, **options)
|
108
109
|
unless (model_name = @_brick_model&.name) ||
|
109
110
|
(is_status = ::Brick.config.add_status && args[0..1] == ['status', ['brick_gem']]) ||
|
110
|
-
(is_orphans = ::Brick.config.add_orphans && args[0..1] == ['orphans', ['brick_gem']])
|
111
|
+
(is_orphans = ::Brick.config.add_orphans && args[0..1] == ['orphans', ['brick_gem']]) ||
|
112
|
+
(is_crosstab = args[0..1] == ['crosstab', ['brick_gem']])
|
111
113
|
if ActionView.version < ::Gem::Version.new('5.0') # %%% Not sure if this should be perhaps 4.2 instead
|
112
114
|
begin
|
113
115
|
if (possible_template = _brick_find_template(*args, **options))
|
@@ -141,10 +143,10 @@ module Brick
|
|
141
143
|
next unless @_brick_model.instance_methods.include?(through) &&
|
142
144
|
(associative = @_brick_model._br_associatives.fetch(hm.first, nil))
|
143
145
|
|
144
|
-
tbl_nm = if (source = hm_assoc.source_reflection).macro == :
|
145
|
-
hm_assoc.through_reflection&.name # for standard HMT, which is HM -> BT
|
146
|
-
else
|
146
|
+
tbl_nm = if (source = hm_assoc.source_reflection).macro == :has_many
|
147
147
|
source.inverse_of&.name # For HM -> HM style HMT
|
148
|
+
else # belongs_to or has_one
|
149
|
+
hm_assoc.through_reflection&.name # for standard HMT, which is HM -> BT
|
148
150
|
end
|
149
151
|
# If there is no inverse available for the source belongs_to association, make one based on the class name
|
150
152
|
unless tbl_nm
|
@@ -203,7 +205,6 @@ module Brick
|
|
203
205
|
# environment or whatever, then get either the controllers or routes list instead
|
204
206
|
prefix = "#{::Brick.config.path_prefix}/" if ::Brick.config.path_prefix
|
205
207
|
table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables).each_with_object({}) do |tbl, s|
|
206
|
-
binding.pry if tbl.is_a?(Symbol)
|
207
208
|
if (tbl_parts = tbl.split('.')).first == apartment_default_schema
|
208
209
|
tbl = tbl_parts.last
|
209
210
|
end
|
@@ -213,6 +214,7 @@ module Brick
|
|
213
214
|
end.html_safe
|
214
215
|
table_options << "<option value=\"#{prefix}brick_status\">(Status)</option>".html_safe if ::Brick.config.add_status
|
215
216
|
table_options << "<option value=\"#{prefix}brick_orphans\">(Orphans)</option>".html_safe if is_orphans
|
217
|
+
table_options << "<option value=\"#{prefix}brick_orphans\">(Crosstab)</option>".html_safe if is_crosstab
|
216
218
|
css = +"<style>
|
217
219
|
h1, h3 {
|
218
220
|
margin-bottom: 0;
|
@@ -911,7 +913,9 @@ erDiagram
|
|
911
913
|
end
|
912
914
|
unless @_brick_sequence # If no sequence is defined, start with all inclusions
|
913
915
|
cust_cols = #{model_name}._br_cust_cols
|
914
|
-
|
916
|
+
# HOT columns, kept as symbols
|
917
|
+
hots = #{model_name}._br_bt_descrip.keys.select { |k| bts.key?(k) }
|
918
|
+
@_brick_sequence = col_keys + cust_cols.keys + hots + #{(hms_keys).inspect}.reject { |assoc_name| @_brick_incl&.exclude?(assoc_name) }
|
915
919
|
end
|
916
920
|
@_brick_sequence.reject! { |nm| @_brick_excl.include?(nm) } if @_brick_excl # Reject exclusions
|
917
921
|
@_brick_sequence.each_with_object(+'') do |col_name, s|
|
@@ -928,8 +932,12 @@ erDiagram
|
|
928
932
|
elsif col # HM column
|
929
933
|
s << \"<th#\{' x-order=\"' + col_name + '\"' if true}>#\{col[2]} \"
|
930
934
|
s << (col.first ? \"#\{col[3]}\" : \"#\{link_to(col[3], send(\"#\{col[1]._brick_index}_path\"))}\")
|
931
|
-
elsif
|
935
|
+
elsif cust_cols.key?(col_name) # Custom column
|
932
936
|
s << \"<th x-order=\\\"#\{col_name}\\\">#\{col_name}\"
|
937
|
+
elsif col_name.is_a?(Symbol) && (hot = bts[col_name]) # has_one :through
|
938
|
+
s << \"<th x-order=\\\"#\{hot.first.to_s}\\\">HOT \" +
|
939
|
+
hot[1].map { |hot_pair| hot_pair.first.bt_link(col_name) }.join(' ')
|
940
|
+
hot[1].first
|
933
941
|
else # Bad column name!
|
934
942
|
s << \"<th title=\\\"<< Unknown column >>\\\">#\{col_name}\"
|
935
943
|
end
|
@@ -947,14 +955,16 @@ erDiagram
|
|
947
955
|
<td><%= link_to '⇛', #{path_obj_name}_path(slashify(#{obj_pk})), { class: 'big-arrow' } %></td>" if obj_pk}
|
948
956
|
<% @_brick_sequence.each do |col_name|
|
949
957
|
val = #{obj_name}.attributes[col_name] %>
|
950
|
-
<td<%= ' class=\"dimmed\"'.html_safe unless cols.key?(col_name) || (cust_col = cust_cols[col_name])
|
958
|
+
<td<%= ' class=\"dimmed\"'.html_safe unless cols.key?(col_name) || (cust_col = cust_cols[col_name]) ||
|
959
|
+
(col_name.is_a?(Symbol) && bts.key?(col_name)) # HOT
|
960
|
+
%>><%
|
951
961
|
if (bt = bts[col_name])
|
952
962
|
if bt[2] # Polymorphic?
|
953
963
|
bt_class = #{obj_name}.send(\"#\{bt.first}_type\")
|
954
964
|
base_class_underscored = (::Brick.existing_stis[bt_class] || bt_class).constantize.base_class._brick_index(:singular)
|
955
965
|
poly_id = #{obj_name}.send(\"#\{bt.first}_id\")
|
956
966
|
%><%= link_to(\"#\{bt_class} ##\{poly_id}\", send(\"#\{base_class_underscored}_path\".to_sym, poly_id)) if poly_id %><%
|
957
|
-
else
|
967
|
+
else # BT or HOT
|
958
968
|
bt_class = bt[1].first.first
|
959
969
|
descrips = @_brick_bt_descrip[bt.first][bt_class]
|
960
970
|
bt_id_col = if descrips.nil?
|
@@ -1041,7 +1051,7 @@ erDiagram
|
|
1041
1051
|
%>
|
1042
1052
|
<tr>
|
1043
1053
|
<td><%= begin
|
1044
|
-
kls = Object.const_get(::Brick.relations
|
1054
|
+
kls = Object.const_get(::Brick.relations.fetch(r[0], nil)&.fetch(:class_name, nil))
|
1045
1055
|
rescue
|
1046
1056
|
end
|
1047
1057
|
kls ? link_to(r[0], send(\"#\{kls._brick_index}_path\".to_sym)) : r[0] %></td>
|
@@ -1051,8 +1061,9 @@ erDiagram
|
|
1051
1061
|
' class=\"dimmed\"'
|
1052
1062
|
end&.html_safe %>><%= # Table
|
1053
1063
|
r[1] %></td>
|
1054
|
-
<td<%=
|
1055
|
-
|
1064
|
+
<td<%= lines = r[2]&.map { |line| \"#\{line.first}:#\{line.last}\" }
|
1065
|
+
' class=\"dimmed\"'.html_safe unless r[2] %>><%= # Migration
|
1066
|
+
lines&.join('<br>')&.html_safe %></td>
|
1056
1067
|
<td<%= ' class=\"dimmed\"'.html_safe unless r[3] %>><%= # Model
|
1057
1068
|
r[3] %></td>
|
1058
1069
|
<td<%= ' class=\"dimmed\"'.html_safe unless r[4] %>><%= # Route
|
@@ -1085,6 +1096,16 @@ erDiagram
|
|
1085
1096
|
#{script}"
|
1086
1097
|
end
|
1087
1098
|
|
1099
|
+
when 'crosstab'
|
1100
|
+
if is_crosstab && ::Brick.config.license_key
|
1101
|
+
decipher = OpenSSL::Cipher::AES256.new(:CBC).decrypt
|
1102
|
+
decipher.iv = "\xB4,\r2\x19\xF5\xFE/\aR\x1A\x8A\xCFV\v\x8C"
|
1103
|
+
decipher.key = Digest::SHA256.hexdigest(::Brick.config.license_key).scan(/../).map { |x| x.hex }.pack('c*')
|
1104
|
+
decipher.update(File.binread("/Users/aga/brick/lib/brick/frameworks/rails/crosstab.brk"))[16..-1]
|
1105
|
+
else
|
1106
|
+
'Crosstab Charting not yet activated -- enter a valid license key in brick.rb'
|
1107
|
+
end
|
1108
|
+
|
1088
1109
|
when 'show', 'new', 'update'
|
1089
1110
|
+"<html>
|
1090
1111
|
<head>
|
@@ -1274,7 +1295,8 @@ end}
|
|
1274
1295
|
"
|
1275
1296
|
|
1276
1297
|
end
|
1277
|
-
|
1298
|
+
unless is_crosstab
|
1299
|
+
inline << "
|
1278
1300
|
<% if is_includes_dates %>
|
1279
1301
|
<link rel=\"stylesheet\" type=\"text/css\" href=\"https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css\">
|
1280
1302
|
<style>
|
@@ -1408,6 +1430,7 @@ document.querySelectorAll(\"input, select\").forEach(function (inp) {
|
|
1408
1430
|
}
|
1409
1431
|
});
|
1410
1432
|
</script>"
|
1433
|
+
end
|
1411
1434
|
# puts "==============="
|
1412
1435
|
# puts inline
|
1413
1436
|
# puts "==============="
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -211,12 +211,13 @@ module Brick
|
|
211
211
|
else
|
212
212
|
s.first[a.foreign_key.to_s] = [a.name, a.klass]
|
213
213
|
end
|
214
|
-
else # This gets
|
215
|
-
if through
|
214
|
+
else # This gets all forms of has_many and has_one
|
215
|
+
if through # has_many :through or has_one :through
|
216
216
|
is_invalid_source = nil
|
217
217
|
begin
|
218
|
-
if a.through_reflection
|
219
|
-
|
218
|
+
if a.through_reflection.macro != :has_many # This HM goes through either a belongs_to or a has_one, so essentially a HOT?
|
219
|
+
# Treat it like a belongs_to - just keyed on the association name instead of a foreign_key
|
220
|
+
s.first[a.name] = [a.name, a.klass]
|
220
221
|
next
|
221
222
|
elsif !a.source_reflection # Had considered: a.active_record.reflect_on_association(a.source_reflection_name).nil?
|
222
223
|
is_invalid_source = true
|
@@ -465,6 +466,10 @@ module Brick
|
|
465
466
|
Brick.config.default_route_fallback = resource_name
|
466
467
|
end
|
467
468
|
|
469
|
+
def license_key=(key)
|
470
|
+
Brick.config.license_key = key
|
471
|
+
end
|
472
|
+
|
468
473
|
# Load additional references (virtual foreign keys)
|
469
474
|
# This is attempted early if a brick initialiser file is found, and then again as a failsafe at the end of our engine's initialisation
|
470
475
|
# %%% Maybe look for differences the second time 'round and just add new stuff instead of entirely deferring
|
@@ -690,6 +695,11 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
690
695
|
get("/#{controller_prefix}brick_orphans", to: 'brick_gem#orphans', as: 'brick_orphans')
|
691
696
|
end
|
692
697
|
|
698
|
+
if instance_variable_get(:@set).named_routes.names.exclude?(:brick_crosstab)
|
699
|
+
get("/#{controller_prefix}brick_crosstab", to: 'brick_gem#crosstab', as: 'brick_crosstab')
|
700
|
+
get("/#{controller_prefix}brick_crosstab/data", to: 'brick_gem#crosstab_data')
|
701
|
+
end
|
702
|
+
|
693
703
|
unless ::Brick.routes_done
|
694
704
|
if Object.const_defined?('Rswag::Ui')
|
695
705
|
rswag_path = ::Rails.application.routes.routes.find { |r| r.app.app == Rswag::Ui::Engine }&.instance_variable_get(:@path_formatter)&.instance_variable_get(:@parts)&.join
|
@@ -1247,6 +1257,67 @@ module ActiveRecord
|
|
1247
1257
|
# For AR >= 4.2
|
1248
1258
|
if self.const_defined?('JoinDependency')
|
1249
1259
|
class JoinDependency
|
1260
|
+
# An intelligent .eager_load() and .includes() that creates t0_r0 style aliases only for the columns
|
1261
|
+
# used in .select(). To enable this behaviour, include the flag :_brick_eager_load as the first
|
1262
|
+
# entry in your .select().
|
1263
|
+
# More information: https://discuss.rubyonrails.org/t/includes-and-select-for-joined-data/81640
|
1264
|
+
def apply_column_aliases(relation)
|
1265
|
+
used_cols = {}
|
1266
|
+
if (sel_vals = relation.select_values.map(&:to_s)).first == '_brick_eager_load'
|
1267
|
+
# Find and expand out all column names being used in select(...)
|
1268
|
+
new_select_values = sel_vals.each_with_object([]) do |col, s|
|
1269
|
+
next if col == '_brick_eager_load'
|
1270
|
+
|
1271
|
+
if col.include?(' ') # Some expression? (No chance for a simple column reference)
|
1272
|
+
s << col # Just pass it through
|
1273
|
+
else
|
1274
|
+
col = if (col_parts = col.split('.')).length == 1
|
1275
|
+
[col]
|
1276
|
+
else
|
1277
|
+
[col_parts[0..-2].join('.'), col_parts.last]
|
1278
|
+
end
|
1279
|
+
used_cols[col] = nil
|
1280
|
+
end
|
1281
|
+
end
|
1282
|
+
if new_select_values.present?
|
1283
|
+
relation.select_values = new_select_values
|
1284
|
+
else
|
1285
|
+
relation.select_values.clear
|
1286
|
+
end
|
1287
|
+
end
|
1288
|
+
|
1289
|
+
@aliases ||= Aliases.new(join_root.each_with_index.map do |join_part, i|
|
1290
|
+
join_alias = join_part.table&.table_alias || join_part.table_name
|
1291
|
+
keys = [join_part.base_klass.primary_key] # Always include the primary key
|
1292
|
+
|
1293
|
+
# # %%% Optional to include all foreign keys:
|
1294
|
+
# keys.concat(join_part.base_klass.reflect_on_all_associations.select { |a| a.belongs_to? }.map(&:foreign_key))
|
1295
|
+
|
1296
|
+
# Add foreign keys out to referenced tables that we belongs_to
|
1297
|
+
join_part.children.each { |child| keys << child.reflection.foreign_key if child.reflection.belongs_to? }
|
1298
|
+
|
1299
|
+
# Add the foreign key that got us here -- "the train we rode in on" -- if we arrived from
|
1300
|
+
# a has_many or has_one:
|
1301
|
+
if join_part.is_a?(ActiveRecord::Associations::JoinDependency::JoinAssociation) &&
|
1302
|
+
!join_part.reflection.belongs_to?
|
1303
|
+
keys << join_part.reflection.foreign_key
|
1304
|
+
end
|
1305
|
+
keys = keys.compact # In case we're using composite_primary_keys
|
1306
|
+
j = 0
|
1307
|
+
columns = join_part.column_names.each_with_object([]) do |column_name, s|
|
1308
|
+
# Include columns chosen in select(...) as well as the PK and any relevant FKs
|
1309
|
+
if used_cols.keys.find { |c| (c.length == 1 || c.first == join_alias) && c.last == column_name } ||
|
1310
|
+
keys.find { |c| c == column_name }
|
1311
|
+
s << Aliases::Column.new(column_name, "t#{i}_r#{j}")
|
1312
|
+
end
|
1313
|
+
j += 1
|
1314
|
+
end
|
1315
|
+
Aliases::Table.new(join_part, columns)
|
1316
|
+
end)
|
1317
|
+
|
1318
|
+
relation._select!(-> { @aliases.columns })
|
1319
|
+
end
|
1320
|
+
|
1250
1321
|
private
|
1251
1322
|
|
1252
1323
|
# %%% Pretty much have to flat-out replace this guy (I think anyway)
|
@@ -1282,7 +1353,6 @@ module ActiveRecord
|
|
1282
1353
|
if (relation = node.instance_variable_get(:@assocs)&.instance_variable_get(:@relation))
|
1283
1354
|
relation.brick_links[link_path] = result.first.table_alias || result.first.table_name
|
1284
1355
|
end
|
1285
|
-
|
1286
1356
|
result
|
1287
1357
|
end
|
1288
1358
|
else # Same idea but for Rails 7
|
@@ -1292,9 +1362,9 @@ module ActiveRecord
|
|
1292
1362
|
|
1293
1363
|
# Capture the table alias name that was chosen
|
1294
1364
|
link_path = child.instance_variable_get(:@link_path)
|
1295
|
-
relation = child.instance_variable_get(:@assocs)&.instance_variable_get(:@relation)
|
1296
|
-
|
1297
|
-
|
1365
|
+
if (relation = child.instance_variable_get(:@assocs)&.instance_variable_get(:@relation))
|
1366
|
+
relation.brick_links[link_path] = result.first.left.table_alias || result.first.left.table_name
|
1367
|
+
end
|
1298
1368
|
result
|
1299
1369
|
end
|
1300
1370
|
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.95
|
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-11-
|
11
|
+
date: 2022-11-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -214,6 +214,7 @@ files:
|
|
214
214
|
- lib/brick/frameworks/cucumber.rb
|
215
215
|
- lib/brick/frameworks/rails.rb
|
216
216
|
- lib/brick/frameworks/rails/controller.rb
|
217
|
+
- lib/brick/frameworks/rails/crosstab.brk
|
217
218
|
- lib/brick/frameworks/rails/engine.rb
|
218
219
|
- lib/brick/frameworks/rspec.rb
|
219
220
|
- lib/brick/join_array.rb
|