brick 1.0.139 → 1.0.141
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/brick/compatibility.rb +1 -0
- data/lib/brick/config.rb +8 -0
- data/lib/brick/extensions.rb +113 -25
- data/lib/brick/frameworks/rails/engine.rb +40 -21
- 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: 9f2d0e5e8ee3fb56acdbdee808b2a6ebe8cbf8b43457fd10b46f6197d82ef054
|
4
|
+
data.tar.gz: f21cca0ba332706e77d7f21245aeef6aa922668fbd41020e4edecd6672fdf4e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2a88167018d956d7066eaae2bb0fa05e69a1efe751b725477a6b13ad575fad7e89237fc1749138533723103f1a63018a23f6ba83cb1b6bed972e22c3bb89321
|
7
|
+
data.tar.gz: a6fc5f155686488fac49aea2eb58bc734021ea2be01fd505acbe29c357f888f65b17ebea78bae4c22ce83e4847eaef456c8a5329193753b8ac756a724d998ed1
|
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 || {} }
|
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,24 @@ 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
|
+
this_model = klass
|
579
|
+
loop do
|
580
|
+
::Brick.config.always_load_fields.fetch(this_model.name, nil)&.each do |alf|
|
581
|
+
selects << alf unless selects.include?(alf)
|
582
|
+
end
|
583
|
+
# ... plus any and all STI superclasses it may inherit from
|
584
|
+
break if (this_model = this_model.superclass).abstract_class? || this_model == ActiveRecord::Base
|
585
|
+
end
|
547
586
|
end
|
548
587
|
|
549
|
-
|
588
|
+
if join_array.present?
|
589
|
+
if ActiveRecord.version < Gem::Version.new('4.2')
|
590
|
+
joins!(join_array)
|
591
|
+
else
|
592
|
+
left_outer_joins!(join_array)
|
593
|
+
end
|
594
|
+
end
|
550
595
|
|
551
596
|
# If it's a CollectionProxy (which inherits from Relation) then need to dig out the
|
552
597
|
# core Relation object which is found in the association scope.
|
@@ -594,11 +639,13 @@ module ActiveRecord
|
|
594
639
|
# Deal with the conflict if there are two parts in the custom column named the same,
|
595
640
|
# "category.name" and "product.name" for instance will end up with aliases of "name"
|
596
641
|
# and "product__name".
|
642
|
+
col_prefix = 'br_cc_' if brick_col_names
|
597
643
|
if (cc_part_idx = cc_part.length - 1).zero?
|
598
|
-
col_alias = "
|
599
|
-
|
644
|
+
col_alias = "#{col_prefix}#{k}__#{table_name.tr('.', '_')}_#{cc_part.first}"
|
645
|
+
elsif brick_col_names ||
|
646
|
+
used_col_aliases.key?(col_alias = k.to_s) # This sets a simpler custom column name if possible
|
600
647
|
while cc_part_idx > 0 &&
|
601
|
-
(col_alias = "
|
648
|
+
(col_alias = "#{col_prefix}#{k}__#{cc_part[cc_part_idx..-1].map(&:to_s).join('__').tr('.', '_')}") &&
|
602
649
|
used_col_aliases.key?(col_alias)
|
603
650
|
cc_part_idx -= 1
|
604
651
|
end
|
@@ -611,7 +658,7 @@ module ActiveRecord
|
|
611
658
|
key_tbl_name = tbl_name
|
612
659
|
cc_part_idx = cc_part.length - 1
|
613
660
|
while cc_part_idx > 0 &&
|
614
|
-
(key_alias = "
|
661
|
+
(key_alias = "#{col_prefix}#{k}__#{(cc_part[cc_part_idx..-2] + [dest_pk]).map(&:to_s).join('__')}") &&
|
615
662
|
key_alias != col_alias && # We break out if this key alias does exactly match the col_alias
|
616
663
|
used_col_aliases.key?(key_alias)
|
617
664
|
cc_part_idx -= 1
|
@@ -781,7 +828,7 @@ module ActiveRecord
|
|
781
828
|
|
782
829
|
pri_tbl = hm.active_record
|
783
830
|
pri_key = hm.options[:primary_key] || pri_tbl.primary_key
|
784
|
-
|
831
|
+
if hm.active_record.abstract_class || hm.active_record.column_names.exclude?(pri_key)
|
785
832
|
# %%% When this gets hit then if an attempt is made to display the ERD, it might end up being blank
|
786
833
|
nix << k
|
787
834
|
next
|
@@ -907,21 +954,60 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
|
|
907
954
|
# Get foreign keys for anything marked to be auto-preloaded, or a self-referencing JOIN
|
908
955
|
klass_cols = klass.column_names
|
909
956
|
reflect_on_all_associations.each do |a|
|
910
|
-
selects << a.foreign_key if a.belongs_to? &&
|
911
|
-
(
|
957
|
+
selects << a.foreign_key if a.belongs_to? &&
|
958
|
+
(preload_values.include?(a.name) ||
|
959
|
+
(!a.options[:polymorphic] && a.klass == klass && klass_cols.include?(a.foreign_key))
|
960
|
+
)
|
912
961
|
end
|
913
962
|
# ActiveStorage compatibility
|
914
963
|
selects << 'service_name' if klass.name == 'ActiveStorage::Blob' && ActiveStorage::Blob.columns_hash.key?('service_name')
|
915
964
|
selects << 'blob_id' if klass.name == 'ActiveStorage::Attachment' && ActiveStorage::Attachment.columns_hash.key?('blob_id')
|
916
965
|
pieces, my_dsl = klass.brick_parse_dsl(join_array = ::Brick::JoinArray.new, [], translations = {}, false, nil, true)
|
917
966
|
brick_select(
|
918
|
-
|
919
|
-
{ '_br' => (descrip_cols = [pieces, my_dsl]) }
|
967
|
+
selects, where_values_hash, nil, translations: translations, join_array: join_array,
|
968
|
+
cust_col_override: { '_br' => (descrip_cols = [pieces, my_dsl]) }
|
920
969
|
)
|
921
|
-
order_values = klass.primary_key
|
970
|
+
order_values = "#{klass.table_name}.#{klass.primary_key}"
|
922
971
|
[self.select(selects), descrip_cols]
|
923
972
|
end
|
924
973
|
|
974
|
+
# Accommodate when a relation gets queried for a model, and in that model it has an #after_initialize block
|
975
|
+
# which references attributes that were not originally included as part of the select_values.
|
976
|
+
def brick_(method, *args, brick_orig_relation: nil, **kwargs, &block)
|
977
|
+
begin
|
978
|
+
send(method, *args, **kwargs, &block) # method will be something like :uniq or :each
|
979
|
+
rescue ActiveModel::MissingAttributeError => e
|
980
|
+
if (err_msg = e.message).start_with?('missing attribute: ') &&
|
981
|
+
klass.column_names.include?(col_name = e.message[19..-1])
|
982
|
+
(dup_rel = dup).select_values << col_name
|
983
|
+
ret = dup_rel.brick_(method, *args, brick_orig_relation: (brick_orig_relation ||= self), **kwargs, &block)
|
984
|
+
always_loads = (::Brick.config.always_load_fields ||= {})
|
985
|
+
|
986
|
+
# Find the most parent STI superclass for this model, and apply an always_load_fields entry for this missing column
|
987
|
+
has_field = false
|
988
|
+
this_model = klass
|
989
|
+
loop do
|
990
|
+
has_field = true if always_loads.key?(this_model.name) && always_loads[this_model.name]&.include?(col_name)
|
991
|
+
break if has_field || (next_model = this_model.superclass).abstract_class? || next_model == ActiveRecord::Base
|
992
|
+
this_model = next_model
|
993
|
+
end
|
994
|
+
unless has_field
|
995
|
+
(brick_orig_relation || self).instance_variable_set(:@brick_new_alf, ((always_loads[this_model.name] ||= []) << col_name))
|
996
|
+
end
|
997
|
+
|
998
|
+
if self.object_id == brick_orig_relation.object_id
|
999
|
+
puts "*** WARNING: Missing field#{'s' if @brick_new_alf.length > 1}!
|
1000
|
+
Might want to add this in your brick.rb:
|
1001
|
+
::Brick.always_load_fields = { #{klass.name.inspect} => #{@brick_new_alf.inspect} }"
|
1002
|
+
remove_instance_variable(:@brick_new_alf)
|
1003
|
+
end
|
1004
|
+
ret
|
1005
|
+
else
|
1006
|
+
[]
|
1007
|
+
end
|
1008
|
+
end
|
1009
|
+
end
|
1010
|
+
|
925
1011
|
private
|
926
1012
|
|
927
1013
|
def shift_or_first(ary)
|
@@ -935,6 +1021,8 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
|
|
935
1021
|
|
936
1022
|
alias _brick_find_sti_class find_sti_class
|
937
1023
|
def find_sti_class(type_name)
|
1024
|
+
return if type_name.is_a?(Numeric)
|
1025
|
+
|
938
1026
|
if ::Brick.sti_models.key?(type_name ||= name)
|
939
1027
|
::Brick.sti_models[type_name].fetch(:base, nil) || _brick_find_sti_class(type_name)
|
940
1028
|
else
|
@@ -1606,7 +1694,7 @@ class Object
|
|
1606
1694
|
|
1607
1695
|
# Add a hash for the inline style to the content-security-policy if one is present
|
1608
1696
|
self.define_method(:add_csp_hash) do |style_value = nil|
|
1609
|
-
if (csp = request.content_security_policy)
|
1697
|
+
if request.respond_to?(:content_security_policy) && (csp = request.content_security_policy)
|
1610
1698
|
if (cspd = csp.directives.fetch('style-src'))
|
1611
1699
|
if style_value
|
1612
1700
|
if (nonce = ::ActionDispatch::ContentSecurityPolicy::Request::NONCE)
|
@@ -1850,9 +1938,9 @@ class Object
|
|
1850
1938
|
|
1851
1939
|
ar_relation = ActiveRecord.version < Gem::Version.new('4') ? real_model.preload : real_model.all
|
1852
1940
|
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)
|
1941
|
+
@_brick_params = ar_relation.brick_select((selects ||= []), params: params, order_by: order_by,
|
1942
|
+
translations: (translations = {}),
|
1943
|
+
join_array: (join_array = ::Brick::JoinArray.new))
|
1856
1944
|
|
1857
1945
|
if is_api # Asking for JSON?
|
1858
1946
|
# 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,8 +1648,8 @@ 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
|
1640
|
-
collection&.each do |obj|
|
1651
|
+
collection, descrip_cols = bt_class&.order(Arel.sql(\"#\{bt_class.table_name}.#\{obj_pk = bt_class.primary_key}\"))&.brick_list
|
1652
|
+
collection&.brick_(:each) do |obj|
|
1641
1653
|
option_detail << [
|
1642
1654
|
obj.brick_descrip(
|
1643
1655
|
descrip_cols&.first&.map { |col| obj.send(col.last) },
|
@@ -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.uniq
|
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.141
|
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-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|