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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a312c2fd5baf0793b4bb4143167b4399c70a514991206a611d525d0784de95b
4
- data.tar.gz: bb1af47d2d073dab2c046ff9757fb5db9447f02dd0688330ad7ac8ad678d6751
3
+ metadata.gz: ad31136f451e04eabcc67ee09980d45d25aa8e35453aaf4c160ffc9aef3f774c
4
+ data.tar.gz: 5e3b705dfad389391b291d26bd3c0571cf0579abce3ed00c2840805fd5e05353
5
5
  SHA512:
6
- metadata.gz: dbfabaf60608b2094e9903cb538bab00706f434f94289b9647320bb128adf0b34317a6404ace34897cf4b2f91700ebd7c028a12b4072ba1ebf9de3380f75add3
7
- data.tar.gz: 6e5444ac763c71de71fc1aca64cdb41db6276f1df57f5b556b03bad0b743373f2720865ba7c6ce36f9887d77ffee3b091f32d233121742c4786ca21e8d512705
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 schema_to_analyse
126
- @mutex.synchronize { @schema_to_analyse }
125
+ def schema_behavior
126
+ @mutex.synchronize { @schema_behavior ||= {} }
127
127
  end
128
128
 
129
- def schema_to_analyse=(schema)
130
- @mutex.synchronize { @schema_to_analyse = schema }
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
@@ -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
- selects << "\"#{table.name}\".\"#{col.name}\""
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) # 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
- class Object
502
- class << self
503
- alias _brick_const_missing const_missing
504
- def const_missing(*args)
505
- return self.const_get(args.first) if self.const_defined?(args.first)
506
- return Object.const_get(args.first) if Object.const_defined?(args.first) unless self == Object
507
-
508
- class_name = args.first.to_s
509
- # See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
510
- # checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
511
- # that is, checking #qualified_name_for with: from_mod, const_name
512
- # If we want to support namespacing in the future, might have to utilise something like this:
513
- # path_suffix = ActiveSupport::Dependencies.qualified_name_for(Object, args.first).underscore
514
- # return self._brick_const_missing(*args) if ActiveSupport::Dependencies.search_for_file(path_suffix)
515
- # If the file really exists, go and snag it:
516
- if !(is_found = ActiveSupport::Dependencies.search_for_file(class_name.underscore)) && (filepath = (self.name || class_name)&.split('::'))
517
- filepath = (filepath[0..-2] + [class_name]).join('/').underscore + '.rb'
518
- end
519
- if is_found
520
- return self._brick_const_missing(*args)
521
- elsif ActiveSupport::Dependencies.search_for_file(filepath) # Last-ditch effort to pick this thing up before we fill in the gaps on our own
522
- my_const = parent.const_missing(class_name) # ends up having: MyModule::MyClass
523
- return my_const
524
- end
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
- relations = ::Brick.instance_variable_get(:@relations)[ActiveRecord::Base.connection_pool.object_id] || {}
527
- result = if ::Brick.enable_controllers? && class_name.end_with?('Controller') && (plural_class_name = class_name[0..-11]).length.positive?
528
- # Otherwise now it's up to us to fill in the gaps
529
- if (model = plural_class_name.singularize.constantize)
530
- # 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.
531
- build_controller(class_name, plural_class_name, model, relations)
532
- end
533
- elsif ::Brick.enable_models?
534
- # See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
535
- # checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
536
- plural_class_name = ActiveSupport::Inflector.pluralize(model_name = class_name)
537
- singular_table_name = ActiveSupport::Inflector.underscore(model_name)
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
- if result
552
- built_class, code = result
553
- puts "\n#{code}"
554
- built_class
555
- elsif ::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}")
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
- self._brick_const_missing(*args)
561
- else
562
- puts "MISSING! #{self.name} #{args.inspect} #{table_name}"
563
- self._brick_const_missing(*args)
564
- end
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[model_name]&.fetch(:base, nil) || ::Brick.existing_stis[model_name]&.constantize)
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
- code = +"class #{model_name} < #{base_model.name}\n"
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 #{model_name}\n\n"
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] = a.foreign_key
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
- code = +"class #{class_name} < ApplicationController\n"
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
- # binding.pry
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
- ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
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 params\n"
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
- [ActiveSupport::Inflector.pluralize(hm_assoc[:inverse_table]), nil]
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
- # Only for Postgres? (Doesn't work in sqlite3)
895
- # puts ActiveRecord::Base.execute_sql("SELECT current_setting('SEARCH_PATH')").to_a.inspect
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
- schema_sql = 'SELECT NULL AS table_schema;'
898
- case ActiveRecord::Base.connection.adapter_name
1049
+ schema_sql = 'SELECT NULL AS table_schema;'
1050
+ case ActiveRecord::Base.connection.adapter_name
899
1051
  when 'PostgreSQL'
