brick 1.0.77 → 1.0.79
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/extensions.rb +144 -47
- data/lib/brick/frameworks/rails/engine.rb +35 -14
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +30 -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: eedc546e7e9693459cf228ee376650cdf53c0325e55d50ef24bbf46d421f8738
|
4
|
+
data.tar.gz: c4feda4b5db8334363d05f3418dc172f0dbf408c21192222008a5b9a174ae448
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c9713099c057b9c9041121576a4f38d5949213926a47d597f88f4a9021ce14cd3df2a30c14b8b929b92ba4549ec4492bdfd8de13cb0a4f710e911c00eab67b6
|
7
|
+
data.tar.gz: 27ca4aa6d40e165aaca5b09cf5b2ce97737b26adc8424b1d25d81b9813100679e9e06a61279c4fd8b96d750a89a4c4cb8eafbfe2d425a4d4c6868644820f297d
|
data/lib/brick/extensions.rb
CHANGED
@@ -258,7 +258,7 @@ module ActiveRecord
|
|
258
258
|
|
259
259
|
def self._brick_index(mode = nil)
|
260
260
|
tbl_parts = ((mode == :singular) ? table_name.singularize : table_name).split('.')
|
261
|
-
tbl_parts.shift if ::Brick.apartment_multitenant && tbl_parts.first == Apartment.default_schema
|
261
|
+
tbl_parts.shift if ::Brick.apartment_multitenant && tbl_parts.length > 1 && tbl_parts.first == Apartment.default_schema
|
262
262
|
tbl_parts.unshift(::Brick.config.path_prefix) if ::Brick.config.path_prefix
|
263
263
|
index = tbl_parts.map(&:underscore).join('_')
|
264
264
|
# Rails applies an _index suffix to that route when the resource name is singular
|
@@ -555,7 +555,10 @@ module ActiveRecord
|
|
555
555
|
tbl_name = "\"#{tbl_name}\"" if ::Brick.is_oracle && rel_dupe._arel_applied_aliases.include?(tbl_name)
|
556
556
|
field_tbl_name = nil
|
557
557
|
v1.map { |x| [translations[x[0..-2].map(&:to_s).join('.')], x.last] }.each_with_index do |sel_col, idx|
|
558
|
-
#
|
558
|
+
# unless chains[sel_col.first]
|
559
|
+
# puts 'You might have some bogus DSL in your brick.rb file'
|
560
|
+
# next
|
561
|
+
# end
|
559
562
|
field_tbl_name = (field_tbl_names[v.first][sel_col.first] ||= shift_or_first(chains[sel_col.first])).split('.').last
|
560
563
|
# If it's Oracle, quote any AREL aliases that had been applied
|
561
564
|
field_tbl_name = "\"#{field_tbl_name}\"" if ::Brick.is_oracle && rel_dupe._arel_applied_aliases.include?(field_tbl_name)
|
@@ -606,19 +609,51 @@ module ActiveRecord
|
|
606
609
|
end
|
607
610
|
end
|
608
611
|
# Add derived table JOIN for the has_many counts
|
612
|
+
nix = []
|
609
613
|
klass._br_hm_counts.each do |k, hm|
|
610
|
-
associative = nil
|
611
614
|
count_column = if hm.options[:through]
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
615
|
+
# Build the chain of JOINs going to the final destination HMT table
|
616
|
+
# (Usually just one JOIN, but could be many.)
|
617
|
+
hmt_assoc = hm
|
618
|
+
x = []
|
619
|
+
x.unshift(hmt_assoc) while hmt_assoc.options[:through] && (hmt_assoc = klass.reflect_on_association(hmt_assoc.options[:through]))
|
620
|
+
from_clause = +"#{x.first.table_name} br_t0"
|
621
|
+
fk_col = x.shift.foreign_key
|
622
|
+
link_back = [klass.primary_key] # %%% Inverse path back to the original object -- used to build out a link with a filter
|
623
|
+
idx = 0
|
624
|
+
bail_out = nil
|
625
|
+
x.map do |a|
|
626
|
+
from_clause << "\n LEFT OUTER JOIN #{a.table_name} br_t#{idx += 1} "
|
627
|
+
from_clause << if (src_ref = a.source_reflection).macro == :belongs_to
|
628
|
+
"ON br_t#{idx}.id = br_t#{idx - 1}.#{a.foreign_key}"
|
629
|
+
elsif src_ref.options[:as]
|
630
|
+
"ON br_t#{idx}.#{src_ref.type} = '#{src_ref.active_record.name}'" + # "polymorphable_type"
|
631
|
+
" AND br_t#{idx}.#{src_ref.foreign_key} = br_t#{idx - 1}.id"
|
632
|
+
elsif src_ref.options[:source_type]
|
633
|
+
print "Skipping #{hm.name} --HMT-> #{hm.source_reflection.name} as it uses source_type which is not supported"
|
634
|
+
nix << k
|
635
|
+
bail_out = true
|
636
|
+
break
|
637
|
+
else # Standard has_many
|
638
|
+
"ON br_t#{idx}.#{a.foreign_key} = br_t#{idx - 1}.id"
|
639
|
+
end
|
640
|
+
link_back.unshift(a.source_reflection.name)
|
641
|
+
[a.table_name, a.foreign_key, a.source_reflection.macro]
|
642
|
+
end
|
643
|
+
next if bail_out
|
644
|
+
# count_column is determined from the originating HMT member
|
645
|
+
if (src_ref = hm.source_reflection).nil?
|
646
|
+
puts "*** Warning: Could not determine destination model for this HMT association in model #{klass.name}:\n has_many :#{hm.name}, through: :#{hm.options[:through]}"
|
647
|
+
nix << k
|
648
|
+
bail_out = true
|
649
|
+
elsif src_ref.macro == :belongs_to # Traditional HMT using an associative table
|
650
|
+
"br_t#{idx}.#{hm.foreign_key}"
|
651
|
+
else # A HMT that goes HM -> HM, something like Categories -> Products -> LineItems
|
652
|
+
"br_t#{idx}.#{src_ref.active_record.primary_key}"
|
618
653
|
end
|
619
654
|
else
|
620
|
-
fk_col = hm.foreign_key
|
621
|
-
poly_type =
|
655
|
+
fk_col = (inv = hm.inverse_of)&.foreign_key || hm.foreign_key
|
656
|
+
poly_type = inv.foreign_type if hm.options.key?(:as)
|
622
657
|
pk = hm.klass.primary_key
|
623
658
|
(pk.is_a?(Array) ? pk.first : pk) || '*'
|
624
659
|
end
|
@@ -652,20 +687,38 @@ module ActiveRecord
|
|
652
687
|
selects << poly_type
|
653
688
|
on_clause << "#{tbl_alias}.#{poly_type} = '#{name}'"
|
654
689
|
end
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
690
|
+
unless from_clause
|
691
|
+
hm_table_name = if is_mysql
|
692
|
+
"`#{hm.table_name}`"
|
693
|
+
elsif is_postgres || is_mssql
|
694
|
+
"\"#{(hm.table_name).gsub('.', '"."')}\""
|
695
|
+
else
|
696
|
+
hm.table_name
|
697
|
+
end
|
698
|
+
end
|
662
699
|
group_bys = ::Brick.is_oracle || is_mssql ? selects : (1..selects.length).to_a
|
663
700
|
join_clause = "LEFT OUTER
|
664
|
-
JOIN (SELECT #{selects.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}#{count_column
|
665
|
-
}) AS c_t_ FROM #{hm_table_name} GROUP BY #{group_bys.join(', ')}) #{tbl_alias}"
|
701
|
+
JOIN (SELECT #{selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}#{count_column
|
702
|
+
}) AS c_t_ FROM #{from_clause || hm_table_name} GROUP BY #{group_bys.join(', ')}) #{tbl_alias}"
|
666
703
|
joins!("#{join_clause} ON #{on_clause.join(' AND ')}")
|
667
704
|
end
|
668
|
-
|
705
|
+
while (n = nix.pop)
|
706
|
+
klass._br_hm_counts.delete(n)
|
707
|
+
end
|
708
|
+
|
709
|
+
unless wheres.empty?
|
710
|
+
# Rewrite the wheres to reference table and correlation names built out by AREL
|
711
|
+
wheres2 = wheres.each_with_object({}) do |v, s|
|
712
|
+
if (v_parts = v.first.split('.')).length == 1
|
713
|
+
s[v.first] = v.last
|
714
|
+
else
|
715
|
+
k1 = klass.reflect_on_association(v_parts.first)&.klass
|
716
|
+
tbl_name = (field_tbl_names[v_parts.first][k1] ||= shift_or_first(chains[k1])).split('.').last
|
717
|
+
s["#{tbl_name}.#{v_parts.last}"] = v.last
|
718
|
+
end
|
719
|
+
end
|
720
|
+
where!(wheres2)
|
721
|
+
end
|
669
722
|
# Must parse the order_by and see if there are any symbols which refer to BT associations
|
670
723
|
# or custom columns as they must be expanded to find the corresponding b_r_model__column
|
671
724
|
# or br_cc_column naming for each.
|
@@ -805,12 +858,30 @@ Module.class_exec do
|
|
805
858
|
end
|
806
859
|
base_module = if self < ActiveRecord::Migration || !self.name
|
807
860
|
brick_root || Object
|
808
|
-
elsif (split_self_name || self.name.split('::')).length > 1
|
809
|
-
|
861
|
+
elsif (split_self_name || self.name.split('::')).length > 1 # Classic mode
|
862
|
+
begin
|
863
|
+
return self._brick_const_missing(*args)
|
864
|
+
|
865
|
+
rescue NameError # %%% Avoid the error "____ cannot be autoloaded from an anonymous class or module"
|
866
|
+
return self.const_get(args.first) if self.const_defined?(args.first)
|
867
|
+
|
868
|
+
# unless self == (prnt = (respond_to?(:parent) ? parent : module_parent))
|
869
|
+
unless self == Object
|
870
|
+
begin
|
871
|
+
return Object._brick_const_missing(*args)
|
872
|
+
|
873
|
+
rescue NameError
|
874
|
+
return Object.const_get(args.first) if Object.const_defined?(args.first)
|
875
|
+
|
876
|
+
end
|
877
|
+
end
|
878
|
+
end
|
879
|
+
Object
|
810
880
|
else
|
811
881
|
self
|
812
882
|
end
|
813
|
-
|
883
|
+
# puts "#{self.name} - #{args.first}"
|
884
|
+
desired_classname = (self == Object || !name) ? requested : "#{name}::#{requested}"
|
814
885
|
if ((is_defined = self.const_defined?(args.first)) && (possible = self.const_get(args.first)) && possible.name == desired_classname) ||
|
815
886
|
# Try to require the respective Ruby file
|
816
887
|
((filename = ActiveSupport::Dependencies.search_for_file(desired_classname.underscore) ||
|
@@ -822,9 +893,7 @@ Module.class_exec do
|
|
822
893
|
# then return what we've found.
|
823
894
|
(is_defined && !::Brick.is_eager_loading) # Used to also have: && possible != self
|
824
895
|
if (!brick_root && (filename || possible.instance_of?(Class))) ||
|
825
|
-
(possible.instance_of?(Module) &&
|
826
|
-
((possible.respond_to?(:module_parent) ? possible.module_parent : possible.parent) == self)
|
827
|
-
) ||
|
896
|
+
(possible.instance_of?(Module) && possible.module_parent == self) ||
|
828
897
|
(possible.instance_of?(Class) && possible == self) # Are we simply searching for ourselves?
|
829
898
|
return possible
|
830
899
|
end
|
@@ -881,9 +950,21 @@ Module.class_exec do
|
|
881
950
|
base_module._brick_const_missing(*args)
|
882
951
|
# elsif base_module != Object
|
883
952
|
# module_parent.const_missing(*args)
|
884
|
-
else
|
885
|
-
|
886
|
-
|
953
|
+
elsif Rails.respond_to?(:autoloaders) && # After finding nothing else, if Zeitwerk is enabled ...
|
954
|
+
(Rails::Autoloaders.respond_to?(:zeitwerk_enabled?) ? Rails::Autoloaders.zeitwerk_enabled? : true)
|
955
|
+
self._brick_const_missing(*args) # ... rely solely on Zeitwerk.
|
956
|
+
else # Classic mode
|
957
|
+
unless (found = base_module._brick_const_missing(*args))
|
958
|
+
puts "MISSING! #{base_module.name} #{args.inspect} #{table_name}"
|
959
|
+
end
|
960
|
+
found
|
961
|
+
end
|
962
|
+
end
|
963
|
+
|
964
|
+
# Support Rails < 6.0 which adds #parent instead of #module_parent
|
965
|
+
unless respond_to?(:module_parent)
|
966
|
+
def module_parent # Weirdly for Grape::API does NOT come in with the proper class, but some anonymous Class thing
|
967
|
+
parent
|
887
968
|
end
|
888
969
|
end
|
889
970
|
end
|
@@ -940,9 +1021,7 @@ class Object
|
|
940
1021
|
schema_name
|
941
1022
|
else
|
942
1023
|
matching = "#{schema_name}.#{matching}"
|
943
|
-
|
944
|
-
# ::Brick.db_schemas[schema_name] ||= self.const_get(schema_name.camelize.to_sym)
|
945
|
-
self.const_get(schema_name.camelize)
|
1024
|
+
(::Brick.db_schemas[schema_name] || {})[:class] ||= self.const_get(schema_name.camelize.to_sym)
|
946
1025
|
end
|
947
1026
|
"#{schema_module&.name}::#{inheritable_name || model_name}"
|
948
1027
|
end
|
@@ -952,10 +1031,10 @@ class Object
|
|
952
1031
|
|
953
1032
|
# Are they trying to use a pluralised class name such as "Employees" instead of "Employee"?
|
954
1033
|
if table_name == singular_table_name && !ActiveSupport::Inflector.inflections.uncountable.include?(table_name)
|
955
|
-
unless ::Brick.config.sti_namespace_prefixes&.key?("::#{singular_table_name.camelize}::")
|
956
|
-
|
957
|
-
|
958
|
-
end
|
1034
|
+
# unless ::Brick.config.sti_namespace_prefixes&.key?("::#{singular_table_name.camelize}::")
|
1035
|
+
# puts "Warning: Class name for a model that references table \"#{matching
|
1036
|
+
# }\" should be \"#{ActiveSupport::Inflector.singularize(inheritable_name || model_name)}\"."
|
1037
|
+
# end
|
959
1038
|
return
|
960
1039
|
end
|
961
1040
|
|
@@ -1309,7 +1388,7 @@ class Object
|
|
1309
1388
|
ordering = params['_brick_order']&.split(',')&.map(&:to_sym) || Object.send(:default_ordering, table_name, pk)
|
1310
1389
|
order_by, _ = model._brick_calculate_ordering(ordering, true) # Don't do the txt part
|
1311
1390
|
|
1312
|
-
@_brick_params = (ar_relation = model.all).brick_select(params, (selects = []),
|
1391
|
+
@_brick_params = (ar_relation = model.all).brick_select(params, (selects = []), order_by,
|
1313
1392
|
translations = {},
|
1314
1393
|
join_array = ::Brick::JoinArray.new)
|
1315
1394
|
# %%% Add custom HM count columns
|
@@ -1393,7 +1472,7 @@ class Object
|
|
1393
1472
|
end
|
1394
1473
|
|
1395
1474
|
if pk.present?
|
1396
|
-
# if (schema = ::Brick.config.schema_behavior[:multitenant]&.fetch(:schema_to_analyse, nil)) && ::Brick.db_schemas&.
|
1475
|
+
# if (schema = ::Brick.config.schema_behavior[:multitenant]&.fetch(:schema_to_analyse, nil)) && ::Brick.db_schemas&.key?(schema)
|
1397
1476
|
# ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
|
1398
1477
|
# end
|
1399
1478
|
|
@@ -1574,6 +1653,7 @@ module ActiveRecord::ConnectionHandling
|
|
1574
1653
|
# return if ActiveRecord::Base.connection.current_database == 'postgres'
|
1575
1654
|
|
1576
1655
|
initializer_loaded = false
|
1656
|
+
orig_schema = nil
|
1577
1657
|
if (relations = ::Brick.relations).empty?
|
1578
1658
|
# If there's schema things configured then we only expect our initializer to be named exactly this
|
1579
1659
|
if File.exist?(brick_initializer = ::Rails.root.join('config/initializers/brick.rb'))
|
@@ -1583,7 +1663,10 @@ module ActiveRecord::ConnectionHandling
|
|
1583
1663
|
# .default_schema are specified then we can work with non-tenanted models more appropriately
|
1584
1664
|
if (apartment = Object.const_defined?('Apartment')) &&
|
1585
1665
|
File.exist?(apartment_initializer = ::Rails.root.join('config/initializers/apartment.rb'))
|
1586
|
-
|
1666
|
+
unless @_apartment_loaded
|
1667
|
+
load apartment_initializer
|
1668
|
+
@_apartment_loaded = true
|
1669
|
+
end
|
1587
1670
|
apartment_excluded = Apartment.excluded_models
|
1588
1671
|
end
|
1589
1672
|
# Only for Postgres (Doesn't work in sqlite3 or MySQL)
|
@@ -1607,12 +1690,14 @@ module ActiveRecord::ConnectionHandling
|
|
1607
1690
|
[row['table_schema'], row['dt']]
|
1608
1691
|
end
|
1609
1692
|
# Remove any system schemas
|
1610
|
-
s[row.first] = row.last unless ['information_schema', 'pg_catalog', 'pg_toast',
|
1611
|
-
|
1693
|
+
s[row.first] = { dt: row.last } unless ['information_schema', 'pg_catalog', 'pg_toast', 'heroku_ext',
|
1694
|
+
'INFORMATION_SCHEMA', 'sys'].include?(row.first)
|
1612
1695
|
end
|
1613
1696
|
if (is_multitenant = (multitenancy = ::Brick.config.schema_behavior[:multitenant]) &&
|
1614
1697
|
(sta = multitenancy[:schema_to_analyse]) != 'public') &&
|
1615
|
-
::Brick.db_schemas.
|
1698
|
+
::Brick.db_schemas.key?(sta)
|
1699
|
+
# Take note of the current schema so we can go back to it at the end of all this
|
1700
|
+
orig_schema = ActiveRecord::Base.execute_sql('SELECT current_schemas(true)').first['current_schemas'][1..-2].split(',')
|
1616
1701
|
::Brick.default_schema = schema = sta
|
1617
1702
|
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", schema)
|
1618
1703
|
end
|
@@ -1622,7 +1707,7 @@ module ActiveRecord::ConnectionHandling
|
|
1622
1707
|
# ActiveRecord::Base.connection.current_database will be something like "XEPDB1"
|
1623
1708
|
::Brick.default_schema = schema = ActiveRecord::Base.connection.raw_connection.username
|
1624
1709
|
::Brick.db_schemas = {}
|
1625
|
-
ActiveRecord::Base.execute_sql("SELECT username FROM sys.all_users WHERE ORACLE_MAINTAINED != 'Y'").each { |s| ::Brick.db_schemas[s.first] =
|
1710
|
+
ActiveRecord::Base.execute_sql("SELECT username FROM sys.all_users WHERE ORACLE_MAINTAINED != 'Y'").each { |s| ::Brick.db_schemas[s.first] = {} }
|
1626
1711
|
when 'SQLite'
|
1627
1712
|
sql = "SELECT m.name AS relation_name, UPPER(m.type) AS table_type,
|
1628
1713
|
p.name AS column_name, p.type AS data_type,
|
@@ -1641,10 +1726,12 @@ module ActiveRecord::ConnectionHandling
|
|
1641
1726
|
if (possible_schema = ::Brick.config.schema_behavior&.[](:multitenant)&.[](:schema_to_analyse))
|
1642
1727
|
if ::Brick.db_schemas.key?(possible_schema)
|
1643
1728
|
::Brick.default_schema = schema = possible_schema
|
1729
|
+
orig_schema = ActiveRecord::Base.execute_sql('SELECT current_schemas(true)').first['current_schemas'][1..-2].split(',')
|
1644
1730
|
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", schema)
|
1645
1731
|
elsif Rails.env == 'test' # When testing, just find the most recently-created schema
|
1646
|
-
::Brick.default_schema = schema = ::Brick.db_schemas.to_a.sort { |a, b| b.last <=> a.last }.first.first
|
1647
|
-
puts "While running tests, had noticed
|
1732
|
+
::Brick.default_schema = schema = ::Brick.db_schemas.to_a.sort { |a, b| b.last[:dt] <=> a.last[:dt] }.first.first
|
1733
|
+
puts "While running tests, had noticed in the brick.rb initializer that the line \"::Brick.schema_behavior = ...\" refers to a schema called \"#{possible_schema}\" which does not exist. Reading table structure from the most recently-created schema, #{schema}."
|
1734
|
+
orig_schema = ActiveRecord::Base.execute_sql('SELECT current_schemas(true)').first['current_schemas'][1..-2].split(',')
|
1648
1735
|
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", schema)
|
1649
1736
|
else
|
1650
1737
|
puts "*** In the brick.rb initializer the line \"::Brick.schema_behavior = ...\" refers to a schema called \"#{possible_schema}\". This schema does not exist. ***"
|
@@ -1848,6 +1935,11 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1848
1935
|
v[:class_name] = (schema_names + [rel_name.last.singularize]).map(&:camelize).join('::')
|
1849
1936
|
end
|
1850
1937
|
::Brick.load_additional_references if initializer_loaded
|
1938
|
+
|
1939
|
+
if orig_schema && (orig_schema = (orig_schema - ['pg_catalog', 'heroku_ext']).first)
|
1940
|
+
puts "Now switching back to \"#{orig_schema}\" schema."
|
1941
|
+
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", orig_schema)
|
1942
|
+
end
|
1851
1943
|
end
|
1852
1944
|
|
1853
1945
|
def retrieve_schema_and_tables(sql = nil, is_postgres = nil, is_mssql = nil, schema = nil)
|
@@ -2134,7 +2226,12 @@ module Brick
|
|
2134
2226
|
end
|
2135
2227
|
end
|
2136
2228
|
end
|
2137
|
-
::Brick.relations.keys.map
|
2229
|
+
::Brick.relations.keys.map do |v|
|
2230
|
+
tbl_parts = v.split('.')
|
2231
|
+
tbl_parts.shift if ::Brick.apartment_multitenant && tbl_parts.length > 1 && tbl_parts.first == Apartment.default_schema
|
2232
|
+
res = tbl_parts.join('.')
|
2233
|
+
[v, (model = models[res])&.last&.table_name, migrations&.fetch(res, nil), model&.first]
|
2234
|
+
end
|
2138
2235
|
end
|
2139
2236
|
|
2140
2237
|
def ensure_unique(name, *sources)
|
@@ -125,8 +125,8 @@ module Brick
|
|
125
125
|
"H#{hm_assoc.macro == :has_one ? 'O' : 'M'}#{'T' if hm_assoc.options[:through]}",
|
126
126
|
(assoc_name = hm.first)]
|
127
127
|
hm_fk_name = if (through = hm_assoc.options[:through])
|
128
|
-
|
129
|
-
|
128
|
+
next unless @_brick_model._br_hm_counts.key?(hm_assoc.name) # Skip any weird HMTs that go through a HM with a source_type
|
129
|
+
|
130
130
|
next unless @_brick_model.instance_methods.include?(through) &&
|
131
131
|
(associative = @_brick_model._br_associatives.fetch(hm.first, nil))
|
132
132
|
|
@@ -353,11 +353,8 @@ def hide_bcrypt(val, max_len = 200)
|
|
353
353
|
'(hidden)'
|
354
354
|
else
|
355
355
|
if val.is_a?(String)
|
356
|
-
if val.length > max_len
|
357
|
-
|
358
|
-
val << '...'
|
359
|
-
end
|
360
|
-
val.force_encoding('UTF-8') unless val.encoding.name == 'UTF-8'
|
356
|
+
val = \"#\{val[0...max_len]}...\" if val.length > max_len
|
357
|
+
val = val.dup.force_encoding('UTF-8') unless val.encoding.name == 'UTF-8'
|
361
358
|
end
|
362
359
|
val
|
363
360
|
end
|
@@ -372,7 +369,7 @@ def display_value(col_type, val)
|
|
372
369
|
if @is_mysql || @is_mssql
|
373
370
|
# MySQL's \"Internal Geometry Format\" and MSSQL's Geography are like WKB, but with an initial 4 bytes that indicates the SRID.
|
374
371
|
if (srid = val&.[](0..3)&.unpack('I'))
|
375
|
-
val = val.force_encoding('BINARY')[4..-1].bytes
|
372
|
+
val = val.dup.force_encoding('BINARY')[4..-1].bytes
|
376
373
|
|
377
374
|
# MSSQL spatial bitwise flags, often 0C for a point:
|
378
375
|
# xxxx xxx1 = HasZValues
|
@@ -409,9 +406,8 @@ def display_value(col_type, val)
|
|
409
406
|
end
|
410
407
|
end
|
411
408
|
# Accommodate composite primary keys that include strings with forward-slash characters
|
412
|
-
def slashify(
|
413
|
-
|
414
|
-
val.map { |val_part| val_part.is_a?(String) ? val_part.gsub('/', '^^sl^^') : val_part }
|
409
|
+
def slashify(*vals)
|
410
|
+
vals.map { |val_part| val_part.is_a?(String) ? val_part.gsub('/', '^^sl^^') : val_part }
|
415
411
|
end
|
416
412
|
callbacks = {} %>"
|
417
413
|
|
@@ -673,7 +669,7 @@ erDiagram
|
|
673
669
|
inline = case args.first
|
674
670
|
when 'index'
|
675
671
|
obj_pk = if pk&.is_a?(Array) # Composite primary key?
|
676
|
-
"
|
672
|
+
"#{pk.map { |pk_part| "#{obj_name}.#{pk_part}" }.join(', ')}" unless pk.empty?
|
677
673
|
elsif pk
|
678
674
|
"#{obj_name}.#{pk}"
|
679
675
|
end
|
@@ -787,7 +783,7 @@ erDiagram
|
|
787
783
|
origin = (key_parts = k.split('.')).length == 1 ? #{model_name} : #{model_name}.reflect_on_association(key_parts.first).klass
|
788
784
|
if (destination_fk = Brick.relations[origin.table_name][:fks].values.find { |fk| fk[:fk] == key_parts.last }) &&
|
789
785
|
(obj = (destination = origin.reflect_on_association(destination_fk[:assoc_name])&.klass)&.find(id)) %>
|
790
|
-
<h3>for <%= link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination._brick_index\}_path\".to_sym, id) %></h3><%
|
786
|
+
<h3>for <%= link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination._brick_index(:singular)\}_path\".to_sym, id) %></h3><%
|
791
787
|
end
|
792
788
|
end %>
|
793
789
|
(<%= link_to 'See all #{model_name.split('::').last.pluralize}', #{@_brick_model._brick_index}_path %>)
|
@@ -817,6 +813,24 @@ erDiagram
|
|
817
813
|
hms_keys << (assoc_name = (assoc = hm.first).name.to_s)
|
818
814
|
"#{assoc_name.inspect} => [#{(assoc.options[:through] && !assoc.through_reflection).inspect}, #{assoc.klass.name}, #{hm[1].inspect}, #{hm[2].inspect}]"
|
819
815
|
end.join(', ')}}
|
816
|
+
# If the resource is missing, has the user simply created an inappropriately pluralised name for a table?
|
817
|
+
@#{table_name} ||= if dym_list = instance_variables.reject do |entry|
|
818
|
+
entry.to_s.start_with?('@_') ||
|
819
|
+
['@cache_hit', '@marked_for_same_origin_verification', '@view_renderer', '@view_flow', '@output_buffer', '@virtual_path'].include?(entry.to_s)
|
820
|
+
end
|
821
|
+
msg = \"Can't find resource \\\"#{table_name}\\\".\"
|
822
|
+
# Can't be sure otherwise of what is up, so check DidYouMean and offer a suggestion.
|
823
|
+
if (dym = DidYouMean::SpellChecker.new(dictionary: dym_list).correct('@#{table_name}')).present?
|
824
|
+
msg << \"\nIf you meant \\\"#\{found_dym = dym.first[1..-1]}\\\" then to avoid this message add this entry into inflections.rb:\n\"
|
825
|
+
msg << \" inflect.singular('#\{found_dym}', '#{obj_name}')\"
|
826
|
+
puts
|
827
|
+
puts \"WARNING: #\{msg}\"
|
828
|
+
puts
|
829
|
+
@#{table_name} = instance_variable_get(dym.first.to_sym)
|
830
|
+
else
|
831
|
+
raise ActiveRecord::RecordNotFound.new(msg)
|
832
|
+
end
|
833
|
+
end
|
820
834
|
col_keys = @#{table_name}.columns.each_with_object([]) do |col, s|
|
821
835
|
col_name = col.name
|
822
836
|
next if @_brick_incl&.exclude?(col_name) ||
|
@@ -940,7 +954,11 @@ erDiagram
|
|
940
954
|
@resources.each do |r|
|
941
955
|
%>
|
942
956
|
<tr>
|
943
|
-
<td><%=
|
957
|
+
<td><%= begin
|
958
|
+
kls = Object.const_get(::Brick.relations[r[0]].fetch(:class_name, nil))
|
959
|
+
rescue
|
960
|
+
end
|
961
|
+
kls ? link_to(r[0], send(\"#\{kls._brick_index}_path\".to_sym)) : r[0] %></td>
|
944
962
|
<td<%= if r[1]
|
945
963
|
' class=\"orphan\"' unless ::Brick.relations.key?(r[1])
|
946
964
|
else
|
@@ -1277,6 +1295,9 @@ document.querySelectorAll(\"input, select\").forEach(function (inp) {
|
|
1277
1295
|
}
|
1278
1296
|
});
|
1279
1297
|
</script>"
|
1298
|
+
# puts "==============="
|
1299
|
+
# puts inline
|
1300
|
+
# puts "==============="
|
1280
1301
|
# As if it were an inline template (see #determine_template in actionview-5.2.6.2/lib/action_view/renderer/template_renderer.rb)
|
1281
1302
|
keys = options.has_key?(:locals) ? options[:locals].keys : []
|
1282
1303
|
handler = ActionView::Template.handler_for_extension(options[:type] || 'erb')
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -126,10 +126,15 @@ module Brick
|
|
126
126
|
attr_accessor :default_schema, :db_schemas, :routes_done, :is_oracle, :is_eager_loading
|
127
127
|
|
128
128
|
def set_db_schema(params = nil)
|
129
|
-
schema = (params ? params['_brick_schema'] : ::Brick.default_schema)
|
130
|
-
if schema && ::Brick.db_schemas&.
|
129
|
+
schema = (params ? params['_brick_schema'] : ::Brick.default_schema)
|
130
|
+
if schema && ::Brick.db_schemas&.key?(schema)
|
131
131
|
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
|
132
132
|
schema
|
133
|
+
elsif ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
134
|
+
# Just return the current schema
|
135
|
+
orig_schema = ActiveRecord::Base.execute_sql('SELECT current_schemas(true)').first['current_schemas'][1..-2].split(',')
|
136
|
+
# ::Brick.apartment_multitenant && tbl_parts.first == Apartment.default_schema
|
137
|
+
(orig_schema - ['pg_catalog']).first
|
133
138
|
end
|
134
139
|
end
|
135
140
|
|
@@ -174,13 +179,21 @@ module Brick
|
|
174
179
|
|
175
180
|
case a.macro
|
176
181
|
when :belongs_to
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
182
|
+
if a.polymorphic?
|
183
|
+
rel_poly_bt = relations[model.table_name][:fks].find { |_k, fk| fk[:assoc_name] == a.name.to_s }
|
184
|
+
if (primary_tables = rel_poly_bt&.last&.fetch(:inverse_table, [])).is_a?(Array)
|
185
|
+
models = primary_tables&.map { |table| table.singularize.camelize.constantize }
|
186
|
+
s.first[a.foreign_key] = [a.name, models, true]
|
187
|
+
else
|
188
|
+
# This will come up when using Devise invitable when invited_by_class_name is not
|
189
|
+
# specified because in that circumstance it adds a polymorphic :invited_by association,
|
190
|
+
# along with appropriate invited_by_type and invited_by_id columns.
|
191
|
+
puts "Missing any real indication as to which models \"has_many\" this polymorphic BT in model #{a.active_record.name}:"
|
192
|
+
puts " belongs_to :#{a.name}, polymorphic: true"
|
193
|
+
end
|
194
|
+
else
|
195
|
+
s.first[a.foreign_key] = [a.name, a.klass]
|
196
|
+
end
|
184
197
|
when :has_many, :has_one # This gets has_many as well as has_many :through
|
185
198
|
# %%% weed out ones that don't have an available model to reference
|
186
199
|
s.last[a.name] = a
|
@@ -435,7 +448,7 @@ module Brick
|
|
435
448
|
end
|
436
449
|
end
|
437
450
|
if (polys = ::Brick.config.polymorphics)
|
438
|
-
if (schema = ::Brick.config.schema_behavior[:multitenant]&.fetch(:schema_to_analyse, nil)) && ::Brick.db_schemas&.
|
451
|
+
if (schema = ::Brick.config.schema_behavior[:multitenant]&.fetch(:schema_to_analyse, nil)) && ::Brick.db_schemas&.key?(schema)
|
439
452
|
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
|
440
453
|
end
|
441
454
|
missing_stis = {}
|
@@ -525,9 +538,9 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
525
538
|
abstract_ar_bases
|
526
539
|
end
|
527
540
|
|
528
|
-
def display_classes(rels, max_length)
|
541
|
+
def display_classes(prefix, rels, max_length)
|
529
542
|
rels.sort.each do |rel|
|
530
|
-
puts "#{rel.first}#{' ' * (max_length - rel.first.length)} /#{rel.last}"
|
543
|
+
puts "#{rel.first}#{' ' * (max_length - rel.first.length)} /#{prefix}#{rel.last}"
|
531
544
|
end
|
532
545
|
puts "\n"
|
533
546
|
end
|
@@ -556,13 +569,13 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
556
569
|
|
557
570
|
# %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
|
558
571
|
# If auto-controllers and auto-models are both enabled then this makes sense:
|
572
|
+
controller_prefix = (::Brick.config.path_prefix ? "#{::Brick.config.path_prefix}/" : '')
|
559
573
|
::Brick.relations.each do |k, v|
|
560
574
|
unless !(controller_name = v.fetch(:resource, nil)&.pluralize) || existing_controllers.key?(controller_name)
|
561
575
|
options = {}
|
562
576
|
options[:only] = [:index, :show] if v.key?(:isView)
|
563
577
|
# First do the API routes
|
564
578
|
full_resource = nil
|
565
|
-
controller_prefix = (::Brick.config.path_prefix ? "#{::Brick.config.path_prefix}/" : '')
|
566
579
|
if (schema_name = v.fetch(:schema, nil))
|
567
580
|
full_resource = "#{schema_name}/#{v[:resource]}"
|
568
581
|
send(:get, "#{::Brick.api_root}#{full_resource}", { to: "#{controller_prefix}#{schema_name}/#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
|
@@ -595,19 +608,19 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
595
608
|
if tables.present?
|
596
609
|
puts "Classes that can be built from tables:#{' ' * (table_class_length - 38)} Path:"
|
597
610
|
puts "======================================#{' ' * (table_class_length - 38)} ====="
|
598
|
-
::Brick.display_classes(tables, table_class_length)
|
611
|
+
::Brick.display_classes(controller_prefix, tables, table_class_length)
|
599
612
|
end
|
600
613
|
if views.present?
|
601
614
|
puts "Classes that can be built from views:#{' ' * (view_class_length - 37)} Path:"
|
602
615
|
puts "=====================================#{' ' * (view_class_length - 37)} ====="
|
603
|
-
::Brick.display_classes(views, view_class_length)
|
616
|
+
::Brick.display_classes(controller_prefix, views, view_class_length)
|
604
617
|
end
|
605
618
|
|
606
619
|
if ::Brick.config.add_status && instance_variable_get(:@set).named_routes.names.exclude?(:brick_status)
|
607
|
-
get(
|
620
|
+
get("/#{controller_prefix}brick_status", to: 'brick_gem#status', as: 'brick_status')
|
608
621
|
end
|
609
622
|
if ::Brick.config.add_orphans && instance_variable_get(:@set).named_routes.names.exclude?(:brick_orphans)
|
610
|
-
get(
|
623
|
+
get("/#{controller_prefix}brick_orphans", to: 'brick_gem#orphans', as: 'brick_orphans')
|
611
624
|
end
|
612
625
|
if Object.const_defined?('Rswag::Ui') && doc_endpoint = Rswag::Ui.config.config_object[:urls].last
|
613
626
|
# Serves JSON swagger info from a path such as '/api-docs/v1/swagger.json'
|
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.79
|
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-10-
|
11
|
+
date: 2022-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|