brick 1.0.93 → 1.0.95
Sign up to get free protection for your applications and to get access to all the features.
- 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
|