900
- schema = 'public' # Too early at this point to be able to pick up: Brick.config.schema_to_analyse
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 ||= ActiveRecord::Base.send(:sanitize_sql_array, [
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 = ? -- COALESCE(current_setting('SEARCH_PATH'), 'public')
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", schema
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
- relation = relations[(relation_name = r['relation_name'])]
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 = ActiveRecord::Base.send(:sanitize_sql_array, [
1007
- "SELECT kcu1.TABLE_NAME, kcu1.COLUMN_NAME, kcu2.TABLE_NAME AS primary_table, kcu1.CONSTRAINT_NAME
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
- WHERE kcu1.CONSTRAINT_SCHEMA = ? -- COALESCE(current_setting('SEARCH_PATH'), 'public')", schema
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.db_schemas = ActiveRecord::Base.execute_sql(schema_sql)
1030
- ::Brick.db_schemas = ::Brick.db_schemas.to_a unless ::Brick.db_schemas.is_a?(Array)
1031
- ::Brick.db_schemas.map! { |row| row['table_schema'] } unless ::Brick.db_schemas.empty? || ::Brick.db_schemas.first.is_a?(String)
1032
- ::Brick.db_schemas -= ['information_schema', 'pg_catalog']
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[1].underscore
1080
- bt_assoc_name = bt_assoc_name[0..-4] if bt_assoc_name.end_with?('_id')
1081
-
1082
- bts = (relation = relations.fetch(fk[0], nil))&.fetch(:fks) { relation[:fks] = {} }
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[2].is_a?(Hash) && fk[2].key?(:class)) ? (primary_class = fk[2][:class].constantize).table_name : fk[2]
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[3])
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) #{fk[0]}_#{is_class ? fk[2][:class].underscore : fk[2]}"
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[0] unless relations.key?(fk[0])
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[0]][:cols]).key?(fk[1]) || (is_polymorphic && cols.key?("#{fk[1]}_id") && cols.key?("#{fk[1]}_type"))
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[1]}. (Columns present in #{fk[0]} are #{columns.join(', ')}.)"
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[0] && v[:fk] == fk[1] && v[:inverse_table] == primary_table })
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[2]
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[1]] unless fk[1] == assoc_bt[:fk]
1122
- elsif assoc_bt[:fk].exclude?(fk[1])
1123
- assoc_bt[:fk] << fk[1]
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[1]}"
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[1], assoc_name: bt_assoc_name, inverse_table: inverse_table || primary_table }
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[0] == exclusion[0] && fk[1] == exclusion[1] && primary_table == exclusion[2] }
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[1]] unless fk[1] == assoc_hm[:fk]
1144
- elsif assoc_hm[:fk].exclude?(fk[1])
1145
- assoc_hm[:fk] << fk[1]
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[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0], inverse: assoc_bt }
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[0]] = hm_counts.fetch(fk[0]) { 0 } + 1
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
- model = args[1].map(&:camelize).join('::').singularize.constantize
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, "#{pk.length == 1 ? pk.first : pk.inspect}"]]
77
+ [[fk_name, pk.length == 1 ? pk.first : pk.inspect]]
76
78
  end
77
- keys << [hm_assoc.inverse_of.foreign_type, "#{hm_assoc.active_record.name}"] if hm_assoc.options.key?(:as)
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
- table_name = model_name.pluralize.underscore
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
- return pathParts[1];
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(<%= #{obj_name}_path(-1, format: :csv).inspect.html_safe %>, \"_brick_schema\", brickSchema), {
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? %><h3>where <%= @_brick_params.each_with_object([]) { |v, s| s << \"#\{v.first\} = #\{v.last.inspect\}\" }.join(', ') %></h3><% end %>
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) || ::Brick.config.metadata_columns.include?(col) || poly_cols.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 '⇛', #{obj_name}_path(#{obj_pk}), { class: 'big-arrow' } %></td>" if obj_pk}
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) || ::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'))) %>
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_#{obj_name}_path %>" unless @_brick_model.is_view?}
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})', #{table_name}_path %>
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) || ::Brick.config.metadata_columns.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>"
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 29
8
+ TINY = 30
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
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'] || 'public'
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 schema_to_analyse=(schema)
291
- Brick.config.schema_to_analyse = schema
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
- ars.each { |fk| ::Brick._add_bt_and_hm(fk[0..2], relations) } if ars
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}").map { |result| result['typ'] }
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 |k, v|
394
- unless existing_controllers.key?(controller_name = k.underscore.pluralize)
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
- send(:resources, controller_name.to_sym, **options)
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.schema_to_analyse = 'engineering'
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.29
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-24 00:00:00.000000000 Z
11
+ date: 2022-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord