brick 1.0.29 → 1.0.30
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 +24 -4
- data/lib/brick/extensions.rb +318 -142
- data/lib/brick/frameworks/rails/engine.rb +51 -29
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +33 -11
- data/lib/generators/brick/install_generator.rb +7 -1
- 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: ad31136f451e04eabcc67ee09980d45d25aa8e35453aaf4c160ffc9aef3f774c
|
4
|
+
data.tar.gz: 5e3b705dfad389391b291d26bd3c0571cf0579abce3ed00c2840805fd5e05353
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac2403152cae57e54ae1840920aa47e5c64297ff7698f691c0a752ae063d824073f24d040aa66580ef0e5eddac0ec164cf720bc720ad73a09ef7862116608476
|
7
|
+
data.tar.gz: b529668cde573b3c371af30b7f141a1e313d477281bc2ef5453c9751a67abea39a4d8a57e3632f6ef8789ea04ab9d4e01170fb29f096fa6ab8772df77f60f564
|
data/lib/brick/config.rb
CHANGED
@@ -122,12 +122,32 @@ module Brick
|
|
122
122
|
@mutex.synchronize { @sti_namespace_prefixes = prefixes }
|
123
123
|
end
|
124
124
|
|
125
|
-
def
|
126
|
-
@mutex.synchronize { @
|
125
|
+
def schema_behavior
|
126
|
+
@mutex.synchronize { @schema_behavior ||= {} }
|
127
127
|
end
|
128
128
|
|
129
|
-
def
|
130
|
-
@mutex.synchronize { @
|
129
|
+
def schema_behavior=(schema)
|
130
|
+
@mutex.synchronize { @schema_behavior = schema }
|
131
|
+
end
|
132
|
+
|
133
|
+
def sti_type_column
|
134
|
+
@mutex.synchronize { @sti_type_column ||= {} }
|
135
|
+
end
|
136
|
+
|
137
|
+
def sti_type_column=(type_col)
|
138
|
+
@mutex.synchronize do
|
139
|
+
(@sti_type_column = type_col).each_with_object({}) do |v, s|
|
140
|
+
if v.last.nil?
|
141
|
+
# Set an STI type column generally
|
142
|
+
ActiveRecord::Base.inheritance_column = v.first
|
143
|
+
else
|
144
|
+
# Custom STI type columns for models built from specific tables
|
145
|
+
(v.last.is_a?(Array) ? v.last : [v.last]).each do |table|
|
146
|
+
::Brick.relations[table][:sti_col] = v.first
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
131
151
|
end
|
132
152
|
|
133
153
|
def default_route_fallback
|
data/lib/brick/extensions.rb
CHANGED
@@ -196,7 +196,7 @@ module ActiveRecord
|
|
196
196
|
def self.bt_link(assoc_name)
|
197
197
|
model_underscore = name.underscore
|
198
198
|
assoc_name = CGI.escapeHTML(assoc_name.to_s)
|
199
|
-
model_path = Rails.application.routes.url_helpers.send("#{model_underscore.pluralize}_path".to_sym)
|
199
|
+
model_path = Rails.application.routes.url_helpers.send("#{model_underscore.tr('/', '_').pluralize}_path".to_sym)
|
200
200
|
link = Class.new.extend(ActionView::Helpers::UrlHelper).link_to(name, model_path)
|
201
201
|
model_underscore == assoc_name ? link : "#{assoc_name}-#{link}".html_safe
|
202
202
|
end
|
@@ -306,8 +306,12 @@ module ActiveRecord
|
|
306
306
|
|
307
307
|
# %%% Skip the metadata columns
|
308
308
|
if selects&.empty? # Default to all columns
|
309
|
+
tbl_no_schema = table.name.split('.').last
|
309
310
|
columns.each do |col|
|
310
|
-
|
311
|
+
if (col_name = col.name) == 'class'
|
312
|
+
col_alias = ' AS _class'
|
313
|
+
end
|
314
|
+
selects << "\"#{tbl_no_schema}\".\"#{col_name}\"#{col_alias}"
|
311
315
|
end
|
312
316
|
end
|
313
317
|
|
@@ -349,7 +353,7 @@ module ActiveRecord
|
|
349
353
|
end
|
350
354
|
|
351
355
|
if join_array.present?
|
352
|
-
left_outer_joins!(join_array)
|
356
|
+
left_outer_joins!(join_array)
|
353
357
|
# Without working from a duplicate, touching the AREL ast tree sets the @arel instance variable, which causes the relation to be immutable.
|
354
358
|
(rel_dupe = dup)._arel_alias_names
|
355
359
|
core_selects = selects.dup
|
@@ -360,10 +364,10 @@ module ActiveRecord
|
|
360
364
|
v.last.each do |k1, v1| # k1 is class, v1 is array of columns to snag
|
361
365
|
next if chains[k1].nil?
|
362
366
|
|
363
|
-
tbl_name = field_tbl_names[v.first][k1] ||= shift_or_first(chains[k1])
|
367
|
+
tbl_name = (field_tbl_names[v.first][k1] ||= shift_or_first(chains[k1])).split('.').last
|
364
368
|
field_tbl_name = nil
|
365
369
|
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])
|
370
|
+
field_tbl_name = (field_tbl_names[v.first][sel_col.first] ||= shift_or_first(chains[sel_col.first])).split('.').last
|
367
371
|
|
368
372
|
selects << "#{"\"#{field_tbl_name}\".\"#{sel_col.last}\""} AS \"#{(col_alias = "_brfk_#{v.first}__#{sel_col.last}")}\""
|
369
373
|
v1[idx] << col_alias
|
@@ -420,6 +424,7 @@ JOIN (SELECT #{selects.join(', ')}, COUNT(#{count_column}) AS _ct_ FROM #{associ
|
|
420
424
|
joins!("#{join_clause} ON #{on_clause.join(' AND ')}")
|
421
425
|
end
|
422
426
|
where!(wheres) unless wheres.empty?
|
427
|
+
limit(1000) # Don't want to get too carried away just yet
|
423
428
|
wheres unless wheres.empty? # Return the specific parameters that we did use
|
424
429
|
end
|
425
430
|
|
@@ -484,7 +489,7 @@ if ActiveSupport::Dependencies.respond_to?(:autoload_module!) # %%% Only works w
|
|
484
489
|
::Brick.sti_models[qualified_name] = { base: base_class }
|
485
490
|
# Build subclass and place it into the specially STI-namespaced module
|
486
491
|
into.const_set(const_name.to_sym, klass = Class.new(base_class))
|
487
|
-
# %%% used to also have: autoload_once_paths.include?(base_path) ||
|
492
|
+
# %%% used to also have: autoload_once_paths.include?(base_path) ||
|
488
493
|
autoloaded_constants << qualified_name unless autoloaded_constants.include?(qualified_name)
|
489
494
|
klass
|
490
495
|
elsif (base_class = ::Brick.config.sti_namespace_prefixes&.fetch("::#{const_name}", nil)&.constantize)
|
@@ -498,75 +503,209 @@ if ActiveSupport::Dependencies.respond_to?(:autoload_module!) # %%% Only works w
|
|
498
503
|
end
|
499
504
|
end
|
500
505
|
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
return
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
506
|
+
Module.class_exec do
|
507
|
+
alias _brick_const_missing const_missing
|
508
|
+
def const_missing(*args)
|
509
|
+
if (self.const_defined?(args.first) && (possible = self.const_get(args.first)) != self) ||
|
510
|
+
(self != Object && Object.const_defined?(args.first) && (possible = Object.const_get(args.first)) != self)
|
511
|
+
return possible
|
512
|
+
end
|
513
|
+
class_name = args.first.to_s
|
514
|
+
# See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
515
|
+
# checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
516
|
+
# that is, checking #qualified_name_for with: from_mod, const_name
|
517
|
+
# If we want to support namespacing in the future, might have to utilise something like this:
|
518
|
+
# path_suffix = ActiveSupport::Dependencies.qualified_name_for(Object, args.first).underscore
|
519
|
+
# return self._brick_const_missing(*args) if ActiveSupport::Dependencies.search_for_file(path_suffix)
|
520
|
+
# If the file really exists, go and snag it:
|
521
|
+
if !(is_found = ActiveSupport::Dependencies.search_for_file(class_name.underscore)) && (filepath = (self.name || class_name)&.split('::'))
|
522
|
+
filepath = (filepath[0..-2] + [class_name]).join('/').underscore + '.rb'
|
523
|
+
end
|
524
|
+
if is_found
|
525
|
+
return self._brick_const_missing(*args)
|
526
|
+
# elsif ActiveSupport::Dependencies.search_for_file(filepath) # Last-ditch effort to pick this thing up before we fill in the gaps on our own
|
527
|
+
# my_const = parent.const_missing(class_name) # ends up having: MyModule::MyClass
|
528
|
+
# return my_const
|
529
|
+
end
|
525
530
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
# Adjust for STI if we know of a base model for the requested model name
|
540
|
-
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil) || ::Brick.existing_stis[model_name]&.constantize)
|
541
|
-
base_model.table_name
|
542
|
-
else
|
543
|
-
ActiveSupport::Inflector.pluralize(singular_table_name)
|
544
|
-
end
|
545
|
-
|
546
|
-
# Maybe, just maybe there's a database table that will satisfy this need
|
547
|
-
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(m) })
|
548
|
-
build_model(model_name, singular_table_name, table_name, relations, matching)
|
549
|
-
end
|
531
|
+
relations = ::Brick.instance_variable_get(:@relations)[ActiveRecord::Base.connection_pool.object_id] || {}
|
532
|
+
# puts "ON OBJECT: #{args.inspect}" if self.module_parent == Object
|
533
|
+
result = if ::Brick.enable_controllers? && class_name.end_with?('Controller') && (plural_class_name = class_name[0..-11]).length.positive?
|
534
|
+
# Otherwise now it's up to us to fill in the gaps
|
535
|
+
# (Go over to underscores for a moment so that if we have something come in like VABCsController then the model name ends up as
|
536
|
+
# VAbc instead of VABC)
|
537
|
+
full_class_name = +''
|
538
|
+
full_class_name << "::#{self.name}" unless self == Object
|
539
|
+
full_class_name << "::#{plural_class_name.underscore.singularize.camelize}"
|
540
|
+
if (model = self.const_get(full_class_name))
|
541
|
+
# if it's a controller and no match or a model doesn't really use the same table name, eager load all models and try to find a model class of the right name.
|
542
|
+
Object.send(:build_controller, self, class_name, plural_class_name, model, relations)
|
550
543
|
end
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
544
|
+
elsif (::Brick.enable_models? || ::Brick.enable_controllers?) && # Schema match?
|
545
|
+
self == Object && # %%% This works for Person::Person -- but also limits us to not being able to allow more than one level of namespacing
|
546
|
+
schema_name = [(singular_table_name = class_name.underscore),
|
547
|
+
(table_name = singular_table_name.pluralize),
|
548
|
+
class_name,
|
549
|
+
(plural_class_name = class_name.pluralize)].find { |s| Brick.db_schemas.include?(s) }
|
550
|
+
# Build out a module for the schema if it's namespaced
|
551
|
+
schema_name = schema_name.camelize
|
552
|
+
self.const_set(schema_name.to_sym, (built_module = Module.new))
|
553
|
+
|
554
|
+
[built_module, "module #{schema_name}; end\n"]
|
555
|
+
# # %%% Perhaps an option to use the first module just as schema, and additional modules as namespace with a table name prefix applied
|
556
|
+
elsif ::Brick.enable_models?
|
557
|
+
# See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
558
|
+
# checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
559
|
+
|
560
|
+
unless self == Object # Are we in some namespace?
|
561
|
+
schema_name = [(singular_schema_name = name.underscore),
|
562
|
+
(schema_name = singular_schema_name.pluralize),
|
563
|
+
name,
|
564
|
+
name.pluralize].find { |s| Brick.db_schemas.include?(s) }
|
565
|
+
end
|
566
|
+
|
567
|
+
plural_class_name = ActiveSupport::Inflector.pluralize(model_name = class_name)
|
568
|
+
# If it's namespaced then we turn the first part into what would be a schema name
|
569
|
+
singular_table_name = ActiveSupport::Inflector.underscore(model_name).gsub('::', '.')
|
570
|
+
|
571
|
+
# Adjust for STI if we know of a base model for the requested model name
|
572
|
+
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil) || ::Brick.existing_stis[model_name]&.constantize)
|
573
|
+
base_model.table_name
|
574
|
+
else
|
575
|
+
ActiveSupport::Inflector.pluralize(singular_table_name)
|
576
|
+
end
|
577
|
+
|
578
|
+
# Maybe, just maybe there's a database table that will satisfy this need
|
579
|
+
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(schema_name ? "#{schema_name}.#{m}" : m) })
|
580
|
+
Object.send(:build_model, schema_name, model_name, singular_table_name, table_name, relations, matching)
|
581
|
+
end
|
582
|
+
end
|
583
|
+
if result
|
584
|
+
built_class, code = result
|
585
|
+
puts "\n#{code}"
|
586
|
+
built_class
|
587
|
+
elsif ::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}") && !schema_name
|
556
588
|
# module_prefixes = type_name.split('::')
|
557
589
|
# path = self.name.split('::')[0..-2] + []
|
558
590
|
# module_prefixes.unshift('') unless module_prefixes.first.blank?
|
559
591
|
# candidate_file = Rails.root.join('app/models' + module_prefixes.map(&:underscore).join('/') + '.rb')
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
592
|
+
self._brick_const_missing(*args)
|
593
|
+
elsif self != Object
|
594
|
+
module_parent.const_missing(*args)
|
595
|
+
else
|
596
|
+
puts "MISSING! mod #{self.name} #{args.inspect} #{table_name}"
|
597
|
+
self._brick_const_missing(*args)
|
565
598
|
end
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
class Object
|
603
|
+
class << self
|
604
|
+
# alias _brick_const_missing const_missing
|
605
|
+
# def const_missing(*args)
|
606
|
+
# # return self.const_get(args.first) if self.const_defined?(args.first)
|
607
|
+
# # return Object.const_get(args.first) if Object.const_defined?(args.first) unless self == Object
|
608
|
+
# if self.const_defined?(args.first) && (possible = self.const_get(args.first)) != self
|
609
|
+
# return possible
|
610
|
+
# end
|
611
|
+
# if self != Object && Object.const_defined?(args.first) && (possible = Object.const_get(args.first)) != self
|
612
|
+
# return possible
|
613
|
+
# end
|
614
|
+
|
615
|
+
# class_name = args.first.to_s
|
616
|
+
# # See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
617
|
+
# # checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
618
|
+
# # that is, checking #qualified_name_for with: from_mod, const_name
|
619
|
+
# # If we want to support namespacing in the future, might have to utilise something like this:
|
620
|
+
# # path_suffix = ActiveSupport::Dependencies.qualified_name_for(Object, args.first).underscore
|
621
|
+
# # return self._brick_const_missing(*args) if ActiveSupport::Dependencies.search_for_file(path_suffix)
|
622
|
+
# # If the file really exists, go and snag it:
|
623
|
+
# if !(is_found = ActiveSupport::Dependencies.search_for_file(class_name.underscore)) && (filepath = (self.name || class_name)&.split('::'))
|
624
|
+
# filepath = (filepath[0..-2] + [class_name]).join('/').underscore + '.rb'
|
625
|
+
# end
|
626
|
+
# if is_found
|
627
|
+
# return self._brick_const_missing(*args)
|
628
|
+
# elsif ActiveSupport::Dependencies.search_for_file(filepath) # Last-ditch effort to pick this thing up before we fill in the gaps on our own
|
629
|
+
# my_const = parent.const_missing(class_name) # ends up having: MyModule::MyClass
|
630
|
+
# return my_const
|
631
|
+
# end
|
632
|
+
# relations = ::Brick.instance_variable_get(:@relations)[ActiveRecord::Base.connection_pool.object_id] || {}
|
633
|
+
# result = if ::Brick.enable_controllers? && class_name.end_with?('Controller') && (plural_class_name = class_name[0..-11]).length.positive?
|
634
|
+
# # Otherwise now it's up to us to fill in the gaps
|
635
|
+
# # (Go over to underscores for a moment so that if we have something come in like VABCsController then the model name ends up as
|
636
|
+
# # VAbc instead of VABC)
|
637
|
+
# if (model = Object.const_get(plural_class_name.underscore.singularize.camelize))
|
638
|
+
# # if it's a controller and no match or a model doesn't really use the same table name, eager load all models and try to find a model class of the right name.
|
639
|
+
# build_controller(nil, class_name, plural_class_name, model, relations)
|
640
|
+
# end
|
641
|
+
# elsif (::Brick.enable_models? || ::Brick.enable_controllers?) && # Schema match?
|
642
|
+
# db_schema_name = [(singular_table_name = class_name.underscore),
|
643
|
+
# (table_name = singular_table_name.pluralize),
|
644
|
+
# class_name,
|
645
|
+
# (plural_class_name = class_name.pluralize)].find { |s| Brick.db_schemas.include?(s) }
|
646
|
+
# # Build out a module for the schema if it's namespaced
|
647
|
+
# schema_name = db_schema_name.camelize
|
648
|
+
# unless Object.const_defined?(schema_name.to_sym)
|
649
|
+
# Object.const_set(schema_name.to_sym, (built_module = Module.new))
|
650
|
+
# Brick.db_schemas[db_schema_name] = built_module
|
651
|
+
# [built_module, "module #{schema_name}; end\n"]
|
652
|
+
# end
|
653
|
+
# # # %%% Perhaps an option to use the first module just as schema, and additional modules as namespace with a table name prefix applied
|
654
|
+
# # schema_name, model_name =
|
655
|
+
# # code = +''
|
656
|
+
# # mod_tree = +''
|
657
|
+
# # model_name.split('::')[0..-2].each do |mod_name|
|
658
|
+
# # mod_tree << "::#{mod_name}"
|
659
|
+
# # Module.new(mod_tree)
|
660
|
+
# # code << "module #{mod_tree}; end\n"
|
661
|
+
# # end
|
662
|
+
# elsif ::Brick.enable_models?
|
663
|
+
# # See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
664
|
+
# # checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
665
|
+
# plural_class_name = ActiveSupport::Inflector.pluralize(model_name = class_name)
|
666
|
+
# singular_table_name = ActiveSupport::Inflector.underscore(model_name)
|
667
|
+
|
668
|
+
# # Adjust for STI if we know of a base model for the requested model name
|
669
|
+
# table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil) || ::Brick.existing_stis[model_name]&.constantize)
|
670
|
+
# base_model.table_name
|
671
|
+
# else
|
672
|
+
# ActiveSupport::Inflector.pluralize(singular_table_name)
|
673
|
+
# end
|
674
|
+
|
675
|
+
# # Maybe, just maybe there's a database table that will satisfy this need
|
676
|
+
# if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(m) })
|
677
|
+
# build_model(nil, model_name, singular_table_name, table_name, relations, matching)
|
678
|
+
# end
|
679
|
+
# end
|
680
|
+
# if result
|
681
|
+
# built_class, code = result
|
682
|
+
# puts "\n#{code}"
|
683
|
+
# built_class
|
684
|
+
# elsif ::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}") && !schema_name
|
685
|
+
# # module_prefixes = type_name.split('::')
|
686
|
+
# # path = self.name.split('::')[0..-2] + []
|
687
|
+
# # module_prefixes.unshift('') unless module_prefixes.first.blank?
|
688
|
+
# # candidate_file = Rails.root.join('app/models' + module_prefixes.map(&:underscore).join('/') + '.rb')
|
689
|
+
# self._brick_const_missing(*args)
|
690
|
+
# elsif self != Object
|
691
|
+
# module_parent.const_missing(*args)
|
692
|
+
# else
|
693
|
+
# puts "MISSING! obj #{self.name}/#{schema_name} #{args.inspect} #{table_name}"
|
694
|
+
# self._brick_const_missing(*args)
|
695
|
+
# end
|
696
|
+
# end
|
566
697
|
|
567
698
|
private
|
568
699
|
|
569
|
-
def build_model(model_name, singular_table_name, table_name, relations, matching)
|
700
|
+
def build_model(schema_name, model_name, singular_table_name, table_name, relations, matching)
|
701
|
+
full_name = if schema_name.blank?
|
702
|
+
model_name
|
703
|
+
else # Prefix the schema to the table name + prefix the schema namespace to the class name
|
704
|
+
schema_module = (Brick.db_schemas[schema_name] ||= self.const_get(schema_name.singularize.camelize))
|
705
|
+
matching = "#{schema_name}.#{matching}"
|
706
|
+
"#{schema_module&.name}::#{model_name}"
|
707
|
+
end
|
708
|
+
|
570
709
|
return if ((is_view = (relation = relations[matching]).key?(:isView)) && ::Brick.config.skip_database_views) ||
|
571
710
|
::Brick.config.exclude_tables.include?(matching)
|
572
711
|
|
@@ -578,14 +717,15 @@ class Object
|
|
578
717
|
return
|
579
718
|
end
|
580
719
|
|
581
|
-
if (base_model = ::Brick.sti_models[
|
720
|
+
if (base_model = ::Brick.sti_models[full_name]&.fetch(:base, nil) || ::Brick.existing_stis[full_name]&.constantize)
|
582
721
|
is_sti = true
|
583
722
|
else
|
584
723
|
base_model = ::Brick.config.models_inherit_from || ActiveRecord::Base
|
585
724
|
end
|
586
|
-
|
725
|
+
hmts = nil
|
726
|
+
code = +"class #{full_name} < #{base_model.name}\n"
|
587
727
|
built_model = Class.new(base_model) do |new_model_class|
|
588
|
-
Object.const_set(model_name.to_sym, new_model_class)
|
728
|
+
(schema_module || Object).const_set(model_name.to_sym, new_model_class)
|
589
729
|
# Accommodate singular or camel-cased table names such as "order_detail" or "OrderDetails"
|
590
730
|
code << " self.table_name = '#{self.table_name = matching}'\n" unless table_name == matching
|
591
731
|
|
@@ -618,6 +758,10 @@ class Object
|
|
618
758
|
else
|
619
759
|
code << " # Could not identify any column(s) to use as a primary key\n" unless is_view
|
620
760
|
end
|
761
|
+
if (sti_col = relation.fetch(:sti_col, nil))
|
762
|
+
new_model_class.send(:'inheritance_column=', sti_col)
|
763
|
+
code << " self.inheritance_column = #{sti_col.inspect}\n"
|
764
|
+
end
|
621
765
|
|
622
766
|
unless is_sti
|
623
767
|
fks = relation[:fks] || {}
|
@@ -635,7 +779,21 @@ class Object
|
|
635
779
|
build_bt_or_hm(relations, model_name, relation, hmts, assoc, inverse_assoc_name, invs, code) unless invs.is_a?(Array)
|
636
780
|
hmts
|
637
781
|
end
|
782
|
+
# # Not NULLables
|
783
|
+
# # %%% For the minute we've had to pull this out because it's been troublesome implementing the NotNull validator
|
784
|
+
# relation[:cols].each do |col, datatype|
|
785
|
+
# if (datatype[3] && _brick_primary_key.exclude?(col) && ::Brick.config.metadata_columns.exclude?(col)) ||
|
786
|
+
# ::Brick.config.not_nullables.include?("#{matching}.#{col}")
|
787
|
+
# code << " validates :#{col}, not_null: true\n"
|
788
|
+
# self.send(:validates, col.to_sym, { not_null: true })
|
789
|
+
# end
|
790
|
+
# end
|
791
|
+
end
|
792
|
+
end # class definition
|
793
|
+
# Having this separate -- will this now work out better?
|
794
|
+
built_model.class_exec do
|
638
795
|
hmts.each do |hmt_fk, fks|
|
796
|
+
hmt_fk = hmt_fk.tr('.', '_')
|
639
797
|
fks.each do |fk|
|
640
798
|
through = fk.first[:assoc_name]
|
641
799
|
hmt_name = if fks.length > 1
|
@@ -656,18 +814,8 @@ class Object
|
|
656
814
|
self.send(:has_many, hmt_name.to_sym, **options)
|
657
815
|
end
|
658
816
|
end
|
659
|
-
# # Not NULLables
|
660
|
-
# # %%% For the minute we've had to pull this out because it's been troublesome implementing the NotNull validator
|
661
|
-
# relation[:cols].each do |col, datatype|
|
662
|
-
# if (datatype[3] && _brick_primary_key.exclude?(col) && ::Brick.config.metadata_columns.exclude?(col)) ||
|
663
|
-
# ::Brick.config.not_nullables.include?("#{matching}.#{col}")
|
664
|
-
# code << " validates :#{col}, not_null: true\n"
|
665
|
-
# self.send(:validates, col.to_sym, { not_null: true })
|
666
|
-
# end
|
667
|
-
# end
|
668
817
|
end
|
669
|
-
code << "end # model #{
|
670
|
-
end # class definition
|
818
|
+
code << "end # model #{full_name}\n\n"
|
671
819
|
[built_model, code]
|
672
820
|
end
|
673
821
|
|
@@ -678,7 +826,7 @@ class Object
|
|
678
826
|
# Try to take care of screwy names if this is a belongs_to going to an STI subclass
|
679
827
|
assoc_name = if (primary_class = assoc.fetch(:primary_class, nil)) &&
|
680
828
|
sti_inverse_assoc = primary_class.reflect_on_all_associations.find do |a|
|
681
|
-
a.macro == :has_many && a.options[:class_name] == self.name && assoc[:fk]
|
829
|
+
a.macro == :has_many && a.options[:class_name] == self.name && assoc[:fk] == a.foreign_key
|
682
830
|
end
|
683
831
|
sti_inverse_assoc.options[:inverse_of]&.to_s || assoc_name
|
684
832
|
else
|
@@ -710,7 +858,7 @@ class Object
|
|
710
858
|
if assoc.key?(:polymorphic)
|
711
859
|
options[:as] = assoc[:fk].to_sym
|
712
860
|
else
|
713
|
-
need_fk = "#{ActiveSupport::Inflector.singularize(assoc[:inverse][:inverse_table])}_id" != assoc[:fk]
|
861
|
+
need_fk = "#{ActiveSupport::Inflector.singularize(assoc[:inverse][:inverse_table].split('.').last)}_id" != assoc[:fk]
|
714
862
|
end
|
715
863
|
# fks[table_name].find { |other_assoc| other_assoc.object_id != assoc.object_id && other_assoc[:assoc_name] == assoc[assoc_name] }
|
716
864
|
if (has_ones = ::Brick.config.has_ones&.fetch(model_name, nil))&.key?(singular_assoc_name = ActiveSupport::Inflector.singularize(assoc_name))
|
@@ -727,7 +875,7 @@ class Object
|
|
727
875
|
end
|
728
876
|
# Figure out if we need to specially call out the class_name and/or foreign key
|
729
877
|
# (and if either of those then definitely also a specific inverse_of)
|
730
|
-
options[:class_name] = assoc[:primary_class]&.name || singular_table_name.camelize if need_class_name
|
878
|
+
options[:class_name] = "::#{assoc[:primary_class]&.name || singular_table_name.split('.').map(&:camelize).join('::')}" if need_class_name
|
731
879
|
# Work around a bug in CPK where self-referencing belongs_to associations double up their foreign keys
|
732
880
|
if need_fk # Funky foreign key?
|
733
881
|
options[:foreign_key] = if assoc[:fk].is_a?(Array)
|
@@ -737,10 +885,11 @@ class Object
|
|
737
885
|
assoc[:fk].to_sym
|
738
886
|
end
|
739
887
|
end
|
740
|
-
options[:inverse_of] = inverse_assoc_name.to_sym if inverse_assoc_name && (need_class_name || need_fk || need_inverse_of)
|
888
|
+
options[:inverse_of] = inverse_assoc_name.tr('.', '_').to_sym if inverse_assoc_name && (need_class_name || need_fk || need_inverse_of)
|
741
889
|
|
742
890
|
# Prepare a list of entries for "has_many :through"
|
743
891
|
if macro == :has_many
|
892
|
+
puts [inverse_table, relations[inverse_table].length].inspect
|
744
893
|
relations[inverse_table][:hmt_fks].each do |k, hmt_fk|
|
745
894
|
next if k == assoc[:fk]
|
746
895
|
|
@@ -748,19 +897,20 @@ class Object
|
|
748
897
|
end
|
749
898
|
end
|
750
899
|
# And finally create a has_one, has_many, or belongs_to for this association
|
751
|
-
assoc_name = assoc_name.to_sym
|
900
|
+
assoc_name = assoc_name.tr('.', '_').to_sym
|
752
901
|
code << " #{macro} #{assoc_name.inspect}#{options.map { |k, v| ", #{k}: #{v.inspect}" }.join}\n"
|
753
902
|
self.send(macro, assoc_name, **options)
|
754
903
|
end
|
755
904
|
|
756
|
-
def build_controller(class_name, plural_class_name, model, relations)
|
905
|
+
def build_controller(namespace, class_name, plural_class_name, model, relations)
|
757
906
|
table_name = ActiveSupport::Inflector.underscore(plural_class_name)
|
758
907
|
singular_table_name = ActiveSupport::Inflector.singularize(table_name)
|
759
908
|
pk = model._brick_primary_key(relations.fetch(table_name, nil))
|
760
909
|
|
761
|
-
|
910
|
+
namespace_name = "#{namespace.name}::" if namespace
|
911
|
+
code = +"class #{namespace_name}#{class_name} < ApplicationController\n"
|
762
912
|
built_controller = Class.new(ActionController::Base) do |new_controller_class|
|
763
|
-
Object.const_set(class_name.to_sym, new_controller_class)
|
913
|
+
(namespace || Object).const_set(class_name.to_sym, new_controller_class)
|
764
914
|
|
765
915
|
code << " def index\n"
|
766
916
|
code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{pk.inspect})" : '.all'}\n"
|
@@ -787,9 +937,10 @@ class Object
|
|
787
937
|
# %%% Add custom HM count columns
|
788
938
|
# %%% What happens when the PK is composite?
|
789
939
|
counts = hm_counts.each_with_object([]) { |v, s| s << "_br_#{v.first}._ct_ AS _br_#{v.first}_ct" }
|
790
|
-
# *selects,
|
791
940
|
instance_variable_set("@#{table_name}".to_sym, ar_relation.dup._select!(*selects, *counts))
|
792
|
-
|
941
|
+
if namespace && (idx = lookup_context.prefixes.index(table_name))
|
942
|
+
lookup_context.prefixes[idx] = "#{namespace.name.underscore}/#{lookup_context.prefixes[idx]}"
|
943
|
+
end
|
793
944
|
@_brick_bt_descrip = bt_descrip
|
794
945
|
@_brick_hm_counts = hm_counts
|
795
946
|
@_brick_join_array = join_array
|
@@ -814,9 +965,9 @@ class Object
|
|
814
965
|
code << " # (Define :new, :create)\n"
|
815
966
|
|
816
967
|
if model.primary_key
|
817
|
-
if (schema = ::Brick.config.schema_to_analyse) && ::Brick.db_schemas&.include?(schema)
|
818
|
-
|
819
|
-
end
|
968
|
+
# if (schema = ::Brick.config.schema_behavior[:multitenant]&.fetch(:schema_to_analyse, nil)) && ::Brick.db_schemas&.include?(schema)
|
969
|
+
# ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
|
970
|
+
# end
|
820
971
|
|
821
972
|
is_need_params = true
|
822
973
|
# code << " # (Define :edit, and :destroy)\n"
|
@@ -827,7 +978,6 @@ class Object
|
|
827
978
|
code << " end\n"
|
828
979
|
self.define_method :update do
|
829
980
|
::Brick.set_db_schema(params)
|
830
|
-
|
831
981
|
if request.format == :csv # Importing CSV?
|
832
982
|
require 'csv'
|
833
983
|
# See if internally it's likely a TSV file (tab-separated)
|
@@ -851,7 +1001,7 @@ class Object
|
|
851
1001
|
|
852
1002
|
if is_need_params
|
853
1003
|
code << "private\n"
|
854
|
-
code << " def
|
1004
|
+
code << " def #{params_name}\n"
|
855
1005
|
code << " params.require(:#{singular_table_name}).permit(#{model.columns_hash.keys.map { |c| c.to_sym.inspect }.join(', ')})\n"
|
856
1006
|
code << " end\n"
|
857
1007
|
self.define_method(params_name) do
|
@@ -861,7 +1011,7 @@ class Object
|
|
861
1011
|
# Get column names for params from relations[model.table_name][:cols].keys
|
862
1012
|
end
|
863
1013
|
end
|
864
|
-
code << "end # #{class_name}\n\n"
|
1014
|
+
code << "end # #{namespace_name}#{class_name}\n\n"
|
865
1015
|
end # class definition
|
866
1016
|
[built_controller, code]
|
867
1017
|
end
|
@@ -871,7 +1021,8 @@ class Object
|
|
871
1021
|
plural = ActiveSupport::Inflector.pluralize(hm_assoc[:alternate_name])
|
872
1022
|
[hm_assoc[:alternate_name] == name.underscore ? "#{hm_assoc[:assoc_name].singularize}_#{plural}" : plural, true]
|
873
1023
|
else
|
874
|
-
|
1024
|
+
assoc_name = hm_assoc[:inverse_table].pluralize
|
1025
|
+
[assoc_name, assoc_name.include?('.')]
|
875
1026
|
end
|
876
1027
|
end
|
877
1028
|
end
|
@@ -891,16 +1042,19 @@ module ActiveRecord::ConnectionHandling
|
|
891
1042
|
|
892
1043
|
def _brick_reflect_tables
|
893
1044
|
if (relations = ::Brick.relations).empty?
|
894
|
-
|
895
|
-
|
1045
|
+
load Rails.root.join('config/initializers/brick.rb') # Hopefully our initializer is named exactly this!
|
1046
|
+
# Only for Postgres? (Doesn't work in sqlite3)
|
1047
|
+
# puts ActiveRecord::Base.execute_sql("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
896
1048
|
|
897
|
-
|
898
|
-
|
1049
|
+
schema_sql = 'SELECT NULL AS table_schema;'
|
1050
|
+
case ActiveRecord::Base.connection.adapter_name
|
899
1051
|
when 'PostgreSQL'
|
900
|
-
|
1052
|
+
if (::Brick.default_schema = schema = ::Brick.config.schema_behavior&.[](:multitenant)&.[](:schema_to_analyse))
|
1053
|
+
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", schema)
|
1054
|
+
end
|
901
1055
|
schema_sql = 'SELECT DISTINCT table_schema FROM INFORMATION_SCHEMA.tables;'
|
902
1056
|
when 'Mysql2'
|
903
|
-
schema = ActiveRecord::Base.connection.current_database
|
1057
|
+
::Brick.default_schema = schema = ActiveRecord::Base.connection.current_database
|
904
1058
|
when 'SQLite'
|
905
1059
|
sql = "SELECT m.name AS relation_name, UPPER(m.type) AS table_type,
|
906
1060
|
p.name AS column_name, p.type AS data_type,
|
@@ -913,8 +1067,7 @@ module ActiveRecord::ConnectionHandling
|
|
913
1067
|
puts "Unfamiliar with connection adapter #{ActiveRecord::Base.connection.adapter_name}"
|
914
1068
|
end
|
915
1069
|
|
916
|
-
sql ||=
|
917
|
-
"SELECT t.table_name AS relation_name, t.table_type,
|
1070
|
+
sql ||= "SELECT t.table_schema AS schema, t.table_name AS relation_name, t.table_type,
|
918
1071
|
c.column_name, c.data_type,
|
919
1072
|
COALESCE(c.character_maximum_length, c.numeric_precision) AS max_length,
|
920
1073
|
tc.constraint_type AS const, kcu.constraint_name AS \"key\",
|
@@ -932,18 +1085,23 @@ module ActiveRecord::ConnectionHandling
|
|
932
1085
|
ON kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
|
933
1086
|
AND kcu.TABLE_NAME = tc.TABLE_NAME
|
934
1087
|
AND kcu.CONSTRAINT_NAME = tc.constraint_name
|
935
|
-
WHERE t.table_schema
|
1088
|
+
WHERE t.table_schema NOT IN ('information_schema', 'pg_catalog')#{"
|
1089
|
+
AND t.table_schema = COALESCE(current_setting('SEARCH_PATH'), 'public')" if schema }
|
936
1090
|
-- AND t.table_type IN ('VIEW') -- 'BASE TABLE', 'FOREIGN TABLE'
|
937
1091
|
AND t.table_name NOT IN ('pg_stat_statements', 'ar_internal_metadata', 'schema_migrations')
|
938
|
-
ORDER BY 1, t.table_type DESC, c.ordinal_position"
|
939
|
-
])
|
940
|
-
|
1092
|
+
ORDER BY 1, t.table_type DESC, c.ordinal_position"
|
941
1093
|
measures = []
|
942
1094
|
case ActiveRecord::Base.connection.adapter_name
|
943
1095
|
when 'PostgreSQL', 'SQLite' # These bring back a hash for each row because the query uses column aliases
|
1096
|
+
schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
944
1097
|
ActiveRecord::Base.execute_sql(sql).each do |r|
|
945
1098
|
# next if internal_views.include?(r['relation_name']) # Skip internal views such as v_all_assessments
|
946
|
-
|
1099
|
+
relation_name = if r['schema'] != schema
|
1100
|
+
"#{schema_name = r['schema']}.#{r['relation_name']}"
|
1101
|
+
else
|
1102
|
+
r['relation_name']
|
1103
|
+
end
|
1104
|
+
relation = relations[relation_name]
|
947
1105
|
relation[:isView] = true if r['table_type'] == 'VIEW'
|
948
1106
|
col_name = r['column_name']
|
949
1107
|
key = case r['const']
|
@@ -1000,11 +1158,11 @@ module ActiveRecord::ConnectionHandling
|
|
1000
1158
|
# end
|
1001
1159
|
# end
|
1002
1160
|
# end
|
1003
|
-
|
1161
|
+
schema = ::Brick.default_schema # Reset back for this next round of fun
|
1004
1162
|
case ActiveRecord::Base.connection.adapter_name
|
1005
1163
|
when 'PostgreSQL', 'Mysql2'
|
1006
|
-
sql =
|
1007
|
-
|
1164
|
+
sql = "SELECT kcu1.CONSTRAINT_SCHEMA, kcu1.TABLE_NAME, kcu1.COLUMN_NAME,
|
1165
|
+
kcu2.CONSTRAINT_SCHEMA AS primary_schema, kcu2.TABLE_NAME AS primary_table, kcu1.CONSTRAINT_NAME AS CONSTRAINT_SCHEMA_FK
|
1008
1166
|
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS rc
|
1009
1167
|
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu1
|
1010
1168
|
ON kcu1.CONSTRAINT_CATALOG = rc.CONSTRAINT_CATALOG
|
@@ -1014,10 +1172,9 @@ module ActiveRecord::ConnectionHandling
|
|
1014
1172
|
ON kcu2.CONSTRAINT_CATALOG = rc.UNIQUE_CONSTRAINT_CATALOG
|
1015
1173
|
AND kcu2.CONSTRAINT_SCHEMA = rc.UNIQUE_CONSTRAINT_SCHEMA
|
1016
1174
|
AND kcu2.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
|
1017
|
-
AND kcu2.ORDINAL_POSITION = kcu1.ORDINAL_POSITION
|
1018
|
-
|
1175
|
+
AND kcu2.ORDINAL_POSITION = kcu1.ORDINAL_POSITION#{"
|
1176
|
+
WHERE kcu1.CONSTRAINT_SCHEMA = COALESCE(current_setting('SEARCH_PATH'), 'public')" if schema }"
|
1019
1177
|
# AND kcu2.TABLE_NAME = ?;", Apartment::Tenant.current, table_name
|
1020
|
-
])
|
1021
1178
|
when 'SQLite'
|
1022
1179
|
sql = "SELECT m.name, fkl.\"from\", fkl.\"table\", m.name || '_' || fkl.\"from\" AS constraint_name
|
1023
1180
|
FROM sqlite_master m
|
@@ -1026,10 +1183,17 @@ module ActiveRecord::ConnectionHandling
|
|
1026
1183
|
else
|
1027
1184
|
end
|
1028
1185
|
if sql
|
1029
|
-
::Brick.
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1186
|
+
::Brick.default_schema ||= schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
1187
|
+
unless (db_schemas = ActiveRecord::Base.execute_sql(schema_sql)).is_a?(Array)
|
1188
|
+
db_schemas = db_schemas.to_a
|
1189
|
+
end
|
1190
|
+
unless db_schemas.empty?
|
1191
|
+
::Brick.db_schemas = db_schemas.each_with_object({}) do |row, s|
|
1192
|
+
row = row.is_a?(String) ? row : row['table_schema']
|
1193
|
+
# Remove whatever default schema we're using and other system schemas
|
1194
|
+
s[row] = nil unless ['information_schema', 'pg_catalog', schema].include?(row)
|
1195
|
+
end
|
1196
|
+
end
|
1033
1197
|
ActiveRecord::Base.execute_sql(sql).each do |fk|
|
1034
1198
|
fk = fk.values unless fk.is_a?(Array)
|
1035
1199
|
::Brick._add_bt_and_hm(fk, relations)
|
@@ -1076,34 +1240,46 @@ module Brick
|
|
1076
1240
|
|
1077
1241
|
class << self
|
1078
1242
|
def _add_bt_and_hm(fk, relations, is_polymorphic = false)
|
1079
|
-
bt_assoc_name = fk[
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1243
|
+
if (bt_assoc_name = fk[2].underscore).end_with?('_id')
|
1244
|
+
bt_assoc_name = bt_assoc_name[0..-4]
|
1245
|
+
elsif bt_assoc_name.end_with?('id') && bt_assoc_name.exclude?('_') # Make the bold assumption that we can just peel off the final ID part
|
1246
|
+
bt_assoc_name = bt_assoc_name[0..-3]
|
1247
|
+
else
|
1248
|
+
bt_assoc_name = "#{bt_assoc_name}_bt"
|
1249
|
+
end
|
1250
|
+
# %%% Temporary schema patch
|
1251
|
+
fk[1] = "#{fk[0]}.#{for_tbl = fk[1]}" if fk[0] && fk[0] != ::Brick.default_schema
|
1252
|
+
for_tbl << '_' if for_tbl
|
1253
|
+
bts = (relation = relations.fetch(fk[1], nil))&.fetch(:fks) { relation[:fks] = {} }
|
1083
1254
|
# %%% Do we miss out on has_many :through or even HM based on constantizing this model early?
|
1084
1255
|
# Maybe it's already gotten this info because we got as far as to say there was a unique class
|
1085
|
-
primary_table = (is_class = fk[
|
1256
|
+
primary_table = if (is_class = fk[4].is_a?(Hash) && fk[4].key?(:class))
|
1257
|
+
pri_tbl = (primary_class = fk[4][:class].constantize).table_name
|
1258
|
+
else
|
1259
|
+
pri_tbl = fk[4]
|
1260
|
+
(fk[3] && fk[3] != ::Brick.default_schema) ? "#{fk[3]}.#{pri_tbl}" : pri_tbl
|
1261
|
+
end
|
1086
1262
|
hms = (relation = relations.fetch(primary_table, nil))&.fetch(:fks) { relation[:fks] = {} } unless is_class
|
1087
1263
|
|
1088
|
-
unless (cnstr_name = fk[
|
1264
|
+
unless (cnstr_name = fk[5])
|
1089
1265
|
# For any appended references (those that come from config), arrive upon a definitely unique constraint name
|
1090
|
-
cnstr_base = cnstr_name = "(brick) #{
|
1266
|
+
cnstr_base = cnstr_name = "(brick) #{for_tbl}#{is_class ? fk[4][:class].underscore : pri_tbl}"
|
1091
1267
|
cnstr_added_num = 1
|
1092
1268
|
cnstr_name = "#{cnstr_base}_#{cnstr_added_num += 1}" while bts&.key?(cnstr_name) || hms&.key?(cnstr_name)
|
1093
1269
|
missing = []
|
1094
|
-
missing << fk[
|
1270
|
+
missing << fk[1] unless relations.key?(fk[1])
|
1095
1271
|
missing << primary_table unless is_class || relations.key?(primary_table)
|
1096
1272
|
unless missing.empty?
|
1097
1273
|
tables = relations.reject { |_k, v| v.fetch(:isView, nil) }.keys.sort
|
1098
1274
|
puts "Brick: Additional reference #{fk.inspect} refers to non-existent #{'table'.pluralize(missing.length)} #{missing.join(' and ')}. (Available tables include #{tables.join(', ')}.)"
|
1099
1275
|
return
|
1100
1276
|
end
|
1101
|
-
unless (cols = relations[fk[
|
1277
|
+
unless (cols = relations[fk[1]][:cols]).key?(fk[2]) || (is_polymorphic && cols.key?("#{fk[2]}_id") && cols.key?("#{fk[2]}_type"))
|
1102
1278
|
columns = cols.map { |k, v| "#{k} (#{v.first.split(' ').first})" }
|
1103
|
-
puts "Brick: Additional reference #{fk.inspect} refers to non-existent column #{fk[
|
1279
|
+
puts "Brick: Additional reference #{fk.inspect} refers to non-existent column #{fk[2]}. (Columns present in #{fk[1]} are #{columns.join(', ')}.)"
|
1104
1280
|
return
|
1105
1281
|
end
|
1106
|
-
if (redundant = bts.find { |_k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[
|
1282
|
+
if (redundant = bts.find { |_k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[1] && v[:fk] == fk[2] && v[:inverse_table] == primary_table })
|
1107
1283
|
if is_class && !redundant.last.key?(:class)
|
1108
1284
|
redundant.last[:primary_class] = primary_class # Round out this BT so it can find the proper :source for a HMT association that references an STI subclass
|
1109
1285
|
else
|
@@ -1115,18 +1291,18 @@ module Brick
|
|
1115
1291
|
if (assoc_bt = bts[cnstr_name])
|
1116
1292
|
if is_polymorphic
|
1117
1293
|
# Assuming same fk (don't yet support composite keys for polymorphics)
|
1118
|
-
assoc_bt[:inverse_table] << fk[
|
1294
|
+
assoc_bt[:inverse_table] << fk[4]
|
1119
1295
|
else # Expect we could have a composite key going
|
1120
1296
|
if assoc_bt[:fk].is_a?(String)
|
1121
|
-
assoc_bt[:fk] = [assoc_bt[:fk], fk[
|
1122
|
-
elsif assoc_bt[:fk].exclude?(fk[
|
1123
|
-
assoc_bt[:fk] << fk[
|
1297
|
+
assoc_bt[:fk] = [assoc_bt[:fk], fk[2]] unless fk[2] == assoc_bt[:fk]
|
1298
|
+
elsif assoc_bt[:fk].exclude?(fk[2])
|
1299
|
+
assoc_bt[:fk] << fk[2]
|
1124
1300
|
end
|
1125
|
-
assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[
|
1301
|
+
assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[2]}"
|
1126
1302
|
end
|
1127
1303
|
else
|
1128
1304
|
inverse_table = [primary_table] if is_polymorphic
|
1129
|
-
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[
|
1305
|
+
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[2], assoc_name: bt_assoc_name, inverse_table: inverse_table || primary_table }
|
1130
1306
|
assoc_bt[:polymorphic] = true if is_polymorphic
|
1131
1307
|
end
|
1132
1308
|
if is_class
|
@@ -1136,21 +1312,21 @@ module Brick
|
|
1136
1312
|
# assoc_bt[:inverse_of] = primary_class.reflect_on_all_associations.find { |a| a.foreign_key == bt[1] }
|
1137
1313
|
end
|
1138
1314
|
|
1139
|
-
return if is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[
|
1315
|
+
return if is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[1] == exclusion[0] && fk[2] == exclusion[1] && primary_table == exclusion[2] } || hms.nil?
|
1140
1316
|
|
1141
1317
|
if (assoc_hm = hms.fetch((hm_cnstr_name = "hm_#{cnstr_name}"), nil))
|
1142
1318
|
if assoc_hm[:fk].is_a?(String)
|
1143
|
-
assoc_hm[:fk] = [assoc_hm[:fk], fk[
|
1144
|
-
elsif assoc_hm[:fk].exclude?(fk[
|
1145
|
-
assoc_hm[:fk] << fk[
|
1319
|
+
assoc_hm[:fk] = [assoc_hm[:fk], fk[2]] unless fk[2] == assoc_hm[:fk]
|
1320
|
+
elsif assoc_hm[:fk].exclude?(fk[2])
|
1321
|
+
assoc_hm[:fk] << fk[2]
|
1146
1322
|
end
|
1147
1323
|
assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
|
1148
1324
|
assoc_hm[:inverse] = assoc_bt
|
1149
1325
|
else
|
1150
|
-
assoc_hm = hms[hm_cnstr_name] = { is_bt: false, fk: fk[
|
1326
|
+
assoc_hm = hms[hm_cnstr_name] = { is_bt: false, fk: fk[2], assoc_name: fk[1].tr('.', '_').pluralize, alternate_name: bt_assoc_name, inverse_table: fk[1], inverse: assoc_bt }
|
1151
1327
|
assoc_hm[:polymorphic] = true if is_polymorphic
|
1152
1328
|
hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
|
1153
|
-
hm_counts[fk[
|
1329
|
+
hm_counts[fk[1]] = hm_counts.fetch(fk[1]) { 0 } + 1
|
1154
1330
|
end
|
1155
1331
|
assoc_bt[:inverse] = assoc_hm
|
1156
1332
|
end
|
@@ -55,7 +55,9 @@ module Brick
|
|
55
55
|
unless (is_template_exists = _brick_template_exists?(*args, **options))
|
56
56
|
# Need to return true if we can fill in the blanks for a missing one
|
57
57
|
# args will be something like: ["index", ["categories"]]
|
58
|
-
|
58
|
+
args[1] = args[1].each_with_object([]) { |a, s| s.concat(a.split('/')) }
|
59
|
+
args[1][args[1].length - 1] = args[1].last.singularize # Make sure the last item, defining the class name, is singular
|
60
|
+
model = args[1].map(&:camelize).join('::').constantize
|
59
61
|
if is_template_exists = model && (
|
60
62
|
['index', 'show'].include?(args.first) || # Everything has index and show
|
61
63
|
# Only CUD stuff has create / update / destroy
|
@@ -72,9 +74,9 @@ module Brick
|
|
72
74
|
fk_name.zip(pk.map { |pk_part| "#{obj_name}.#{pk_part}" })
|
73
75
|
else
|
74
76
|
pk = pk.each_with_object([]) { |pk_part, s| s << "#{obj_name}.#{pk_part}" }
|
75
|
-
[[fk_name,
|
77
|
+
[[fk_name, pk.length == 1 ? pk.first : pk.inspect]]
|
76
78
|
end
|
77
|
-
keys << [hm_assoc.inverse_of.foreign_type,
|
79
|
+
keys << [hm_assoc.inverse_of.foreign_type, hm_assoc.active_record.name] if hm_assoc.options.key?(:as)
|
78
80
|
keys.map { |x| "#{x.first}: #{x.last}"}.join(', ')
|
79
81
|
end
|
80
82
|
|
@@ -84,8 +86,9 @@ module Brick
|
|
84
86
|
|
85
87
|
model_name = @_brick_model.name
|
86
88
|
pk = @_brick_model._brick_primary_key(::Brick.relations.fetch(model_name, nil))
|
87
|
-
obj_name = model_name.underscore
|
88
|
-
|
89
|
+
obj_name = model_name.split('::').last.underscore
|
90
|
+
path_obj_name = model_name.underscore.tr('/', '_')
|
91
|
+
table_name = obj_name.pluralize
|
89
92
|
template_link = nil
|
90
93
|
bts, hms, associatives = ::Brick.get_bts_and_hms(@_brick_model) # This gets BT and HM and also has_many :through (HMT)
|
91
94
|
hms_columns = [] # Used for 'index'
|
@@ -108,21 +111,21 @@ module Brick
|
|
108
111
|
"#{obj_name}.#{attrib_name} || 0"
|
109
112
|
end
|
110
113
|
"<%= ct = #{set_ct}
|
111
|
-
link_to \"#\{ct || 'View'\} #{assoc_name}\", #{hm_assoc.klass.name.underscore.pluralize}_path({ #{path_keys(hm_assoc, hm_fk_name, obj_name, pk)} }) unless ct&.zero? %>\n"
|
114
|
+
link_to \"#\{ct || 'View'\} #{assoc_name}\", #{hm_assoc.klass.name.underscore.tr('/', '_').pluralize}_path({ #{path_keys(hm_assoc, hm_fk_name, obj_name, pk)} }) unless ct&.zero? %>\n"
|
112
115
|
else # has_one
|
113
116
|
"<%= obj = #{obj_name}.#{hm.first}; link_to(obj.brick_descrip, obj) if obj %>\n"
|
114
117
|
end
|
115
118
|
elsif args.first == 'show'
|
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"
|
119
|
+
hm_stuff << "<%= link_to '#{assoc_name}', #{hm_assoc.klass.name.underscore.tr('/', '_').pluralize}_path({ #{path_keys(hm_assoc, hm_fk_name, "@#{obj_name}", pk)} }) %>\n"
|
117
120
|
end
|
118
121
|
s << hm_stuff
|
119
122
|
end
|
120
123
|
|
121
|
-
schema_options = ::Brick.db_schemas.each_with_object(+'') { |v, s| s << "<option value=\"#{v}\">#{v}</option>" }.html_safe
|
124
|
+
schema_options = ::Brick.db_schemas.keys.each_with_object(+'') { |v, s| s << "<option value=\"#{v}\">#{v}</option>" }.html_safe
|
122
125
|
# %%% If we are not auto-creating controllers (or routes) then omit by default, and if enabled anyway, such as in a development
|
123
126
|
# environment or whatever, then get either the controllers or routes list instead
|
124
|
-
table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables)
|
125
|
-
.each_with_object(+'') { |v, s| s << "<option value=\"#{v.underscore.pluralize}\">#{v}</option>" }.html_safe
|
127
|
+
table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables).sort
|
128
|
+
.each_with_object(+'') { |v, s| s << "<option value=\"#{v.underscore.gsub('.', '/').pluralize}\">#{v}</option>" }.html_safe
|
126
129
|
css = +"<style>
|
127
130
|
#dropper {
|
128
131
|
background-color: #eee;
|
@@ -261,7 +264,8 @@ if (schemaSelect) {
|
|
261
264
|
|
262
265
|
var tblSelect = document.getElementById(\"tbl\");
|
263
266
|
if (tblSelect) {
|
264
|
-
tblSelect.value = changeout(location.href);
|
267
|
+
tblSelect.value = changeout(location.href)[0];
|
268
|
+
if (tblSelect.selectedIndex < 0) tblSelect.value = changeout(location.href)[1];
|
265
269
|
tblSelect.addEventListener(\"change\", function () {
|
266
270
|
var lhr = changeout(location.href, null, this.value);
|
267
271
|
if (brickSchema)
|
@@ -276,7 +280,8 @@ function changeout(href, param, value) {
|
|
276
280
|
hrefParts = hrefParts[0].split(\"://\");
|
277
281
|
var pathParts = hrefParts[hrefParts.length - 1].split(\"/\");
|
278
282
|
if (value === undefined)
|
279
|
-
|
283
|
+
// A couple possibilities if it's namespaced, starting with two parts in the path -- and then try just one
|
284
|
+
return [pathParts.slice(1, 3).join('/'), pathParts.slice(1, 2)];
|
280
285
|
else
|
281
286
|
return hrefParts[0] + \"://\" + pathParts[0] + \"/\" + value;
|
282
287
|
}
|
@@ -313,7 +318,7 @@ function changeout(href, param, value) {
|
|
313
318
|
btnImport.style.display = droppedTSV.length > 0 ? \"block\" : \"none\";
|
314
319
|
});
|
315
320
|
btnImport.addEventListener(\"click\", function () {
|
316
|
-
fetch(changeout(<%= #{
|
321
|
+
fetch(changeout(<%= #{path_obj_name}_path(-1, format: :csv).inspect.html_safe %>, \"_brick_schema\", brickSchema), {
|
317
322
|
method: 'PATCH',
|
318
323
|
headers: { 'Content-Type': 'text/tab-separated-values' },
|
319
324
|
body: droppedTSV
|
@@ -384,17 +389,32 @@ function changeout(href, param, value) {
|
|
384
389
|
<script async defer src=\"https://apis.google.com/js/api.js\" onload=\"gapiLoaded()\"></script>
|
385
390
|
"
|
386
391
|
end
|
392
|
+
# %%% Instead of our current "for Janet Leverling (Employee)" kind of link we previously had this code that did a "where x = 123" thing:
|
393
|
+
# (where <%= @_brick_params.each_with_object([]) { |v, s| s << \"#\{v.first\} = #\{v.last.inspect\}\" }.join(', ') %>)
|
387
394
|
"#{css}
|
388
395
|
<p style=\"color: green\"><%= notice %></p>#{"
|
389
|
-
<select id=\"schema\">#{schema_options}</select>" if ::Brick.db_schemas.length > 1}
|
396
|
+
<select id=\"schema\">#{schema_options}</select>" if ::Brick.config.schema_behavior[:multitenant] && ::Brick.db_schemas.length > 1}
|
390
397
|
<select id=\"tbl\">#{table_options}</select>
|
391
|
-
<h1>#{model_name.pluralize}</h1>#{template_link}
|
392
|
-
|
393
|
-
<% if @_brick_params&.present?
|
398
|
+
<h1>#{model_plural = model_name.pluralize}</h1>#{template_link}
|
399
|
+
|
400
|
+
<% if @_brick_params&.present? %>
|
401
|
+
<% if @_brick_params.length == 1 # %%% Does not yet work with composite keys
|
402
|
+
k, id = @_brick_params.first
|
403
|
+
id = id.first if id.is_a?(Array) && id.length == 1
|
404
|
+
origin = (key_parts = k.split('.')).length == 1 ? #{model_name} : #{model_name}.reflect_on_association(key_parts.first).klass
|
405
|
+
# binding.pry
|
406
|
+
if (destination_fk = Brick.relations[origin.table_name][:fks].values.find { |fk| puts fk.inspect; fk[:fk] == key_parts.last }) &&
|
407
|
+
(obj = (destination = origin.reflect_on_association(destination_fk[:assoc_name])&.klass)&.find(id)) %>
|
408
|
+
<h3>for <%= link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination.name.underscore.tr('/', '_')\}_path\".to_sym, id) %></h3><%
|
409
|
+
end
|
410
|
+
end %>
|
411
|
+
(<%= link_to 'See all #{model_plural.split('::').last}', #{path_obj_name.pluralize}_path %>)
|
412
|
+
<% end %>
|
394
413
|
<table id=\"#{table_name}\">
|
395
414
|
<thead><tr>#{'<th></th>' if pk.present?}
|
396
415
|
<% @#{table_name}.columns.map(&:name).each do |col| %>
|
397
|
-
<% next if #{(pk || []).inspect}.include?(col)
|
416
|
+
<% next if (#{(pk || []).inspect}.include?(col) && #{model_name}.column_for_attribute(col).type == :integer && !bts.key?(col)) ||
|
417
|
+
::Brick.config.metadata_columns.include?(col) || poly_cols.include?(col) %>
|
398
418
|
<th>
|
399
419
|
<% if (bt = bts[col]) %>
|
400
420
|
BT <%
|
@@ -407,15 +427,16 @@ function changeout(href, param, value) {
|
|
407
427
|
</th>
|
408
428
|
<% end %>
|
409
429
|
<%# Consider getting the name from the association -- h.first.name -- if a more \"friendly\" alias should be used for a screwy table name %>
|
410
|
-
#{hms_headers.map { |h| "<th>#{h[1]} <%= link_to('#{h[2]}', #{h.first.klass.name.underscore.pluralize}_path) %></th>\n" }.join}
|
430
|
+
#{hms_headers.map { |h| "<th>#{h[1]} <%= link_to('#{h[2]}', #{h.first.klass.name.underscore.tr('/', '_').pluralize}_path) %></th>\n" }.join}
|
411
431
|
</tr></thead>
|
412
432
|
|
413
433
|
<tbody>
|
414
434
|
<% @#{table_name}.each do |#{obj_name}| %>
|
415
435
|
<tr>#{"
|
416
|
-
<td><%= link_to '⇛', #{
|
436
|
+
<td><%= link_to '⇛', #{path_obj_name}_path(#{obj_pk}), { class: 'big-arrow' } %></td>" if obj_pk}
|
417
437
|
<% #{obj_name}.attributes.each do |k, val| %>
|
418
|
-
<% next if #{(obj_pk || []).inspect}.include?(k)
|
438
|
+
<% next if (#{(obj_pk || []).inspect}.include?(k) && #{model_name}.column_for_attribute(k).type == :integer && !bts.key?(k)) ||
|
439
|
+
::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'))) %>
|
419
440
|
<td>
|
420
441
|
<% if (bt = bts[k]) %>
|
421
442
|
<%# binding.pry # Postgres column names are limited to 63 characters %>
|
@@ -430,7 +451,7 @@ function changeout(href, param, value) {
|
|
430
451
|
#{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)
|
431
452
|
)
|
432
453
|
bt_id = #{obj_name}.send(*bt_id_col) if bt_id_col&.present? %>
|
433
|
-
<%= bt_id ? link_to(bt_txt, send(\"#\{bt_class.base_class.name.underscore\}_path\".to_sym, bt_id)) : bt_txt %>
|
454
|
+
<%= bt_id ? link_to(bt_txt, send(\"#\{bt_class.base_class.name.underscore.tr('/', '_')\}_path\".to_sym, bt_id)) : bt_txt %>
|
434
455
|
<%#= 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 %>
|
435
456
|
<% end %>
|
436
457
|
<% else %>
|
@@ -440,19 +461,19 @@ function changeout(href, param, value) {
|
|
440
461
|
<% end %>
|
441
462
|
#{hms_columns.each_with_object(+'') { |hm_col, s| s << "<td>#{hm_col}</td>" }}
|
442
463
|
</tr>
|
443
|
-
</tbody>
|
444
464
|
<% end %>
|
465
|
+
</tbody>
|
445
466
|
</table>
|
446
467
|
|
447
|
-
#{"<hr><%= link_to \"New #{obj_name}\", new_#{
|
468
|
+
#{"<hr><%= link_to \"New #{obj_name}\", new_#{path_obj_name}_path %>" unless @_brick_model.is_view?}
|
448
469
|
#{script}"
|
449
470
|
when 'show', 'update'
|
450
471
|
"#{css}
|
451
472
|
<p style=\"color: green\"><%= notice %></p>#{"
|
452
|
-
<select id=\"schema\">#{schema_options}</select>" if ::Brick.db_schemas.length > 1}
|
473
|
+
<select id=\"schema\">#{schema_options}</select>" if ::Brick.config.schema_behavior[:multitenant] && ::Brick.db_schemas.length > 1}
|
453
474
|
<select id=\"tbl\">#{table_options}</select>
|
454
475
|
<h1>#{model_name}: <%= (obj = @#{obj_name})&.brick_descrip || controller_name %></h1>
|
455
|
-
<%= link_to '(See all #{obj_name.pluralize})', #{
|
476
|
+
<%= link_to '(See all #{obj_name.pluralize})', #{path_obj_name.pluralize}_path %>
|
456
477
|
<% if obj %>
|
457
478
|
<%= # path_options = [obj.#{pk}]
|
458
479
|
# path_options << { '_brick_schema': } if
|
@@ -462,7 +483,8 @@ function changeout(href, param, value) {
|
|
462
483
|
<% has_fields = false
|
463
484
|
@#{obj_name}.attributes.each do |k, val| %>
|
464
485
|
<tr>
|
465
|
-
<% next if #{(pk || []).inspect}.include?(k)
|
486
|
+
<% next if (#{(pk || []).inspect}.include?(k) && !bts.key?(k)) ||
|
487
|
+
::Brick.config.metadata_columns.include?(k) %>
|
466
488
|
<th class=\"show-field\">
|
467
489
|
<% has_fields = true
|
468
490
|
if (bt = bts[k])
|
@@ -504,7 +526,7 @@ function changeout(href, param, value) {
|
|
504
526
|
html_options = { prompt: \"Select #\{bt_name\}\" }
|
505
527
|
html_options[:class] = 'dimmed' unless val %>
|
506
528
|
<%= f.select k.to_sym, bt[3], { value: val || '^^^brick_NULL^^^' }, html_options %>
|
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 %>
|
529
|
+
<%= bt_obj = bt_class&.find_by(bt_pair[1] => val); link_to('⇛', send(\"#\{bt_class.base_class.name.underscore.tr('/', '_')\}_path\".to_sym, bt_obj.send(bt_class.primary_key.to_sym)), { class: 'show-arrow' }) if bt_obj %>
|
508
530
|
<% else case #{model_name}.column_for_attribute(k).type
|
509
531
|
when :string, :text %>
|
510
532
|
<% if is_bcrypt?(val) # || .readonly? %>
|
@@ -546,7 +568,7 @@ function changeout(href, param, value) {
|
|
546
568
|
<tr><td>(none)</td></tr>
|
547
569
|
<% else %>
|
548
570
|
<% 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>
|
571
|
+
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm.first.klass.name.underscore.tr('/', '_')}_path([#{obj_pk}])) %></td></tr>
|
550
572
|
<% end %>
|
551
573
|
<% end %>
|
552
574
|
</table>"
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -90,10 +90,10 @@ module Brick
|
|
90
90
|
end
|
91
91
|
|
92
92
|
class << self
|
93
|
-
attr_accessor :db_schemas
|
93
|
+
attr_accessor :default_schema, :db_schemas
|
94
94
|
|
95
95
|
def set_db_schema(params)
|
96
|
-
schema = params['_brick_schema']
|
96
|
+
schema = params['_brick_schema']
|
97
97
|
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema) if schema && ::Brick.db_schemas&.include?(schema)
|
98
98
|
end
|
99
99
|
|
@@ -287,8 +287,16 @@ module Brick
|
|
287
287
|
# Database schema to use when analysing existing data, such as deriving a list of polymorphic classes
|
288
288
|
# for polymorphics in which it wasn't originally specified.
|
289
289
|
# @api public
|
290
|
-
def
|
291
|
-
Brick.config.
|
290
|
+
def schema_behavior=(behavior)
|
291
|
+
Brick.config.schema_behavior = (behavior.is_a?(Symbol) ? { behavior => nil } : behavior)
|
292
|
+
end
|
293
|
+
# For any Brits out there
|
294
|
+
def schema_behaviour=(behavior)
|
295
|
+
Brick.schema_behavior = behavior
|
296
|
+
end
|
297
|
+
|
298
|
+
def sti_type_column=(type_col)
|
299
|
+
Brick.config.sti_type_column = (type_col.is_a?(String) ? { type_col => nil } : type_col)
|
292
300
|
end
|
293
301
|
|
294
302
|
def default_route_fallback=(resource_name)
|
@@ -303,18 +311,23 @@ module Brick
|
|
303
311
|
|
304
312
|
relations = ::Brick.relations
|
305
313
|
if (ars = ::Brick.config.additional_references) || ::Brick.config.polymorphics
|
306
|
-
|
314
|
+
if ars
|
315
|
+
ars.each do |ar|
|
316
|
+
fk = ar.length < 5 ? [nil, +ar[0], ar[1], nil, +ar[2]] : [ar[0], +ar[1], ar[2], ar[3], +ar[4], ar[5]]
|
317
|
+
::Brick._add_bt_and_hm(fk, relations)
|
318
|
+
end
|
319
|
+
end
|
307
320
|
if (polys = ::Brick.config.polymorphics)
|
308
|
-
if (schema = ::Brick.config.schema_to_analyse) && ::Brick.db_schemas&.include?(schema)
|
321
|
+
if (schema = ::Brick.config.schema_behavior[:multitenant]&.fetch(:schema_to_analyse, nil)) && ::Brick.db_schemas&.include?(schema)
|
309
322
|
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
|
310
323
|
end
|
311
324
|
missing_stis = {}
|
312
325
|
polys.each do |k, v|
|
313
326
|
table_name, poly = k.split('.')
|
314
|
-
v ||= ActiveRecord::Base.execute_sql("SELECT DISTINCT #{poly}_type AS typ FROM #{table_name}").
|
327
|
+
v ||= ActiveRecord::Base.execute_sql("SELECT DISTINCT #{poly}_type AS typ FROM #{table_name}").each_with_object([]) { |result, s| s << result['typ'] if result['typ'] }
|
315
328
|
v.each do |type|
|
316
329
|
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)
|
330
|
+
::Brick._add_bt_and_hm([nil, table_name, poly, nil, primary_table, "(brick) #{table_name}_#{poly}"], relations, true)
|
318
331
|
else
|
319
332
|
missing_stis[primary_table] = type unless ::Brick.existing_stis.key?(type)
|
320
333
|
end
|
@@ -390,11 +403,20 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
390
403
|
end
|
391
404
|
# %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
|
392
405
|
# If auto-controllers and auto-models are both enabled then this makes sense:
|
393
|
-
::Brick.relations.each do |
|
394
|
-
|
406
|
+
::Brick.relations.each do |rel_name, v|
|
407
|
+
rel_name = rel_name.split('.').map(&:underscore)
|
408
|
+
schema_names = rel_name[0..-2]
|
409
|
+
k = rel_name.last
|
410
|
+
unless existing_controllers.key?(controller_name = k.pluralize)
|
395
411
|
options = {}
|
396
412
|
options[:only] = [:index, :show] if v.key?(:isView)
|
397
|
-
|
413
|
+
if schema_names.present? # && !Object.const_defined('Apartment')
|
414
|
+
send(:namespace, schema_names.first) do
|
415
|
+
send(:resources, controller_name.to_sym, **options)
|
416
|
+
end
|
417
|
+
else
|
418
|
+
send(:resources, controller_name.to_sym, **options)
|
419
|
+
end
|
398
420
|
end
|
399
421
|
end
|
400
422
|
end
|
@@ -215,9 +215,15 @@ module Brick
|
|
215
215
|
# Brick.sti_namespace_prefixes = { '::Animals::' => 'Animal',
|
216
216
|
# '::Snake' => 'Reptile' }
|
217
217
|
|
218
|
+
# # Custom inheritance_column to be used for STI. This is by default \"type\", and applies to all models. With this
|
219
|
+
# # option you can change this either for specific models, or apply a new overall name generally:
|
220
|
+
# Brick.sti_type_column = 'sti_type'
|
221
|
+
# Brick.sti_type_column = { 'rails_type' => ['sales.specialoffer'] }
|
222
|
+
|
218
223
|
# # Database schema to use when analysing existing data, such as deriving a list of polymorphic classes in the case that
|
219
224
|
# # it wasn't originally specified.
|
220
|
-
# Brick.
|
225
|
+
# Brick.schema_behavior = :namespaced
|
226
|
+
# Brick.schema_behavior = { multitenant: { schema_to_analyse: 'engineering' } }
|
221
227
|
|
222
228
|
# # Polymorphic associations are set up by providing a model name and polymorphic association name#{poly}
|
223
229
|
|
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.30
|
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-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|