brick 1.0.75 → 1.0.76
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 +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
|