brick 1.0.94 → 1.0.96
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 +79 -0
- data/lib/brick/frameworks/rails/crosstab.brk +0 -0
- data/lib/brick/frameworks/rails/engine.rb +17 -2
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +75 -4
- 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: 0061ad25b22deee43d1939b6541257924a61a67b3b8b004fb6a0072302746ca2
|
4
|
+
data.tar.gz: 99968b9de834f73c60842bf5da03b5bddd750f1ff8c3ac1dac294078f0eb2358
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4958e2b0c18522be75134ab29df481213594189b4addd6ef9368ae8677aba603cbb278176c57c51217a49c0042e98927cb33465995bc4aae01f3af59b2d64f15
|
7
|
+
data.tar.gz: 1d486886f30ce384edc7d84a441617906b26b001c7c3562558289eedfd4b0c12e2a860c587f71acb6e6727649a3756223f1240c07f4e1013029cfe5b67a9c117
|
data/lib/brick/config.rb
CHANGED
data/lib/brick/extensions.rb
CHANGED
@@ -1416,6 +1416,85 @@ class Object
|
|
1416
1416
|
self.define_method :orphans do
|
1417
1417
|
instance_variable_set(:@orphans, ::Brick.find_orphans(::Brick.set_db_schema(params).first))
|
1418
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
|
+
|
1419
1498
|
return [new_controller_class, code + "end # BrickGem controller\n"]
|
1420
1499
|
when 'BrickOpenapi'
|
1421
1500
|
is_openapi = true
|
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))
|
@@ -212,6 +214,7 @@ module Brick
|
|
212
214
|
end.html_safe
|
213
215
|
table_options << "<option value=\"#{prefix}brick_status\">(Status)</option>".html_safe if ::Brick.config.add_status
|
214
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
|
215
218
|
css = +"<style>
|
216
219
|
h1, h3 {
|
217
220
|
margin-bottom: 0;
|
@@ -1093,6 +1096,16 @@ erDiagram
|
|
1093
1096
|
#{script}"
|
1094
1097
|
end
|
1095
1098
|
|
1099
|
+
when 'crosstab'
|
1100
|
+
if is_crosstab && ::Brick.config.license
|
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).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
|
+
|
1096
1109
|
when 'show', 'new', 'update'
|
1097
1110
|
+"<html>
|
1098
1111
|
<head>
|
@@ -1282,7 +1295,8 @@ end}
|
|
1282
1295
|
"
|
1283
1296
|
|
1284
1297
|
end
|
1285
|
-
|
1298
|
+
unless is_crosstab
|
1299
|
+
inline << "
|
1286
1300
|
<% if is_includes_dates %>
|
1287
1301
|
<link rel=\"stylesheet\" type=\"text/css\" href=\"https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css\">
|
1288
1302
|
<style>
|
@@ -1416,6 +1430,7 @@ document.querySelectorAll(\"input, select\").forEach(function (inp) {
|
|
1416
1430
|
}
|
1417
1431
|
});
|
1418
1432
|
</script>"
|
1433
|
+
end
|
1419
1434
|
# puts "==============="
|
1420
1435
|
# puts inline
|
1421
1436
|
# puts "==============="
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -466,6 +466,10 @@ module Brick
|
|
466
466
|
Brick.config.default_route_fallback = resource_name
|
467
467
|
end
|
468
468
|
|
469
|
+
def license=(key)
|
470
|
+
Brick.config.license = key
|
471
|
+
end
|
472
|
+
|
469
473
|
# Load additional references (virtual foreign keys)
|
470
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
|
471
475
|
# %%% Maybe look for differences the second time 'round and just add new stuff instead of entirely deferring
|
@@ -691,6 +695,11 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
691
695
|
get("/#{controller_prefix}brick_orphans", to: 'brick_gem#orphans', as: 'brick_orphans')
|
692
696
|
end
|
693
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
|
+
|
694
703
|
unless ::Brick.routes_done
|
695
704
|
if Object.const_defined?('Rswag::Ui')
|
696
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
|
@@ -1248,6 +1257,69 @@ module ActiveRecord
|
|
1248
1257
|
# For AR >= 4.2
|
1249
1258
|
if self.const_defined?('JoinDependency')
|
1250
1259
|
class JoinDependency
|
1260
|
+
if ActiveRecord.version >= ::Gem::Version.new('6.0')
|
1261
|
+
# An intelligent .eager_load() and .includes() that creates t0_r0 style aliases only for the columns
|
1262
|
+
# used in .select(). To enable this behaviour, include the flag :_brick_eager_load as the first
|
1263
|
+
# entry in your .select().
|
1264
|
+
# More information: https://discuss.rubyonrails.org/t/includes-and-select-for-joined-data/81640
|
1265
|
+
def apply_column_aliases(relation)
|
1266
|
+
used_cols = {}
|
1267
|
+
if (sel_vals = relation.select_values.map(&:to_s)).first == '_brick_eager_load'
|
1268
|
+
# Find and expand out all column names being used in select(...)
|
1269
|
+
new_select_values = sel_vals.each_with_object([]) do |col, s|
|
1270
|
+
next if col == '_brick_eager_load'
|
1271
|
+
|
1272
|
+
if col.include?(' ') # Some expression? (No chance for a simple column reference)
|
1273
|
+
s << col # Just pass it through
|
1274
|
+
else
|
1275
|
+
col = if (col_parts = col.split('.')).length == 1
|
1276
|
+
[col]
|
1277
|
+
else
|
1278
|
+
[col_parts[0..-2].join('.'), col_parts.last]
|
1279
|
+
end
|
1280
|
+
used_cols[col] = nil
|
1281
|
+
end
|
1282
|
+
end
|
1283
|
+
if new_select_values.present?
|
1284
|
+
relation.select_values = new_select_values
|
1285
|
+
else
|
1286
|
+
relation.select_values.clear
|
1287
|
+
end
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
@aliases ||= Aliases.new(join_root.each_with_index.map do |join_part, i|
|
1291
|
+
join_alias = join_part.table&.table_alias || join_part.table_name
|
1292
|
+
keys = [join_part.base_klass.primary_key] # Always include the primary key
|
1293
|
+
|
1294
|
+
# # %%% Optional to include all foreign keys:
|
1295
|
+
# keys.concat(join_part.base_klass.reflect_on_all_associations.select { |a| a.belongs_to? }.map(&:foreign_key))
|
1296
|
+
|
1297
|
+
# Add foreign keys out to referenced tables that we belongs_to
|
1298
|
+
join_part.children.each { |child| keys << child.reflection.foreign_key if child.reflection.belongs_to? }
|
1299
|
+
|
1300
|
+
# Add the foreign key that got us here -- "the train we rode in on" -- if we arrived from
|
1301
|
+
# a has_many or has_one:
|
1302
|
+
if join_part.is_a?(ActiveRecord::Associations::JoinDependency::JoinAssociation) &&
|
1303
|
+
!join_part.reflection.belongs_to?
|
1304
|
+
keys << join_part.reflection.foreign_key
|
1305
|
+
end
|
1306
|
+
keys = keys.compact # In case we're using composite_primary_keys
|
1307
|
+
j = 0
|
1308
|
+
columns = join_part.column_names.each_with_object([]) do |column_name, s|
|
1309
|
+
# Include columns chosen in select(...) as well as the PK and any relevant FKs
|
1310
|
+
if used_cols.keys.find { |c| (c.length == 1 || c.first == join_alias) && c.last == column_name } ||
|
1311
|
+
keys.find { |c| c == column_name }
|
1312
|
+
s << Aliases::Column.new(column_name, "t#{i}_r#{j}")
|
1313
|
+
end
|
1314
|
+
j += 1
|
1315
|
+
end
|
1316
|
+
Aliases::Table.new(join_part, columns)
|
1317
|
+
end)
|
1318
|
+
|
1319
|
+
relation._select!(-> { @aliases.columns })
|
1320
|
+
end
|
1321
|
+
end
|
1322
|
+
|
1251
1323
|
private
|
1252
1324
|
|
1253
1325
|
# %%% Pretty much have to flat-out replace this guy (I think anyway)
|
@@ -1283,7 +1355,6 @@ module ActiveRecord
|
|
1283
1355
|
if (relation = node.instance_variable_get(:@assocs)&.instance_variable_get(:@relation))
|
1284
1356
|
relation.brick_links[link_path] = result.first.table_alias || result.first.table_name
|
1285
1357
|
end
|
1286
|
-
|
1287
1358
|
result
|
1288
1359
|
end
|
1289
1360
|
else # Same idea but for Rails 7
|
@@ -1293,9 +1364,9 @@ module ActiveRecord
|
|
1293
1364
|
|
1294
1365
|
# Capture the table alias name that was chosen
|
1295
1366
|
link_path = child.instance_variable_get(:@link_path)
|
1296
|
-
relation = child.instance_variable_get(:@assocs)&.instance_variable_get(:@relation)
|
1297
|
-
|
1298
|
-
|
1367
|
+
if (relation = child.instance_variable_get(:@assocs)&.instance_variable_get(:@relation))
|
1368
|
+
relation.brick_links[link_path] = result.first.left.table_alias || result.first.left.table_name
|
1369
|
+
end
|
1299
1370
|
result
|
1300
1371
|
end
|
1301
1372
|
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.96
|
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-25 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
|