brick 1.0.26 → 1.0.29
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 +8 -0
- data/lib/brick/extensions.rb +61 -35
- data/lib/brick/frameworks/rails/engine.rb +41 -24
- data/lib/brick/join_array.rb +1 -1
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +10 -3
- data/lib/generators/brick/install_generator.rb +8 -8
- 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: 1a312c2fd5baf0793b4bb4143167b4399c70a514991206a611d525d0784de95b
|
4
|
+
data.tar.gz: bb1af47d2d073dab2c046ff9757fb5db9447f02dd0688330ad7ac8ad678d6751
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbfabaf60608b2094e9903cb538bab00706f434f94289b9647320bb128adf0b34317a6404ace34897cf4b2f91700ebd7c028a12b4072ba1ebf9de3380f75add3
|
7
|
+
data.tar.gz: 6e5444ac763c71de71fc1aca64cdb41db6276f1df57f5b556b03bad0b743373f2720865ba7c6ce36f9887d77ffee3b091f32d233121742c4786ca21e8d512705
|
data/lib/brick/config.rb
CHANGED
@@ -130,6 +130,14 @@ module Brick
|
|
130
130
|
@mutex.synchronize { @schema_to_analyse = schema }
|
131
131
|
end
|
132
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
|
+
|
133
141
|
def skip_database_views
|
134
142
|
@mutex.synchronize { @skip_database_views }
|
135
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
|
@@ -62,6 +67,18 @@ module ActiveRecord
|
|
62
67
|
false
|
63
68
|
end
|
64
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
|
+
if relation && pk.present?
|
76
|
+
@_brick_primary_key ||= pk.any? { |pk_part| !relation[:cols].key?(pk_part) } ? [] : pk
|
77
|
+
else # No definitive key yet, so return what we can without setting the instance variable
|
78
|
+
pk
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
65
82
|
# Used to show a little prettier name for an object
|
66
83
|
def self.brick_get_dsl
|
67
84
|
# If there's no DSL yet specified, just try to find the first usable column on this model
|
@@ -290,7 +307,7 @@ module ActiveRecord
|
|
290
307
|
# %%% Skip the metadata columns
|
291
308
|
if selects&.empty? # Default to all columns
|
292
309
|
columns.each do |col|
|
293
|
-
selects << "#{table.name}
|
310
|
+
selects << "\"#{table.name}\".\"#{col.name}\""
|
294
311
|
end
|
295
312
|
end
|
296
313
|
|
@@ -344,23 +361,19 @@ module ActiveRecord
|
|
344
361
|
next if chains[k1].nil?
|
345
362
|
|
346
363
|
tbl_name = field_tbl_names[v.first][k1] ||= shift_or_first(chains[k1])
|
347
|
-
# if (col_name = v1[1].last&.last) # col_name is weak when there are multiple, using sel_col.last instead
|
348
364
|
field_tbl_name = nil
|
349
|
-
v1.map { |x|
|
350
|
-
|
351
|
-
}.each_with_index do |sel_col, idx|
|
352
|
-
field_tbl_name ||= field_tbl_names[v.first][sel_col.first] ||= shift_or_first(chains[sel_col.first])
|
365
|
+
v1.map { |x| [translations[x[0..-2].map(&:to_s).join('.')], x.last] }.each_with_index do |sel_col, idx|
|
366
|
+
field_tbl_name = field_tbl_names[v.first][sel_col.first] ||= shift_or_first(chains[sel_col.first])
|
353
367
|
|
354
|
-
selects << "#{"#{field_tbl_name}
|
368
|
+
selects << "#{"\"#{field_tbl_name}\".\"#{sel_col.last}\""} AS \"#{(col_alias = "_brfk_#{v.first}__#{sel_col.last}")}\""
|
355
369
|
v1[idx] << col_alias
|
356
370
|
end
|
357
|
-
# end
|
358
371
|
|
359
372
|
unless id_for_tables.key?(v.first)
|
360
373
|
# Accommodate composite primary key by allowing id_col to come in as an array
|
361
374
|
((id_col = k1.primary_key).is_a?(Array) ? id_col : [id_col]).each do |id_part|
|
362
375
|
id_for_tables[v.first] << if id_part
|
363
|
-
selects << "#{"#{tbl_name}
|
376
|
+
selects << "#{"\"#{tbl_name}\".\"#{id_part}\""} AS \"#{(id_alias = "_brfk_#{v.first}__#{id_part}")}\""
|
364
377
|
id_alias
|
365
378
|
end
|
366
379
|
end
|
@@ -385,7 +398,8 @@ module ActiveRecord
|
|
385
398
|
else
|
386
399
|
fk_col = hm.foreign_key
|
387
400
|
poly_type = hm.inverse_of.foreign_type if hm.options.key?(:as)
|
388
|
-
hm.klass.primary_key
|
401
|
+
pk = hm.klass.primary_key
|
402
|
+
(pk.is_a?(Array) ? pk.first : pk) || '*'
|
389
403
|
end
|
390
404
|
tbl_alias = "_br_#{hm.name}"
|
391
405
|
pri_tbl = hm.active_record
|
@@ -523,7 +537,7 @@ class Object
|
|
523
537
|
singular_table_name = ActiveSupport::Inflector.underscore(model_name)
|
524
538
|
|
525
539
|
# Adjust for STI if we know of a base model for the requested model name
|
526
|
-
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, ::Brick.existing_stis[model_name]&.constantize)
|
540
|
+
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil) || ::Brick.existing_stis[model_name]&.constantize)
|
527
541
|
base_model.table_name
|
528
542
|
else
|
529
543
|
ActiveSupport::Inflector.pluralize(singular_table_name)
|
@@ -564,7 +578,7 @@ class Object
|
|
564
578
|
return
|
565
579
|
end
|
566
580
|
|
567
|
-
if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, ::Brick.existing_stis[model_name]&.constantize)
|
581
|
+
if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil) || ::Brick.existing_stis[model_name]&.constantize)
|
568
582
|
is_sti = true
|
569
583
|
else
|
570
584
|
base_model = ::Brick.config.models_inherit_from || ActiveRecord::Base
|
@@ -584,16 +598,14 @@ class Object
|
|
584
598
|
code << " def self.is_view?; true; end\n"
|
585
599
|
end
|
586
600
|
|
587
|
-
# Missing a primary key column? (Usually "id")
|
588
|
-
ar_pks = primary_key.is_a?(String) ? [primary_key] : primary_key || []
|
589
601
|
db_pks = relation[:cols]&.map(&:first)
|
590
|
-
has_pk =
|
602
|
+
has_pk = _brick_primary_key(relation).length.positive? && (db_pks & _brick_primary_key).sort == _brick_primary_key.sort
|
591
603
|
our_pks = relation[:pkey].values.first
|
592
604
|
# No primary key, but is there anything UNIQUE?
|
593
605
|
# (Sort so that if there are multiple UNIQUE constraints we'll pick one that uses the least number of columns.)
|
594
606
|
our_pks = relation[:ukeys].values.sort { |a, b| a.length <=> b.length }.first unless our_pks&.present?
|
595
607
|
if has_pk
|
596
|
-
code << " # Primary key: #{
|
608
|
+
code << " # Primary key: #{_brick_primary_key.join(', ')}\n" unless _brick_primary_key == ['id']
|
597
609
|
elsif our_pks&.present?
|
598
610
|
if our_pks.length > 1 && respond_to?(:'primary_keys=') # Using the composite_primary_keys gem?
|
599
611
|
new_model_class.primary_keys = our_pks
|
@@ -602,6 +614,7 @@ class Object
|
|
602
614
|
new_model_class.primary_key = (pk_sym = our_pks.first.to_sym)
|
603
615
|
code << " self.primary_key = #{pk_sym.inspect}\n"
|
604
616
|
end
|
617
|
+
_brick_primary_key(relation) # Set the newly-found PK in the instance variable
|
605
618
|
else
|
606
619
|
code << " # Could not identify any column(s) to use as a primary key\n" unless is_view
|
607
620
|
end
|
@@ -613,10 +626,13 @@ class Object
|
|
613
626
|
# The key in each hash entry (fk.first) is the constraint name
|
614
627
|
inverse_assoc_name = (assoc = fk.last)[:inverse]&.fetch(:assoc_name, nil)
|
615
628
|
if (invs = assoc[:inverse_table]).is_a?(Array)
|
616
|
-
|
617
|
-
|
618
|
-
|
629
|
+
if assoc[:is_bt]
|
630
|
+
invs = invs.first # Just do the first one of what would be multiple identical polymorphic belongs_to
|
631
|
+
else
|
632
|
+
invs.each { |inv| build_bt_or_hm(relations, model_name, relation, hmts, assoc, inverse_assoc_name, inv, code) }
|
633
|
+
end
|
619
634
|
end
|
635
|
+
build_bt_or_hm(relations, model_name, relation, hmts, assoc, inverse_assoc_name, invs, code) unless invs.is_a?(Array)
|
620
636
|
hmts
|
621
637
|
end
|
622
638
|
hmts.each do |hmt_fk, fks|
|
@@ -643,7 +659,7 @@ class Object
|
|
643
659
|
# # Not NULLables
|
644
660
|
# # %%% For the minute we've had to pull this out because it's been troublesome implementing the NotNull validator
|
645
661
|
# relation[:cols].each do |col, datatype|
|
646
|
-
# if (datatype[3] &&
|
662
|
+
# if (datatype[3] && _brick_primary_key.exclude?(col) && ::Brick.config.metadata_columns.exclude?(col)) ||
|
647
663
|
# ::Brick.config.not_nullables.include?("#{matching}.#{col}")
|
648
664
|
# code << " validates :#{col}, not_null: true\n"
|
649
665
|
# self.send(:validates, col.to_sym, { not_null: true })
|
@@ -740,13 +756,14 @@ class Object
|
|
740
756
|
def build_controller(class_name, plural_class_name, model, relations)
|
741
757
|
table_name = ActiveSupport::Inflector.underscore(plural_class_name)
|
742
758
|
singular_table_name = ActiveSupport::Inflector.singularize(table_name)
|
759
|
+
pk = model._brick_primary_key(relations.fetch(table_name, nil))
|
743
760
|
|
744
761
|
code = +"class #{class_name} < ApplicationController\n"
|
745
762
|
built_controller = Class.new(ActionController::Base) do |new_controller_class|
|
746
763
|
Object.const_set(class_name.to_sym, new_controller_class)
|
747
764
|
|
748
765
|
code << " def index\n"
|
749
|
-
code << " @#{table_name} = #{model.name}#{
|
766
|
+
code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{pk.inspect})" : '.all'}\n"
|
750
767
|
code << " @#{table_name}.brick_select(params)\n"
|
751
768
|
code << " end\n"
|
752
769
|
self.protect_from_forgery unless: -> { self.request.format.js? }
|
@@ -764,7 +781,8 @@ class Object
|
|
764
781
|
return
|
765
782
|
end
|
766
783
|
|
767
|
-
|
784
|
+
order = pk.each_with_object([]) { |pk_part, s| s << "#{model.table_name}.#{pk_part}" }
|
785
|
+
ar_relation = order.present? ? model.order("#{order.join(', ')}") : model.all
|
768
786
|
@_brick_params = ar_relation.brick_select(params, (selects = []), (bt_descrip = {}), (hm_counts = {}), (join_array = ::Brick::JoinArray.new))
|
769
787
|
# %%% Add custom HM count columns
|
770
788
|
# %%% What happens when the PK is composite?
|
@@ -779,11 +797,15 @@ class Object
|
|
779
797
|
|
780
798
|
if model.primary_key
|
781
799
|
code << " def show\n"
|
782
|
-
code << (find_by_id = "
|
800
|
+
code << (find_by_id = " id = params[:id]&.split(/[\\/,_]/)
|
801
|
+
id = id.first if id.is_a?(Array) && id.length == 1
|
802
|
+
@#{singular_table_name} = #{model.name}.find(id)\n")
|
783
803
|
code << " end\n"
|
784
804
|
self.define_method :show do
|
785
805
|
::Brick.set_db_schema(params)
|
786
|
-
|
806
|
+
id = params[:id]&.split(/[\/,_]/)
|
807
|
+
id = id.first if id.is_a?(Array) && id.length == 1
|
808
|
+
instance_variable_set("@#{singular_table_name}".to_sym, model.find(id))
|
787
809
|
end
|
788
810
|
end
|
789
811
|
|
@@ -792,6 +814,10 @@ class Object
|
|
792
814
|
code << " # (Define :new, :create)\n"
|
793
815
|
|
794
816
|
if model.primary_key
|
817
|
+
if (schema = ::Brick.config.schema_to_analyse) && ::Brick.db_schemas&.include?(schema)
|
818
|
+
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
|
819
|
+
end
|
820
|
+
|
795
821
|
is_need_params = true
|
796
822
|
# code << " # (Define :edit, and :destroy)\n"
|
797
823
|
code << " def update\n"
|
@@ -866,12 +892,12 @@ module ActiveRecord::ConnectionHandling
|
|
866
892
|
def _brick_reflect_tables
|
867
893
|
if (relations = ::Brick.relations).empty?
|
868
894
|
# Only for Postgres? (Doesn't work in sqlite3)
|
869
|
-
# puts ActiveRecord::Base.
|
895
|
+
# puts ActiveRecord::Base.execute_sql("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
870
896
|
|
871
897
|
schema_sql = 'SELECT NULL AS table_schema;'
|
872
898
|
case ActiveRecord::Base.connection.adapter_name
|
873
899
|
when 'PostgreSQL'
|
874
|
-
schema = 'public'
|
900
|
+
schema = 'public' # Too early at this point to be able to pick up: Brick.config.schema_to_analyse
|
875
901
|
schema_sql = 'SELECT DISTINCT table_schema FROM INFORMATION_SCHEMA.tables;'
|
876
902
|
when 'Mysql2'
|
877
903
|
schema = ActiveRecord::Base.connection.current_database
|
@@ -915,7 +941,7 @@ module ActiveRecord::ConnectionHandling
|
|
915
941
|
measures = []
|
916
942
|
case ActiveRecord::Base.connection.adapter_name
|
917
943
|
when 'PostgreSQL', 'SQLite' # These bring back a hash for each row because the query uses column aliases
|
918
|
-
ActiveRecord::Base.
|
944
|
+
ActiveRecord::Base.execute_sql(sql).each do |r|
|
919
945
|
# next if internal_views.include?(r['relation_name']) # Skip internal views such as v_all_assessments
|
920
946
|
relation = relations[(relation_name = r['relation_name'])]
|
921
947
|
relation[:isView] = true if r['table_type'] == 'VIEW'
|
@@ -934,7 +960,7 @@ module ActiveRecord::ConnectionHandling
|
|
934
960
|
# puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
|
935
961
|
end
|
936
962
|
else # MySQL2 acts a little differently, bringing back an array for each row
|
937
|
-
ActiveRecord::Base.
|
963
|
+
ActiveRecord::Base.execute_sql(sql).each do |r|
|
938
964
|
# next if internal_views.include?(r['relation_name']) # Skip internal views such as v_all_assessments
|
939
965
|
relation = relations[(relation_name = r[0])] # here relation represents a table or view from the database
|
940
966
|
relation[:isView] = true if r[1] == 'VIEW' # table_type
|
@@ -1000,11 +1026,11 @@ module ActiveRecord::ConnectionHandling
|
|
1000
1026
|
else
|
1001
1027
|
end
|
1002
1028
|
if sql
|
1003
|
-
::Brick.db_schemas = ActiveRecord::Base.
|
1029
|
+
::Brick.db_schemas = ActiveRecord::Base.execute_sql(schema_sql)
|
1004
1030
|
::Brick.db_schemas = ::Brick.db_schemas.to_a unless ::Brick.db_schemas.is_a?(Array)
|
1005
1031
|
::Brick.db_schemas.map! { |row| row['table_schema'] } unless ::Brick.db_schemas.empty? || ::Brick.db_schemas.first.is_a?(String)
|
1006
1032
|
::Brick.db_schemas -= ['information_schema', 'pg_catalog']
|
1007
|
-
ActiveRecord::Base.
|
1033
|
+
ActiveRecord::Base.execute_sql(sql).each do |fk|
|
1008
1034
|
fk = fk.values unless fk.is_a?(Array)
|
1009
1035
|
::Brick._add_bt_and_hm(fk, relations)
|
1010
1036
|
end
|
@@ -1113,10 +1139,10 @@ module Brick
|
|
1113
1139
|
return if is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[0] == exclusion[0] && fk[1] == exclusion[1] && primary_table == exclusion[2] }
|
1114
1140
|
|
1115
1141
|
if (assoc_hm = hms.fetch((hm_cnstr_name = "hm_#{cnstr_name}"), nil))
|
1116
|
-
if
|
1117
|
-
|
1118
|
-
elsif
|
1119
|
-
|
1142
|
+
if assoc_hm[:fk].is_a?(String)
|
1143
|
+
assoc_hm[:fk] = [assoc_hm[:fk], fk[1]] unless fk[1] == assoc_hm[:fk]
|
1144
|
+
elsif assoc_hm[:fk].exclude?(fk[1])
|
1145
|
+
assoc_hm[:fk] << fk[1]
|
1120
1146
|
end
|
1121
1147
|
assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
|
1122
1148
|
assoc_hm[:inverse] = assoc_bt
|
@@ -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.fetch(model_name, nil))
|
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
|
@@ -206,7 +207,13 @@ input[type=submit] {
|
|
206
207
|
val.is_a?(String) && val.length == 60 && val.start_with?('$2a$')
|
207
208
|
end
|
208
209
|
def hide_bcrypt(val)
|
209
|
-
is_bcrypt?(val)
|
210
|
+
if is_bcrypt?(val)
|
211
|
+
'(hidden)'
|
212
|
+
elsif val.is_a?(String) && val.encoding.name != 'UTF-8'
|
213
|
+
val[0..1000].force_encoding('UTF-8')
|
214
|
+
else
|
215
|
+
val
|
216
|
+
end
|
210
217
|
end %>"
|
211
218
|
|
212
219
|
if ['index', 'show', 'update'].include?(args.first)
|
@@ -283,7 +290,7 @@ function changeout(href, param, value) {
|
|
283
290
|
inline = case args.first
|
284
291
|
when 'index'
|
285
292
|
obj_pk = if pk&.is_a?(Array) # Composite primary key?
|
286
|
-
"[#{pk.map { |pk_part| "#{obj_name}.#{pk_part}" }.join(', ')}]"
|
293
|
+
"[#{pk.map { |pk_part| "#{obj_name}.#{pk_part}" }.join(', ')}]" unless pk.empty?
|
287
294
|
elsif pk
|
288
295
|
"#{obj_name}.#{pk}"
|
289
296
|
end
|
@@ -385,9 +392,9 @@ function changeout(href, param, value) {
|
|
385
392
|
|
386
393
|
<% if @_brick_params&.present? %><h3>where <%= @_brick_params.each_with_object([]) { |v, s| s << \"#\{v.first\} = #\{v.last.inspect\}\" }.join(', ') %></h3><% end %>
|
387
394
|
<table id=\"#{table_name}\">
|
388
|
-
<thead><tr>#{'<th></th>' if pk}
|
395
|
+
<thead><tr>#{'<th></th>' if pk.present?}
|
389
396
|
<% @#{table_name}.columns.map(&:name).each do |col| %>
|
390
|
-
<% next if
|
397
|
+
<% next if #{(pk || []).inspect}.include?(col) || ::Brick.config.metadata_columns.include?(col) || poly_cols.include?(col) %>
|
391
398
|
<th>
|
392
399
|
<% if (bt = bts[col]) %>
|
393
400
|
BT <%
|
@@ -408,7 +415,7 @@ function changeout(href, param, value) {
|
|
408
415
|
<tr>#{"
|
409
416
|
<td><%= link_to '⇛', #{obj_name}_path(#{obj_pk}), { class: 'big-arrow' } %></td>" if obj_pk}
|
410
417
|
<% #{obj_name}.attributes.each do |k, val| %>
|
411
|
-
<% next if
|
418
|
+
<% 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'))) %>
|
412
419
|
<td>
|
413
420
|
<% if (bt = bts[k]) %>
|
414
421
|
<%# binding.pry # Postgres column names are limited to 63 characters %>
|
@@ -444,7 +451,7 @@ function changeout(href, param, value) {
|
|
444
451
|
<p style=\"color: green\"><%= notice %></p>#{"
|
445
452
|
<select id=\"schema\">#{schema_options}</select>" if ::Brick.db_schemas.length > 1}
|
446
453
|
<select id=\"tbl\">#{table_options}</select>
|
447
|
-
<h1>#{model_name}: <%= (obj = @#{obj_name}
|
454
|
+
<h1>#{model_name}: <%= (obj = @#{obj_name})&.brick_descrip || controller_name %></h1>
|
448
455
|
<%= link_to '(See all #{obj_name.pluralize})', #{table_name}_path %>
|
449
456
|
<% if obj %>
|
450
457
|
<%= # path_options = [obj.#{pk}]
|
@@ -453,10 +460,9 @@ function changeout(href, param, value) {
|
|
453
460
|
form_for(obj.becomes(#{model_name})) do |f| %>
|
454
461
|
<table>
|
455
462
|
<% has_fields = false
|
456
|
-
@#{obj_name}.
|
463
|
+
@#{obj_name}.attributes.each do |k, val| %>
|
457
464
|
<tr>
|
458
|
-
|
459
|
-
<% next if k == '#{pk}' || ::Brick.config.metadata_columns.include?(k) %>
|
465
|
+
<% next if #{(pk || []).inspect}.include?(k) || ::Brick.config.metadata_columns.include?(k) %>
|
460
466
|
<th class=\"show-field\">
|
461
467
|
<% has_fields = true
|
462
468
|
if (bt = bts[k])
|
@@ -464,21 +470,31 @@ function changeout(href, param, value) {
|
|
464
470
|
bt_name = bt[1].map { |x| x.first.name }.join('/')
|
465
471
|
# %%% Only do this if the user has permissions to edit this bt field
|
466
472
|
if bt[2] # Polymorphic?
|
467
|
-
poly_class_name = @#{obj_name}.
|
468
|
-
bt_pair =
|
473
|
+
poly_class_name = orig_poly_name = @#{obj_name}.send(\"#\{bt.first\}_type\")
|
474
|
+
bt_pair = nil
|
475
|
+
loop do
|
476
|
+
bt_pair = bt[1].find { |pair| pair.first.name == poly_class_name }
|
477
|
+
# Acxommodate any valid STI by going up the chain of inheritance
|
478
|
+
break unless bt_pair.nil? && poly_class_name = ::Brick.existing_stis[poly_class_name]
|
479
|
+
end
|
480
|
+
puts \"*** Might be missing an STI class called #\{orig_poly_name\} whose base class should have this:
|
481
|
+
*** has_many :#{table_name}, as: :#\{bt.first\}
|
482
|
+
*** Can probably auto-configure everything using these lines in an initialiser:
|
483
|
+
*** Brick.sti_namespace_prefixes = { '::#\{orig_poly_name\}' => 'SomeParentModel' }
|
484
|
+
*** Brick.polymorphics = { '#{table_name}.#\{bt.first\}' => ['SomeParentModel'] }\" if bt_pair.nil?
|
469
485
|
# descrips = @_brick_bt_descrip[bt.first][bt_class]
|
470
|
-
poly_id = @#{obj_name}.
|
486
|
+
poly_id = @#{obj_name}.send(\"#\{bt.first\}_id\")
|
471
487
|
# bt_class.order(obj_pk = bt_class.primary_key).each { |obj| option_detail << [obj.brick_descrip(nil, obj_pk), obj.send(obj_pk)] }
|
472
488
|
else # No polymorphism, so just get the first one
|
473
489
|
bt_pair = bt[1].first
|
474
490
|
end
|
475
|
-
bt_class = bt_pair
|
491
|
+
bt_class = bt_pair&.first
|
476
492
|
if bt.length < 4
|
477
493
|
bt << (option_detail = [[\"(No #\{bt_name\} chosen)\", '^^^brick_NULL^^^']])
|
478
494
|
# %%% Accommodate composite keys for obj.pk at the end here
|
479
|
-
bt_class
|
495
|
+
bt_class&.order(obj_pk = bt_class.primary_key)&.each { |obj| option_detail << [obj.brick_descrip(nil, obj_pk), obj.send(obj_pk)] }
|
480
496
|
end %>
|
481
|
-
BT <%= bt_class
|
497
|
+
BT <%= bt_class&.bt_link(bt.first) || orig_poly_name %>
|
482
498
|
<% else %>
|
483
499
|
<%= k %>
|
484
500
|
<% end %>
|
@@ -488,7 +504,7 @@ function changeout(href, param, value) {
|
|
488
504
|
html_options = { prompt: \"Select #\{bt_name\}\" }
|
489
505
|
html_options[:class] = 'dimmed' unless val %>
|
490
506
|
<%= f.select k.to_sym, bt[3], { value: val || '^^^brick_NULL^^^' }, html_options %>
|
491
|
-
<%= bt_obj = bt_class
|
507
|
+
<%= 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 %>
|
492
508
|
<% else case #{model_name}.column_for_attribute(k).type
|
493
509
|
when :string, :text %>
|
494
510
|
<% if is_bcrypt?(val) # || .readonly? %>
|
@@ -520,16 +536,17 @@ function changeout(href, param, value) {
|
|
520
536
|
|
521
537
|
#{hms_headers.each_with_object(+'') do |hm, s|
|
522
538
|
if (pk = hm.first.klass.primary_key)
|
523
|
-
|
539
|
+
hm_singular_name = (hm_name = hm.first.name.to_s).singularize.underscore
|
540
|
+
obj_pk = (pk.is_a?(Array) ? pk : [pk]).each_with_object([]) { |pk_part, s| s << "#{hm_singular_name}.#{pk_part}" }.join(', ')
|
541
|
+
s << "<table id=\"#{hm_name}\">
|
524
542
|
<tr><th>#{hm[3]}</th></tr>
|
525
|
-
<% collection = @#{obj_name}
|
543
|
+
<% collection = @#{obj_name}.#{hm_name}
|
526
544
|
collection = collection.is_a?(ActiveRecord::Associations::CollectionProxy) ? collection.order(#{pk.inspect}) : [collection]
|
527
545
|
if collection.empty? %>
|
528
546
|
<tr><td>(none)</td></tr>
|
529
547
|
<% else %>
|
530
|
-
<% collection.uniq.each do |#{hm_singular_name
|
531
|
-
|
532
|
-
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm.first.klass.name.underscore}_path(#{hm_singular_name}.#{pk})) %></td></tr>
|
548
|
+
<% collection.uniq.each do |#{hm_singular_name}| %>
|
549
|
+
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm.first.klass.name.underscore}_path([#{obj_pk}])) %></td></tr>
|
533
550
|
<% end %>
|
534
551
|
<% end %>
|
535
552
|
</table>"
|
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
@@ -291,6 +291,10 @@ module Brick
|
|
291
291
|
Brick.config.schema_to_analyse = schema
|
292
292
|
end
|
293
293
|
|
294
|
+
def default_route_fallback=(resource_name)
|
295
|
+
Brick.config.default_route_fallback = resource_name
|
296
|
+
end
|
297
|
+
|
294
298
|
# Load additional references (virtual foreign keys)
|
295
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
|
296
300
|
# %%% Maybe look for differences the second time 'round and just add new stuff instead of entirely deferring
|
@@ -321,9 +325,9 @@ module Brick
|
|
321
325
|
You might be missing an STI namespace prefix entry for these tables: #{missing_stis.keys.join(', ')}.
|
322
326
|
In config/initializers/brick.rb appropriate entries would look something like:
|
323
327
|
Brick.sti_namespace_prefixes = {"
|
324
|
-
puts missing_stis.map { |_k, missing_sti| "\n '::#{missing_sti}' => '
|
328
|
+
puts missing_stis.map { |_k, missing_sti| "\n '::#{missing_sti}' => 'SomeParentModel'" }.join(',')
|
325
329
|
puts " }
|
326
|
-
(Just trade out
|
330
|
+
(Just trade out SomeParentModel with some more appropriate one.)"
|
327
331
|
end
|
328
332
|
end
|
329
333
|
@_additional_references_loaded = true
|
@@ -381,6 +385,9 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
381
385
|
def finalize!
|
382
386
|
existing_controllers = routes.each_with_object({}) { |r, s| c = r.defaults[:controller]; s[c] = nil if c }
|
383
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
|
384
391
|
# %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
|
385
392
|
# If auto-controllers and auto-models are both enabled then this makes sense:
|
386
393
|
::Brick.relations.each do |k, v|
|
@@ -444,7 +451,7 @@ ActiveSupport.on_load(:active_record) do
|
|
444
451
|
relation = clone # spawn
|
445
452
|
relation.select_values = column_names
|
446
453
|
result = if klass.connection.class.name.end_with?('::PostgreSQLAdapter')
|
447
|
-
rslt = klass.
|
454
|
+
rslt = klass.execute_sql(relation.arel.to_sql)
|
448
455
|
rslt.type_map =
|
449
456
|
@type_map ||= proc do
|
450
457
|
# This aliasing avoids the warning:
|
@@ -113,24 +113,24 @@ module Brick
|
|
113
113
|
# # #{resembles_fks.join(', ')}"
|
114
114
|
end
|
115
115
|
|
116
|
-
poly = case (possible_polymorphics = possible_polymorphics.values.flatten).length
|
116
|
+
poly = case (possible_polymorphics = possible_polymorphics.values.flatten.map { |poss_poly| "#{poss_poly} => nil"}).length
|
117
117
|
when 0
|
118
118
|
" like this:
|
119
|
-
# Brick.polymorphics =
|
120
|
-
# 'comments.commentable',
|
121
|
-
# 'images.imageable'
|
122
|
-
#
|
119
|
+
# Brick.polymorphics = {
|
120
|
+
# 'comments.commentable' => nil,
|
121
|
+
# 'images.imageable' => nil
|
122
|
+
# }"
|
123
123
|
when 1
|
124
124
|
".
|
125
125
|
# # Here is a possible polymorphic association that has been auto-identified for the #{ActiveRecord::Base.connection.current_database} database:
|
126
|
-
# Brick.polymorphics =
|
126
|
+
# Brick.polymorphics = { #{possible_additional_references.first} }"
|
127
127
|
|
128
128
|
else
|
129
129
|
".
|
130
130
|
# # Here are possible polymorphic associations that have been auto-identified for the #{ActiveRecord::Base.connection.current_database} database:
|
131
|
-
# Brick.polymorphics =
|
131
|
+
# Brick.polymorphics = {
|
132
132
|
# #{possible_polymorphics.join(",\n# ")}
|
133
|
-
#
|
133
|
+
# }"
|
134
134
|
end
|
135
135
|
|
136
136
|
create_file(filename, "# frozen_string_literal: true
|
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.29
|
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
|