brick 1.0.29 → 1.0.30
Sign up to get free protection for your applications and to get access to all the features.
- 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
|