brick 1.0.25 → 1.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/brick/config.rb +16 -0
- data/lib/brick/extensions.rb +73 -56
- data/lib/brick/frameworks/rails/engine.rb +52 -38
- data/lib/brick/join_array.rb +1 -1
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +41 -5
- data/lib/generators/brick/install_generator.rb +78 -18
- 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: ab7e6a8198638a0e6d3cde289db17d107e699fad2b4b5a200703cb1982d19569
|
4
|
+
data.tar.gz: 708360bb9463e45c8f7fece60dc762dc65b2b3893d8044ffce9ee87e33f63b80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92229661eff9aac30ec7fad96603bc649a122131956e8632562e22d11c8bae67db0272ec5453ab2cc233bd11f7ab72222c3520f2acceecb83e35d4c54cbd8f4d
|
7
|
+
data.tar.gz: ddc169b2effaf58e60861d0153f191f6f1e2f11d459363a000da2c1f44c1583c8cd037f78269c238a84de58e04d7633c7a7ecbf046c08ef48fdaf5fb01de76a3
|
data/lib/brick/config.rb
CHANGED
@@ -122,6 +122,22 @@ module Brick
|
|
122
122
|
@mutex.synchronize { @sti_namespace_prefixes = prefixes }
|
123
123
|
end
|
124
124
|
|
125
|
+
def schema_to_analyse
|
126
|
+
@mutex.synchronize { @schema_to_analyse }
|
127
|
+
end
|
128
|
+
|
129
|
+
def schema_to_analyse=(schema)
|
130
|
+
@mutex.synchronize { @schema_to_analyse = schema }
|
131
|
+
end
|
132
|
+
|
133
|
+
def default_route_fallback
|
134
|
+
@mutex.synchronize { @default_route_fallback }
|
135
|
+
end
|
136
|
+
|
137
|
+
def default_route_fallback=(resource_name)
|
138
|
+
@mutex.synchronize { @default_route_fallback = resource_name }
|
139
|
+
end
|
140
|
+
|
125
141
|
def skip_database_views
|
126
142
|
@mutex.synchronize { @skip_database_views }
|
127
143
|
end
|
data/lib/brick/extensions.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Some future enhancement ideas:
|
4
|
+
|
3
5
|
# Have markers on HM relationships to indicate "load this one every time" or "lazy load it" or "don't bother"
|
4
6
|
# Others on BT to indicate "this is a lookup"
|
5
7
|
|
@@ -18,7 +20,7 @@
|
|
18
20
|
# Sensitive stuff -- make a lock icon thing so people don't accidentally edit stuff
|
19
21
|
|
20
22
|
# Static text that can go on pages - headings and footers and whatever
|
21
|
-
# Eventually some indication about if it should be a paginated table / unpaginated / a list of just some fields / etc
|
23
|
+
# Eventually some indication about if it should be a paginated table / unpaginated / a list of just some fields / columns shown in a different sequence / etc
|
22
24
|
|
23
25
|
# Grid where each cell is one field and then when you mouse over then it shows a popup other table of detail inside
|
24
26
|
|
@@ -32,6 +34,9 @@
|
|
32
34
|
|
33
35
|
# Currently quadrupling up routes
|
34
36
|
|
37
|
+
# Modal pop-up things for editing large text / date ranges / hierarchies of data
|
38
|
+
|
39
|
+
# For recognised self-references, have the show page display all related objects up to the parent (or the start of a circular reference)
|
35
40
|
|
36
41
|
# ==========================================================
|
37
42
|
# Dynamically create model or controller classes when needed
|
@@ -52,14 +57,6 @@ module Arel
|
|
52
57
|
end
|
53
58
|
end
|
54
59
|
|
55
|
-
# module ActiveModel
|
56
|
-
# class NotNullValidator < EachValidator
|
57
|
-
# def validate_each(record, attribute, value)
|
58
|
-
# record.errors[attribute] << "must not be null" if value.nil?
|
59
|
-
# end
|
60
|
-
# end
|
61
|
-
# end
|
62
|
-
|
63
60
|
module ActiveRecord
|
64
61
|
class Base
|
65
62
|
def self._assoc_names
|
@@ -70,6 +67,14 @@ module ActiveRecord
|
|
70
67
|
false
|
71
68
|
end
|
72
69
|
|
70
|
+
def self._brick_primary_key(relation = nil)
|
71
|
+
return instance_variable_get(:@_brick_primary_key) if instance_variable_defined?(:@_brick_primary_key)
|
72
|
+
|
73
|
+
pk = primary_key.is_a?(String) ? [primary_key] : primary_key || []
|
74
|
+
# Just return [] if we're missing any part of the primary key. (PK is usually just "id")
|
75
|
+
@_brick_primary_key ||= pk.any? { |pk_part| !relation[:cols].key?(pk_part) } ? [] : pk
|
76
|
+
end
|
77
|
+
|
73
78
|
# Used to show a little prettier name for an object
|
74
79
|
def self.brick_get_dsl
|
75
80
|
# If there's no DSL yet specified, just try to find the first usable column on this model
|
@@ -82,7 +87,6 @@ module ActiveRecord
|
|
82
87
|
dsl
|
83
88
|
end
|
84
89
|
|
85
|
-
# Pass in true for build_array, or just pass in a JoinArray
|
86
90
|
def self.brick_parse_dsl(build_array = nil, prefix = [], translations = {}, is_polymorphic = false)
|
87
91
|
build_array = ::Brick::JoinArray.new.tap { |ary| ary.replace([build_array]) } if build_array.is_a?(::Brick::JoinHash)
|
88
92
|
build_array = ::Brick::JoinArray.new unless build_array.nil? || build_array.is_a?(Array)
|
@@ -308,6 +312,8 @@ module ActiveRecord
|
|
308
312
|
if is_add_bts || is_add_hms
|
309
313
|
bts, hms, associatives = ::Brick.get_bts_and_hms(klass)
|
310
314
|
bts.each do |_k, bt|
|
315
|
+
next if bt[2] # Polymorphic?
|
316
|
+
|
311
317
|
# join_array will receive this relation name when calling #brick_parse_dsl
|
312
318
|
bt_descrip[bt.first] = if bt[1].is_a?(Array)
|
313
319
|
bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations, true) }
|
@@ -351,27 +357,24 @@ module ActiveRecord
|
|
351
357
|
next if chains[k1].nil?
|
352
358
|
|
353
359
|
tbl_name = field_tbl_names[v.first][k1] ||= shift_or_first(chains[k1])
|
354
|
-
# if (col_name = v1[1].last&.last) # col_name is weak when there are multiple, using sel_col.last instead
|
355
360
|
field_tbl_name = nil
|
356
|
-
v1.map { |x|
|
357
|
-
|
358
|
-
}.each_with_index do |sel_col, idx|
|
359
|
-
field_tbl_name ||= field_tbl_names[v.first][sel_col.first] ||= shift_or_first(chains[sel_col.first])
|
361
|
+
v1.map { |x| [translations[x[0..-2].map(&:to_s).join('.')], x.last] }.each_with_index do |sel_col, idx|
|
362
|
+
field_tbl_name = field_tbl_names[v.first][sel_col.first] ||= shift_or_first(chains[sel_col.first])
|
360
363
|
|
361
364
|
selects << "#{"#{field_tbl_name}.#{sel_col.last}"} AS \"#{(col_alias = "_brfk_#{v.first}__#{sel_col.last}")}\""
|
362
365
|
v1[idx] << col_alias
|
363
366
|
end
|
364
|
-
# end
|
365
367
|
|
366
|
-
|
368
|
+
unless id_for_tables.key?(v.first)
|
367
369
|
# Accommodate composite primary key by allowing id_col to come in as an array
|
368
|
-
(id_col.is_a?(Array) ? id_col : [id_col]).each do |id_part|
|
369
|
-
|
370
|
-
|
370
|
+
((id_col = k1.primary_key).is_a?(Array) ? id_col : [id_col]).each do |id_part|
|
371
|
+
id_for_tables[v.first] << if id_part
|
372
|
+
selects << "#{"#{tbl_name}.#{id_part}"} AS \"#{(id_alias = "_brfk_#{v.first}__#{id_part}")}\""
|
373
|
+
id_alias
|
374
|
+
end
|
371
375
|
end
|
372
|
-
v1 << id_for_tables[v.first]
|
376
|
+
v1 << id_for_tables[v.first].compact
|
373
377
|
end
|
374
|
-
|
375
378
|
end
|
376
379
|
end
|
377
380
|
join_array.each do |assoc_name|
|
@@ -391,7 +394,8 @@ module ActiveRecord
|
|
391
394
|
else
|
392
395
|
fk_col = hm.foreign_key
|
393
396
|
poly_type = hm.inverse_of.foreign_type if hm.options.key?(:as)
|
394
|
-
hm.klass.primary_key
|
397
|
+
pk = hm.klass.primary_key
|
398
|
+
(pk.is_a?(Array) ? pk.first : pk) || '*'
|
395
399
|
end
|
396
400
|
tbl_alias = "_br_#{hm.name}"
|
397
401
|
pri_tbl = hm.active_record
|
@@ -454,8 +458,7 @@ JOIN (SELECT #{selects.join(', ')}, COUNT(#{count_column}) AS _ct_ FROM #{associ
|
|
454
458
|
this_module.const_get(class_name)
|
455
459
|
else
|
456
460
|
# Build STI subclass and place it into the namespace module
|
457
|
-
|
458
|
-
puts [this_module.const_set(class_name, klass = Class.new(self)).name, class_name].inspect
|
461
|
+
this_module.const_set(class_name, klass = Class.new(self))
|
459
462
|
klass
|
460
463
|
end
|
461
464
|
end
|
@@ -530,7 +533,7 @@ class Object
|
|
530
533
|
singular_table_name = ActiveSupport::Inflector.underscore(model_name)
|
531
534
|
|
532
535
|
# Adjust for STI if we know of a base model for the requested model name
|
533
|
-
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
|
536
|
+
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil) || ::Brick.existing_stis[model_name]&.constantize)
|
534
537
|
base_model.table_name
|
535
538
|
else
|
536
539
|
ActiveSupport::Inflector.pluralize(singular_table_name)
|
@@ -571,7 +574,7 @@ class Object
|
|
571
574
|
return
|
572
575
|
end
|
573
576
|
|
574
|
-
if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
|
577
|
+
if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil) || ::Brick.existing_stis[model_name]&.constantize)
|
575
578
|
is_sti = true
|
576
579
|
else
|
577
580
|
base_model = ::Brick.config.models_inherit_from || ActiveRecord::Base
|
@@ -591,16 +594,14 @@ class Object
|
|
591
594
|
code << " def self.is_view?; true; end\n"
|
592
595
|
end
|
593
596
|
|
594
|
-
# Missing a primary key column? (Usually "id")
|
595
|
-
ar_pks = primary_key.is_a?(String) ? [primary_key] : primary_key || []
|
596
597
|
db_pks = relation[:cols]&.map(&:first)
|
597
|
-
has_pk =
|
598
|
+
has_pk = _brick_primary_key(relation).length.positive? && (db_pks & _brick_primary_key).sort == _brick_primary_key.sort
|
598
599
|
our_pks = relation[:pkey].values.first
|
599
600
|
# No primary key, but is there anything UNIQUE?
|
600
601
|
# (Sort so that if there are multiple UNIQUE constraints we'll pick one that uses the least number of columns.)
|
601
602
|
our_pks = relation[:ukeys].values.sort { |a, b| a.length <=> b.length }.first unless our_pks&.present?
|
602
603
|
if has_pk
|
603
|
-
code << " # Primary key: #{
|
604
|
+
code << " # Primary key: #{_brick_primary_key.join(', ')}\n" unless _brick_primary_key == ['id']
|
604
605
|
elsif our_pks&.present?
|
605
606
|
if our_pks.length > 1 && respond_to?(:'primary_keys=') # Using the composite_primary_keys gem?
|
606
607
|
new_model_class.primary_keys = our_pks
|
@@ -620,10 +621,13 @@ class Object
|
|
620
621
|
# The key in each hash entry (fk.first) is the constraint name
|
621
622
|
inverse_assoc_name = (assoc = fk.last)[:inverse]&.fetch(:assoc_name, nil)
|
622
623
|
if (invs = assoc[:inverse_table]).is_a?(Array)
|
623
|
-
|
624
|
-
|
625
|
-
|
624
|
+
if assoc[:is_bt]
|
625
|
+
invs = invs.first # Just do the first one of what would be multiple identical polymorphic belongs_to
|
626
|
+
else
|
627
|
+
invs.each { |inv| build_bt_or_hm(relations, model_name, relation, hmts, assoc, inverse_assoc_name, inv, code) }
|
628
|
+
end
|
626
629
|
end
|
630
|
+
build_bt_or_hm(relations, model_name, relation, hmts, assoc, inverse_assoc_name, invs, code) unless invs.is_a?(Array)
|
627
631
|
hmts
|
628
632
|
end
|
629
633
|
hmts.each do |hmt_fk, fks|
|
@@ -650,7 +654,7 @@ class Object
|
|
650
654
|
# # Not NULLables
|
651
655
|
# # %%% For the minute we've had to pull this out because it's been troublesome implementing the NotNull validator
|
652
656
|
# relation[:cols].each do |col, datatype|
|
653
|
-
# if (datatype[3] &&
|
657
|
+
# if (datatype[3] && _brick_primary_key.exclude?(col) && ::Brick.config.metadata_columns.exclude?(col)) ||
|
654
658
|
# ::Brick.config.not_nullables.include?("#{matching}.#{col}")
|
655
659
|
# code << " validates :#{col}, not_null: true\n"
|
656
660
|
# self.send(:validates, col.to_sym, { not_null: true })
|
@@ -698,9 +702,8 @@ class Object
|
|
698
702
|
# need_class_name = ActiveSupport::Inflector.singularize(assoc_name) == ActiveSupport::Inflector.singularize(table_name.underscore)
|
699
703
|
# Are there multiple foreign keys out to the same table?
|
700
704
|
assoc_name, need_class_name = _brick_get_hm_assoc_name(relation, assoc)
|
701
|
-
# binding.pry if assoc.key?(:polymorphic)
|
702
705
|
if assoc.key?(:polymorphic)
|
703
|
-
options[:as] = assoc[:fk].to_sym
|
706
|
+
options[:as] = assoc[:fk].to_sym
|
704
707
|
else
|
705
708
|
need_fk = "#{ActiveSupport::Inflector.singularize(assoc[:inverse][:inverse_table])}_id" != assoc[:fk]
|
706
709
|
end
|
@@ -748,13 +751,14 @@ class Object
|
|
748
751
|
def build_controller(class_name, plural_class_name, model, relations)
|
749
752
|
table_name = ActiveSupport::Inflector.underscore(plural_class_name)
|
750
753
|
singular_table_name = ActiveSupport::Inflector.singularize(table_name)
|
754
|
+
pk = model._brick_primary_key(relations[table_name])
|
751
755
|
|
752
756
|
code = +"class #{class_name} < ApplicationController\n"
|
753
757
|
built_controller = Class.new(ActionController::Base) do |new_controller_class|
|
754
758
|
Object.const_set(class_name.to_sym, new_controller_class)
|
755
759
|
|
756
760
|
code << " def index\n"
|
757
|
-
code << " @#{table_name} = #{model.name}#{
|
761
|
+
code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{pk.inspect})" : '.all'}\n"
|
758
762
|
code << " @#{table_name}.brick_select(params)\n"
|
759
763
|
code << " end\n"
|
760
764
|
self.protect_from_forgery unless: -> { self.request.format.js? }
|
@@ -772,7 +776,8 @@ class Object
|
|
772
776
|
return
|
773
777
|
end
|
774
778
|
|
775
|
-
|
779
|
+
order = pk.each_with_object([]) { |pk_part, s| s << "#{model.table_name}.#{pk_part}" }
|
780
|
+
ar_relation = order.present? ? model.order("#{order.join(', ')}") : model.all
|
776
781
|
@_brick_params = ar_relation.brick_select(params, (selects = []), (bt_descrip = {}), (hm_counts = {}), (join_array = ::Brick::JoinArray.new))
|
777
782
|
# %%% Add custom HM count columns
|
778
783
|
# %%% What happens when the PK is composite?
|
@@ -787,11 +792,15 @@ class Object
|
|
787
792
|
|
788
793
|
if model.primary_key
|
789
794
|
code << " def show\n"
|
790
|
-
code << (find_by_id = "
|
795
|
+
code << (find_by_id = " id = params[:id]&.split(/[\\/,_]/)
|
796
|
+
id = id.first if id.is_a?(Array) && id.length == 1
|
797
|
+
@#{singular_table_name} = #{model.name}.find(id)\n")
|
791
798
|
code << " end\n"
|
792
799
|
self.define_method :show do
|
793
800
|
::Brick.set_db_schema(params)
|
794
|
-
|
801
|
+
id = params[:id]&.split(/[\/,_]/)
|
802
|
+
id = id.first if id.is_a?(Array) && id.length == 1
|
803
|
+
instance_variable_set("@#{singular_table_name}".to_sym, model.find(id))
|
795
804
|
end
|
796
805
|
end
|
797
806
|
|
@@ -800,6 +809,10 @@ class Object
|
|
800
809
|
code << " # (Define :new, :create)\n"
|
801
810
|
|
802
811
|
if model.primary_key
|
812
|
+
if (schema = ::Brick.config.schema_to_analyse) && ::Brick.db_schemas&.include?(schema)
|
813
|
+
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
|
814
|
+
end
|
815
|
+
|
803
816
|
is_need_params = true
|
804
817
|
# code << " # (Define :edit, and :destroy)\n"
|
805
818
|
code << " def update\n"
|
@@ -874,12 +887,12 @@ module ActiveRecord::ConnectionHandling
|
|
874
887
|
def _brick_reflect_tables
|
875
888
|
if (relations = ::Brick.relations).empty?
|
876
889
|
# Only for Postgres? (Doesn't work in sqlite3)
|
877
|
-
# puts ActiveRecord::Base.
|
890
|
+
# puts ActiveRecord::Base.execute_sql("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
878
891
|
|
879
892
|
schema_sql = 'SELECT NULL AS table_schema;'
|
880
893
|
case ActiveRecord::Base.connection.adapter_name
|
881
894
|
when 'PostgreSQL'
|
882
|
-
schema = 'public'
|
895
|
+
schema = 'public' # Too early at this point to be able to pick up: Brick.config.schema_to_analyse
|
883
896
|
schema_sql = 'SELECT DISTINCT table_schema FROM INFORMATION_SCHEMA.tables;'
|
884
897
|
when 'Mysql2'
|
885
898
|
schema = ActiveRecord::Base.connection.current_database
|
@@ -923,7 +936,7 @@ module ActiveRecord::ConnectionHandling
|
|
923
936
|
measures = []
|
924
937
|
case ActiveRecord::Base.connection.adapter_name
|
925
938
|
when 'PostgreSQL', 'SQLite' # These bring back a hash for each row because the query uses column aliases
|
926
|
-
ActiveRecord::Base.
|
939
|
+
ActiveRecord::Base.execute_sql(sql).each do |r|
|
927
940
|
# next if internal_views.include?(r['relation_name']) # Skip internal views such as v_all_assessments
|
928
941
|
relation = relations[(relation_name = r['relation_name'])]
|
929
942
|
relation[:isView] = true if r['table_type'] == 'VIEW'
|
@@ -942,7 +955,7 @@ module ActiveRecord::ConnectionHandling
|
|
942
955
|
# puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
|
943
956
|
end
|
944
957
|
else # MySQL2 acts a little differently, bringing back an array for each row
|
945
|
-
ActiveRecord::Base.
|
958
|
+
ActiveRecord::Base.execute_sql(sql).each do |r|
|
946
959
|
# next if internal_views.include?(r['relation_name']) # Skip internal views such as v_all_assessments
|
947
960
|
relation = relations[(relation_name = r[0])] # here relation represents a table or view from the database
|
948
961
|
relation[:isView] = true if r[1] == 'VIEW' # table_type
|
@@ -1008,11 +1021,11 @@ module ActiveRecord::ConnectionHandling
|
|
1008
1021
|
else
|
1009
1022
|
end
|
1010
1023
|
if sql
|
1011
|
-
::Brick.db_schemas = ActiveRecord::Base.
|
1024
|
+
::Brick.db_schemas = ActiveRecord::Base.execute_sql(schema_sql)
|
1012
1025
|
::Brick.db_schemas = ::Brick.db_schemas.to_a unless ::Brick.db_schemas.is_a?(Array)
|
1013
1026
|
::Brick.db_schemas.map! { |row| row['table_schema'] } unless ::Brick.db_schemas.empty? || ::Brick.db_schemas.first.is_a?(String)
|
1014
1027
|
::Brick.db_schemas -= ['information_schema', 'pg_catalog']
|
1015
|
-
ActiveRecord::Base.
|
1028
|
+
ActiveRecord::Base.execute_sql(sql).each do |fk|
|
1016
1029
|
fk = fk.values unless fk.is_a?(Array)
|
1017
1030
|
::Brick._add_bt_and_hm(fk, relations)
|
1018
1031
|
end
|
@@ -1064,12 +1077,8 @@ module Brick
|
|
1064
1077
|
bts = (relation = relations.fetch(fk[0], nil))&.fetch(:fks) { relation[:fks] = {} }
|
1065
1078
|
# %%% Do we miss out on has_many :through or even HM based on constantizing this model early?
|
1066
1079
|
# Maybe it's already gotten this info because we got as far as to say there was a unique class
|
1067
|
-
|
1068
|
-
|
1069
|
-
# else
|
1070
|
-
primary_table = (is_class = fk[2].is_a?(Hash) && fk[2].key?(:class)) ? (primary_class = fk[2][:class].constantize).table_name : fk[2]
|
1071
|
-
hms = (relation = relations.fetch(primary_table, nil))&.fetch(:fks) { relation[:fks] = {} } unless is_class
|
1072
|
-
# end
|
1080
|
+
primary_table = (is_class = fk[2].is_a?(Hash) && fk[2].key?(:class)) ? (primary_class = fk[2][:class].constantize).table_name : fk[2]
|
1081
|
+
hms = (relation = relations.fetch(primary_table, nil))&.fetch(:fks) { relation[:fks] = {} } unless is_class
|
1073
1082
|
|
1074
1083
|
unless (cnstr_name = fk[3])
|
1075
1084
|
# For any appended references (those that come from config), arrive upon a definitely unique constraint name
|
@@ -1102,8 +1111,12 @@ module Brick
|
|
1102
1111
|
if is_polymorphic
|
1103
1112
|
# Assuming same fk (don't yet support composite keys for polymorphics)
|
1104
1113
|
assoc_bt[:inverse_table] << fk[2]
|
1105
|
-
else # Expect we
|
1106
|
-
|
1114
|
+
else # Expect we could have a composite key going
|
1115
|
+
if assoc_bt[:fk].is_a?(String)
|
1116
|
+
assoc_bt[:fk] = [assoc_bt[:fk], fk[1]] unless fk[1] == assoc_bt[:fk]
|
1117
|
+
elsif assoc_bt[:fk].exclude?(fk[1])
|
1118
|
+
assoc_bt[:fk] << fk[1]
|
1119
|
+
end
|
1107
1120
|
assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[1]}"
|
1108
1121
|
end
|
1109
1122
|
else
|
@@ -1121,7 +1134,11 @@ module Brick
|
|
1121
1134
|
return if is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[0] == exclusion[0] && fk[1] == exclusion[1] && primary_table == exclusion[2] }
|
1122
1135
|
|
1123
1136
|
if (assoc_hm = hms.fetch((hm_cnstr_name = "hm_#{cnstr_name}"), nil))
|
1124
|
-
|
1137
|
+
if assoc_hm[:fk].is_a?(String)
|
1138
|
+
assoc_hm[:fk] = [assoc_hm[:fk], fk[1]] unless fk[1] == assoc_hm[:fk]
|
1139
|
+
elsif assoc_hm[:fk].exclude?(fk[1])
|
1140
|
+
assoc_hm[:fk] << fk[1]
|
1141
|
+
end
|
1125
1142
|
assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
|
1126
1143
|
assoc_hm[:inverse] = assoc_bt
|
1127
1144
|
else
|
@@ -71,7 +71,8 @@ module Brick
|
|
71
71
|
keys = if fk_name.is_a?(Array) && pk.is_a?(Array) # Composite keys?
|
72
72
|
fk_name.zip(pk.map { |pk_part| "#{obj_name}.#{pk_part}" })
|
73
73
|
else
|
74
|
-
[
|
74
|
+
pk = pk.each_with_object([]) { |pk_part, s| s << "#{obj_name}.#{pk_part}" }
|
75
|
+
[[fk_name, "#{pk.length == 1 ? pk.first : pk.inspect}"]]
|
75
76
|
end
|
76
77
|
keys << [hm_assoc.inverse_of.foreign_type, "#{hm_assoc.active_record.name}"] if hm_assoc.options.key?(:as)
|
77
78
|
keys.map { |x| "#{x.first}: #{x.last}"}.join(', ')
|
@@ -82,7 +83,7 @@ module Brick
|
|
82
83
|
return _brick_find_template(*args, **options) unless @_brick_model
|
83
84
|
|
84
85
|
model_name = @_brick_model.name
|
85
|
-
pk = @_brick_model.
|
86
|
+
pk = @_brick_model._brick_primary_key(::Brick.relations[model_name])
|
86
87
|
obj_name = model_name.underscore
|
87
88
|
table_name = model_name.pluralize.underscore
|
88
89
|
template_link = nil
|
@@ -112,7 +113,7 @@ module Brick
|
|
112
113
|
"<%= obj = #{obj_name}.#{hm.first}; link_to(obj.brick_descrip, obj) if obj %>\n"
|
113
114
|
end
|
114
115
|
elsif args.first == 'show'
|
115
|
-
hm_stuff << "<%= link_to '#{assoc_name}', #{hm_assoc.klass.name.underscore.pluralize}_path({ #{path_keys(hm_assoc, hm_fk_name, "@#{obj_name}
|
116
|
+
hm_stuff << "<%= link_to '#{assoc_name}', #{hm_assoc.klass.name.underscore.pluralize}_path({ #{path_keys(hm_assoc, hm_fk_name, "@#{obj_name}", pk)} }) %>\n"
|
116
117
|
end
|
117
118
|
s << hm_stuff
|
118
119
|
end
|
@@ -210,16 +211,19 @@ def hide_bcrypt(val)
|
|
210
211
|
end %>"
|
211
212
|
|
212
213
|
if ['index', 'show', 'update'].include?(args.first)
|
214
|
+
poly_cols = []
|
213
215
|
css << "<% bts = { #{
|
214
216
|
bts.each_with_object([]) do |v, s|
|
215
|
-
foreign_models = if v.last[
|
217
|
+
foreign_models = if v.last[2] # Polymorphic?
|
218
|
+
poly_cols << @_brick_model.reflect_on_association(v[1].first).foreign_type
|
216
219
|
v.last[1].each_with_object([]) { |x, s| s << "[#{x.name}, #{x.primary_key.inspect}]" }.join(', ')
|
217
220
|
else
|
218
221
|
"[#{v.last[1].name}, #{v.last[1].primary_key.inspect}]"
|
219
222
|
end
|
220
|
-
s << "#{v.first.inspect} => [#{v.last.first.inspect}, [#{foreign_models}]]"
|
223
|
+
s << "#{v.first.inspect} => [#{v.last.first.inspect}, [#{foreign_models}], #{v.last[2].inspect}]"
|
221
224
|
end.join(', ')
|
222
|
-
} }
|
225
|
+
} }
|
226
|
+
poly_cols = #{poly_cols.inspect} %>"
|
223
227
|
end
|
224
228
|
|
225
229
|
# %%% When doing schema select, if there's an ID then remove it, or if we're on a new page go to index
|
@@ -280,7 +284,7 @@ function changeout(href, param, value) {
|
|
280
284
|
inline = case args.first
|
281
285
|
when 'index'
|
282
286
|
obj_pk = if pk&.is_a?(Array) # Composite primary key?
|
283
|
-
"[#{pk.map { |pk_part| "#{obj_name}.#{pk_part}" }.join(', ')}]"
|
287
|
+
"[#{pk.map { |pk_part| "#{obj_name}.#{pk_part}" }.join(', ')}]" unless pk.empty?
|
284
288
|
elsif pk
|
285
289
|
"#{obj_name}.#{pk}"
|
286
290
|
end
|
@@ -382,9 +386,9 @@ function changeout(href, param, value) {
|
|
382
386
|
|
383
387
|
<% if @_brick_params&.present? %><h3>where <%= @_brick_params.each_with_object([]) { |v, s| s << \"#\{v.first\} = #\{v.last.inspect\}\" }.join(', ') %></h3><% end %>
|
384
388
|
<table id=\"#{table_name}\">
|
385
|
-
<thead><tr>#{'<th></th>' if pk}
|
389
|
+
<thead><tr>#{'<th></th>' if pk.present?}
|
386
390
|
<% @#{table_name}.columns.map(&:name).each do |col| %>
|
387
|
-
<% next if
|
391
|
+
<% next if #{pk.inspect}.include?(col) || ::Brick.config.metadata_columns.include?(col) || poly_cols.include?(col) %>
|
388
392
|
<th>
|
389
393
|
<% if (bt = bts[col]) %>
|
390
394
|
BT <%
|
@@ -405,22 +409,22 @@ function changeout(href, param, value) {
|
|
405
409
|
<tr>#{"
|
406
410
|
<td><%= link_to '⇛', #{obj_name}_path(#{obj_pk}), { class: 'big-arrow' } %></td>" if obj_pk}
|
407
411
|
<% #{obj_name}.attributes.each do |k, val| %>
|
408
|
-
<% next if
|
412
|
+
<% next if #{obj_pk.inspect}.include?(k) || ::Brick.config.metadata_columns.include?(k) || poly_cols.include?(k) || k.start_with?('_brfk_') || (k.start_with?('_br_') && (k.length == 63 || k.end_with?('_ct'))) %>
|
409
413
|
<td>
|
410
414
|
<% if (bt = bts[k]) %>
|
411
415
|
<%# binding.pry # Postgres column names are limited to 63 characters %>
|
412
|
-
<% if
|
416
|
+
<% if bt[2] # Polymorphic?
|
413
417
|
bt_class = #{obj_name}.send(\"#\{bt.first\}_type\")
|
414
|
-
|
418
|
+
base_class = (::Brick.existing_stis[bt_class] || bt_class).constantize.base_class.name.underscore
|
415
419
|
poly_id = #{obj_name}.send(\"#\{bt.first\}_id\")
|
416
420
|
%><%= link_to(\"#\{bt_class\} ##\{poly_id\}\",
|
417
|
-
send(\"#\{
|
418
|
-
else
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
<%= bt_id ? link_to(bt_txt, send(\"#\{bt_class.name.underscore\}_path\".to_sym, bt_id)) : bt_txt %>
|
421
|
+
send(\"#\{base_class\}_path\".to_sym, poly_id)) if poly_id %><%
|
422
|
+
else
|
423
|
+
bt_txt = (bt_class = bt[1].first.first).brick_descrip(
|
424
|
+
#{obj_name}, (descrips = @_brick_bt_descrip[bt.first][bt_class])[0..-2].map { |z| #{obj_name}.send(z.last[0..62]) }, (bt_id_col = descrips.last)
|
425
|
+
)
|
426
|
+
bt_id = #{obj_name}.send(*bt_id_col) if bt_id_col&.present? %>
|
427
|
+
<%= bt_id ? link_to(bt_txt, send(\"#\{bt_class.base_class.name.underscore\}_path\".to_sym, bt_id)) : bt_txt %>
|
424
428
|
<%#= Previously was: bt_obj = bt[1].first.first.find_by(bt[2] => val); link_to(bt_obj.brick_descrip, send(\"#\{bt[1].first.first.name.underscore\}_path\".to_sym, bt_obj.send(bt[1].first.first.primary_key.to_sym))) if bt_obj %>
|
425
429
|
<% end %>
|
426
430
|
<% else %>
|
@@ -441,7 +445,7 @@ function changeout(href, param, value) {
|
|
441
445
|
<p style=\"color: green\"><%= notice %></p>#{"
|
442
446
|
<select id=\"schema\">#{schema_options}</select>" if ::Brick.db_schemas.length > 1}
|
443
447
|
<select id=\"tbl\">#{table_options}</select>
|
444
|
-
<h1>#{model_name}: <%= (obj = @#{obj_name}
|
448
|
+
<h1>#{model_name}: <%= (obj = @#{obj_name})&.brick_descrip || controller_name %></h1>
|
445
449
|
<%= link_to '(See all #{obj_name.pluralize})', #{table_name}_path %>
|
446
450
|
<% if obj %>
|
447
451
|
<%= # path_options = [obj.#{pk}]
|
@@ -450,32 +454,42 @@ function changeout(href, param, value) {
|
|
450
454
|
form_for(obj.becomes(#{model_name})) do |f| %>
|
451
455
|
<table>
|
452
456
|
<% has_fields = false
|
453
|
-
@#{obj_name}.
|
457
|
+
@#{obj_name}.attributes.each do |k, val| %>
|
454
458
|
<tr>
|
455
459
|
<%# %%% Accommodate composite keys %>
|
456
|
-
<% next if
|
460
|
+
<% next if #{pk}.include?(k) || ::Brick.config.metadata_columns.include?(k) %>
|
457
461
|
<th class=\"show-field\">
|
458
462
|
<% has_fields = true
|
459
463
|
if (bt = bts[k])
|
460
464
|
# Add a final member in this array with descriptive options to be used in <select> drop-downs
|
461
465
|
bt_name = bt[1].map { |x| x.first.name }.join('/')
|
462
466
|
# %%% Only do this if the user has permissions to edit this bt field
|
463
|
-
if
|
464
|
-
poly_class_name = @#{obj_name}.
|
465
|
-
bt_pair =
|
467
|
+
if bt[2] # Polymorphic?
|
468
|
+
poly_class_name = orig_poly_name = @#{obj_name}.send(\"#\{bt.first\}_type\")
|
469
|
+
bt_pair = nil
|
470
|
+
loop do
|
471
|
+
bt_pair = bt[1].find { |pair| pair.first.name == poly_class_name }
|
472
|
+
# Acxommodate any valid STI by going up the chain of inheritance
|
473
|
+
break unless bt_pair.nil? && poly_class_name = ::Brick.existing_stis[poly_class_name]
|
474
|
+
end
|
475
|
+
puts \"*** Might be missing an STI class called #\{orig_poly_name\} whose base class should have this:
|
476
|
+
*** has_many :#{table_name}, as: :#\{bt.first\}
|
477
|
+
*** Can probably auto-configure everything using these lines in an initialiser:
|
478
|
+
*** Brick.sti_namespace_prefixes = { '::#\{orig_poly_name\}' => 'SomeParentModel' }
|
479
|
+
*** Brick.polymorphics = { '#{table_name}.#\{bt.first\}' => ['SomeParentModel'] }\" if bt_pair.nil?
|
466
480
|
# descrips = @_brick_bt_descrip[bt.first][bt_class]
|
467
|
-
poly_id = @#{obj_name}.
|
481
|
+
poly_id = @#{obj_name}.send(\"#\{bt.first\}_id\")
|
468
482
|
# bt_class.order(obj_pk = bt_class.primary_key).each { |obj| option_detail << [obj.brick_descrip(nil, obj_pk), obj.send(obj_pk)] }
|
469
483
|
else # No polymorphism, so just get the first one
|
470
484
|
bt_pair = bt[1].first
|
471
485
|
end
|
472
|
-
bt_class = bt_pair
|
473
|
-
if bt.length <
|
486
|
+
bt_class = bt_pair&.first
|
487
|
+
if bt.length < 4
|
474
488
|
bt << (option_detail = [[\"(No #\{bt_name\} chosen)\", '^^^brick_NULL^^^']])
|
475
489
|
# %%% Accommodate composite keys for obj.pk at the end here
|
476
|
-
bt_class
|
490
|
+
bt_class&.order(obj_pk = bt_class.primary_key)&.each { |obj| option_detail << [obj.brick_descrip(nil, obj_pk), obj.send(obj_pk)] }
|
477
491
|
end %>
|
478
|
-
BT <%= bt_class
|
492
|
+
BT <%= bt_class&.bt_link(bt.first) || orig_poly_name %>
|
479
493
|
<% else %>
|
480
494
|
<%= k %>
|
481
495
|
<% end %>
|
@@ -484,8 +498,8 @@ function changeout(href, param, value) {
|
|
484
498
|
<% if bt
|
485
499
|
html_options = { prompt: \"Select #\{bt_name\}\" }
|
486
500
|
html_options[:class] = 'dimmed' unless val %>
|
487
|
-
<%= f.select k.to_sym, bt[
|
488
|
-
<%= bt_obj = bt_class
|
501
|
+
<%= f.select k.to_sym, bt[3], { value: val || '^^^brick_NULL^^^' }, html_options %>
|
502
|
+
<%= bt_obj = bt_class&.find_by(bt_pair[1] => val); link_to('⇛', send(\"#\{bt_class.base_class.name.underscore\}_path\".to_sym, bt_obj.send(bt_class.primary_key.to_sym)), { class: 'show-arrow' }) if bt_obj %>
|
489
503
|
<% else case #{model_name}.column_for_attribute(k).type
|
490
504
|
when :string, :text %>
|
491
505
|
<% if is_bcrypt?(val) # || .readonly? %>
|
@@ -517,16 +531,17 @@ function changeout(href, param, value) {
|
|
517
531
|
|
518
532
|
#{hms_headers.each_with_object(+'') do |hm, s|
|
519
533
|
if (pk = hm.first.klass.primary_key)
|
520
|
-
|
534
|
+
hm_singular_name = (hm_name = hm.first.name.to_s).singularize.underscore
|
535
|
+
obj_pk = (pk.is_a?(Array) ? pk : [pk]).each_with_object([]) { |pk_part, s| s << "#{hm_singular_name}.#{pk_part}" }.join(', ')
|
536
|
+
s << "<table id=\"#{hm_name}\">
|
521
537
|
<tr><th>#{hm[3]}</th></tr>
|
522
|
-
<% collection = @#{obj_name}
|
538
|
+
<% collection = @#{obj_name}.#{hm_name}
|
523
539
|
collection = collection.is_a?(ActiveRecord::Associations::CollectionProxy) ? collection.order(#{pk.inspect}) : [collection]
|
524
540
|
if collection.empty? %>
|
525
541
|
<tr><td>(none)</td></tr>
|
526
542
|
<% else %>
|
527
|
-
<% collection.uniq.each do |#{hm_singular_name
|
528
|
-
|
529
|
-
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm.first.klass.name.underscore}_path(#{hm_singular_name}.#{pk})) %></td></tr>
|
543
|
+
<% collection.uniq.each do |#{hm_singular_name}| %>
|
544
|
+
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm.first.klass.name.underscore}_path([#{obj_pk}])) %></td></tr>
|
530
545
|
<% end %>
|
531
546
|
<% end %>
|
532
547
|
</table>"
|
@@ -538,7 +553,6 @@ function changeout(href, param, value) {
|
|
538
553
|
#{script}"
|
539
554
|
|
540
555
|
end
|
541
|
-
puts inline
|
542
556
|
# As if it were an inline template (see #determine_template in actionview-5.2.6.2/lib/action_view/renderer/template_renderer.rb)
|
543
557
|
keys = options.has_key?(:locals) ? options[:locals].keys : []
|
544
558
|
handler = ActionView::Template.handler_for_extension(options[:type] || 'erb')
|
data/lib/brick/join_array.rb
CHANGED
@@ -213,7 +213,7 @@ module Brick
|
|
213
213
|
puts "X3"
|
214
214
|
super(key, ::Brick::JoinArray.new.replace([current, value]))
|
215
215
|
when ::Brick::JoinArray # Concatenate new stuff onto any existing JoinArray
|
216
|
-
current.set_matching(value, nil)
|
216
|
+
current.set_matching(value, nil) if value
|
217
217
|
when ::Brick::JoinHash # Graduate an existing hash into being in an array if things are dissimilar
|
218
218
|
super(key, ::Brick::JoinArray.new.replace([current, value]))
|
219
219
|
value
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -85,12 +85,16 @@ module Brick
|
|
85
85
|
@sti_models ||= {}
|
86
86
|
end
|
87
87
|
|
88
|
+
def self.existing_stis
|
89
|
+
@existing_stis ||= Brick.config.sti_namespace_prefixes.each_with_object({}) { |snp, s| s[snp.first[2..-1]] = snp.last unless snp.first.end_with?('::') }
|
90
|
+
end
|
91
|
+
|
88
92
|
class << self
|
89
93
|
attr_accessor :db_schemas
|
90
94
|
|
91
95
|
def set_db_schema(params)
|
92
96
|
schema = params['_brick_schema'] || 'public'
|
93
|
-
ActiveRecord::Base.
|
97
|
+
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema) if schema && ::Brick.db_schemas&.include?(schema)
|
94
98
|
end
|
95
99
|
|
96
100
|
# All tables and views (what Postgres calls "relations" including column and foreign key info)
|
@@ -103,14 +107,14 @@ module Brick
|
|
103
107
|
|
104
108
|
def get_bts_and_hms(model)
|
105
109
|
bts, hms = model.reflect_on_all_associations.each_with_object([{}, {}]) do |a, s|
|
106
|
-
next if
|
110
|
+
next if !const_defined?(a.name.to_s.singularize.camelize) && ::Brick.config.exclude_tables.include?(a.plural_name)
|
107
111
|
|
108
112
|
case a.macro
|
109
113
|
when :belongs_to
|
110
114
|
s.first[a.foreign_key] = if a.polymorphic?
|
111
115
|
primary_tables = relations[model.table_name][:fks].find { |_k, fk| fk[:assoc_name] == a.name.to_s }&.last&.fetch(:inverse_table, [])
|
112
116
|
models = primary_tables&.map { |table| table.singularize.camelize.constantize }
|
113
|
-
[a.name, models]
|
117
|
+
[a.name, models, true]
|
114
118
|
else
|
115
119
|
[a.name, a.klass]
|
116
120
|
end
|
@@ -264,6 +268,7 @@ module Brick
|
|
264
268
|
|
265
269
|
# Polymorphic associations
|
266
270
|
def polymorphics=(polys)
|
271
|
+
polys = polys.each_with_object({}) { |poly, s| s[poly] = nil } if polys.is_a?(Array)
|
267
272
|
Brick.config.polymorphics = polys || {}
|
268
273
|
end
|
269
274
|
|
@@ -279,6 +284,17 @@ module Brick
|
|
279
284
|
Brick.config.sti_namespace_prefixes = snp
|
280
285
|
end
|
281
286
|
|
287
|
+
# Database schema to use when analysing existing data, such as deriving a list of polymorphic classes
|
288
|
+
# for polymorphics in which it wasn't originally specified.
|
289
|
+
# @api public
|
290
|
+
def schema_to_analyse=(schema)
|
291
|
+
Brick.config.schema_to_analyse = schema
|
292
|
+
end
|
293
|
+
|
294
|
+
def default_route_fallback=(resource_name)
|
295
|
+
Brick.config.default_route_fallback = resource_name
|
296
|
+
end
|
297
|
+
|
282
298
|
# Load additional references (virtual foreign keys)
|
283
299
|
# 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
|
284
300
|
# %%% Maybe look for differences the second time 'round and just add new stuff instead of entirely deferring
|
@@ -289,13 +305,30 @@ module Brick
|
|
289
305
|
if (ars = ::Brick.config.additional_references) || ::Brick.config.polymorphics
|
290
306
|
ars.each { |fk| ::Brick._add_bt_and_hm(fk[0..2], relations) } if ars
|
291
307
|
if (polys = ::Brick.config.polymorphics)
|
308
|
+
if (schema = ::Brick.config.schema_to_analyse) && ::Brick.db_schemas&.include?(schema)
|
309
|
+
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
|
310
|
+
end
|
311
|
+
missing_stis = {}
|
292
312
|
polys.each do |k, v|
|
293
313
|
table_name, poly = k.split('.')
|
294
314
|
v ||= ActiveRecord::Base.execute_sql("SELECT DISTINCT #{poly}_type AS typ FROM #{table_name}").map { |result| result['typ'] }
|
295
315
|
v.each do |type|
|
296
|
-
|
316
|
+
if relations.key?(primary_table = type.underscore.pluralize)
|
317
|
+
::Brick._add_bt_and_hm([table_name, poly, primary_table, "(brick) #{table_name}_#{poly}"], relations, true)
|
318
|
+
else
|
319
|
+
missing_stis[primary_table] = type unless ::Brick.existing_stis.key?(type)
|
320
|
+
end
|
297
321
|
end
|
298
322
|
end
|
323
|
+
unless missing_stis.empty?
|
324
|
+
print "
|
325
|
+
You might be missing an STI namespace prefix entry for these tables: #{missing_stis.keys.join(', ')}.
|
326
|
+
In config/initializers/brick.rb appropriate entries would look something like:
|
327
|
+
Brick.sti_namespace_prefixes = {"
|
328
|
+
puts missing_stis.map { |_k, missing_sti| "\n '::#{missing_sti}' => 'SomeParentModel'" }.join(',')
|
329
|
+
puts " }
|
330
|
+
(Just trade out SomeParentModel with some more appropriate one.)"
|
331
|
+
end
|
299
332
|
end
|
300
333
|
@_additional_references_loaded = true
|
301
334
|
end
|
@@ -352,6 +385,9 @@ module Brick
|
|
352
385
|
def finalize!
|
353
386
|
existing_controllers = routes.each_with_object({}) { |r, s| c = r.defaults[:controller]; s[c] = nil if c }
|
354
387
|
::Rails.application.routes.append do
|
388
|
+
unless ::Brick.config.default_route_fallback.blank? || ::Rails.application.routes.named_routes.send(:routes)[:root]
|
389
|
+
send(:root, "#{::Brick.config.default_route_fallback}#index")
|
390
|
+
end
|
355
391
|
# %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
|
356
392
|
# If auto-controllers and auto-models are both enabled then this makes sense:
|
357
393
|
::Brick.relations.each do |k, v|
|
@@ -415,7 +451,7 @@ ActiveSupport.on_load(:active_record) do
|
|
415
451
|
relation = clone # spawn
|
416
452
|
relation.select_values = column_names
|
417
453
|
result = if klass.connection.class.name.end_with?('::PostgreSQLAdapter')
|
418
|
-
rslt = klass.
|
454
|
+
rslt = klass.execute_sql(relation.arel.to_sql)
|
419
455
|
rslt.type_map =
|
420
456
|
@type_map ||= proc do
|
421
457
|
# This aliasing avoids the warning:
|
@@ -19,11 +19,31 @@ module Brick
|
|
19
19
|
|
20
20
|
def create_initializer_file
|
21
21
|
unless File.exist?(filename = 'config/initializers/brick.rb')
|
22
|
-
# See if we can make suggestions for additional_references
|
23
|
-
resembles_fks = []
|
24
|
-
|
25
|
-
|
22
|
+
# See if we can make suggestions for additional_references and polymorphic associations
|
23
|
+
resembles_fks = Hash.new { |h, k| h[k] = [] }
|
24
|
+
possible_polymorphics = {}
|
25
|
+
possible_additional_references = (relations = ::Brick.relations).each_with_object(Hash.new { |h, k| h[k] = [] }) do |v, s|
|
26
|
+
model_filename = "app/models/#{ActiveSupport::Inflector.singularize(v.first)}.rb"
|
27
|
+
v.last[:cols].each do |col, type|
|
26
28
|
col_down = col.downcase
|
29
|
+
|
30
|
+
if (is_possible_poly = ['character varying', 'text'].include?(type.first))
|
31
|
+
if col_down.end_with?('_type') &&
|
32
|
+
poly_type_cut_length = -6
|
33
|
+
col_down = col_down[0..-6]
|
34
|
+
elsif col_down.end_with?('type')
|
35
|
+
poly_type_cut_length = -5
|
36
|
+
col_down = col_down[0..-5]
|
37
|
+
else
|
38
|
+
is_possible_poly = false
|
39
|
+
end
|
40
|
+
is_possible_poly = false if col_down.length < 6 # Was it simply called "type" or something else really short?
|
41
|
+
if is_possible_poly && !File.exist?(model_filename) # Make sure a model file isn't present
|
42
|
+
possible_polymorphics["#{v.first}.#{col_down}"] = "'#{v.first}.#{col[0..poly_type_cut_length]}'"
|
43
|
+
next
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
27
47
|
is_possible = true
|
28
48
|
if col_down.end_with?('_id')
|
29
49
|
col_down = col_down[0..-4]
|
@@ -40,30 +60,48 @@ module Brick
|
|
40
60
|
if col_down.start_with?('fk_')
|
41
61
|
is_possible = true
|
42
62
|
col_down = col_down[3..-1]
|
63
|
+
elsif col_down.start_with?('fk')
|
64
|
+
is_possible = true
|
65
|
+
col_down = col_down[2..-1]
|
43
66
|
end
|
44
67
|
# This possible key not really a primary key and not yet used as a foreign key?
|
45
68
|
if is_possible && !(relation = relations.fetch(v.first, {}))[:pkey].first&.last&.include?(col) &&
|
46
69
|
!relations.fetch(v.first, {})[:fks]&.any? { |_k, v| v[:is_bt] && v[:fk] == col }
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
70
|
+
# Starting to look promising ... make sure a model file isn't present
|
71
|
+
if !File.exist?(model_filename)
|
72
|
+
if (relations.fetch(f_table = col_down, nil) ||
|
73
|
+
relations.fetch(f_table = ActiveSupport::Inflector.pluralize(col_down), nil)) &&
|
74
|
+
s["#{v.first}.#{col_down}"] << "['#{v.first}', '#{col}', '#{f_table}']"
|
75
|
+
else
|
76
|
+
resembles_fks["#{v.first}.#{col_down}"] << "#{v.first}.#{col}"
|
77
|
+
end
|
54
78
|
end
|
55
79
|
end
|
56
80
|
end
|
57
|
-
s
|
58
81
|
end
|
59
82
|
|
60
|
-
|
83
|
+
possible_polymorphics.each_key do |k|
|
84
|
+
# Also matching one of the FK suggestions means it could be polymorphic,
|
85
|
+
# so delete any suggestions for a FK of the same name and only recommend
|
86
|
+
# the polymorphic association.
|
87
|
+
if resembles_fks.key?(k)
|
88
|
+
resembles_fks.delete(k)
|
89
|
+
elsif possible_additional_references.key?(k)
|
90
|
+
possible_additional_references.delete(k)
|
91
|
+
else
|
92
|
+
# While this one has a type, it's missing a corresponding ID column so it isn't polymorphic
|
93
|
+
possible_polymorphics.delete(k)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
resembles_fks = resembles_fks.values.flatten
|
97
|
+
|
98
|
+
bar = case (possible_additional_references = possible_additional_references.values.flatten).length
|
61
99
|
when 0
|
62
100
|
+"# Brick.additional_references = [['orders', 'customer_id', 'customer'],
|
63
101
|
# ['customer', 'region_id', 'regions']]"
|
64
102
|
when 1
|
65
103
|
+"# # Here is a possible additional reference that has been auto-identified for the #{ActiveRecord::Base.connection.current_database} database:
|
66
|
-
# Brick.additional_references = [
|
104
|
+
# Brick.additional_references = [#{possible_additional_references.first}]"
|
67
105
|
else
|
68
106
|
+"# # Here are possible additional references that have been auto-identified for the #{ActiveRecord::Base.connection.current_database} database:
|
69
107
|
# Brick.additional_references = [
|
@@ -75,6 +113,26 @@ module Brick
|
|
75
113
|
# # #{resembles_fks.join(', ')}"
|
76
114
|
end
|
77
115
|
|
116
|
+
poly = case (possible_polymorphics = possible_polymorphics.values.flatten.map { |poss_poly| "#{poss_poly} => nil"}).length
|
117
|
+
when 0
|
118
|
+
" like this:
|
119
|
+
# Brick.polymorphics = {
|
120
|
+
# 'comments.commentable' => nil,
|
121
|
+
# 'images.imageable' => nil
|
122
|
+
# }"
|
123
|
+
when 1
|
124
|
+
".
|
125
|
+
# # Here is a possible polymorphic association that has been auto-identified for the #{ActiveRecord::Base.connection.current_database} database:
|
126
|
+
# Brick.polymorphics = { #{possible_additional_references.first} }"
|
127
|
+
|
128
|
+
else
|
129
|
+
".
|
130
|
+
# # Here are possible polymorphic associations that have been auto-identified for the #{ActiveRecord::Base.connection.current_database} database:
|
131
|
+
# Brick.polymorphics = {
|
132
|
+
# #{possible_polymorphics.join(",\n# ")}
|
133
|
+
# }"
|
134
|
+
end
|
135
|
+
|
78
136
|
create_file(filename, "# frozen_string_literal: true
|
79
137
|
|
80
138
|
# # Settings for the Brick gem
|
@@ -119,7 +177,7 @@ module Brick
|
|
119
177
|
# # Skip showing counts for these specific has_many associations when building auto-generated #index views.
|
120
178
|
# # When there are related tables with a significant number of records, this can lessen the load on the database
|
121
179
|
# # considerably, sometimes fixing what might appear to be an index page that just \"hangs\" for no apparent reason.
|
122
|
-
Brick.skip_index_hms = ['User.litany_of_woes']
|
180
|
+
# Brick.skip_index_hms = ['User.litany_of_woes']
|
123
181
|
|
124
182
|
# # By default primary tables involved in a foreign key relationship will indicate a \"has_many\" relationship pointing
|
125
183
|
# # back to the foreign table. In order to represent a \"has_one\" association instead, an override can be provided
|
@@ -157,9 +215,11 @@ Brick.skip_index_hms = ['User.litany_of_woes']
|
|
157
215
|
# Brick.sti_namespace_prefixes = { '::Animals::' => 'Animal',
|
158
216
|
# '::Snake' => 'Reptile' }
|
159
217
|
|
160
|
-
# #
|
161
|
-
# #
|
162
|
-
# Brick.
|
218
|
+
# # Database schema to use when analysing existing data, such as deriving a list of polymorphic classes in the case that
|
219
|
+
# # it wasn't originally specified.
|
220
|
+
# Brick.schema_to_analyse = 'engineering'
|
221
|
+
|
222
|
+
# # Polymorphic associations are set up by providing a model name and polymorphic association name#{poly}
|
163
223
|
|
164
224
|
# # If a default route is not supplied, Brick attempts to find the most \"central\" table and wires up the default
|
165
225
|
# # route to go to the :index action for what would be a controller for that table. You can specify any controller
|
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.28
|
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-05-
|
11
|
+
date: 2022-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|