brick 1.0.139 → 1.0.140
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/compatibility.rb +1 -0
- data/lib/brick/config.rb +8 -0
- data/lib/brick/extensions.rb +89 -24
- data/lib/brick/frameworks/rails/engine.rb +39 -20
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +87 -17
- 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: 995f6ed970029309b465f80193521b91c63335e12400cbb21e0ef0d60fe60dd2
|
4
|
+
data.tar.gz: c610df65f26a9e9bf4f081c8d0172fea6fc69cda350d26cf5287362237e7ceca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 534bde690d45d11d8371d0e666df86badc47879242bcf67a219aeff6dc71fb1ac668b02bba5059f2fceafa63ddb6312cf3e42e993c5ba1de47ebf276abd9bb7c
|
7
|
+
data.tar.gz: 18e0c976540749fbc93fd89be05735d2e9544db37d04a3738bc06e82fdc2811d9478398fa66881d17e9e18cb44e78720f66cc8db74660ddd6b5e23e523ea2db7
|
data/lib/brick/compatibility.rb
CHANGED
@@ -26,6 +26,7 @@ if Object.const_defined?('ActionPack') && !ActionPack.respond_to?(:version)
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
29
|
+
require 'action_view' # Needed for Rails <= 4.0
|
29
30
|
if Object.const_defined?('ActionView') && !ActionView.respond_to?(:version)
|
30
31
|
module ActionView
|
31
32
|
def self.version
|
data/lib/brick/config.rb
CHANGED
@@ -347,6 +347,14 @@ module Brick
|
|
347
347
|
@mutex.synchronize { @not_nullables = columns }
|
348
348
|
end
|
349
349
|
|
350
|
+
def always_load_fields
|
351
|
+
@mutex.synchronize { @always_load_fields || Hash.new { |h, k| h[k] = [] } }
|
352
|
+
end
|
353
|
+
|
354
|
+
def always_load_fields=(field_set)
|
355
|
+
@mutex.synchronize { @always_load_fields = field_set }
|
356
|
+
end
|
357
|
+
|
350
358
|
# Add status page showing all resources and what files have been built out for them
|
351
359
|
def add_status
|
352
360
|
true
|
data/lib/brick/extensions.rb
CHANGED
@@ -82,7 +82,7 @@ module ActiveRecord
|
|
82
82
|
|
83
83
|
def json_column?(col)
|
84
84
|
col.type == :json || ::Brick.config.json_columns[table_name]&.include?(col.name) ||
|
85
|
-
((attr_types = attribute_types[col.name]).respond_to?(:coder) &&
|
85
|
+
(respond_to?(:attribute_types) && (attr_types = attribute_types[col.name]).respond_to?(:coder) &&
|
86
86
|
(attr_types.coder.is_a?(Class) ? attr_types.coder : attr_types.coder&.class)&.name&.end_with?('JSON'))
|
87
87
|
end
|
88
88
|
end
|
@@ -235,7 +235,7 @@ module ActiveRecord
|
|
235
235
|
caches.fetch(obj_name) { caches[obj_name] = this_obj&.send(part.to_sym) }
|
236
236
|
rescue
|
237
237
|
clsnm = part.camelize
|
238
|
-
if (possible = this_obj.class.reflect_on_all_associations.select { |a| a.class_name == clsnm || a.klass.base_class.name == clsnm }.first)
|
238
|
+
if (possible = this_obj.class.reflect_on_all_associations.select { |a| !a.polymorphic? && (a.class_name == clsnm || a.klass.base_class.name == clsnm) }.first)
|
239
239
|
caches[obj_name] = this_obj&.send(possible.name)
|
240
240
|
end
|
241
241
|
end
|
@@ -286,7 +286,7 @@ module ActiveRecord
|
|
286
286
|
assoc_html_name = unless (assoc_name = assoc_name.to_s).camelize == name
|
287
287
|
CGI.escapeHTML(assoc_name)
|
288
288
|
end
|
289
|
-
model_path = ::Rails.application.routes.url_helpers.send("#{_brick_index}_path".to_sym)
|
289
|
+
model_path = ::Rails.application.routes.url_helpers.send("#{_brick_index || table_name}_path".to_sym)
|
290
290
|
model_path << "?#{self.inheritance_column}=#{self.name}" if self != base_class
|
291
291
|
av_class = Class.new.extend(ActionView::Helpers::UrlHelper)
|
292
292
|
av_class.extend(ActionView::Helpers::TagHelper) if ActionView.version < ::Gem::Version.new('7')
|
@@ -296,6 +296,8 @@ module ActiveRecord
|
|
296
296
|
|
297
297
|
# Providing a relation object allows auto-modules built from table name prefixes to work
|
298
298
|
def self._brick_index(mode = nil, separator = '_', relation = nil)
|
299
|
+
return if abstract_class
|
300
|
+
|
299
301
|
tbl_parts = ((mode == :singular) ? table_name.singularize : table_name).split('.')
|
300
302
|
tbl_parts.shift if ::Brick.apartment_multitenant && tbl_parts.length > 1 && tbl_parts.first == ::Brick.apartment_default_tenant
|
301
303
|
if (aps = relation&.fetch(:auto_prefixed_schema, nil)) && tbl_parts.last.start_with?(aps)
|
@@ -352,7 +354,7 @@ module ActiveRecord
|
|
352
354
|
pieces, my_dsl = brick_parse_dsl(join_array, [], translations, false, cc, true)
|
353
355
|
_br_cust_cols[k] = [pieces, my_dsl, fk_col]
|
354
356
|
end
|
355
|
-
bts, hms, associatives = ::Brick.get_bts_and_hms(self)
|
357
|
+
bts, hms, associatives = ::Brick.get_bts_and_hms(self, true)
|
356
358
|
bts.each do |_k, bt|
|
357
359
|
next if bt[2] # Polymorphic?
|
358
360
|
|
@@ -431,8 +433,16 @@ module ActiveRecord
|
|
431
433
|
[order_by, order_by_txt]
|
432
434
|
end
|
433
435
|
|
434
|
-
def self.brick_select(params
|
435
|
-
|
436
|
+
def self.brick_select(*args, params: {}, brick_col_names: false, **kwargs)
|
437
|
+
selects = if args[0].is_a?(Array)
|
438
|
+
other_args = args[1..-1]
|
439
|
+
args[0]
|
440
|
+
else
|
441
|
+
other_args = []
|
442
|
+
args
|
443
|
+
end
|
444
|
+
(relation = all).brick_select(selects, *other_args,
|
445
|
+
params: params, brick_col_names: brick_col_names, **kwargs)
|
436
446
|
relation.select(selects)
|
437
447
|
end
|
438
448
|
|
@@ -472,10 +482,29 @@ module ActiveRecord
|
|
472
482
|
@brick_links ||= { '' => table_name }
|
473
483
|
end
|
474
484
|
|
475
|
-
def brick_select(
|
476
|
-
join_array
|
477
|
-
cust_col_override
|
478
|
-
|
485
|
+
def brick_select(*args, params: {}, order_by: nil, translations: {},
|
486
|
+
join_array: ::Brick::JoinArray.new,
|
487
|
+
cust_col_override: nil,
|
488
|
+
brick_col_names: true)
|
489
|
+
selects = args[0].is_a?(Array) ? args[0] : args
|
490
|
+
if selects.present? && cust_col_override.nil? # See if there's any fancy ones in the select list
|
491
|
+
idx = 0
|
492
|
+
while idx < selects.length
|
493
|
+
v = selects[idx]
|
494
|
+
if v.is_a?(String) && v.index('.')
|
495
|
+
# No prefixes and not polymorphic
|
496
|
+
pieces = self.brick_parse_dsl(join_array, [], translations, false, dsl = "[#{v}]")
|
497
|
+
(cust_col_override ||= {})[v.tr('.', '_').to_sym] = [pieces, dsl, true]
|
498
|
+
selects.delete_at(idx)
|
499
|
+
else
|
500
|
+
idx += 1
|
501
|
+
end
|
502
|
+
end
|
503
|
+
elsif selects.is_a?(Hash) && params.empty? && cust_col_override.nil? # Make sense of things if they've passed in only params
|
504
|
+
params = selects
|
505
|
+
selects = []
|
506
|
+
end
|
507
|
+
is_add_bts = is_add_hms = !cust_col_override
|
479
508
|
|
480
509
|
# Build out cust_cols, bt_descrip and hm_counts now so that they are available on the
|
481
510
|
# model early in case the user wants to do an ORDER BY based on any of that.
|
@@ -487,6 +516,7 @@ module ActiveRecord
|
|
487
516
|
is_distinct = nil
|
488
517
|
wheres = {}
|
489
518
|
params.each do |k, v|
|
519
|
+
k = k.to_s # Rails < 4.2 comes in as a symbol
|
490
520
|
next if ['_brick_schema', '_brick_order',
|
491
521
|
'_brick_erd', '_brick_exclude', '_brick_unexclude',
|
492
522
|
'_brick_page', '_brick_page_size', '_brick_offset', '_brick_limit',
|
@@ -544,9 +574,19 @@ module ActiveRecord
|
|
544
574
|
"'<#{typ.end_with?('_TYP') ? typ[0..-5] : typ}>' AS #{col.name}"
|
545
575
|
end
|
546
576
|
end
|
577
|
+
else # Having some select columns chosen, add any missing always_load_fields for this model
|
578
|
+
::Brick.config.always_load_fields.fetch(klass.name, nil)&.each do |alf|
|
579
|
+
selects << alf unless selects.include?(alf)
|
580
|
+
end
|
547
581
|
end
|
548
582
|
|
549
|
-
|
583
|
+
if join_array.present?
|
584
|
+
if ActiveRecord.version < Gem::Version.new('4.2')
|
585
|
+
joins!(join_array)
|
586
|
+
else
|
587
|
+
left_outer_joins!(join_array)
|
588
|
+
end
|
589
|
+
end
|
550
590
|
|
551
591
|
# If it's a CollectionProxy (which inherits from Relation) then need to dig out the
|
552
592
|
# core Relation object which is found in the association scope.
|
@@ -594,11 +634,12 @@ module ActiveRecord
|
|
594
634
|
# Deal with the conflict if there are two parts in the custom column named the same,
|
595
635
|
# "category.name" and "product.name" for instance will end up with aliases of "name"
|
596
636
|
# and "product__name".
|
637
|
+
col_prefix = 'br_cc_' if brick_col_names
|
597
638
|
if (cc_part_idx = cc_part.length - 1).zero?
|
598
|
-
col_alias = "
|
639
|
+
col_alias = "#{col_prefix}#{k}__#{table_name.tr('.', '_')}_#{cc_part.first}"
|
599
640
|
else
|
600
641
|
while cc_part_idx > 0 &&
|
601
|
-
(col_alias = "
|
642
|
+
(col_alias = "#{col_prefix}#{k}__#{cc_part[cc_part_idx..-1].map(&:to_s).join('__').tr('.', '_')}") &&
|
602
643
|
used_col_aliases.key?(col_alias)
|
603
644
|
cc_part_idx -= 1
|
604
645
|
end
|
@@ -611,7 +652,7 @@ module ActiveRecord
|
|
611
652
|
key_tbl_name = tbl_name
|
612
653
|
cc_part_idx = cc_part.length - 1
|
613
654
|
while cc_part_idx > 0 &&
|
614
|
-
(key_alias = "
|
655
|
+
(key_alias = "#{col_prefix}#{k}__#{(cc_part[cc_part_idx..-2] + [dest_pk]).map(&:to_s).join('__')}") &&
|
615
656
|
key_alias != col_alias && # We break out if this key alias does exactly match the col_alias
|
616
657
|
used_col_aliases.key?(key_alias)
|
617
658
|
cc_part_idx -= 1
|
@@ -781,7 +822,7 @@ module ActiveRecord
|
|
781
822
|
|
782
823
|
pri_tbl = hm.active_record
|
783
824
|
pri_key = hm.options[:primary_key] || pri_tbl.primary_key
|
784
|
-
|
825
|
+
if hm.active_record.abstract_class || hm.active_record.column_names.exclude?(pri_key)
|
785
826
|
# %%% When this gets hit then if an attempt is made to display the ERD, it might end up being blank
|
786
827
|
nix << k
|
787
828
|
next
|
@@ -907,21 +948,43 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
|
|
907
948
|
# Get foreign keys for anything marked to be auto-preloaded, or a self-referencing JOIN
|
908
949
|
klass_cols = klass.column_names
|
909
950
|
reflect_on_all_associations.each do |a|
|
910
|
-
selects << a.foreign_key if a.belongs_to? &&
|
911
|
-
(
|
951
|
+
selects << a.foreign_key if a.belongs_to? &&
|
952
|
+
(preload_values.include?(a.name) ||
|
953
|
+
(!a.options[:polymorphic] && a.klass == klass && klass_cols.include?(a.foreign_key))
|
954
|
+
)
|
912
955
|
end
|
913
956
|
# ActiveStorage compatibility
|
914
957
|
selects << 'service_name' if klass.name == 'ActiveStorage::Blob' && ActiveStorage::Blob.columns_hash.key?('service_name')
|
915
958
|
selects << 'blob_id' if klass.name == 'ActiveStorage::Attachment' && ActiveStorage::Attachment.columns_hash.key?('blob_id')
|
916
959
|
pieces, my_dsl = klass.brick_parse_dsl(join_array = ::Brick::JoinArray.new, [], translations = {}, false, nil, true)
|
917
960
|
brick_select(
|
918
|
-
|
919
|
-
{ '_br' => (descrip_cols = [pieces, my_dsl]) }
|
961
|
+
selects, where_values_hash, nil, translations: translations, join_array: join_array,
|
962
|
+
cust_col_override: { '_br' => (descrip_cols = [pieces, my_dsl]) }
|
920
963
|
)
|
921
|
-
order_values = klass.primary_key
|
964
|
+
order_values = "#{klass.table_name}.#{klass.primary_key}"
|
922
965
|
[self.select(selects), descrip_cols]
|
923
966
|
end
|
924
967
|
|
968
|
+
def brick_uniq
|
969
|
+
begin
|
970
|
+
uniq
|
971
|
+
rescue ActiveModel::MissingAttributeError => e
|
972
|
+
# If this model has an #after_initialize then it might try to reference attributes we haven't brought in
|
973
|
+
if (err_msg = e.message).start_with?('missing attribute: ') &&
|
974
|
+
klass.column_names.include?(col_name = e.message[19..-1])
|
975
|
+
(dup_rel = dup).select_values << col_name
|
976
|
+
ret = dup_rel.brick_uniq
|
977
|
+
puts "*** WARNING: Missing field!
|
978
|
+
Might want to add this in your brick.rb:
|
979
|
+
::Brick.always_load_fields = { #{klass.name.inspect} => [#{col_name.inspect}]}"
|
980
|
+
::Brick.config.always_load_fields[klass.name] << col_name
|
981
|
+
ret
|
982
|
+
else
|
983
|
+
[]
|
984
|
+
end
|
985
|
+
end
|
986
|
+
end
|
987
|
+
|
925
988
|
private
|
926
989
|
|
927
990
|
def shift_or_first(ary)
|
@@ -935,6 +998,8 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
|
|
935
998
|
|
936
999
|
alias _brick_find_sti_class find_sti_class
|
937
1000
|
def find_sti_class(type_name)
|
1001
|
+
return if type_name.is_a?(Numeric)
|
1002
|
+
|
938
1003
|
if ::Brick.sti_models.key?(type_name ||= name)
|
939
1004
|
::Brick.sti_models[type_name].fetch(:base, nil) || _brick_find_sti_class(type_name)
|
940
1005
|
else
|
@@ -1606,7 +1671,7 @@ class Object
|
|
1606
1671
|
|
1607
1672
|
# Add a hash for the inline style to the content-security-policy if one is present
|
1608
1673
|
self.define_method(:add_csp_hash) do |style_value = nil|
|
1609
|
-
if (csp = request.content_security_policy)
|
1674
|
+
if request.respond_to?(:content_security_policy) && (csp = request.content_security_policy)
|
1610
1675
|
if (cspd = csp.directives.fetch('style-src'))
|
1611
1676
|
if style_value
|
1612
1677
|
if (nonce = ::ActionDispatch::ContentSecurityPolicy::Request::NONCE)
|
@@ -1850,9 +1915,9 @@ class Object
|
|
1850
1915
|
|
1851
1916
|
ar_relation = ActiveRecord.version < Gem::Version.new('4') ? real_model.preload : real_model.all
|
1852
1917
|
params['_brick_is_api'] = true if (is_api = request.format == :js || current_api_root)
|
1853
|
-
@_brick_params = ar_relation.brick_select(
|
1854
|
-
translations = {},
|
1855
|
-
join_array = ::Brick::JoinArray.new)
|
1918
|
+
@_brick_params = ar_relation.brick_select((selects ||= []), params: params, order_by: order_by,
|
1919
|
+
translations: (translations = {}),
|
1920
|
+
join_array: (join_array = ::Brick::JoinArray.new))
|
1856
1921
|
|
1857
1922
|
if is_api # Asking for JSON?
|
1858
1923
|
# Apply column renaming
|
@@ -209,11 +209,15 @@ function linkSchemas() {
|
|
209
209
|
# paths['app/models'] << 'lib/brick/frameworks/active_record/models'
|
210
210
|
config.brick = ActiveSupport::OrderedOptions.new
|
211
211
|
ActiveSupport.on_load(:before_initialize) do |app|
|
212
|
-
::Rails.application.reloader
|
212
|
+
if ::Rails.application.respond_to?(:reloader)
|
213
|
+
::Rails.application.reloader.to_prepare { Module.class_exec &::Brick::ADD_CONST_MISSING }
|
214
|
+
else # For Rails < 5.0, just load it once at the start
|
215
|
+
Module.class_exec &::Brick::ADD_CONST_MISSING
|
216
|
+
end
|
217
|
+
require 'brick/join_array'
|
213
218
|
is_development = (ENV['RAILS_ENV'] || ENV['RACK_ENV']) == 'development'
|
214
219
|
::Brick.enable_models = app.config.brick.fetch(:enable_models, true)
|
215
220
|
::Brick.enable_controllers = app.config.brick.fetch(:enable_controllers, is_development)
|
216
|
-
require 'brick/join_array' if ::Brick.enable_controllers?
|
217
221
|
::Brick.enable_views = app.config.brick.fetch(:enable_views, is_development)
|
218
222
|
::Brick.enable_routes = app.config.brick.fetch(:enable_routes, is_development)
|
219
223
|
::Brick.skip_database_views = app.config.brick.fetch(:skip_database_views, false)
|
@@ -599,12 +603,14 @@ window.addEventListener(\"popstate\", linkSchemas);
|
|
599
603
|
# ====================================
|
600
604
|
if ::Brick.enable_views?
|
601
605
|
# Add the params to the lookup_context so that we have context about STI classes when setting @_brick_model
|
602
|
-
ActionView
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
606
|
+
if ActionView.const_defined?('ViewPaths')
|
607
|
+
ActionView::ViewPaths.class_exec do
|
608
|
+
alias :_brick_lookup_context :lookup_context
|
609
|
+
def lookup_context(*args)
|
610
|
+
ret = _brick_lookup_context(*args)
|
611
|
+
@_lookup_context.instance_variable_set(:@_brick_req_params, params)
|
612
|
+
ret
|
613
|
+
end
|
608
614
|
end
|
609
615
|
end
|
610
616
|
|
@@ -679,11 +685,11 @@ window.addEventListener(\"popstate\", linkSchemas);
|
|
679
685
|
rescue StandardError => e
|
680
686
|
# Search through the routes to confirm that something might match (Devise stuff for instance, which has its own view templates),
|
681
687
|
# and bubble the same exception (probably an ActionView::MissingTemplate) if a legitimate option is found.
|
682
|
-
raise if ::Rails.application.routes.set.find { |x| args[1].include?(x.defaults[:controller]) && args[0] == x.defaults[:action] }
|
688
|
+
raise if ::Rails.application.routes.set.find { |x| args[1].include?(x.defaults[:controller]) && args[0] == x.defaults[:action] } &&
|
689
|
+
ActionView.version >= ::Gem::Version.new('5.0')
|
683
690
|
|
684
691
|
find_template_err = e
|
685
692
|
end
|
686
|
-
# Used to also have: ActionView.version < ::Gem::Version.new('5.0') &&
|
687
693
|
model_name = set_brick_model(args, @_brick_req_params)&.name
|
688
694
|
end
|
689
695
|
|
@@ -741,7 +747,7 @@ window.addEventListener(\"popstate\", linkSchemas);
|
|
741
747
|
(hm_fk_name.is_a?(String) && hm_fk_name.include?('.')) # HMT? (Could do a better check for this)
|
742
748
|
predicates = path_keys(hm_assoc, hm_fk_name, pk).map do |k, v|
|
743
749
|
if v == '[sti_type]'
|
744
|
-
"'#{k}': (@#{obj_name}.#{hm_assoc.active_record.inheritance_column})
|
750
|
+
"'#{k}': (@#{obj_name}.#{hm_assoc.active_record.inheritance_column})&.constantize&.base_class&.name"
|
745
751
|
else
|
746
752
|
v.is_a?(String) ? "'#{k}': '#{v}'" : "'#{k}': @#{obj_name}.#{v}"
|
747
753
|
end
|
@@ -1462,7 +1468,7 @@ end
|
|
1462
1468
|
#{schema_options}" if schema_options}
|
1463
1469
|
<select id=\"tbl\">#{table_options}</select>
|
1464
1470
|
<h1>Status</h1>
|
1465
|
-
<table id=\"
|
1471
|
+
<table id=\"resourceName\" class=\"shadow\"><thead><tr>
|
1466
1472
|
<th>Resource</th>
|
1467
1473
|
<th>Table</th>
|
1468
1474
|
<th>Migration</th>
|
@@ -1481,7 +1487,11 @@ end
|
|
1481
1487
|
kls = Object.const_get(::Brick.relations.fetch(r[0], nil)&.fetch(:class_name, nil))
|
1482
1488
|
rescue
|
1483
1489
|
end
|
1484
|
-
kls.is_a?(Class)
|
1490
|
+
if kls.is_a?(Class) && (path_helper = respond_to?(bi_path = \"#\{kls._brick_index}_path\".to_sym) ? bi_path : nil)
|
1491
|
+
link_to(r[0], send(path_helper))
|
1492
|
+
else
|
1493
|
+
r[0]
|
1494
|
+
end %></td>
|
1485
1495
|
<td<%= if r[1]
|
1486
1496
|
' class=\"orphan\"' unless ::Brick.relations.key?(r[1])
|
1487
1497
|
else
|
@@ -1541,9 +1551,11 @@ end
|
|
1541
1551
|
base_model = (model = (obj = @#{obj_name})&.class).base_class
|
1542
1552
|
see_all_path = send(\"#\{base_model._brick_index}_path\")
|
1543
1553
|
#{(inh_col = @_brick_model.inheritance_column).present? &&
|
1544
|
-
" if obj.respond_to?(:#{inh_col}) && (model_name = @#{obj_name}.#{inh_col})
|
1554
|
+
" if obj.respond_to?(:#{inh_col}) && (model_name = @#{obj_name}.#{inh_col}) &&
|
1555
|
+
!model_name.is_a?(Numeric) && model_name != base_model.name
|
1545
1556
|
see_all_path << \"?#{inh_col}=#\{model_name}\"
|
1546
|
-
end
|
1557
|
+
end
|
1558
|
+
model_name = base_model.name if model_name.is_a?(Numeric)"}
|
1547
1559
|
page_title = (\"#\{model_name ||= model.name}: #\{obj&.brick_descrip || controller_name}\")
|
1548
1560
|
%></title>
|
1549
1561
|
</head>
|
@@ -1636,7 +1648,7 @@ end
|
|
1636
1648
|
if bt.length < 4
|
1637
1649
|
bt << (option_detail = [[\"(No #\{bt_name\} chosen)\", '^^^brick_NULL^^^']])
|
1638
1650
|
# %%% Accommodate composite keys for obj.pk at the end here
|
1639
|
-
collection, descrip_cols = bt_class&.order(obj_pk = bt_class.primary_key)&.brick_list
|
1651
|
+
collection, descrip_cols = bt_class&.order(Arel.sql(\"#\{bt_class.table_name}.#\{obj_pk = bt_class.primary_key}\"))&.brick_list
|
1640
1652
|
collection&.each do |obj|
|
1641
1653
|
option_detail << [
|
1642
1654
|
obj.brick_descrip(
|
@@ -1717,14 +1729,21 @@ end
|
|
1717
1729
|
else # We get an array back when AR < 4.2
|
1718
1730
|
collection2 = collection.to_a.compact
|
1719
1731
|
end
|
1720
|
-
collection2 = collection2.
|
1732
|
+
collection2 = collection2.brick_uniq
|
1721
1733
|
if collection2.empty? %>
|
1722
1734
|
<tr><td>(none)</td></tr>
|
1723
1735
|
<% else
|
1724
1736
|
collection2.each do |br_#{hm_singular_name}| %>
|
1725
|
-
<tr><td><%= br_descrip = br_#{hm_singular_name}.
|
1726
|
-
|
1727
|
-
|
1737
|
+
<tr><td><%= br_descrip = if br_#{hm_singular_name}.respond_to?(descrip_cols&.first&.first&.last)
|
1738
|
+
br_#{hm_singular_name}.brick_descrip(
|
1739
|
+
descrip_cols&.first&.map { |col| br_#{hm_singular_name}.send(col.last) }
|
1740
|
+
)
|
1741
|
+
else # If the HM association has a scope, might not have picked up our SELECT detail
|
1742
|
+
pks = (klass = br_#{hm_singular_name}.class).primary_key
|
1743
|
+
pks = [pks] unless pks.is_a?(Array)
|
1744
|
+
pks.map! { |pk| br_#{hm_singular_name}.send(pk).to_s }
|
1745
|
+
\"#\{klass.name} ##\{pks.join(', ')}\"
|
1746
|
+
end
|
1728
1747
|
link_to(br_descrip, #{hm.first.klass._brick_index(:singular)}_path(slashify(br_#{obj_pk}))) %></td></tr>
|
1729
1748
|
<% end %>
|
1730
1749
|
<% end %>
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -56,7 +56,7 @@ if (is_ruby_2_7 = (ruby_version = ::Gem::Version.new(RUBY_VERSION)) >= ::Gem::Ve
|
|
56
56
|
# end
|
57
57
|
end
|
58
58
|
|
59
|
-
# Add
|
59
|
+
# Add left_outer_joins! to Associations::JoinDependency and Relation::QueryMethods
|
60
60
|
if ActiveRecord.version >= ::Gem::Version.new('4') && ActiveRecord.version < ::Gem::Version.new('5')
|
61
61
|
::Brick::Util._patch_require(
|
62
62
|
'active_record/associations/join_dependency.rb', '/activerecord', # /associations
|
@@ -84,7 +84,7 @@ if ActiveRecord.version >= ::Gem::Version.new('4') && ActiveRecord.version < ::G
|
|
84
84
|
['build_joins(arel, joins_values.flatten) unless joins_values.empty?',
|
85
85
|
"build_joins(arel, joins_values.flatten) unless joins_values.empty?
|
86
86
|
build_left_outer_joins(arel, left_outer_joins_values.flatten) unless left_outer_joins_values.empty?"
|
87
|
-
|
87
|
+
],
|
88
88
|
# Change 2 - Line 992
|
89
89
|
["raise 'unknown class: %s' % join.class.name
|
90
90
|
end
|
@@ -99,8 +99,8 @@ if ActiveRecord.version >= ::Gem::Version.new('4') && ActiveRecord.version < ::G
|
|
99
99
|
def build_join_query(manager, buckets, join_type)"
|
100
100
|
],
|
101
101
|
# Change 3 - Line 1012
|
102
|
-
|
103
|
-
|
102
|
+
['s = join_dependency.join_constraints stashed_association_joins',
|
103
|
+
's = join_dependency.join_constraints stashed_association_joins, join_type'
|
104
104
|
]
|
105
105
|
],
|
106
106
|
:QueryMethods
|
@@ -202,7 +202,11 @@ module Brick
|
|
202
202
|
end
|
203
203
|
end
|
204
204
|
|
205
|
-
def get_bts_and_hms(model)
|
205
|
+
def get_bts_and_hms(model, recalculate = nil)
|
206
|
+
if !recalculate && (ret = model.instance_variable_get(:@_brick_bts_and_hms))
|
207
|
+
return ret
|
208
|
+
end
|
209
|
+
|
206
210
|
model_cols = model.columns_hash
|
207
211
|
pk_type = if (mpk = model.primary_key).is_a?(Array)
|
208
212
|
# Composite keys should really use: model.primary_key.map { |pk_part| model_cols[pk_part].type }
|
@@ -228,8 +232,24 @@ module Brick
|
|
228
232
|
# This will come up when using Devise invitable when invited_by_class_name is not
|
229
233
|
# specified because in that circumstance it adds a polymorphic :invited_by association,
|
230
234
|
# along with appropriate invited_by_type and invited_by_id columns.
|
231
|
-
|
232
|
-
|
235
|
+
|
236
|
+
# See if any currently-loaded models have a has_many association over to this polymorphic belongs_to
|
237
|
+
hm_models = ActiveRecord::Base.descendants.select do |m|
|
238
|
+
m.reflect_on_all_associations.any? { |assoc| !assoc.belongs_to? && assoc.options[:as]&.to_sym == a.name }
|
239
|
+
end
|
240
|
+
# No need to include subclassed models if their parent is already in the list
|
241
|
+
hm_models.reject! { |m| hm_models.any? { |parent| parent != m && m < parent } }
|
242
|
+
if hm_models.empty?
|
243
|
+
puts "Missing any real indication as to which models \"has_many\" this polymorphic BT in model #{a.active_record.name}:"
|
244
|
+
puts " belongs_to :#{a.name}, polymorphic: true"
|
245
|
+
else
|
246
|
+
puts "Having analysed all currently-loaded models to infer the various polymorphic has_many associations for #{model.name}, here are the current results:"
|
247
|
+
puts "::Brick.polymorphics = { \"#{model.table_name}.#{a.name}\" =>
|
248
|
+
#{hm_models.map(&:name).inspect}
|
249
|
+
}"
|
250
|
+
puts 'If you add the above to your brick.rb, it will "cement" these options into place, and avoid this lookup process.'
|
251
|
+
s.first[a.foreign_key.to_s] = [a.name, hm_models, true]
|
252
|
+
end
|
233
253
|
end
|
234
254
|
else
|
235
255
|
bt_key = a.foreign_key.is_a?(Array) ? a.foreign_key : a.foreign_key.to_s
|
@@ -279,7 +299,7 @@ module Brick
|
|
279
299
|
end
|
280
300
|
end
|
281
301
|
skip_hms.each { |k, _v| hms.delete(k) }
|
282
|
-
[bts, hms]
|
302
|
+
model.instance_variable_set(:@_brick_bts_and_hms, [bts, hms]) # Cache and return this result
|
283
303
|
end
|
284
304
|
|
285
305
|
def exclude_column(table, col)
|
@@ -523,6 +543,10 @@ module Brick
|
|
523
543
|
Brick.config.license = key
|
524
544
|
end
|
525
545
|
|
546
|
+
def always_load_fields=(field_set)
|
547
|
+
Brick.config.always_load_fields = field_set
|
548
|
+
end
|
549
|
+
|
526
550
|
# Load additional references (virtual foreign keys)
|
527
551
|
# This is attempted early if a brick initialiser file is found, and then again as a failsafe at the end of our engine's initialisation
|
528
552
|
# %%% Maybe look for differences the second time 'round and just add new stuff instead of entirely deferring
|
@@ -1294,7 +1318,8 @@ ActiveSupport.on_load(:active_record) do
|
|
1294
1318
|
# rubocop:enable Lint/ConstantDefinitionInBlock
|
1295
1319
|
|
1296
1320
|
arsc = ::ActiveRecord::StatementCache
|
1297
|
-
if is_ruby_2_7 &&
|
1321
|
+
if is_ruby_2_7 && arsc.respond_to?(:create) &&
|
1322
|
+
(params = arsc.method(:create).parameters).length == 2 && params.last == [:opt, :block]
|
1298
1323
|
arsc.class_exec do
|
1299
1324
|
def self.create(connection, callable = nil, &block)
|
1300
1325
|
relation = (callable || block).call ::ActiveRecord::StatementCache::Params.new
|
@@ -1387,7 +1412,7 @@ ActiveSupport.on_load(:active_record) do
|
|
1387
1412
|
# def aliased_table_for(table_name, aliased_name, type_caster)
|
1388
1413
|
|
1389
1414
|
class ActiveRecord::Associations::JoinDependency
|
1390
|
-
if JoinBase.instance_method(:initialize).arity
|
1415
|
+
if JoinBase.instance_method(:initialize).arity < 3 # Older ActiveRecord <= 5.1?
|
1391
1416
|
def initialize(base, associations, joins, eager_loading: true)
|
1392
1417
|
araat = ::ActiveRecord::Associations::AliasTracker
|
1393
1418
|
if araat.respond_to?(:create_with_joins) # Rails 5.0 and 5.1
|
@@ -1395,17 +1420,28 @@ ActiveSupport.on_load(:active_record) do
|
|
1395
1420
|
cwj_options << base.type_caster if araat.method(:create_with_joins).arity > 3 # Rails <= 5.1
|
1396
1421
|
@alias_tracker = araat.create_with_joins(*cwj_options)
|
1397
1422
|
@eager_loading = eager_loading # (Unused in Rails 5.0)
|
1398
|
-
|
1423
|
+
elsif araat.respond_to?(:create) # Rails 4.1 and 4.2
|
1399
1424
|
@alias_tracker = araat.create(base.connection, joins)
|
1400
1425
|
@alias_tracker.aliased_table_for(base, base.table_name) # Updates the count for base.table_name to 1
|
1426
|
+
else # Rails 4.0
|
1427
|
+
is_rails_4 = true
|
1428
|
+
@base_klass = base
|
1429
|
+
@table_joins = joins
|
1430
|
+
@join_parts = [JoinBase.new(base)]
|
1431
|
+
@associations = {}
|
1432
|
+
@reflections = []
|
1433
|
+
@alias_tracker = araat.new(base.connection, joins)
|
1434
|
+
@alias_tracker.aliased_name_for(base.table_name) # Updates the count for base.table_name to 1
|
1435
|
+
tree = build(associations)
|
1401
1436
|
end
|
1402
|
-
tree
|
1437
|
+
tree ||= self.class.make_tree associations
|
1403
1438
|
|
1404
1439
|
# Provide a way to find the original relation that this tree is being used for
|
1405
1440
|
# (so that we can maintain a list of links for all tables used in JOINs)
|
1406
1441
|
if (relation = associations.instance_variable_get(:@relation))
|
1407
1442
|
tree.instance_variable_set(:@relation, relation)
|
1408
1443
|
end
|
1444
|
+
return if is_rails_4 # Rails 4.0 doesn't know about the rest
|
1409
1445
|
|
1410
1446
|
@join_root = JoinBase.new base, build(tree, base)
|
1411
1447
|
@join_root.children.each { |child| construct_tables! @join_root, child }
|
@@ -1538,6 +1574,21 @@ if ActiveRecord.version < ::Gem::Version.new('5.2')
|
|
1538
1574
|
end
|
1539
1575
|
end
|
1540
1576
|
|
1577
|
+
# By default the awesome_nested_set gem from CollectiveIdea does not prefix the ORDER BY column with its table name.
|
1578
|
+
# You can see this snag in action in the popular Spree project -- check out the Taxonomy model. Here is a fix:
|
1579
|
+
if Gem::Specification.all_names.find { |g| g.start_with?('awesome_nested_set-') }
|
1580
|
+
require 'awesome_nested_set/columns'
|
1581
|
+
::CollectiveIdea::Acts::NestedSet::Columns.class_exec do
|
1582
|
+
alias _brick_order_column_name order_column_name
|
1583
|
+
def order_column_name
|
1584
|
+
unless (ord_col = _brick_order_column_name).start_with?(tbl_prefix = "#{table_name}.")
|
1585
|
+
ord_col = tbl_prefix << ord_col
|
1586
|
+
end
|
1587
|
+
ord_col
|
1588
|
+
end
|
1589
|
+
end
|
1590
|
+
end
|
1591
|
+
|
1541
1592
|
# The "brick_links" patch -- this finds how every AR chain of association names
|
1542
1593
|
# relates back to an exact table correlation name chosen by AREL when the AST tree is
|
1543
1594
|
# walked. For instance, from a Customer model there could be a join_tree such as
|
@@ -1567,21 +1618,40 @@ module ActiveRecord
|
|
1567
1618
|
_brick_build_join_query(manager, buckets, *args) # , **kwargs)
|
1568
1619
|
end
|
1569
1620
|
|
1570
|
-
else
|
1571
|
-
|
1621
|
+
else # elsif private_instance_methods.include?(:select_association_list)
|
1572
1622
|
alias _brick_select_association_list select_association_list
|
1573
1623
|
def select_association_list(associations, stashed_joins = nil)
|
1574
1624
|
result = _brick_select_association_list(associations, stashed_joins)
|
1575
1625
|
result.instance_variable_set(:@relation, self)
|
1576
1626
|
result
|
1577
1627
|
end
|
1628
|
+
|
1629
|
+
# else # Rails 4.1 ? and older
|
1630
|
+
# alias _brick_build_joins build_joins
|
1631
|
+
# def build_joins(manager, joins)
|
1632
|
+
# result = _brick_build_joins(manager, joins)
|
1633
|
+
# result.instance_variable_set(:@relation, self)
|
1634
|
+
# result
|
1635
|
+
# end
|
1578
1636
|
end
|
1579
1637
|
end
|
1580
1638
|
|
1581
1639
|
# require 'active_record/associations/join_dependency'
|
1582
1640
|
module Associations
|
1583
|
-
#
|
1584
|
-
|
1641
|
+
if self.const_defined?('JoinHelper') # ActiveRecord < 4.1
|
1642
|
+
module JoinHelper
|
1643
|
+
alias _brick_construct_tables construct_tables
|
1644
|
+
def construct_tables
|
1645
|
+
result = _brick_construct_tables
|
1646
|
+
# Capture the table alias name that was chosen
|
1647
|
+
# if (relation = node.instance_variable_get(:@assocs)&.instance_variable_get(:@relation))
|
1648
|
+
# link_path = node.instance_variable_get(:@link_path)
|
1649
|
+
# relation.brick_links[link_path] = result.first.table_alias || result.first.table_name
|
1650
|
+
# end
|
1651
|
+
result
|
1652
|
+
end
|
1653
|
+
end
|
1654
|
+
else # For AR >= 4.2
|
1585
1655
|
class JoinDependency
|
1586
1656
|
# An intelligent .eager_load() and .includes() that creates t0_r0 style aliases only for the columns
|
1587
1657
|
# used in .select(). To enable this behaviour, include the flag :_brick_eager_load as the first
|
@@ -1655,7 +1725,7 @@ module ActiveRecord
|
|
1655
1725
|
associations.map do |name, right|
|
1656
1726
|
reflection = find_reflection base_klass, name
|
1657
1727
|
reflection.check_validity!
|
1658
|
-
reflection.check_eager_loadable!
|
1728
|
+
reflection.check_eager_loadable! if reflection.respond_to?(:check_eager_loadable!) # Used in AR >= 4.2
|
1659
1729
|
|
1660
1730
|
if reflection.polymorphic?
|
1661
1731
|
raise EagerLoadPolymorphicError.new(reflection)
|
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.140
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lorin Thwaits
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|