brick 1.0.46 → 1.0.49
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 +167 -72
- data/lib/brick/frameworks/rails/engine.rb +51 -17
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +91 -48
- 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: 8b42f1ad45e6dc942ed2e875d39318f8705f9a467c0cb31f267a71a01053bfd4
|
4
|
+
data.tar.gz: 1e8f0cd1c374c5d75be4e4f1a8b659dd32b46f991c85cce4d234a6949d017e4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 437d415278f01a2ffced31687f77324c21416e6e7ef395d6a5d3db5a1853ccfb301c01716747ba4427417164a1200100205e7b7c75443bef83f1050f01683dda
|
7
|
+
data.tar.gz: d42367f291f2900876c5263aab30280f7b5b3d9f205c4199349b9e24d35326b5457e1db8fd0d39a512f59af9691bbe44e29dd05d3384d5a0013c05ffe6447d8c
|
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,62 @@ module ActiveRecord
|
|
221
221
|
template
|
222
222
|
end
|
223
223
|
|
224
|
+
# belongs_to DSL descriptions
|
225
|
+
def self._br_bt_descrip
|
226
|
+
@_br_bt_descrip ||= {}
|
227
|
+
end
|
228
|
+
# has_many count definitions
|
229
|
+
def self._br_hm_counts
|
230
|
+
@_br_hm_counts ||= {}
|
231
|
+
end
|
232
|
+
|
233
|
+
# Search for BT, HM, and HMT DSL stuff
|
234
|
+
def self._brick_calculate_bts_hms(translations, join_array)
|
235
|
+
bts, hms, associatives = ::Brick.get_bts_and_hms(self)
|
236
|
+
bts.each do |_k, bt|
|
237
|
+
next if bt[2] # Polymorphic?
|
238
|
+
|
239
|
+
# join_array will receive this relation name when calling #brick_parse_dsl
|
240
|
+
_br_bt_descrip[bt.first] = if bt[1].is_a?(Array)
|
241
|
+
bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations, true) }
|
242
|
+
else
|
243
|
+
{ bt.last => bt[1].brick_parse_dsl(join_array, bt.first, translations) }
|
244
|
+
end
|
245
|
+
end
|
246
|
+
skip_klass_hms = ::Brick.config.skip_index_hms[self.name] || {}
|
247
|
+
hms.each do |k, hm|
|
248
|
+
next if skip_klass_hms.key?(k)
|
249
|
+
|
250
|
+
if hm.macro == :has_one
|
251
|
+
# For our purposes a :has_one is similar enough to a :belongs_to that we can just join forces
|
252
|
+
_br_bt_descrip[k] = { hm.klass => hm.klass.brick_parse_dsl(join_array, k, translations) }
|
253
|
+
else # Standard :has_many
|
254
|
+
_br_hm_counts[k] = hm
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def self._brick_calculate_ordering(ordering, is_do_txt = true)
|
260
|
+
quoted_table_name = table_name.split('.').map { |x| "\"#{x}\"" }.join('.')
|
261
|
+
order_by_txt = [] if is_do_txt
|
262
|
+
ordering = [ordering] if ordering && !ordering.is_a?(Array)
|
263
|
+
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
|
264
|
+
case ord_part
|
265
|
+
when String
|
266
|
+
ord_expr = ord_part.gsub('^^^', quoted_table_name)
|
267
|
+
order_by_txt&.<<("Arel.sql(#{ord_expr})")
|
268
|
+
Arel.sql(ord_expr)
|
269
|
+
else # Expecting only Symbol
|
270
|
+
ord_part = "_br_#{ord_part}_ct" if _br_hm_counts.key?(ord_part)
|
271
|
+
# Retain any reference to a bt_descrip as being a symbol
|
272
|
+
# Was: "#{quoted_table_name}.\"#{ord_part}\""
|
273
|
+
order_by_txt&.<<(_br_bt_descrip.key?(ord_part) ? ord_part : ord_part.inspect)
|
274
|
+
ord_part
|
275
|
+
end
|
276
|
+
end
|
277
|
+
[order_by, order_by_txt]
|
278
|
+
end
|
279
|
+
|
224
280
|
private
|
225
281
|
|
226
282
|
def self._brick_get_fks
|
@@ -310,13 +366,12 @@ module ActiveRecord
|
|
310
366
|
end
|
311
367
|
end
|
312
368
|
|
313
|
-
def brick_select(params, selects = nil,
|
314
|
-
# , is_add_bts, is_add_hms
|
315
|
-
)
|
316
|
-
is_add_bts = is_add_hms = true
|
369
|
+
def brick_select(params, selects = nil, order_by = nil, translations = {}, join_array = ::Brick::JoinArray.new)
|
317
370
|
is_distinct = nil
|
318
371
|
wheres = {}
|
319
372
|
params.each do |k, v|
|
373
|
+
next if ['_brick_schema', '_brick_order'].include?(k)
|
374
|
+
|
320
375
|
case (ks = k.split('.')).length
|
321
376
|
when 1
|
322
377
|
next unless klass._brick_get_fks.include?(k)
|
@@ -345,33 +400,6 @@ module ActiveRecord
|
|
345
400
|
end
|
346
401
|
end
|
347
402
|
|
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
403
|
if join_array.present?
|
376
404
|
left_outer_joins!(join_array)
|
377
405
|
# Without working from a duplicate, touching the AREL ast tree sets the @arel instance variable, which causes the relation to be immutable.
|
@@ -380,7 +408,7 @@ module ActiveRecord
|
|
380
408
|
chains = rel_dupe._brick_chains
|
381
409
|
id_for_tables = Hash.new { |h, k| h[k] = [] }
|
382
410
|
field_tbl_names = Hash.new { |h, k| h[k] = {} }
|
383
|
-
bt_columns =
|
411
|
+
bt_columns = klass._br_bt_descrip.each_with_object([]) do |v, s|
|
384
412
|
v.last.each do |k1, v1| # k1 is class, v1 is array of columns to snag
|
385
413
|
next if chains[k1].nil?
|
386
414
|
|
@@ -416,7 +444,7 @@ module ActiveRecord
|
|
416
444
|
end
|
417
445
|
end
|
418
446
|
# Add derived table JOIN for the has_many counts
|
419
|
-
|
447
|
+
klass._br_hm_counts.each do |k, hm|
|
420
448
|
associative = nil
|
421
449
|
count_column = if hm.options[:through]
|
422
450
|
fk_col = (associative = associatives[hm.name])&.foreign_key
|
@@ -450,12 +478,31 @@ JOIN (SELECT #{selects.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}
|
|
450
478
|
joins!("#{join_clause} ON #{on_clause.join(' AND ')}")
|
451
479
|
end
|
452
480
|
where!(wheres) unless wheres.empty?
|
481
|
+
# Must parse the order_by and see if there are any symbols which refer to BT associations
|
482
|
+
# as they must be expanded to find the corresponding _br_model__column naming for each.
|
483
|
+
if order_by.present?
|
484
|
+
final_order_by = *order_by.each_with_object([]) do |v, s|
|
485
|
+
if v.is_a?(Symbol)
|
486
|
+
# Add the ordered series of columns derived from the BT based on its DSL
|
487
|
+
if (bt_cols = klass._br_bt_descrip[v])
|
488
|
+
bt_cols.values.each do |v1|
|
489
|
+
v1.each { |v2| s << v2.last if v2.length > 1 }
|
490
|
+
end
|
491
|
+
else
|
492
|
+
s << v
|
493
|
+
end
|
494
|
+
else # String stuff just comes straight through
|
495
|
+
s << v
|
496
|
+
end
|
497
|
+
end
|
498
|
+
order!(*final_order_by)
|
499
|
+
end
|
453
500
|
limit!(1000) # Don't want to get too carried away just yet
|
454
501
|
wheres unless wheres.empty? # Return the specific parameters that we did use
|
455
502
|
end
|
456
503
|
|
457
504
|
private
|
458
|
-
|
505
|
+
|
459
506
|
def shift_or_first(ary)
|
460
507
|
ary.length > 1 ? ary.shift : ary.first
|
461
508
|
end
|
@@ -596,7 +643,7 @@ Module.class_exec do
|
|
596
643
|
# See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
597
644
|
# checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
598
645
|
|
599
|
-
if (base_model = ::Brick.config.sti_namespace_prefixes&.fetch("::#{name}::", nil)&.constantize) || # Are we part of an auto-STI namespace? ...
|
646
|
+
if (base_model = ::Brick.config.sti_namespace_prefixes&.fetch("::#{self.name}::", nil)&.constantize) || # Are we part of an auto-STI namespace? ...
|
600
647
|
self != Object # ... or otherwise already in some namespace?
|
601
648
|
schema_name = [(singular_schema_name = name.underscore),
|
602
649
|
(schema_name = singular_schema_name.pluralize),
|
@@ -619,7 +666,7 @@ Module.class_exec do
|
|
619
666
|
else
|
620
667
|
ActiveSupport::Inflector.pluralize(singular_table_name)
|
621
668
|
end
|
622
|
-
if ::Brick.
|
669
|
+
if ::Brick.apartment_multitenant &&
|
623
670
|
Apartment.excluded_models.include?(table_name.singularize.camelize)
|
624
671
|
schema_name = Apartment.default_schema
|
625
672
|
end
|
@@ -654,7 +701,7 @@ class Object
|
|
654
701
|
private
|
655
702
|
|
656
703
|
def build_model(schema_name, inheritable_name, model_name, singular_table_name, table_name, relations, matching)
|
657
|
-
if ::Brick.
|
704
|
+
if ::Brick.apartment_multitenant &&
|
658
705
|
schema_name == Apartment.default_schema
|
659
706
|
relation = relations["#{schema_name}.#{matching}"]
|
660
707
|
end
|
@@ -665,7 +712,7 @@ class Object
|
|
665
712
|
schema_name
|
666
713
|
else
|
667
714
|
matching = "#{schema_name}.#{matching}"
|
668
|
-
(Brick.db_schemas[schema_name] ||= self.const_get(schema_name.
|
715
|
+
(Brick.db_schemas[schema_name] ||= self.const_get(schema_name.camelize))
|
669
716
|
end
|
670
717
|
"#{schema_module&.name}::#{inheritable_name || model_name}"
|
671
718
|
end
|
@@ -773,33 +820,47 @@ class Object
|
|
773
820
|
end # class definition
|
774
821
|
# Having this separate -- will this now work out better?
|
775
822
|
built_model.class_exec do
|
776
|
-
hmts&.each do |hmt_fk,
|
823
|
+
hmts&.each do |hmt_fk, hms|
|
777
824
|
hmt_fk = hmt_fk.tr('.', '_')
|
778
|
-
|
779
|
-
# %%%
|
780
|
-
through = ::Brick.config.schema_behavior[:multitenant] ?
|
781
|
-
|
782
|
-
|
783
|
-
|
825
|
+
hms.each do |hm|
|
826
|
+
# %%% Need to confirm that HMTs work when they are built from has_manys with custom names
|
827
|
+
through = ::Brick.config.schema_behavior[:multitenant] ? hm.first[:assoc_name] : hm.first[:inverse_table].tr('.', '_').pluralize
|
828
|
+
options = {}
|
829
|
+
hmt_name = if hms.length > 1
|
830
|
+
if hms[0].first[:inverse][:assoc_name] == hms[1].first[:inverse][:assoc_name] # Same BT names pointing back to us? (Most common scenario)
|
831
|
+
"#{hmt_fk}_through_#{hm.first[:assoc_name]}"
|
784
832
|
else # Use BT names to provide uniqueness
|
785
|
-
|
786
|
-
|
833
|
+
if self.name.underscore.singularize == hm.first[:alternate_name]
|
834
|
+
# Has previously been:
|
835
|
+
# # If it folds back on itself then look at the other side
|
836
|
+
# # (At this point just infer the source be the inverse of the first has_many that
|
837
|
+
# # we find that is not ourselves. If there are more than two then uh oh, can't
|
838
|
+
# # yet handle that rare circumstance!)
|
839
|
+
# other = hms.find { |hm1| hm1 != hm } # .first[:fk]
|
840
|
+
# options[:source] = other.first[:inverse][:assoc_name].to_sym
|
841
|
+
# And also has been:
|
842
|
+
# hm.first[:inverse][:assoc_name].to_sym
|
843
|
+
options[:source] = hm.last.to_sym
|
844
|
+
else
|
845
|
+
through = hm.first[:alternate_name].pluralize
|
846
|
+
end
|
847
|
+
singular_assoc_name = hm.first[:inverse][:assoc_name].singularize
|
787
848
|
"#{singular_assoc_name}_#{hmt_fk}"
|
788
849
|
end
|
789
850
|
else
|
790
851
|
hmt_fk
|
791
852
|
end
|
792
|
-
options =
|
853
|
+
options[:through] = through.to_sym
|
793
854
|
if relation[:fks].any? { |k, v| v[:assoc_name] == hmt_name }
|
794
|
-
hmt_name = "#{hmt_name.singularize}_#{
|
855
|
+
hmt_name = "#{hmt_name.singularize}_#{hm.first[:assoc_name]}"
|
795
856
|
# Was:
|
796
|
-
# options[:class_name] =
|
797
|
-
# options[:foreign_key] =
|
798
|
-
far_assoc = relations[
|
857
|
+
# options[:class_name] = hm.first[:inverse_table].singularize.camelize
|
858
|
+
# options[:foreign_key] = hm.first[:fk].to_sym
|
859
|
+
far_assoc = relations[hm.first[:inverse_table]][:fks].find { |_k, v| v[:assoc_name] == hm.last }
|
799
860
|
options[:class_name] = far_assoc.last[:inverse_table].singularize.camelize
|
800
861
|
options[:foreign_key] = far_assoc.last[:fk].to_sym
|
801
862
|
end
|
802
|
-
options[:source]
|
863
|
+
options[:source] ||= hm.last.to_sym unless hmt_name.singularize == hm.last
|
803
864
|
code << " has_many :#{hmt_name}#{options.map { |opt| ", #{opt.first}: #{opt.last.inspect}" }.join}\n"
|
804
865
|
self.send(:has_many, hmt_name.to_sym, **options)
|
805
866
|
end
|
@@ -832,7 +893,7 @@ class Object
|
|
832
893
|
if (inverse = assoc[:inverse])
|
833
894
|
# If it's multitenant with something like: public.____ ...
|
834
895
|
if (it_parts = inverse_table.split('.')).length > 1 &&
|
835
|
-
::Brick.
|
896
|
+
::Brick.apartment_multitenant &&
|
836
897
|
it_parts.first == Apartment.default_schema
|
837
898
|
it_parts.shift # ... then ditch the generic schema name
|
838
899
|
end
|
@@ -912,21 +973,26 @@ class Object
|
|
912
973
|
(namespace || Object).const_set(class_name.to_sym, new_controller_class)
|
913
974
|
|
914
975
|
# Brick-specific pages
|
915
|
-
|
976
|
+
case plural_class_name
|
977
|
+
when 'BrickGem'
|
916
978
|
self.define_method :orphans do
|
917
979
|
instance_variable_set(:@orphans, ::Brick.find_orphans(::Brick.set_db_schema(params)))
|
918
980
|
end
|
919
981
|
return [new_controller_class, code + ' # BrickGem controller']
|
982
|
+
when 'BrickSwagger'
|
983
|
+
is_swagger = true # if request.format == :json)
|
920
984
|
end
|
921
985
|
|
922
|
-
unless (is_swagger = plural_class_name == 'BrickSwagger') # && request.format == :json)
|
923
|
-
code << " def index\n"
|
924
|
-
code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{pk.inspect})" : '.all'}\n"
|
925
|
-
code << " @#{table_name}.brick_select(params)\n"
|
926
|
-
code << " end\n"
|
927
|
-
end
|
928
986
|
self.protect_from_forgery unless: -> { self.request.format.js? }
|
929
987
|
self.define_method :index do
|
988
|
+
# We do all of this now so that bt_descrip and hm_counts are available on the model early in case the user
|
989
|
+
# wants to do an ORDER BY based on any of that
|
990
|
+
translations = {}
|
991
|
+
join_array = ::Brick::JoinArray.new
|
992
|
+
is_add_bts = is_add_hms = true
|
993
|
+
# This builds out bt_descrip and hm_counts on the model
|
994
|
+
model._brick_calculate_bts_hms(translations, join_array) if is_add_bts || is_add_hms
|
995
|
+
|
930
996
|
if is_swagger
|
931
997
|
json = { 'openapi': '3.0.1', 'info': { 'title': 'API V1', 'version': 'v1' },
|
932
998
|
'servers': [
|
@@ -976,6 +1042,32 @@ class Object
|
|
976
1042
|
render inline: json.to_json, content_type: request.format
|
977
1043
|
return
|
978
1044
|
end
|
1045
|
+
|
1046
|
+
# Normal (non-swagger) request
|
1047
|
+
|
1048
|
+
# %%% Allow params to define which columns to use for order_by
|
1049
|
+
ordering = if (order_tbl = ::Brick.config.order[table_name])
|
1050
|
+
case (order_default = order_tbl[:_brick_default])
|
1051
|
+
when Array
|
1052
|
+
order_default.map { |od_part| order_tbl[od_part] || od_part }
|
1053
|
+
when Symbol
|
1054
|
+
order_tbl[order_default] || order_default
|
1055
|
+
else
|
1056
|
+
pk
|
1057
|
+
end
|
1058
|
+
else
|
1059
|
+
pk # If it's not a custom ORDER BY, just use the key
|
1060
|
+
end
|
1061
|
+
order_by, order_by_txt = model._brick_calculate_ordering(ordering)
|
1062
|
+
if (order_params = params['_brick_order']&.split(',')&.map(&:to_sym)) # Overriding the default by providing a querystring param?
|
1063
|
+
order_by, _ = model._brick_calculate_ordering(order_params, true) # Don't do the txt part
|
1064
|
+
end
|
1065
|
+
|
1066
|
+
code << " def index\n"
|
1067
|
+
code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{order_by_txt.join(', ')})" : '.all'}\n"
|
1068
|
+
code << " @#{table_name}.brick_select(params)\n"
|
1069
|
+
code << " end\n"
|
1070
|
+
|
979
1071
|
::Brick.set_db_schema(params)
|
980
1072
|
if request.format == :csv # Asking for a template?
|
981
1073
|
require 'csv'
|
@@ -989,19 +1081,16 @@ class Object
|
|
989
1081
|
return
|
990
1082
|
end
|
991
1083
|
|
992
|
-
|
993
|
-
order = pk.each_with_object([]) { |pk_part, s| s << "#{quoted_table_name}.\"#{pk_part}\"" }
|
994
|
-
ar_relation = order.present? ? model.order("#{order.join(', ')}") : model.all
|
995
|
-
@_brick_params = ar_relation.brick_select(params, (selects = []), (bt_descrip = {}), (hm_counts = {}), (join_array = ::Brick::JoinArray.new))
|
1084
|
+
@_brick_params = (ar_relation = model.all).brick_select(params, (selects = []), order_by, translations, join_array)
|
996
1085
|
# %%% Add custom HM count columns
|
997
1086
|
# %%% What happens when the PK is composite?
|
998
|
-
counts =
|
1087
|
+
counts = model._br_hm_counts.each_with_object([]) { |v, s| s << "_br_#{v.first}._ct_ AS _br_#{v.first}_ct" }
|
999
1088
|
instance_variable_set("@#{table_name}".to_sym, ar_relation.dup._select!(*selects, *counts))
|
1000
1089
|
if namespace && (idx = lookup_context.prefixes.index(table_name))
|
1001
1090
|
lookup_context.prefixes[idx] = "#{namespace.name.underscore}/#{lookup_context.prefixes[idx]}"
|
1002
1091
|
end
|
1003
|
-
@_brick_bt_descrip =
|
1004
|
-
@_brick_hm_counts =
|
1092
|
+
@_brick_bt_descrip = model._br_bt_descrip
|
1093
|
+
@_brick_hm_counts = model._br_hm_counts
|
1005
1094
|
@_brick_join_array = join_array
|
1006
1095
|
end
|
1007
1096
|
|
@@ -1128,7 +1217,8 @@ module ActiveRecord::ConnectionHandling
|
|
1128
1217
|
end
|
1129
1218
|
# Load the initializer for the Apartment gem a little early so that if .excluded_models and
|
1130
1219
|
# .default_schema are specified then we can work with non-tenanted models more appropriately
|
1131
|
-
|
1220
|
+
apartment = Object.const_defined?('Apartment')
|
1221
|
+
if apartment && File.exist?(apartment_initializer = Rails.root.join('config/initializers/apartment.rb'))
|
1132
1222
|
load apartment_initializer
|
1133
1223
|
apartment_excluded = Apartment.excluded_models
|
1134
1224
|
end
|
@@ -1331,17 +1421,20 @@ module ActiveRecord::ConnectionHandling
|
|
1331
1421
|
end
|
1332
1422
|
end
|
1333
1423
|
|
1334
|
-
apartment = Object.const_defined?('Apartment') && Apartment
|
1335
1424
|
tables = []
|
1336
1425
|
views = []
|
1337
1426
|
relations.each do |k, v|
|
1338
1427
|
name_parts = k.split('.')
|
1428
|
+
idx = 1
|
1429
|
+
name_parts = name_parts.map do |x|
|
1430
|
+
((idx += 1) < name_parts.length ? x.singularize : x).camelize
|
1431
|
+
end
|
1339
1432
|
if v.key?(:isView)
|
1340
1433
|
views
|
1341
1434
|
else
|
1342
1435
|
name_parts.shift if apartment && name_parts.length > 1 && name_parts.first == Apartment.default_schema
|
1343
1436
|
tables
|
1344
|
-
end << name_parts.
|
1437
|
+
end << name_parts.join('::')
|
1345
1438
|
end
|
1346
1439
|
unless tables.empty?
|
1347
1440
|
puts "\nClasses that can be built from tables:"
|
@@ -1423,7 +1516,7 @@ module Brick
|
|
1423
1516
|
unless (cnstr_name = fk[5])
|
1424
1517
|
# For any appended references (those that come from config), arrive upon a definitely unique constraint name
|
1425
1518
|
pri_tbl = is_class ? fk[4][:class].underscore : pri_tbl
|
1426
|
-
pri_tbl = "#{bt_assoc_name}_#{pri_tbl}" if pri_tbl
|
1519
|
+
pri_tbl = "#{bt_assoc_name}_#{pri_tbl}" if pri_tbl&.singularize != bt_assoc_name
|
1427
1520
|
cnstr_base = cnstr_name = "(brick) #{for_tbl}_#{pri_tbl}"
|
1428
1521
|
cnstr_added_num = 1
|
1429
1522
|
cnstr_name = "#{cnstr_base}_#{cnstr_added_num += 1}" while bts&.key?(cnstr_name) || hms&.key?(cnstr_name)
|
@@ -1449,6 +1542,8 @@ module Brick
|
|
1449
1542
|
return
|
1450
1543
|
end
|
1451
1544
|
end
|
1545
|
+
return unless bts # Rails 5.0 and older can have bts end up being nil
|
1546
|
+
|
1452
1547
|
if (assoc_bt = bts[cnstr_name])
|
1453
1548
|
if is_polymorphic
|
1454
1549
|
# 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
|
|
@@ -105,13 +108,26 @@ module Brick
|
|
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|
|
108
|
-
hm_stuff = [(hm_assoc = hm.last),
|
111
|
+
hm_stuff = [(hm_assoc = hm.last),
|
112
|
+
"H#{hm_assoc.macro == :has_one ? 'O' : 'M'}#{'T' if hm_assoc.options[:through]}",
|
113
|
+
(assoc_name = hm.first)]
|
109
114
|
hm_fk_name = if hm_assoc.options[:through]
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
+
associative = associatives[hm.first]
|
116
|
+
tbl_nm = if hm_assoc.options[:source]
|
117
|
+
associative.klass.reflect_on_association(hm_assoc.options[:source]).inverse_of&.name
|
118
|
+
else
|
119
|
+
associative.name
|
120
|
+
end
|
121
|
+
# If there is no inverse available for the source belongs_to association, make one based on the class name
|
122
|
+
unless tbl_nm
|
123
|
+
tbl_nm = associative.class_name.underscore
|
124
|
+
tbl_nm.slice!(0) if tbl_nm[0] == ('/')
|
125
|
+
tbl_nm = tbl_nm.tr('/', '_').pluralize
|
126
|
+
end
|
127
|
+
"'#{tbl_nm}.#{associative.foreign_key}'"
|
128
|
+
else
|
129
|
+
hm_assoc.foreign_key
|
130
|
+
end
|
115
131
|
case args.first
|
116
132
|
when 'index'
|
117
133
|
hms_columns << if hm_assoc.macro == :has_many
|
@@ -149,7 +165,7 @@ module Brick
|
|
149
165
|
schema_options = ::Brick.db_schemas.keys.each_with_object(+'') { |v, s| s << "<option value=\"#{v}\">#{v}</option>" }.html_safe
|
150
166
|
# %%% If we are not auto-creating controllers (or routes) then omit by default, and if enabled anyway, such as in a development
|
151
167
|
# environment or whatever, then get either the controllers or routes list instead
|
152
|
-
apartment_default_schema = ::Brick.
|
168
|
+
apartment_default_schema = ::Brick.apartment_multitenant && Apartment.default_schema
|
153
169
|
table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables).map do |tbl|
|
154
170
|
if (tbl_parts = tbl.split('.')).first == apartment_default_schema
|
155
171
|
tbl = tbl_parts.last
|
@@ -186,6 +202,9 @@ table thead tr th, table tr th {
|
|
186
202
|
color: #fff;
|
187
203
|
text-align: left;
|
188
204
|
}
|
205
|
+
#headerTop th:hover, #headerTop th:hover {
|
206
|
+
background-color: #18B090;
|
207
|
+
}
|
189
208
|
table thead tr th a, table tr th a {
|
190
209
|
color: #80FFB8;
|
191
210
|
}
|
@@ -524,7 +543,7 @@ if (headerTop) {
|
|
524
543
|
k, id = @_brick_params.first
|
525
544
|
id = id.first if id.is_a?(Array) && id.length == 1
|
526
545
|
origin = (key_parts = k.split('.')).length == 1 ? #{model_name} : #{model_name}.reflect_on_association(key_parts.first).klass
|
527
|
-
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 }) &&
|
528
547
|
(obj = (destination = origin.reflect_on_association(destination_fk[:assoc_name])&.klass)&.find(id)) %>
|
529
548
|
<h3>for <%= link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination.name.underscore.tr('/', '_')\}_path\".to_sym, id) %></h3><%
|
530
549
|
end
|
@@ -534,30 +553,33 @@ if (headerTop) {
|
|
534
553
|
<br>
|
535
554
|
<table id=\"headerTop\">
|
536
555
|
<table id=\"#{table_name}\">
|
537
|
-
<thead><tr>#{
|
556
|
+
<thead><tr>#{"<th x-order=\"#{pk.join(',')}\"></th>" if pk.present?}<%
|
538
557
|
col_order = []
|
539
558
|
@#{table_name}.columns.each do |col|
|
540
559
|
next if (#{(pk || []).inspect}.include?(col_name = col.name) && col.type == :integer && !bts.key?(col_name)) ||
|
541
560
|
::Brick.config.metadata_columns.include?(col_name) || poly_cols.include?(col_name)
|
542
561
|
|
543
562
|
col_order << col_name
|
544
|
-
%><th<%= \" title
|
563
|
+
%><th<%= \" title=\\\"#\{col.comment}\\\"\".html_safe if col.respond_to?(:comment) && !col.comment.blank? %><%
|
545
564
|
if (bt = bts[col_name]) %>
|
546
|
-
|
565
|
+
<%= \" x-order=\\\"#\{bt.first}\\\"\".html_safe if true # Currently we always allow click to sort
|
566
|
+
%>>BT <%
|
547
567
|
bt[1].each do |bt_pair| %><%=
|
548
568
|
bt_pair.first.bt_link(bt.first) %> <%
|
549
569
|
end %><%
|
550
|
-
else %><%=
|
551
|
-
col_name %><%
|
570
|
+
else %><%= \" x-order=\\\"#\{col_name}\\\"\".html_safe if true # Currently we always allow click to sort
|
571
|
+
%>><%= col_name %><%
|
552
572
|
end
|
553
573
|
%></th><%
|
554
574
|
end
|
555
575
|
# Consider getting the name from the association -- h.first.name -- if a more \"friendly\" alias should be used for a screwy table name
|
556
576
|
%>#{hms_headers.map do |h|
|
577
|
+
# Currently we always allow click to sort
|
578
|
+
"<th#{" x-order=\"#{h.first.name}\"" if true}>" +
|
557
579
|
if h.first.options[:through] && !h.first.through_reflection
|
558
|
-
"
|
580
|
+
"#{h[1]} #{h[2]} %></th>" # %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
|
559
581
|
else
|
560
|
-
"
|
582
|
+
"#{h[1]} <%= link_to('#{h[2]}', #{h.first.klass.name.underscore.tr('/', '_').pluralize}_path) %></th>"
|
561
583
|
end
|
562
584
|
end.join
|
563
585
|
}</tr></thead>
|
@@ -645,7 +667,7 @@ end
|
|
645
667
|
<tr>
|
646
668
|
<% next if (#{(pk || []).inspect}.include?(k) && !bts.key?(k)) ||
|
647
669
|
::Brick.config.metadata_columns.include?(k) %>
|
648
|
-
<th class=\"show-field\"<%= \" title
|
670
|
+
<th class=\"show-field\"<%= \" title=\\\"#\{col.comment}\\\"\".html_safe if col.respond_to?(:comment) && !col.comment.blank? %>>
|
649
671
|
<% has_fields = true
|
650
672
|
if (bt = bts[k])
|
651
673
|
# Add a final member in this array with descriptive options to be used in <select> drop-downs
|
@@ -716,9 +738,14 @@ end
|
|
716
738
|
<% when :uuid %>
|
717
739
|
<%=
|
718
740
|
# Postgres naturally uses the +uuid_generate_v4()+ function from the uuid-ossp extension
|
719
|
-
# If it's not yet enabled then:
|
741
|
+
# If it's not yet enabled then: create extension \"uuid-ossp\";
|
720
742
|
# ActiveUUID gem created a new :uuid type
|
721
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 %>
|
722
749
|
<% when :binary, :primary_key %>
|
723
750
|
<% end %>
|
724
751
|
<% end %>
|
@@ -776,6 +803,13 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
|
|
776
803
|
</script>
|
777
804
|
<% end %>
|
778
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
|
+
location.href = changeout(location.href, \"_brick_order\", this.getAttribute(\"x-order\"));
|
811
|
+
});
|
812
|
+
});
|
779
813
|
document.querySelectorAll(\"input, select\").forEach(function (inp) {
|
780
814
|
var origVal = getInpVal(),
|
781
815
|
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 ||= {}
|
@@ -168,8 +174,12 @@ module Brick
|
|
168
174
|
skip_hms = {}
|
169
175
|
associatives = hms.each_with_object({}) do |hmt, s|
|
170
176
|
if (through = hmt.last.options[:through])
|
171
|
-
skip_hms[through] = nil
|
172
|
-
|
177
|
+
skip_hms[through] = nil # if hms[through]
|
178
|
+
# binding.pry if !hms[through]
|
179
|
+
# End up with a hash of HMT names pointing to join-table associations
|
180
|
+
# Last part was: hmt.last.name
|
181
|
+
# Changed up because looking for: hms[:issue_issue_duplicates]
|
182
|
+
s[hmt.first] = hms[through] # || hms["#{(opt = hmt.last.options)[:through].to_s.singularize}_#{opt[:source].to_s.pluralize}".to_sym]
|
173
183
|
elsif hmt.last.inverse_of.nil?
|
174
184
|
puts "SKIPPING #{hmt.last.name.inspect}"
|
175
185
|
# %%% If we don't do this then below associative.name will find that associative is nil
|
@@ -273,6 +283,11 @@ module Brick
|
|
273
283
|
end
|
274
284
|
end
|
275
285
|
|
286
|
+
# @api public
|
287
|
+
def order=(value)
|
288
|
+
Brick.config.order = value
|
289
|
+
end
|
290
|
+
|
276
291
|
# Skip creating a has_many association for these
|
277
292
|
# (Uses the same exact three-part format as would define an additional_reference)
|
278
293
|
# @api public
|
@@ -447,7 +462,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
447
462
|
::Brick.relations.each do |rel_name, v|
|
448
463
|
rel_name = rel_name.split('.').map(&:underscore)
|
449
464
|
schema_names = rel_name[0..-2]
|
450
|
-
schema_names.shift if ::Brick.
|
465
|
+
schema_names.shift if ::Brick.apartment_multitenant && schema_names.first == Apartment.default_schema
|
451
466
|
k = rel_name.last
|
452
467
|
unless existing_controllers.key?(controller_name = k.pluralize)
|
453
468
|
options = {}
|
@@ -477,7 +492,8 @@ require 'brick/version_number'
|
|
477
492
|
# Older versions of ActiveRecord would only show more serious error information from "panic" level, which is
|
478
493
|
# a level only available in Postgres 12 and older. This patch will allow older and newer versions of Postgres
|
479
494
|
# to work along with fairly old versions of Rails.
|
480
|
-
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')
|
481
497
|
::Brick::Util._patch_require(
|
482
498
|
'active_record/connection_adapters/postgresql_adapter.rb', '/activerecord', ["'panic'", "'error'"]
|
483
499
|
)
|
@@ -485,13 +501,28 @@ end
|
|
485
501
|
|
486
502
|
require 'active_record'
|
487
503
|
require 'active_record/relation'
|
488
|
-
|
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')
|
489
507
|
|
490
508
|
# Rake tasks
|
491
509
|
class Railtie < Rails::Railtie
|
492
510
|
Dir.glob("#{File.expand_path(__dir__)}/brick/tasks/**/*.rake").each { |task| load task }
|
493
511
|
end
|
494
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
|
+
|
495
526
|
# Major compatibility fixes for ActiveRecord < 4.2
|
496
527
|
# ================================================
|
497
528
|
ActiveSupport.on_load(:active_record) do
|
@@ -506,6 +537,15 @@ ActiveSupport.on_load(:active_record) do
|
|
506
537
|
end
|
507
538
|
end
|
508
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
|
509
549
|
end
|
510
550
|
|
511
551
|
# Rails < 4.0 cannot do #find_by, #find_or_create_by, or do #pluck on multiple columns, so here are the patches:
|
@@ -711,58 +751,60 @@ ActiveSupport.on_load(:active_record) do
|
|
711
751
|
end
|
712
752
|
end
|
713
753
|
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
[info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
|
725
|
-
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) }
|
726
764
|
end
|
727
765
|
end
|
728
|
-
|
729
|
-
|
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
|
730
775
|
end
|
731
|
-
|
732
|
-
|
776
|
+
end
|
777
|
+
module QueryMethods
|
778
|
+
attr_writer :left_outer_joins_values
|
779
|
+
def left_outer_joins_values
|
780
|
+
@left_outer_joins_values ||= []
|
733
781
|
end
|
734
|
-
module QueryMethods
|
735
|
-
attr_writer :left_outer_joins_values
|
736
|
-
def left_outer_joins_values
|
737
|
-
@left_outer_joins_values ||= []
|
738
|
-
end
|
739
782
|
|
740
|
-
|
741
|
-
|
783
|
+
def left_outer_joins(*args)
|
784
|
+
check_if_method_has_arguments!(:left_outer_joins, args)
|
742
785
|
|
743
|
-
|
744
|
-
|
786
|
+
args.compact!
|
787
|
+
args.flatten!
|
745
788
|
|
746
|
-
|
747
|
-
|
789
|
+
spawn.left_outer_joins!(*args)
|
790
|
+
end
|
748
791
|
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
792
|
+
def left_outer_joins!(*args) # :nodoc:
|
793
|
+
self.left_outer_joins_values += args
|
794
|
+
self
|
795
|
+
end
|
753
796
|
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
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'
|
762
804
|
end
|
763
|
-
|
764
|
-
build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
|
765
805
|
end
|
806
|
+
|
807
|
+
build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
|
766
808
|
end
|
767
809
|
end
|
768
810
|
# (End of left_outer_joins support)
|
@@ -784,7 +826,8 @@ ActiveSupport.on_load(:active_record) do
|
|
784
826
|
end
|
785
827
|
|
786
828
|
# Do this earlier because stuff here gets mixed into JoinDependency::JoinAssociation and AssociationScope
|
787
|
-
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
|
788
831
|
# Avoid pg gem deprecation warning: "You should use PG::Connection, PG::Result, and PG::Error instead"
|
789
832
|
PGconn = PG::Connection
|
790
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.49
|
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-24 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
|