brick 1.0.47 → 1.0.50
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 +24 -0
- data/lib/brick/extensions.rb +151 -59
- data/lib/brick/frameworks/rails/engine.rb +39 -16
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +88 -49
- data/lib/generators/brick/install_generator.rb +46 -2
- metadata +2 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5f5d009df0d4a7ce06a5dec8986d2664671e8b151f7493e5ce3f8f690ec7028
|
4
|
+
data.tar.gz: 974e86906775728e0ff0c7022cfbd87578b7c3013a8f9a5fe0db2243dcad9bf1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f652cf44f9d18eca35304d1660ed3967d39c60dff42bcc05043607fada17d427308b7052a4b474c71d8a65462f86970838771471b6f1dd7a89accabc31ddd27
|
7
|
+
data.tar.gz: 386030a8bd3c603264ed9f534e1188eb5710fe67777ffc9634a9c318e8bac4153d2716bffbf9c34a7ff63999c3af39b2fa6db3b324abc5093467a33b144b41b8
|
data/lib/brick/config.rb
CHANGED
@@ -190,6 +190,30 @@ module Brick
|
|
190
190
|
@mutex.synchronize { @table_name_prefixes = value }
|
191
191
|
end
|
192
192
|
|
193
|
+
def order
|
194
|
+
@mutex.synchronize { @order || {} }
|
195
|
+
end
|
196
|
+
|
197
|
+
# Get something like:
|
198
|
+
# Override how code sorts with:
|
199
|
+
# { 'on_call_list' => { code: "ORDER BY STRING_TO_ARRAY(code, '.')::int[]" } }
|
200
|
+
# Specify default thing to order_by with:
|
201
|
+
# { 'on_call_list' => { _brick_default: [:last_name, :first_name] } }
|
202
|
+
# { 'on_call_list' => { _brick_default: :sequence } }
|
203
|
+
def order=(orders)
|
204
|
+
@mutex.synchronize do
|
205
|
+
case (brick_default = orders.fetch(:_brick_default, nil))
|
206
|
+
when NilClass
|
207
|
+
orders[:_brick_default] = orders.keys.reject { |k| k == :_brick_default }.first
|
208
|
+
when String
|
209
|
+
orders[:_brick_default] = [brick_default.to_sym]
|
210
|
+
when Symbol
|
211
|
+
orders[:_brick_default] = [brick_default]
|
212
|
+
end
|
213
|
+
@order = orders
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
193
217
|
def metadata_columns
|
194
218
|
@mutex.synchronize { @metadata_columns }
|
195
219
|
end
|
data/lib/brick/extensions.rb
CHANGED
@@ -221,6 +221,68 @@ module ActiveRecord
|
|
221
221
|
template
|
222
222
|
end
|
223
223
|
|
224
|
+
class << self
|
225
|
+
# belongs_to DSL descriptions
|
226
|
+
def _br_bt_descrip
|
227
|
+
@_br_bt_descrip ||= {}
|
228
|
+
end
|
229
|
+
# has_many count definitions
|
230
|
+
def _br_hm_counts
|
231
|
+
@_br_hm_counts ||= {}
|
232
|
+
end
|
233
|
+
# has_many :through associative tables
|
234
|
+
def _br_associatives
|
235
|
+
@_br_associatives ||= {}
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Search for BT, HM, and HMT DSL stuff
|
240
|
+
def self._brick_calculate_bts_hms(translations, join_array)
|
241
|
+
bts, hms, associatives = ::Brick.get_bts_and_hms(self)
|
242
|
+
bts.each do |_k, bt|
|
243
|
+
next if bt[2] # Polymorphic?
|
244
|
+
|
245
|
+
# join_array will receive this relation name when calling #brick_parse_dsl
|
246
|
+
_br_bt_descrip[bt.first] = if bt[1].is_a?(Array)
|
247
|
+
bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations, true) }
|
248
|
+
else
|
249
|
+
{ bt.last => bt[1].brick_parse_dsl(join_array, bt.first, translations) }
|
250
|
+
end
|
251
|
+
end
|
252
|
+
skip_klass_hms = ::Brick.config.skip_index_hms[self.name] || {}
|
253
|
+
hms.each do |k, hm|
|
254
|
+
next if skip_klass_hms.key?(k)
|
255
|
+
|
256
|
+
if hm.macro == :has_one
|
257
|
+
# For our purposes a :has_one is similar enough to a :belongs_to that we can just join forces
|
258
|
+
_br_bt_descrip[k] = { hm.klass => hm.klass.brick_parse_dsl(join_array, k, translations) }
|
259
|
+
else # Standard :has_many
|
260
|
+
_br_hm_counts[k] = hm
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def self._brick_calculate_ordering(ordering, is_do_txt = true)
|
266
|
+
quoted_table_name = table_name.split('.').map { |x| "\"#{x}\"" }.join('.')
|
267
|
+
order_by_txt = [] if is_do_txt
|
268
|
+
ordering = [ordering] if ordering && !ordering.is_a?(Array)
|
269
|
+
order_by = ordering&.map do |ord_part| # %%% If a term is also used as an eqi-condition in the WHERE clause, it can be omitted from ORDER BY
|
270
|
+
case ord_part
|
271
|
+
when String
|
272
|
+
ord_expr = ord_part.gsub('^^^', quoted_table_name)
|
273
|
+
order_by_txt&.<<("Arel.sql(#{ord_expr})")
|
274
|
+
Arel.sql(ord_expr)
|
275
|
+
else # Expecting only Symbol
|
276
|
+
ord_part = "_br_#{ord_part}_ct" if _br_hm_counts.key?(ord_part)
|
277
|
+
# Retain any reference to a bt_descrip as being a symbol
|
278
|
+
# Was: "#{quoted_table_name}.\"#{ord_part}\""
|
279
|
+
order_by_txt&.<<(_br_bt_descrip.key?(ord_part) ? ord_part : ord_part.inspect)
|
280
|
+
ord_part
|
281
|
+
end
|
282
|
+
end
|
283
|
+
[order_by, order_by_txt]
|
284
|
+
end
|
285
|
+
|
224
286
|
private
|
225
287
|
|
226
288
|
def self._brick_get_fks
|
@@ -310,16 +372,15 @@ module ActiveRecord
|
|
310
372
|
end
|
311
373
|
end
|
312
374
|
|
313
|
-
def brick_select(params, selects = nil,
|
314
|
-
# , is_add_bts, is_add_hms
|
315
|
-
)
|
316
|
-
is_add_bts = is_add_hms = true
|
375
|
+
def brick_select(params, selects = nil, order_by = nil, translations = {}, join_array = ::Brick::JoinArray.new)
|
317
376
|
is_distinct = nil
|
318
377
|
wheres = {}
|
319
378
|
params.each do |k, v|
|
379
|
+
next if ['_brick_schema', '_brick_order'].include?(k)
|
380
|
+
|
320
381
|
case (ks = k.split('.')).length
|
321
382
|
when 1
|
322
|
-
next unless klass._brick_get_fks.include?(k)
|
383
|
+
next unless klass.column_names.any?(k) || klass._brick_get_fks.include?(k)
|
323
384
|
when 2
|
324
385
|
assoc_name = ks.first.to_sym
|
325
386
|
# Make sure it's a good association name and that the model has that column name
|
@@ -345,33 +406,6 @@ module ActiveRecord
|
|
345
406
|
end
|
346
407
|
end
|
347
408
|
|
348
|
-
# Search for BT, HM, and HMT DSL stuff
|
349
|
-
translations = {}
|
350
|
-
if is_add_bts || is_add_hms
|
351
|
-
bts, hms, associatives = ::Brick.get_bts_and_hms(klass)
|
352
|
-
bts.each do |_k, bt|
|
353
|
-
next if bt[2] # Polymorphic?
|
354
|
-
|
355
|
-
# join_array will receive this relation name when calling #brick_parse_dsl
|
356
|
-
bt_descrip[bt.first] = if bt[1].is_a?(Array)
|
357
|
-
bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations, true) }
|
358
|
-
else
|
359
|
-
{ bt.last => bt[1].brick_parse_dsl(join_array, bt.first, translations) }
|
360
|
-
end
|
361
|
-
end
|
362
|
-
skip_klass_hms = ::Brick.config.skip_index_hms[klass.name] || {}
|
363
|
-
hms.each do |k, hm|
|
364
|
-
next if skip_klass_hms.key?(k)
|
365
|
-
|
366
|
-
if hm.macro == :has_one
|
367
|
-
# For our purposes a :has_one is similar enough to a :belongs_to that we can just join forces
|
368
|
-
bt_descrip[k] = { hm.klass => hm.klass.brick_parse_dsl(join_array, k, translations) }
|
369
|
-
else # Standard :has_many
|
370
|
-
hm_counts[k] = hm
|
371
|
-
end
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
409
|
if join_array.present?
|
376
410
|
left_outer_joins!(join_array)
|
377
411
|
# Without working from a duplicate, touching the AREL ast tree sets the @arel instance variable, which causes the relation to be immutable.
|
@@ -380,7 +414,8 @@ module ActiveRecord
|
|
380
414
|
chains = rel_dupe._brick_chains
|
381
415
|
id_for_tables = Hash.new { |h, k| h[k] = [] }
|
382
416
|
field_tbl_names = Hash.new { |h, k| h[k] = {} }
|
383
|
-
|
417
|
+
used_col_aliases = {} # Used to make sure there is not a name clash
|
418
|
+
bt_columns = klass._br_bt_descrip.each_with_object([]) do |v, s|
|
384
419
|
v.last.each do |k1, v1| # k1 is class, v1 is array of columns to snag
|
385
420
|
next if chains[k1].nil?
|
386
421
|
|
@@ -391,7 +426,12 @@ module ActiveRecord
|
|
391
426
|
|
392
427
|
# Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
|
393
428
|
is_xml = is_distinct && Brick.relations[sel_col.first.table_name]&.[](:cols)&.[](sel_col.last)&.first&.start_with?('xml')
|
394
|
-
|
429
|
+
# If it's not unique then also include the belongs_to association name before the column name
|
430
|
+
if used_col_aliases.key?(col_alias = "_brfk_#{v.first}__#{sel_col.last}")
|
431
|
+
col_alias = "_brfk_#{v.first}__#{v1[idx][-2..-1].map(&:to_s).join('__')}"
|
432
|
+
end
|
433
|
+
selects << "\"#{field_tbl_name}\".\"#{sel_col.last}\"#{'::text' if is_xml} AS \"#{col_alias}\""
|
434
|
+
used_col_aliases[col_alias] = nil
|
395
435
|
v1[idx] << col_alias
|
396
436
|
end
|
397
437
|
|
@@ -416,11 +456,10 @@ module ActiveRecord
|
|
416
456
|
end
|
417
457
|
end
|
418
458
|
# Add derived table JOIN for the has_many counts
|
419
|
-
|
459
|
+
klass._br_hm_counts.each do |k, hm|
|
420
460
|
associative = nil
|
421
461
|
count_column = if hm.options[:through]
|
422
|
-
fk_col = (associative =
|
423
|
-
hm.foreign_key if fk_col
|
462
|
+
hm.foreign_key if (fk_col = (associative = klass._br_associatives&.[](hm.name))&.foreign_key)
|
424
463
|
else
|
425
464
|
fk_col = hm.foreign_key
|
426
465
|
poly_type = hm.inverse_of.foreign_type if hm.options.key?(:as)
|
@@ -450,6 +489,25 @@ JOIN (SELECT #{selects.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}
|
|
450
489
|
joins!("#{join_clause} ON #{on_clause.join(' AND ')}")
|
451
490
|
end
|
452
491
|
where!(wheres) unless wheres.empty?
|
492
|
+
# Must parse the order_by and see if there are any symbols which refer to BT associations
|
493
|
+
# as they must be expanded to find the corresponding _br_model__column naming for each.
|
494
|
+
if order_by.present?
|
495
|
+
final_order_by = *order_by.each_with_object([]) do |v, s|
|
496
|
+
if v.is_a?(Symbol)
|
497
|
+
# Add the ordered series of columns derived from the BT based on its DSL
|
498
|
+
if (bt_cols = klass._br_bt_descrip[v])
|
499
|
+
bt_cols.values.each do |v1|
|
500
|
+
v1.each { |v2| s << v2.last if v2.length > 1 }
|
501
|
+
end
|
502
|
+
else
|
503
|
+
s << v
|
504
|
+
end
|
505
|
+
else # String stuff just comes straight through
|
506
|
+
s << v
|
507
|
+
end
|
508
|
+
end
|
509
|
+
order!(*final_order_by)
|
510
|
+
end
|
453
511
|
limit!(1000) # Don't want to get too carried away just yet
|
454
512
|
wheres unless wheres.empty? # Return the specific parameters that we did use
|
455
513
|
end
|
@@ -619,7 +677,7 @@ Module.class_exec do
|
|
619
677
|
else
|
620
678
|
ActiveSupport::Inflector.pluralize(singular_table_name)
|
621
679
|
end
|
622
|
-
if ::Brick.
|
680
|
+
if ::Brick.apartment_multitenant &&
|
623
681
|
Apartment.excluded_models.include?(table_name.singularize.camelize)
|
624
682
|
schema_name = Apartment.default_schema
|
625
683
|
end
|
@@ -654,7 +712,7 @@ class Object
|
|
654
712
|
private
|
655
713
|
|
656
714
|
def build_model(schema_name, inheritable_name, model_name, singular_table_name, table_name, relations, matching)
|
657
|
-
if ::Brick.
|
715
|
+
if ::Brick.apartment_multitenant &&
|
658
716
|
schema_name == Apartment.default_schema
|
659
717
|
relation = relations["#{schema_name}.#{matching}"]
|
660
718
|
end
|
@@ -665,7 +723,7 @@ class Object
|
|
665
723
|
schema_name
|
666
724
|
else
|
667
725
|
matching = "#{schema_name}.#{matching}"
|
668
|
-
(Brick.db_schemas[schema_name] ||= self.const_get(schema_name.
|
726
|
+
(Brick.db_schemas[schema_name] ||= self.const_get(schema_name.camelize))
|
669
727
|
end
|
670
728
|
"#{schema_module&.name}::#{inheritable_name || model_name}"
|
671
729
|
end
|
@@ -846,7 +904,7 @@ class Object
|
|
846
904
|
if (inverse = assoc[:inverse])
|
847
905
|
# If it's multitenant with something like: public.____ ...
|
848
906
|
if (it_parts = inverse_table.split('.')).length > 1 &&
|
849
|
-
::Brick.
|
907
|
+
::Brick.apartment_multitenant &&
|
850
908
|
it_parts.first == Apartment.default_schema
|
851
909
|
it_parts.shift # ... then ditch the generic schema name
|
852
910
|
end
|
@@ -926,21 +984,26 @@ class Object
|
|
926
984
|
(namespace || Object).const_set(class_name.to_sym, new_controller_class)
|
927
985
|
|
928
986
|
# Brick-specific pages
|
929
|
-
|
987
|
+
case plural_class_name
|
988
|
+
when 'BrickGem'
|
930
989
|
self.define_method :orphans do
|
931
990
|
instance_variable_set(:@orphans, ::Brick.find_orphans(::Brick.set_db_schema(params)))
|
932
991
|
end
|
933
992
|
return [new_controller_class, code + ' # BrickGem controller']
|
993
|
+
when 'BrickSwagger'
|
994
|
+
is_swagger = true # if request.format == :json)
|
934
995
|
end
|
935
996
|
|
936
|
-
unless (is_swagger = plural_class_name == 'BrickSwagger') # && request.format == :json)
|
937
|
-
code << " def index\n"
|
938
|
-
code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{pk.inspect})" : '.all'}\n"
|
939
|
-
code << " @#{table_name}.brick_select(params)\n"
|
940
|
-
code << " end\n"
|
941
|
-
end
|
942
997
|
self.protect_from_forgery unless: -> { self.request.format.js? }
|
943
998
|
self.define_method :index do
|
999
|
+
# We do all of this now so that bt_descrip and hm_counts are available on the model early in case the user
|
1000
|
+
# wants to do an ORDER BY based on any of that
|
1001
|
+
translations = {}
|
1002
|
+
join_array = ::Brick::JoinArray.new
|
1003
|
+
is_add_bts = is_add_hms = true
|
1004
|
+
# This builds out bt_descrip and hm_counts on the model
|
1005
|
+
model._brick_calculate_bts_hms(translations, join_array) if is_add_bts || is_add_hms
|
1006
|
+
|
944
1007
|
if is_swagger
|
945
1008
|
json = { 'openapi': '3.0.1', 'info': { 'title': 'API V1', 'version': 'v1' },
|
946
1009
|
'servers': [
|
@@ -990,6 +1053,32 @@ class Object
|
|
990
1053
|
render inline: json.to_json, content_type: request.format
|
991
1054
|
return
|
992
1055
|
end
|
1056
|
+
|
1057
|
+
# Normal (non-swagger) request
|
1058
|
+
|
1059
|
+
# %%% Allow params to define which columns to use for order_by
|
1060
|
+
ordering = if (order_tbl = ::Brick.config.order[table_name])
|
1061
|
+
case (order_default = order_tbl[:_brick_default])
|
1062
|
+
when Array
|
1063
|
+
order_default.map { |od_part| order_tbl[od_part] || od_part }
|
1064
|
+
when Symbol
|
1065
|
+
order_tbl[order_default] || order_default
|
1066
|
+
else
|
1067
|
+
pk
|
1068
|
+
end
|
1069
|
+
else
|
1070
|
+
pk # If it's not a custom ORDER BY, just use the key
|
1071
|
+
end
|
1072
|
+
order_by, order_by_txt = model._brick_calculate_ordering(ordering)
|
1073
|
+
if (order_params = params['_brick_order']&.split(',')&.map(&:to_sym)) # Overriding the default by providing a querystring param?
|
1074
|
+
order_by, _ = model._brick_calculate_ordering(order_params, true) # Don't do the txt part
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
code << " def index\n"
|
1078
|
+
code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{order_by_txt.join(', ')})" : '.all'}\n"
|
1079
|
+
code << " @#{table_name}.brick_select(params)\n"
|
1080
|
+
code << " end\n"
|
1081
|
+
|
993
1082
|
::Brick.set_db_schema(params)
|
994
1083
|
if request.format == :csv # Asking for a template?
|
995
1084
|
require 'csv'
|
@@ -1003,19 +1092,16 @@ class Object
|
|
1003
1092
|
return
|
1004
1093
|
end
|
1005
1094
|
|
1006
|
-
|
1007
|
-
order = pk.each_with_object([]) { |pk_part, s| s << "#{quoted_table_name}.\"#{pk_part}\"" }
|
1008
|
-
ar_relation = order.present? ? model.order("#{order.join(', ')}") : model.all
|
1009
|
-
@_brick_params = ar_relation.brick_select(params, (selects = []), (bt_descrip = {}), (hm_counts = {}), (join_array = ::Brick::JoinArray.new))
|
1095
|
+
@_brick_params = (ar_relation = model.all).brick_select(params, (selects = []), order_by, translations, join_array)
|
1010
1096
|
# %%% Add custom HM count columns
|
1011
1097
|
# %%% What happens when the PK is composite?
|
1012
|
-
counts =
|
1098
|
+
counts = model._br_hm_counts.each_with_object([]) { |v, s| s << "_br_#{v.first}._ct_ AS _br_#{v.first}_ct" }
|
1013
1099
|
instance_variable_set("@#{table_name}".to_sym, ar_relation.dup._select!(*selects, *counts))
|
1014
1100
|
if namespace && (idx = lookup_context.prefixes.index(table_name))
|
1015
1101
|
lookup_context.prefixes[idx] = "#{namespace.name.underscore}/#{lookup_context.prefixes[idx]}"
|
1016
1102
|
end
|
1017
|
-
@_brick_bt_descrip =
|
1018
|
-
@_brick_hm_counts =
|
1103
|
+
@_brick_bt_descrip = model._br_bt_descrip
|
1104
|
+
@_brick_hm_counts = model._br_hm_counts
|
1019
1105
|
@_brick_join_array = join_array
|
1020
1106
|
end
|
1021
1107
|
|
@@ -1142,7 +1228,8 @@ module ActiveRecord::ConnectionHandling
|
|
1142
1228
|
end
|
1143
1229
|
# Load the initializer for the Apartment gem a little early so that if .excluded_models and
|
1144
1230
|
# .default_schema are specified then we can work with non-tenanted models more appropriately
|
1145
|
-
|
1231
|
+
apartment = Object.const_defined?('Apartment')
|
1232
|
+
if apartment && File.exist?(apartment_initializer = Rails.root.join('config/initializers/apartment.rb'))
|
1146
1233
|
load apartment_initializer
|
1147
1234
|
apartment_excluded = Apartment.excluded_models
|
1148
1235
|
end
|
@@ -1345,17 +1432,20 @@ module ActiveRecord::ConnectionHandling
|
|
1345
1432
|
end
|
1346
1433
|
end
|
1347
1434
|
|
1348
|
-
apartment = Object.const_defined?('Apartment') && Apartment
|
1349
1435
|
tables = []
|
1350
1436
|
views = []
|
1351
1437
|
relations.each do |k, v|
|
1352
1438
|
name_parts = k.split('.')
|
1439
|
+
idx = 1
|
1440
|
+
name_parts = name_parts.map do |x|
|
1441
|
+
((idx += 1) < name_parts.length ? x.singularize : x).camelize
|
1442
|
+
end
|
1353
1443
|
if v.key?(:isView)
|
1354
1444
|
views
|
1355
1445
|
else
|
1356
1446
|
name_parts.shift if apartment && name_parts.length > 1 && name_parts.first == Apartment.default_schema
|
1357
1447
|
tables
|
1358
|
-
end << name_parts.
|
1448
|
+
end << name_parts.join('::')
|
1359
1449
|
end
|
1360
1450
|
unless tables.empty?
|
1361
1451
|
puts "\nClasses that can be built from tables:"
|
@@ -1437,7 +1527,7 @@ module Brick
|
|
1437
1527
|
unless (cnstr_name = fk[5])
|
1438
1528
|
# For any appended references (those that come from config), arrive upon a definitely unique constraint name
|
1439
1529
|
pri_tbl = is_class ? fk[4][:class].underscore : pri_tbl
|
1440
|
-
pri_tbl = "#{bt_assoc_name}_#{pri_tbl}" if pri_tbl
|
1530
|
+
pri_tbl = "#{bt_assoc_name}_#{pri_tbl}" if pri_tbl&.singularize != bt_assoc_name
|
1441
1531
|
cnstr_base = cnstr_name = "(brick) #{for_tbl}_#{pri_tbl}"
|
1442
1532
|
cnstr_added_num = 1
|
1443
1533
|
cnstr_name = "#{cnstr_base}_#{cnstr_added_num += 1}" while bts&.key?(cnstr_name) || hms&.key?(cnstr_name)
|
@@ -1463,6 +1553,8 @@ module Brick
|
|
1463
1553
|
return
|
1464
1554
|
end
|
1465
1555
|
end
|
1556
|
+
return unless bts # Rails 5.0 and older can have bts end up being nil
|
1557
|
+
|
1466
1558
|
if (assoc_bt = bts[cnstr_name])
|
1467
1559
|
if is_polymorphic
|
1468
1560
|
# Assuming same fk (don't yet support composite keys for polymorphics)
|
@@ -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
|
+
# When table names have specific prefixes, automatically place them in their own module with a table_name_prefix.
|
37
|
+
::Brick.order = app.config.brick.fetch(:order, {})
|
38
|
+
|
36
39
|
# Skip creating a has_many association for these
|
37
40
|
::Brick.exclude_hms = app.config.brick.fetch(:exclude_hms, nil)
|
38
41
|
|
@@ -101,7 +104,7 @@ module Brick
|
|
101
104
|
path_obj_name = model_name.underscore.tr('/', '_')
|
102
105
|
table_name = obj_name.pluralize
|
103
106
|
template_link = nil
|
104
|
-
bts, hms
|
107
|
+
bts, hms = ::Brick.get_bts_and_hms(@_brick_model) # This gets BT and HM and also has_many :through (HMT)
|
105
108
|
hms_columns = [] # Used for 'index'
|
106
109
|
skip_klass_hms = ::Brick.config.skip_index_hms[model_name] || {}
|
107
110
|
hms_headers = hms.each_with_object([]) do |hm, s|
|
@@ -109,7 +112,7 @@ module Brick
|
|
109
112
|
"H#{hm_assoc.macro == :has_one ? 'O' : 'M'}#{'T' if hm_assoc.options[:through]}",
|
110
113
|
(assoc_name = hm.first)]
|
111
114
|
hm_fk_name = if hm_assoc.options[:through]
|
112
|
-
associative =
|
115
|
+
associative = @_brick_model._br_associatives[hm.first]
|
113
116
|
tbl_nm = if hm_assoc.options[:source]
|
114
117
|
associative.klass.reflect_on_association(hm_assoc.options[:source]).inverse_of&.name
|
115
118
|
else
|
@@ -162,13 +165,13 @@ module Brick
|
|
162
165
|
schema_options = ::Brick.db_schemas.keys.each_with_object(+'') { |v, s| s << "<option value=\"#{v}\">#{v}</option>" }.html_safe
|
163
166
|
# %%% If we are not auto-creating controllers (or routes) then omit by default, and if enabled anyway, such as in a development
|
164
167
|
# environment or whatever, then get either the controllers or routes list instead
|
165
|
-
apartment_default_schema = ::Brick.
|
166
|
-
table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables).
|
168
|
+
apartment_default_schema = ::Brick.apartment_multitenant && Apartment.default_schema
|
169
|
+
table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables).each_with_object({}) do |tbl, s|
|
167
170
|
if (tbl_parts = tbl.split('.')).first == apartment_default_schema
|
168
171
|
tbl = tbl_parts.last
|
169
172
|
end
|
170
|
-
tbl
|
171
|
-
end.sort.each_with_object(+'') do |v, s|
|
173
|
+
s[tbl] = nil
|
174
|
+
end.keys.sort.each_with_object(+'') do |v, s|
|
172
175
|
s << "<option value=\"#{v.underscore.gsub('.', '/').pluralize}\">#{v}</option>"
|
173
176
|
end.html_safe
|
174
177
|
table_options << '<option value="brick_orphans">(Orphans)</option>'.html_safe if is_orphans
|
@@ -199,6 +202,9 @@ table thead tr th, table tr th {
|
|
199
202
|
color: #fff;
|
200
203
|
text-align: left;
|
201
204
|
}
|
205
|
+
#headerTop th:hover, #headerTop th:hover {
|
206
|
+
background-color: #18B090;
|
207
|
+
}
|
202
208
|
table thead tr th a, table tr th a {
|
203
209
|
color: #80FFB8;
|
204
210
|
}
|
@@ -537,7 +543,7 @@ if (headerTop) {
|
|
537
543
|
k, id = @_brick_params.first
|
538
544
|
id = id.first if id.is_a?(Array) && id.length == 1
|
539
545
|
origin = (key_parts = k.split('.')).length == 1 ? #{model_name} : #{model_name}.reflect_on_association(key_parts.first).klass
|
540
|
-
if (destination_fk = Brick.relations[origin.table_name][:fks].values.find { |fk|
|
546
|
+
if (destination_fk = Brick.relations[origin.table_name][:fks].values.find { |fk| fk[:fk] == key_parts.last }) &&
|
541
547
|
(obj = (destination = origin.reflect_on_association(destination_fk[:assoc_name])&.klass)&.find(id)) %>
|
542
548
|
<h3>for <%= link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination.name.underscore.tr('/', '_')\}_path\".to_sym, id) %></h3><%
|
543
549
|
end
|
@@ -547,30 +553,33 @@ if (headerTop) {
|
|
547
553
|
<br>
|
548
554
|
<table id=\"headerTop\">
|
549
555
|
<table id=\"#{table_name}\">
|
550
|
-
<thead><tr>#{
|
556
|
+
<thead><tr>#{"<th x-order=\"#{pk.join(',')}\"></th>" if pk.present?}<%
|
551
557
|
col_order = []
|
552
558
|
@#{table_name}.columns.each do |col|
|
553
559
|
next if (#{(pk || []).inspect}.include?(col_name = col.name) && col.type == :integer && !bts.key?(col_name)) ||
|
554
560
|
::Brick.config.metadata_columns.include?(col_name) || poly_cols.include?(col_name)
|
555
561
|
|
556
562
|
col_order << col_name
|
557
|
-
%><th<%= \" title
|
563
|
+
%><th<%= \" title=\\\"#\{col.comment}\\\"\".html_safe if col.respond_to?(:comment) && !col.comment.blank? %><%
|
558
564
|
if (bt = bts[col_name]) %>
|
559
|
-
BT
|
565
|
+
<%= \" x-order=\\\"#\{bt.first}\\\"\".html_safe unless bt[2] # Allow sorting any BT except polymorphics
|
566
|
+
%>>BT <%
|
560
567
|
bt[1].each do |bt_pair| %><%=
|
561
568
|
bt_pair.first.bt_link(bt.first) %> <%
|
562
569
|
end %><%
|
563
|
-
else %><%=
|
564
|
-
col_name %><%
|
570
|
+
else %><%= \" x-order=\\\"#\{col_name}\\\"\".html_safe if true # Currently we always allow click to sort
|
571
|
+
%>><%= col_name %><%
|
565
572
|
end
|
566
573
|
%></th><%
|
567
574
|
end
|
568
575
|
# Consider getting the name from the association -- h.first.name -- if a more \"friendly\" alias should be used for a screwy table name
|
569
576
|
%>#{hms_headers.map do |h|
|
577
|
+
# Currently we always allow click to sort
|
578
|
+
"<th#{" x-order=\"#{h.first.name}\"" if true}>" +
|
570
579
|
if h.first.options[:through] && !h.first.through_reflection
|
571
|
-
"
|
580
|
+
"#{h[1]} #{h[2]} %></th>" # %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
|
572
581
|
else
|
573
|
-
"
|
582
|
+
"#{h[1]} <%= link_to('#{h[2]}', #{h.first.klass.name.underscore.tr('/', '_').pluralize}_path) %></th>"
|
574
583
|
end
|
575
584
|
end.join
|
576
585
|
}</tr></thead>
|
@@ -658,7 +667,7 @@ end
|
|
658
667
|
<tr>
|
659
668
|
<% next if (#{(pk || []).inspect}.include?(k) && !bts.key?(k)) ||
|
660
669
|
::Brick.config.metadata_columns.include?(k) %>
|
661
|
-
<th class=\"show-field\"<%= \" title
|
670
|
+
<th class=\"show-field\"<%= \" title=\\\"#\{col.comment}\\\"\".html_safe if col.respond_to?(:comment) && !col.comment.blank? %>>
|
662
671
|
<% has_fields = true
|
663
672
|
if (bt = bts[k])
|
664
673
|
# Add a final member in this array with descriptive options to be used in <select> drop-downs
|
@@ -729,9 +738,14 @@ end
|
|
729
738
|
<% when :uuid %>
|
730
739
|
<%=
|
731
740
|
# Postgres naturally uses the +uuid_generate_v4()+ function from the uuid-ossp extension
|
732
|
-
# If it's not yet enabled then:
|
741
|
+
# If it's not yet enabled then: create extension \"uuid-ossp\";
|
733
742
|
# ActiveUUID gem created a new :uuid type
|
734
743
|
val %>
|
744
|
+
<% when :ltree %>
|
745
|
+
<%=
|
746
|
+
# In Postgres labels of data stored in a hierarchical tree-like structure
|
747
|
+
# If it's not yet enabled then: create extension ltree;
|
748
|
+
val %>
|
735
749
|
<% when :binary, :primary_key %>
|
736
750
|
<% end %>
|
737
751
|
<% end %>
|
@@ -789,6 +803,15 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
|
|
789
803
|
</script>
|
790
804
|
<% end %>
|
791
805
|
<script>
|
806
|
+
<% # Make column headers sort when clicked
|
807
|
+
# %%% Create a smart javascript routine which can do this client-side %>
|
808
|
+
[... document.getElementsByTagName(\"TH\")].forEach(function (th) {
|
809
|
+
th.addEventListener(\"click\", function (e) {
|
810
|
+
var xOrder;
|
811
|
+
if (xOrder = this.getAttribute(\"x-order\"))
|
812
|
+
location.href = changeout(location.href, \"_brick_order\", xOrder);
|
813
|
+
});
|
814
|
+
});
|
792
815
|
document.querySelectorAll(\"input, select\").forEach(function (inp) {
|
793
816
|
var origVal = getInpVal(),
|
794
817
|
prevVal = origVal;
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -47,7 +47,6 @@ end
|
|
47
47
|
|
48
48
|
# Add left_outer_join! to Associations::JoinDependency and Relation::QueryMethods
|
49
49
|
if ActiveRecord.version < ::Gem::Version.new('5')
|
50
|
-
is_add_left_outer_join = true
|
51
50
|
::Brick::Util._patch_require(
|
52
51
|
'active_record/associations/join_dependency.rb', '/activerecord', # /associations
|
53
52
|
["def join_constraints(outer_joins)
|
@@ -140,6 +139,13 @@ module Brick
|
|
140
139
|
(@relations ||= {})[ActiveRecord::Base.connection_pool.object_id] ||= Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = {} } }
|
141
140
|
end
|
142
141
|
|
142
|
+
def apartment_multitenant
|
143
|
+
if @apartment_multitenant.nil?
|
144
|
+
@apartment_multitenant = ::Brick.config.schema_behavior[:multitenant] && Object.const_defined?('Apartment')
|
145
|
+
end
|
146
|
+
@apartment_multitenant
|
147
|
+
end
|
148
|
+
|
143
149
|
# If multitenancy is enabled, a list of non-tenanted "global" models
|
144
150
|
def non_tenanted_models
|
145
151
|
@pending_models ||= {}
|
@@ -166,14 +172,14 @@ module Brick
|
|
166
172
|
# Mark has_manys that go to an associative ("join") table so that they are skipped in the UI,
|
167
173
|
# as well as any possible polymorphic associations
|
168
174
|
skip_hms = {}
|
169
|
-
|
175
|
+
hms.each do |hmt|
|
170
176
|
if (through = hmt.last.options[:through])
|
171
177
|
skip_hms[through] = nil # if hms[through]
|
172
178
|
# binding.pry if !hms[through]
|
173
179
|
# End up with a hash of HMT names pointing to join-table associations
|
174
180
|
# Last part was: hmt.last.name
|
175
181
|
# Changed up because looking for: hms[:issue_issue_duplicates]
|
176
|
-
|
182
|
+
model._br_associatives[hmt.first] = hms[through] # || hms["#{(opt = hmt.last.options)[:through].to_s.singularize}_#{opt[:source].to_s.pluralize}".to_sym]
|
177
183
|
elsif hmt.last.inverse_of.nil?
|
178
184
|
puts "SKIPPING #{hmt.last.name.inspect}"
|
179
185
|
# %%% If we don't do this then below associative.name will find that associative is nil
|
@@ -181,7 +187,7 @@ module Brick
|
|
181
187
|
end
|
182
188
|
end
|
183
189
|
skip_hms.each { |k, _v| hms.delete(k) }
|
184
|
-
[bts, hms
|
190
|
+
[bts, hms]
|
185
191
|
end
|
186
192
|
|
187
193
|
# Switches Brick auto-models on or off, for all threads
|
@@ -277,6 +283,11 @@ module Brick
|
|
277
283
|
end
|
278
284
|
end
|
279
285
|
|
286
|
+
# @api public
|
287
|
+
def order=(value)
|
288
|
+
Brick.config.order = value
|
289
|
+
end
|
290
|
+
|
280
291
|
# Skip creating a has_many association for these
|
281
292
|
# (Uses the same exact three-part format as would define an additional_reference)
|
282
293
|
# @api public
|
@@ -451,7 +462,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
451
462
|
::Brick.relations.each do |rel_name, v|
|
452
463
|
rel_name = rel_name.split('.').map(&:underscore)
|
453
464
|
schema_names = rel_name[0..-2]
|
454
|
-
schema_names.shift if ::Brick.
|
465
|
+
schema_names.shift if ::Brick.apartment_multitenant && schema_names.first == Apartment.default_schema
|
455
466
|
k = rel_name.last
|
456
467
|
unless existing_controllers.key?(controller_name = k.pluralize)
|
457
468
|
options = {}
|
@@ -481,7 +492,8 @@ require 'brick/version_number'
|
|
481
492
|
# Older versions of ActiveRecord would only show more serious error information from "panic" level, which is
|
482
493
|
# a level only available in Postgres 12 and older. This patch will allow older and newer versions of Postgres
|
483
494
|
# to work along with fairly old versions of Rails.
|
484
|
-
if Object.const_defined?('PG::VERSION')
|
495
|
+
if (is_postgres = (Object.const_defined?('PG::VERSION') || Gem::Specification.find_all_by_name('pg').present?)) &&
|
496
|
+
ActiveRecord.version < ::Gem::Version.new('4.2.6')
|
485
497
|
::Brick::Util._patch_require(
|
486
498
|
'active_record/connection_adapters/postgresql_adapter.rb', '/activerecord', ["'panic'", "'error'"]
|
487
499
|
)
|
@@ -489,13 +501,28 @@ end
|
|
489
501
|
|
490
502
|
require 'active_record'
|
491
503
|
require 'active_record/relation'
|
492
|
-
|
504
|
+
# To support adding left_outer_join
|
505
|
+
require 'active_record/relation/query_methods' if ActiveRecord.version < ::Gem::Version.new('5')
|
506
|
+
require 'rails/railtie' if ActiveRecord.version < ::Gem::Version.new('4.2')
|
493
507
|
|
494
508
|
# Rake tasks
|
495
509
|
class Railtie < Rails::Railtie
|
496
510
|
Dir.glob("#{File.expand_path(__dir__)}/brick/tasks/**/*.rake").each { |task| load task }
|
497
511
|
end
|
498
512
|
|
513
|
+
# Rails < 4.2 does not have env
|
514
|
+
module Rails
|
515
|
+
unless respond_to?(:env)
|
516
|
+
def self.env
|
517
|
+
@_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development")
|
518
|
+
end
|
519
|
+
|
520
|
+
def self.env=(environment)
|
521
|
+
@_env = ActiveSupport::StringInquirer.new(environment)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
499
526
|
# Major compatibility fixes for ActiveRecord < 4.2
|
500
527
|
# ================================================
|
501
528
|
ActiveSupport.on_load(:active_record) do
|
@@ -510,6 +537,15 @@ ActiveSupport.on_load(:active_record) do
|
|
510
537
|
end
|
511
538
|
end
|
512
539
|
end
|
540
|
+
# ActiveRecord < 4.2 does not have default_timezone
|
541
|
+
# :singleton-method:
|
542
|
+
# Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
|
543
|
+
# dates and times from the database. This is set to :utc by default.
|
544
|
+
unless respond_to?(:default_timezone)
|
545
|
+
puts "ADDING!!! 4.w"
|
546
|
+
mattr_accessor :default_timezone, instance_writer: false
|
547
|
+
self.default_timezone = :utc
|
548
|
+
end
|
513
549
|
end
|
514
550
|
|
515
551
|
# Rails < 4.0 cannot do #find_by, #find_or_create_by, or do #pluck on multiple columns, so here are the patches:
|
@@ -715,58 +751,60 @@ ActiveSupport.on_load(:active_record) do
|
|
715
751
|
end
|
716
752
|
end
|
717
753
|
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
[info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
|
729
|
-
end
|
754
|
+
# Final pieces for left_outer_joins support, which was derived from this commit:
|
755
|
+
# https://github.com/rails/rails/commit/3f46ef1ddab87482b730a3f53987e04308783d8b
|
756
|
+
module Associations
|
757
|
+
class JoinDependency
|
758
|
+
def make_left_outer_joins(parent, child)
|
759
|
+
tables = child.tables
|
760
|
+
join_type = Arel::Nodes::OuterJoin
|
761
|
+
info = make_constraints parent, child, tables, join_type
|
762
|
+
|
763
|
+
[info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
|
730
764
|
end
|
731
765
|
end
|
732
|
-
|
733
|
-
|
766
|
+
end
|
767
|
+
module Querying
|
768
|
+
delegate :left_outer_joins, to: :all
|
769
|
+
end
|
770
|
+
class Relation
|
771
|
+
unless MULTI_VALUE_METHODS.include?(:left_outer_joins)
|
772
|
+
_multi_value_methods = MULTI_VALUE_METHODS + [:left_outer_joins]
|
773
|
+
send(:remove_const, :MULTI_VALUE_METHODS)
|
774
|
+
MULTI_VALUE_METHODS = _multi_value_methods
|
734
775
|
end
|
735
|
-
|
736
|
-
|
776
|
+
end
|
777
|
+
module QueryMethods
|
778
|
+
attr_writer :left_outer_joins_values
|
779
|
+
def left_outer_joins_values
|
780
|
+
@left_outer_joins_values ||= []
|
737
781
|
end
|
738
|
-
module QueryMethods
|
739
|
-
attr_writer :left_outer_joins_values
|
740
|
-
def left_outer_joins_values
|
741
|
-
@left_outer_joins_values ||= []
|
742
|
-
end
|
743
782
|
|
744
|
-
|
745
|
-
|
783
|
+
def left_outer_joins(*args)
|
784
|
+
check_if_method_has_arguments!(:left_outer_joins, args)
|
746
785
|
|
747
|
-
|
748
|
-
|
786
|
+
args.compact!
|
787
|
+
args.flatten!
|
749
788
|
|
750
|
-
|
751
|
-
|
789
|
+
spawn.left_outer_joins!(*args)
|
790
|
+
end
|
752
791
|
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
792
|
+
def left_outer_joins!(*args) # :nodoc:
|
793
|
+
self.left_outer_joins_values += args
|
794
|
+
self
|
795
|
+
end
|
757
796
|
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
end
|
797
|
+
def build_left_outer_joins(manager, outer_joins)
|
798
|
+
buckets = outer_joins.group_by do |join|
|
799
|
+
case join
|
800
|
+
when Hash, Symbol, Array
|
801
|
+
:association_join
|
802
|
+
else
|
803
|
+
raise ArgumentError, 'only Hash, Symbol and Array are allowed'
|
766
804
|
end
|
767
|
-
|
768
|
-
build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
|
769
805
|
end
|
806
|
+
|
807
|
+
build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
|
770
808
|
end
|
771
809
|
end
|
772
810
|
# (End of left_outer_joins support)
|
@@ -788,7 +826,8 @@ ActiveSupport.on_load(:active_record) do
|
|
788
826
|
end
|
789
827
|
|
790
828
|
# Do this earlier because stuff here gets mixed into JoinDependency::JoinAssociation and AssociationScope
|
791
|
-
if ActiveRecord.version < ::Gem::Version.new('5.0') && Object.const_defined?('PG::Connection')
|
829
|
+
if is_postgres && ActiveRecord.version < ::Gem::Version.new('5.0') # Was: && Object.const_defined?('PG::Connection')
|
830
|
+
require 'pg' # For ActiveRecord < 4.2
|
792
831
|
# Avoid pg gem deprecation warning: "You should use PG::Connection, PG::Result, and PG::Error instead"
|
793
832
|
PGconn = PG::Connection
|
794
833
|
PGresult = PG::Result
|
@@ -159,6 +159,41 @@ module Brick
|
|
159
159
|
# # When table names have specific prefixes automatically place them in their own module with a table_name_prefix.
|
160
160
|
# Brick.table_name_prefixes = { 'nav_' => 'Navigation' }
|
161
161
|
|
162
|
+
# # COLUMN SEQUENCING AND INCLUSION / EXCLUSION
|
163
|
+
|
164
|
+
# # By default if there is a primary key present then rows in an index view are ordered by this primary key. To
|
165
|
+
# # use a different rule for doing ORDER BY, you can override this default ordering done by The Brick, for instance
|
166
|
+
# # to have the rows in a contact list sorted by email:
|
167
|
+
# Brick.order = { 'contacts' => { _brick_default: :email } }
|
168
|
+
# # or by last name then first name:
|
169
|
+
# Brick.order = { 'contacts' => { _brick_default: [:lastname, :firstname] } }
|
170
|
+
# # Totally legitimate to have the default order be the name of a belongs_to or has_many association instead of an
|
171
|
+
# # actual column name, in which case for has_many it just orders by the count of how many records are associated,
|
172
|
+
# # and for belongs_to it's based on the primary table's DSL if any is defined (since that is what is used to
|
173
|
+
# # calculate what is shown when a foreign table lists out related records). If contacts relates to addresses,
|
174
|
+
# # then this is perfectly fine:
|
175
|
+
# Brick.order = { 'contacts' => { _brick_default: :address } }
|
176
|
+
# # You can even have a specific custom clause used in the ORDER BY. In this case it is recommended to include a
|
177
|
+
# # special placeholder for the table name with the sequence \"^^^\". Here is an example of having the default
|
178
|
+
# # ordering happening on the \"code\" column, and also defining custom sorting to be done, in this case proper
|
179
|
+
# # ordering if that code is stored as a dotted numeric value:
|
180
|
+
# Brick.order = { 'document_trees' => { _brick_default: :code,
|
181
|
+
# code: \"ORDER BY STRING_TO_ARRAY(^^^.code, '.')::int[]\" } }
|
182
|
+
|
183
|
+
# # Sequence of columns for each model. This also allows you to add read-only calculated columns in the same
|
184
|
+
# # kind of way that they can be added in the include: portion of include/exclude columns, below.
|
185
|
+
# # Designated by { <table name> => [<column name>, <column name>] }
|
186
|
+
# Brick.column_sequence = { 'users' => ['email', 'profile.firstname', 'profile.lastname'] }
|
187
|
+
|
188
|
+
# # Specific columns to include or exclude for each model. If there are only inclusions then only those
|
189
|
+
# # columns show. If there are any exclusions then all non-excluded columns are attempted to be shown,
|
190
|
+
# # which negates the usefulness of inclusions except to add calculated column detail built from DSL.
|
191
|
+
# # Designated by <table name>.<column name>
|
192
|
+
# Brick.column_sequence = { 'users' => { include: ['email', 'profile.firstname', 'profile.lastname'] },
|
193
|
+
# 'profile' => { exclude: ['birthdate'] } }
|
194
|
+
|
195
|
+
# # EXTRA FOREIGN KEYS AND OTHER HAS_MANY SETTINGS
|
196
|
+
|
162
197
|
# # Additional table references which are used to create has_many / belongs_to associations inside auto-created
|
163
198
|
# # models. (You can consider these to be \"virtual foreign keys\" if you wish)... You only have to add these
|
164
199
|
# # in cases where your database for some reason does not have foreign key constraints defined. Sometimes for
|
@@ -176,8 +211,9 @@ module Brick
|
|
176
211
|
# Brick.exclude_hms = [['users', 'favourite_colour_id', 'colours']]
|
177
212
|
|
178
213
|
# # Skip showing counts for these specific has_many associations when building auto-generated #index views.
|
179
|
-
# # When there are related tables with a significant number of records, this can lessen
|
180
|
-
# # considerably, sometimes fixing what might appear to be an index page that just \"hangs\"
|
214
|
+
# # When there are related tables with a significant number of records (generally 100,000 or more), this can lessen
|
215
|
+
# # the load on the database considerably, sometimes fixing what might appear to be an index page that just \"hangs\"
|
216
|
+
# # for no apparent reason.
|
181
217
|
# Brick.skip_index_hms = ['User.litany_of_woes']
|
182
218
|
|
183
219
|
# # By default primary tables involved in a foreign key relationship will indicate a \"has_many\" relationship pointing
|
@@ -199,6 +235,8 @@ module Brick
|
|
199
235
|
# # Designated by <table name>.<column name>
|
200
236
|
# Brick.not_nullables = ['users.name']
|
201
237
|
|
238
|
+
# # FRIENDLY DSL
|
239
|
+
|
202
240
|
# # A simple DSL is available to allow more user-friendly display of objects. Normally a user object might be shown
|
203
241
|
# # as its first non-metadata column, or if that is not available then something like \"User #45\" where 45 is that
|
204
242
|
# # object's ID. If there is no primary key then even that is not possible, so the object's .to_s method is called.
|
@@ -206,6 +244,8 @@ module Brick
|
|
206
244
|
# # user, then you can use model_descrips like this, putting expressions with property references in square brackets:
|
207
245
|
# Brick.model_descrips = { 'User' => '[profile.firstname] [profile.lastname]' }
|
208
246
|
|
247
|
+
# # SINGLE TABLE INHERITANCE
|
248
|
+
|
209
249
|
# # Specify STI subclasses either directly by name or as a general module prefix that should always relate to a specific
|
210
250
|
# # parent STI class. The prefixed :: here for these examples is mandatory. Also having a suffixed :: means instead of
|
211
251
|
# # a class reference, this is for a general namespace reference. So in this case requests for, say, either of the
|
@@ -221,6 +261,8 @@ module Brick
|
|
221
261
|
# Brick.sti_type_column = 'sti_type'
|
222
262
|
# Brick.sti_type_column = { 'rails_type' => ['sales.specialoffer'] }
|
223
263
|
|
264
|
+
# # POLYMORPHIC ASSOCIATIONS
|
265
|
+
|
224
266
|
# # Database schema to use when analysing existing data, such as deriving a list of polymorphic classes in the case that
|
225
267
|
# # it wasn't originally specified.
|
226
268
|
# Brick.schema_behavior = :namespaced
|
@@ -231,6 +273,8 @@ module Brick
|
|
231
273
|
|
232
274
|
# # Polymorphic associations are set up by providing a model name and polymorphic association name#{poly}
|
233
275
|
|
276
|
+
# # DEFAULT ROOT ROUTE
|
277
|
+
|
234
278
|
# # If a default route is not supplied, Brick attempts to find the most \"central\" table and wires up the default
|
235
279
|
# # route to go to the :index action for what would be a controller for that table. You can specify any controller
|
236
280
|
# # name and action you wish in order to override this and have that be the default route when none other has been
|
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.50
|
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-07-
|
11
|
+
date: 2022-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -17,9 +17,6 @@ dependencies:
|
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '4.2'
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '7.2'
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,9 +24,6 @@ dependencies:
|
|
27
24
|
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '4.2'
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '7.2'
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: fancy_gets
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|