brick 1.0.75 → 1.0.76
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 +9 -0
- data/lib/brick/extensions.rb +173 -87
- data/lib/brick/frameworks/rails/engine.rb +46 -4
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +9 -0
- data/lib/generators/brick/install_generator.rb +6 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 615640b22db113a3959644ee9c2d08ff3a2b37aa10f12dc92d68effef091c228
|
4
|
+
data.tar.gz: b0d3e616b0f44584cf6960f8b8b191fc449b9afc426a6486da11c2deadece7a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02a9f1c74af24e1e23df8b64972d7af8d74bbb870f1510b69d0814f362959e2527f2a7ee9ff7ca65187eb97be4fad1136d64bea7e01a8327a57608924a8f12d8
|
7
|
+
data.tar.gz: '0265977b746d0955c76bef269067d14363cda4373fc5f726f19fb284baf31d0df3bdaae6ffad7301b71cd630a2745db4d30f1da79ec43ef4ae1a92bc986058e9'
|
data/lib/brick/config.rb
CHANGED
@@ -90,6 +90,15 @@ module Brick
|
|
90
90
|
@mutex.synchronize { @additional_references = references }
|
91
91
|
end
|
92
92
|
|
93
|
+
# Custom columns to add to a table, minimally defined with a name and DSL string
|
94
|
+
def custom_columns
|
95
|
+
@mutex.synchronize { @custom_columns }
|
96
|
+
end
|
97
|
+
|
98
|
+
def custom_columns=(cust_cols)
|
99
|
+
@mutex.synchronize { @custom_columns = cust_cols }
|
100
|
+
end
|
101
|
+
|
93
102
|
# Skip creating a has_many association for these
|
94
103
|
def exclude_hms
|
95
104
|
@mutex.synchronize { @exclude_hms }
|
data/lib/brick/extensions.rb
CHANGED
@@ -100,73 +100,86 @@ module ActiveRecord
|
|
100
100
|
dsl
|
101
101
|
end
|
102
102
|
|
103
|
-
def self.brick_parse_dsl(build_array = nil, prefix = [], translations = {},
|
104
|
-
|
105
|
-
|
103
|
+
def self.brick_parse_dsl(build_array = nil, prefix = [], translations = {}, is_polymorphic = false, dsl = nil, emit_dsl = false)
|
104
|
+
unless build_array.is_a?(::Brick::JoinArray)
|
105
|
+
build_array = ::Brick::JoinArray.new.tap { |ary| ary.replace([build_array]) } if build_array.is_a?(::Brick::JoinHash)
|
106
|
+
build_array = ::Brick::JoinArray.new unless build_array.nil? || build_array.is_a?(Array)
|
107
|
+
end
|
108
|
+
prefix = [prefix] unless prefix.is_a?(Array)
|
106
109
|
members = []
|
110
|
+
unless dsl || (dsl = ::Brick.config.model_descrips[name] || brick_get_dsl)
|
111
|
+
# With no DSL available, still put this prefix into the JoinArray so we can get primary key (ID) info from this table
|
112
|
+
x = prefix.each_with_object(build_array) { |v, s| s[v.to_sym] }
|
113
|
+
x[prefix.last] = nil unless prefix.empty? # Using []= will "hydrate" any missing part(s) in our whole series
|
114
|
+
return members
|
115
|
+
end
|
116
|
+
|
117
|
+
# Do the actual dirty work of recursing through nested DSL
|
107
118
|
bracket_name = nil
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
parts[0..-3].each { |v| s = s[v.to_sym] }
|
127
|
-
s[parts[-2]] = nil # unless parts[-2].empty? # Using []= will "hydrate" any missing part(s) in our whole series
|
128
|
-
end
|
129
|
-
translations[parts[0..-2].join('.')] = klass
|
119
|
+
dsl2 = +'' # To replace our own DSL definition in case it needs to be expanded
|
120
|
+
dsl3 = +'' # To return expanded DSL that is nested from another model
|
121
|
+
klass = nil
|
122
|
+
dsl.each_char do |ch|
|
123
|
+
if bracket_name
|
124
|
+
if ch == ']' # Time to process a bracketed thing?
|
125
|
+
parts = bracket_name.split('.')
|
126
|
+
first_parts = parts[0..-2].map do |part|
|
127
|
+
klass = (orig_class = klass).reflect_on_association(part_sym = part.to_sym)&.klass
|
128
|
+
puts "Couldn't reference #{orig_class.name}##{part} that's part of the DSL \"#{dsl}\"." if klass.nil?
|
129
|
+
part_sym
|
130
|
+
end
|
131
|
+
parts = prefix + first_parts + [parts[-1]]
|
132
|
+
if parts.length > 1
|
133
|
+
unless is_polymorphic
|
134
|
+
s = build_array
|
135
|
+
parts[0..-3].each { |v| s = s[v.to_sym] }
|
136
|
+
s[parts[-2]] = nil # unless parts[-2].empty? # Using []= will "hydrate" any missing part(s) in our whole series
|
130
137
|
end
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
dsl3 << dsl2a
|
138
|
-
else
|
139
|
-
dsl2 << "[#{bracket_name}]"
|
140
|
-
if emit_dsl
|
141
|
-
dsl3 << "[#{prefix[1..-1].map { |p| "#{p.to_s}." }.join if prefix.length > 1}#{bracket_name}]"
|
142
|
-
end
|
143
|
-
members << parts
|
138
|
+
translations[parts[0..-2].join('.')] = klass
|
139
|
+
end
|
140
|
+
if klass.column_names.exclude?(parts.last) &&
|
141
|
+
(klass = (orig_class = klass).reflect_on_association(possible_dsl = parts.pop.to_sym)&.klass)
|
142
|
+
if prefix.empty? # Custom columns start with an empty prefix
|
143
|
+
prefix << parts.shift until parts.empty?
|
144
144
|
end
|
145
|
-
|
145
|
+
# Expand this entry which refers to an association name
|
146
|
+
members2, dsl2a = klass.brick_parse_dsl(build_array, prefix + [possible_dsl], translations, is_polymorphic, nil, true)
|
147
|
+
members += members2
|
148
|
+
dsl2 << dsl2a
|
149
|
+
dsl3 << dsl2a
|
146
150
|
else
|
147
|
-
|
151
|
+
dsl2 << "[#{bracket_name}]"
|
152
|
+
if emit_dsl
|
153
|
+
dsl3 << "[#{prefix[1..-1].map { |p| "#{p.to_s}." }.join if prefix.length > 1}#{bracket_name}]"
|
154
|
+
end
|
155
|
+
members << parts
|
148
156
|
end
|
149
|
-
|
150
|
-
bracket_name = +''
|
151
|
-
klass = self
|
157
|
+
bracket_name = nil
|
152
158
|
else
|
153
|
-
|
154
|
-
dsl3 << ch
|
159
|
+
bracket_name << ch
|
155
160
|
end
|
161
|
+
elsif ch == '['
|
162
|
+
bracket_name = +''
|
163
|
+
klass = self
|
164
|
+
else
|
165
|
+
dsl2 << ch
|
166
|
+
dsl3 << ch
|
156
167
|
end
|
157
|
-
# Rewrite the DSL in case it's now different from having to expand it
|
158
|
-
# if ::Brick.config.model_descrips[name] != dsl2
|
159
|
-
# puts ::Brick.config.model_descrips[name]
|
160
|
-
# puts dsl2.inspect
|
161
|
-
# puts dsl3.inspect
|
162
|
-
# binding.pry
|
163
|
-
# end
|
164
|
-
::Brick.config.model_descrips[name] = dsl2 unless emit_dsl
|
165
|
-
else # With no DSL available, still put this prefix into the JoinArray so we can get primary key (ID) info from this table
|
166
|
-
x = prefix.each_with_object(build_array) { |v, s| s[v.to_sym] }
|
167
|
-
x[prefix.last] = nil unless prefix.empty? # Using []= will "hydrate" any missing part(s) in our whole series
|
168
168
|
end
|
169
|
-
|
169
|
+
# Rewrite the DSL in case it's now different from having to expand it
|
170
|
+
# if ::Brick.config.model_descrips[name] != dsl2
|
171
|
+
# puts ::Brick.config.model_descrips[name]
|
172
|
+
# puts dsl2.inspect
|
173
|
+
# puts dsl3.inspect
|
174
|
+
# binding.pry
|
175
|
+
# end
|
176
|
+
if emit_dsl
|
177
|
+
# Had been: [members, dsl2, dsl3]
|
178
|
+
[members, dsl3]
|
179
|
+
else
|
180
|
+
::Brick.config.model_descrips[name] = dsl2
|
181
|
+
members
|
182
|
+
end
|
170
183
|
end
|
171
184
|
|
172
185
|
# If available, parse simple DSL attached to a model in order to provide a friendlier name.
|
@@ -177,7 +190,8 @@ module ActiveRecord
|
|
177
190
|
end
|
178
191
|
|
179
192
|
def self.brick_descrip(obj, data = nil, pk_alias = nil)
|
180
|
-
|
193
|
+
dsl = obj if obj.is_a?(String)
|
194
|
+
if (dsl ||= ::Brick.config.model_descrips[(klass = self).name] || klass.brick_get_dsl)
|
181
195
|
idx = -1
|
182
196
|
caches = {}
|
183
197
|
output = +''
|
@@ -272,18 +286,28 @@ module ActiveRecord
|
|
272
286
|
def _br_associatives
|
273
287
|
@_br_associatives ||= {}
|
274
288
|
end
|
289
|
+
# Custom columns
|
290
|
+
def _br_cust_cols
|
291
|
+
@_br_cust_cols ||= {}
|
292
|
+
end
|
275
293
|
end
|
276
294
|
|
277
|
-
# Search for BT, HM, and HMT DSL stuff
|
295
|
+
# Search for custom column, BT, HM, and HMT DSL stuff
|
278
296
|
def self._brick_calculate_bts_hms(translations, join_array)
|
297
|
+
# Add any custom columns
|
298
|
+
::Brick.config.custom_columns&.fetch(table_name, nil)&.each do |k, cc|
|
299
|
+
# false = not polymorphic, and true = yes -- please emit_dsl
|
300
|
+
pieces, my_dsl = brick_parse_dsl(join_array, [], translations, false, cc, true)
|
301
|
+
_br_cust_cols[k] = [pieces, my_dsl]
|
302
|
+
end
|
279
303
|
bts, hms, associatives = ::Brick.get_bts_and_hms(self)
|
280
304
|
bts.each do |_k, bt|
|
281
305
|
next if bt[2] # Polymorphic?
|
282
306
|
|
283
307
|
# join_array will receive this relation name when calling #brick_parse_dsl
|
284
308
|
_br_bt_descrip[bt.first] = if bt[1].is_a?(Array)
|
285
|
-
# Last
|
286
|
-
bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations,
|
309
|
+
# Last params here: "true" is for yes, we are polymorphic
|
310
|
+
bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations, true) }
|
287
311
|
else
|
288
312
|
{ bt.last => bt[1].brick_parse_dsl(join_array, bt.first, translations) }
|
289
313
|
end
|
@@ -314,7 +338,7 @@ module ActiveRecord
|
|
314
338
|
else # Expecting only Symbol
|
315
339
|
if _br_hm_counts.key?(ord_part)
|
316
340
|
ord_part = "\"b_r_#{ord_part}_ct\""
|
317
|
-
elsif !_br_bt_descrip.key?(ord_part) && !column_names.include?(ord_part.to_s)
|
341
|
+
elsif !_br_bt_descrip.key?(ord_part) && !_br_cust_cols.key?(ord_part) && !column_names.include?(ord_part.to_s)
|
318
342
|
# Disallow ordering by a bogus column
|
319
343
|
# %%% Note this bogus entry so that Javascript can remove any bogus _brick_order
|
320
344
|
# parameter from the querystring, pushing it into the browser history.
|
@@ -331,6 +355,11 @@ module ActiveRecord
|
|
331
355
|
[order_by, order_by_txt]
|
332
356
|
end
|
333
357
|
|
358
|
+
def self.brick_select(params = {}, selects = [], *args)
|
359
|
+
(relation = all).brick_select(params, selects, *args)
|
360
|
+
relation.select(selects)
|
361
|
+
end
|
362
|
+
|
334
363
|
private
|
335
364
|
|
336
365
|
def self._brick_get_fks
|
@@ -421,7 +450,13 @@ module ActiveRecord
|
|
421
450
|
end
|
422
451
|
end
|
423
452
|
|
424
|
-
def brick_select(params, selects =
|
453
|
+
def brick_select(params, selects = [], order_by = nil, translations = {}, join_array = ::Brick::JoinArray.new)
|
454
|
+
is_add_bts = is_add_hms = true
|
455
|
+
|
456
|
+
# Build out cust_cols, bt_descrip and hm_counts now so that they are available on the
|
457
|
+
# model early in case the user wants to do an ORDER BY based on any of that.
|
458
|
+
model._brick_calculate_bts_hms(translations, join_array) if is_add_bts || is_add_hms
|
459
|
+
|
425
460
|
is_postgres = ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
426
461
|
is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2'
|
427
462
|
is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer'
|
@@ -446,7 +481,7 @@ module ActiveRecord
|
|
446
481
|
end
|
447
482
|
|
448
483
|
# %%% Skip the metadata columns
|
449
|
-
if selects
|
484
|
+
if selects.empty? # Default to all columns
|
450
485
|
tbl_no_schema = table.name.split('.').last
|
451
486
|
# %%% Have once gotten this error with MSSQL referring to http://localhost:3000/warehouse/cold_room_temperatures__archive
|
452
487
|
# ActiveRecord::StatementInvalid (TinyTds::Error: DBPROCESS is dead or not enabled)
|
@@ -481,7 +516,36 @@ module ActiveRecord
|
|
481
516
|
id_for_tables = Hash.new { |h, k| h[k] = [] }
|
482
517
|
field_tbl_names = Hash.new { |h, k| h[k] = {} }
|
483
518
|
used_col_aliases = {} # Used to make sure there is not a name clash
|
484
|
-
|
519
|
+
|
520
|
+
# CUSTOM COLUMNS
|
521
|
+
# ==============
|
522
|
+
klass._br_cust_cols.each do |k, cc|
|
523
|
+
if respond_to?(k) # Name already taken?
|
524
|
+
# %%% Use ensure_unique here in this kind of fashion:
|
525
|
+
# cnstr_name = ensure_unique(+"(brick) #{for_tbl}_#{pri_tbl}", bts, hms)
|
526
|
+
# binding.pry
|
527
|
+
next
|
528
|
+
end
|
529
|
+
|
530
|
+
cc.first.each do |cc_part|
|
531
|
+
dest_klass = cc_part[0..-2].inject(klass) { |kl, cc_part_term| kl.reflect_on_association(cc_part_term).klass }
|
532
|
+
tbl_name = (field_tbl_names[k][cc_part.last] ||= shift_or_first(chains[dest_klass])).split('.').last
|
533
|
+
# Deal with the conflict if there are two parts in the custom column named the same,
|
534
|
+
# "category.name" and "product.name" for instance will end up with aliases of "name"
|
535
|
+
# and "product__name".
|
536
|
+
cc_part_idx = cc_part.length - 1
|
537
|
+
while cc_part_idx > 0 &&
|
538
|
+
(col_alias = "br_cc_#{k}__#{cc_part[cc_part_idx..-1].map(&:to_s).join('__')}") &&
|
539
|
+
used_col_aliases.key?(col_alias)
|
540
|
+
cc_part_idx -= 1
|
541
|
+
end
|
542
|
+
selects << "#{tbl_name}.#{cc_part.last} AS #{col_alias}"
|
543
|
+
cc_part << col_alias
|
544
|
+
used_col_aliases[col_alias] = nil
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
klass._br_bt_descrip.each do |v|
|
485
549
|
v.last.each do |k1, v1| # k1 is class, v1 is array of columns to snag
|
486
550
|
next if chains[k1].nil?
|
487
551
|
|
@@ -544,7 +608,13 @@ module ActiveRecord
|
|
544
608
|
klass._br_hm_counts.each do |k, hm|
|
545
609
|
associative = nil
|
546
610
|
count_column = if hm.options[:through]
|
547
|
-
|
611
|
+
if (fk_col = (associative = klass._br_associatives&.[](hm.name))&.foreign_key)
|
612
|
+
if hm.source_reflection.macro == :belongs_to # Traditional HMT using an associative table
|
613
|
+
hm.foreign_key
|
614
|
+
else # A HMT that goes HM -> HM, something like Categories -> Products -> LineItems
|
615
|
+
hm.source_reflection.active_record.primary_key
|
616
|
+
end
|
617
|
+
end
|
548
618
|
else
|
549
619
|
fk_col = hm.foreign_key
|
550
620
|
poly_type = hm.inverse_of.foreign_type if hm.options.key?(:as)
|
@@ -596,7 +666,8 @@ JOIN (SELECT #{selects.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}
|
|
596
666
|
end
|
597
667
|
where!(wheres) unless wheres.empty?
|
598
668
|
# Must parse the order_by and see if there are any symbols which refer to BT associations
|
599
|
-
# as they must be expanded to find the corresponding b_r_model__column
|
669
|
+
# or custom columns as they must be expanded to find the corresponding b_r_model__column
|
670
|
+
# or br_cc_column naming for each.
|
600
671
|
if order_by.present?
|
601
672
|
final_order_by = *order_by.each_with_object([]) do |v, s|
|
602
673
|
if v.is_a?(Symbol)
|
@@ -605,6 +676,8 @@ JOIN (SELECT #{selects.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}
|
|
605
676
|
bt_cols.values.each do |v1|
|
606
677
|
v1.each { |v2| s << "\"#{v2.last}\"" if v2.length > 1 }
|
607
678
|
end
|
679
|
+
elsif (cc_cols = klass._br_cust_cols[v])
|
680
|
+
cc_cols.first.each { |v1| s << "\"#{v1.last}\"" if v1.length > 1 }
|
608
681
|
else
|
609
682
|
s << v
|
610
683
|
end
|
@@ -1204,22 +1277,6 @@ class Object
|
|
1204
1277
|
return
|
1205
1278
|
end
|
1206
1279
|
|
1207
|
-
# Normal (non-swagger) request
|
1208
|
-
|
1209
|
-
# We do all of this now so that bt_descrip and hm_counts are available on the model early in case the user
|
1210
|
-
# wants to do an ORDER BY based on any of that
|
1211
|
-
translations = {}
|
1212
|
-
join_array = ::Brick::JoinArray.new
|
1213
|
-
is_add_bts = is_add_hms = true
|
1214
|
-
# This builds out bt_descrip and hm_counts on the model
|
1215
|
-
model._brick_calculate_bts_hms(translations, join_array) if is_add_bts || is_add_hms
|
1216
|
-
|
1217
|
-
# %%% Allow params to define which columns to use for order_by
|
1218
|
-
# Overriding the default by providing a querystring param?
|
1219
|
-
ordering = params['_brick_order']&.split(',')&.map(&:to_sym) || Object.send(:default_ordering, table_name, pk)
|
1220
|
-
order_by, _ = model._brick_calculate_ordering(ordering, true) # Don't do the txt part
|
1221
|
-
|
1222
|
-
::Brick.set_db_schema(params)
|
1223
1280
|
if request.format == :csv # Asking for a template?
|
1224
1281
|
require 'csv'
|
1225
1282
|
exported_csv = CSV.generate(force_quotes: false) do |csv_out|
|
@@ -1233,7 +1290,16 @@ class Object
|
|
1233
1290
|
return
|
1234
1291
|
end
|
1235
1292
|
|
1236
|
-
|
1293
|
+
# Normal (not swagger or CSV) request
|
1294
|
+
|
1295
|
+
# %%% Allow params to define which columns to use for order_by
|
1296
|
+
# Overriding the default by providing a querystring param?
|
1297
|
+
ordering = params['_brick_order']&.split(',')&.map(&:to_sym) || Object.send(:default_ordering, table_name, pk)
|
1298
|
+
order_by, _ = model._brick_calculate_ordering(ordering, true) # Don't do the txt part
|
1299
|
+
|
1300
|
+
@_brick_params = (ar_relation = model.all).brick_select(params, (selects = []), nil,
|
1301
|
+
translations = {},
|
1302
|
+
join_array = ::Brick::JoinArray.new)
|
1237
1303
|
# %%% Add custom HM count columns
|
1238
1304
|
# %%% What happens when the PK is composite?
|
1239
1305
|
counts = model._br_hm_counts.each_with_object([]) do |v, s|
|
@@ -1902,9 +1968,7 @@ module Brick
|
|
1902
1968
|
# For any appended references (those that come from config), arrive upon a definitely unique constraint name
|
1903
1969
|
pri_tbl = is_class ? fk[4][:class].underscore : pri_tbl
|
1904
1970
|
pri_tbl = "#{bt_assoc_name}_#{pri_tbl}" if pri_tbl&.singularize != bt_assoc_name
|
1905
|
-
|
1906
|
-
cnstr_added_num = 1
|
1907
|
-
cnstr_name = "#{cnstr_base}_#{cnstr_added_num += 1}" while bts&.key?(cnstr_name) || hms&.key?(cnstr_name)
|
1971
|
+
cnstr_name = ensure_unique(+"(brick) #{for_tbl}_#{pri_tbl}", bts, hms)
|
1908
1972
|
missing = []
|
1909
1973
|
missing << fk[1] unless relations.key?(fk[1])
|
1910
1974
|
missing << primary_table unless is_class || relations.key?(primary_table)
|
@@ -2029,6 +2093,28 @@ module Brick
|
|
2029
2093
|
::Brick.relations.keys.map { |v| [(r = v.pluralize), (model = models[r])&.last&.table_name || v, migrations&.fetch(r, nil), model&.first] }
|
2030
2094
|
end
|
2031
2095
|
|
2096
|
+
def ensure_unique(name, *sources)
|
2097
|
+
base = name
|
2098
|
+
if (added_num = name.slice!(/_(\d+)$/))
|
2099
|
+
added_num = added_num[1..-1].to_i
|
2100
|
+
else
|
2101
|
+
added_num = 1
|
2102
|
+
end
|
2103
|
+
while (
|
2104
|
+
name = "#{base}_#{added_num += 1}"
|
2105
|
+
sources.each_with_object(nil) do |v, s|
|
2106
|
+
s || case v
|
2107
|
+
when Hash
|
2108
|
+
v.key?(name)
|
2109
|
+
when Array
|
2110
|
+
v.include?(name)
|
2111
|
+
end
|
2112
|
+
end
|
2113
|
+
)
|
2114
|
+
end
|
2115
|
+
name
|
2116
|
+
end
|
2117
|
+
|
2032
2118
|
# Locate orphaned records
|
2033
2119
|
def find_orphans(multi_schema)
|
2034
2120
|
is_default_schema = multi_schema&.==(Apartment.default_schema)
|
@@ -33,6 +33,9 @@ module Brick
|
|
33
33
|
# Additional references (virtual foreign keys)
|
34
34
|
::Brick.additional_references = app.config.brick.fetch(:additional_references, nil)
|
35
35
|
|
36
|
+
# Custom columns to add to a table, minimally defined with a name and DSL string
|
37
|
+
::Brick.custom_columns = app.config.brick.fetch(:custom_columns, nil)
|
38
|
+
|
36
39
|
# When table names have specific prefixes, automatically place them in their own module with a table_name_prefix.
|
37
40
|
::Brick.order = app.config.brick.fetch(:order, {})
|
38
41
|
|
@@ -249,8 +252,8 @@ tr th {
|
|
249
252
|
right: 0;
|
250
253
|
cursor: pointer;
|
251
254
|
}
|
252
|
-
#headerTop tr th:hover {
|
253
|
-
background-color: #
|
255
|
+
#headerTop tr th:hover, #headerTop tr th.highlight {
|
256
|
+
background-color: #28B898;
|
254
257
|
}
|
255
258
|
#exclusions {
|
256
259
|
font-size: 0.7em;
|
@@ -273,6 +276,10 @@ tr th, tr td {
|
|
273
276
|
padding: 0.2em 0.5em;
|
274
277
|
}
|
275
278
|
|
279
|
+
tr td.highlight {
|
280
|
+
background-color: #B0B0FF;
|
281
|
+
}
|
282
|
+
|
276
283
|
.show-field {
|
277
284
|
background-color: #004998;
|
278
285
|
}
|
@@ -500,6 +507,34 @@ function changeout(href, param, value, trimAfter) {
|
|
500
507
|
var grid = document.getElementById(\"#{table_name}\");
|
501
508
|
#{table_name}HtColumns = grid && [grid.getElementsByTagName(\"TR\")[0]];
|
502
509
|
var headerTop = document.getElementById(\"headerTop\");
|
510
|
+
var headerCols;
|
511
|
+
if (grid) {
|
512
|
+
// COLUMN HEADER AND TABLE CELL HIGHLIGHTING
|
513
|
+
var gridHighHeader = null,
|
514
|
+
gridHighCell = null;
|
515
|
+
grid.addEventListener(\"mouseenter\", gridMove);
|
516
|
+
grid.addEventListener(\"mousemove\", gridMove);
|
517
|
+
grid.addEventListener(\"mouseleave\", function (evt) {
|
518
|
+
if (gridHighCell) gridHighCell.classList.remove(\"highlight\");
|
519
|
+
gridHighCell = null;
|
520
|
+
if (gridHighHeader) gridHighHeader.classList.remove(\"highlight\");
|
521
|
+
gridHighHeader = null;
|
522
|
+
});
|
523
|
+
function gridMove(evt) {
|
524
|
+
var lastHighCell = gridHighCell;
|
525
|
+
gridHighCell = document.elementFromPoint(evt.x, evt.y);
|
526
|
+
if (lastHighCell !== gridHighCell) {
|
527
|
+
gridHighCell.classList.add(\"highlight\");
|
528
|
+
if (lastHighCell) lastHighCell.classList.remove(\"highlight\");
|
529
|
+
}
|
530
|
+
var lastHighHeader = gridHighHeader;
|
531
|
+
gridHighHeader = headerCols[gridHighCell.cellIndex];
|
532
|
+
if (lastHighHeader !== gridHighHeader) {
|
533
|
+
if (gridHighHeader) gridHighHeader.classList.add(\"highlight\");
|
534
|
+
if (lastHighHeader) lastHighHeader.classList.remove(\"highlight\");
|
535
|
+
}
|
536
|
+
}
|
537
|
+
}
|
503
538
|
function setHeaderSizes() {
|
504
539
|
// console.log(\"start\");
|
505
540
|
// See if the headerTop is already populated
|
@@ -531,6 +566,7 @@ function setHeaderSizes() {
|
|
531
566
|
}
|
532
567
|
}
|
533
568
|
}
|
569
|
+
headerCols = tr.childNodes;
|
534
570
|
if (isEmpty) headerTop.appendChild(tr);
|
535
571
|
}
|
536
572
|
grid.style.marginTop = \"-\" + getComputedStyle(headerTop).height;
|
@@ -779,7 +815,8 @@ erDiagram
|
|
779
815
|
cols[col_name] = col
|
780
816
|
end
|
781
817
|
unless @_brick_sequence # If no sequence is defined, start with all inclusions
|
782
|
-
|
818
|
+
cust_cols = #{model_name}._br_cust_cols
|
819
|
+
@_brick_sequence = col_keys + cust_cols.keys + #{(hms_keys).inspect}.reject { |assoc_name| @_brick_incl&.exclude?(assoc_name) }
|
783
820
|
end
|
784
821
|
@_brick_sequence.reject! { |nm| @_brick_excl.include?(nm) } if @_brick_excl # Reject exclusions
|
785
822
|
@_brick_sequence.each_with_object(+'') do |col_name, s|
|
@@ -796,6 +833,8 @@ erDiagram
|
|
796
833
|
elsif col # HM column
|
797
834
|
s << \"<th#\{' x-order=\"' + col_name + '\"' if true}>#\{col[2]} \"
|
798
835
|
s << (col.first ? \"#\{col[3]}\" : \"#\{link_to(col[3], send(\"#\{col[1]._brick_index}_path\"))}\")
|
836
|
+
elsif (cc = cust_cols.key?(col_name)) # Custom column
|
837
|
+
s << \"<th x-order=\\\"#\{col_name}\\\">#\{col_name}\"
|
799
838
|
else # Bad column name!
|
800
839
|
s << \"<th title=\\\"<< Unknown column >>\\\">#\{col_name}\"
|
801
840
|
end
|
@@ -813,7 +852,7 @@ erDiagram
|
|
813
852
|
<td><%= link_to '⇛', #{path_obj_name}_path(#{obj_pk}), { class: 'big-arrow' } %></td>" if obj_pk}
|
814
853
|
<% @_brick_sequence.each do |col_name|
|
815
854
|
val = #{obj_name}.attributes[col_name] %>
|
816
|
-
<td<%= ' class=\"dimmed\"'.html_safe unless cols.key?(col_name)%>><%
|
855
|
+
<td<%= ' class=\"dimmed\"'.html_safe unless cols.key?(col_name) || (cust_col = cust_cols[col_name])%>><%
|
817
856
|
if (bt = bts[col_name])
|
818
857
|
if bt[2] # Polymorphic?
|
819
858
|
bt_class = #{obj_name}.send(\"#\{bt.first\}_type\")
|
@@ -847,6 +886,9 @@ erDiagram
|
|
847
886
|
elsif (col = cols[col_name])
|
848
887
|
col_type = col&.sql_type == 'geography' ? col.sql_type : col&.type
|
849
888
|
%><%= display_value(col_type || col&.sql_type, val) %><%
|
889
|
+
elsif cust_col
|
890
|
+
data = cust_col.first.map { |cc_part| #{obj_name}.send(cc_part.last) }
|
891
|
+
%><%= #{model_name}.brick_descrip(cust_col.last, data) %><%
|
850
892
|
else # Bad column name!
|
851
893
|
%>?<%
|
852
894
|
end
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -329,6 +329,15 @@ module Brick
|
|
329
329
|
end
|
330
330
|
end
|
331
331
|
|
332
|
+
# Custom columns to add to a table, minimally defined with a name and DSL string.
|
333
|
+
# @api public
|
334
|
+
def custom_columns=(cust_cols)
|
335
|
+
if cust_cols
|
336
|
+
cust_cols = cust_cols.call if cust_cols.is_a?(Proc)
|
337
|
+
Brick.config.custom_columns = cust_cols
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
332
341
|
# @api public
|
333
342
|
def order=(value)
|
334
343
|
Brick.config.order = value
|
@@ -207,6 +207,12 @@ module Brick
|
|
207
207
|
# # to be the primary key.)
|
208
208
|
#{bar}
|
209
209
|
|
210
|
+
# # Custom columns to add to a table, minimally defined with a name and DSL string.
|
211
|
+
# Brick.custom_columns = { 'users' => { messages: ['[COUNT(messages)] messages', 'messages'] },
|
212
|
+
# 'orders' => { salesperson: '[salesperson.first] [salesperson.last]',
|
213
|
+
# products: ['[COUNT(order_items.product)] products', 'order_items.product' ] }
|
214
|
+
# }
|
215
|
+
|
210
216
|
# # Skip creating a has_many association for these (only retain the belongs_to built from this additional_reference).
|
211
217
|
# # (Uses the same exact three-part format as would define an additional_reference)
|
212
218
|
# # Say for instance that we didn't care to display the favourite colours that users have:
|
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.76
|
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
|
+
date: 2022-10-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|