brick 1.0.28 → 1.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/brick/config.rb +24 -4
- data/lib/brick/extensions.rb +330 -146
- data/lib/brick/frameworks/rails/engine.rb +59 -32
- 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: f529097f82af103af54ae472e226244ce8932b64cf974b836d1d23be6cedfbce
|
4
|
+
data.tar.gz: 1561bea9cdcdd366d2ef9798cff36f3a74f58768f1f14be5c2bfbf4f40feac8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5d812af4ba7eadae06e28bb9686ea60b79f9df721a51cbddbb0ce87d6abfe78e0675b45446768bd2460f00c1f02bba5276d21d9ec96345dcffa8079d87f2ec0
|
7
|
+
data.tar.gz: b7ff59c6229dac732eb06a01c59d4a2913675b02a0a8a54106c2dfdcf965ea58cff5dc6d8dfb1c54fb8d9569fa31f2d8217ed281593bdbc167e89d42178225dd
|
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
@@ -72,7 +72,11 @@ module ActiveRecord
|
|
72
72
|
|
73
73
|
pk = primary_key.is_a?(String) ? [primary_key] : primary_key || []
|
74
74
|
# Just return [] if we're missing any part of the primary key. (PK is usually just "id")
|
75
|
-
|
75
|
+
if relation && pk.present?
|
76
|
+
@_brick_primary_key ||= pk.any? { |pk_part| !relation[:cols].key?(pk_part) } ? [] : pk
|
77
|
+
else # No definitive key yet, so return what we can without setting the instance variable
|
78
|
+
pk
|
79
|
+
end
|
76
80
|
end
|
77
81
|
|
78
82
|
# Used to show a little prettier name for an object
|
@@ -192,7 +196,7 @@ module ActiveRecord
|
|
192
196
|
def self.bt_link(assoc_name)
|
193
197
|
model_underscore = name.underscore
|
194
198
|
assoc_name = CGI.escapeHTML(assoc_name.to_s)
|
195
|
-
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)
|
196
200
|
link = Class.new.extend(ActionView::Helpers::UrlHelper).link_to(name, model_path)
|
197
201
|
model_underscore == assoc_name ? link : "#{assoc_name}-#{link}".html_safe
|
198
202
|
end
|
@@ -302,8 +306,12 @@ module ActiveRecord
|
|
302
306
|
|
303
307
|
# %%% Skip the metadata columns
|
304
308
|
if selects&.empty? # Default to all columns
|
309
|
+
tbl_no_schema = table.name.split('.').last
|
305
310
|
columns.each do |col|
|
306
|
-
|
311
|
+
if (col_name = col.name) == 'class'
|
312
|
+
col_alias = ' AS _class'
|
313
|
+
end
|
314
|
+
selects << "\"#{tbl_no_schema}\".\"#{col_name}\"#{col_alias}"
|
307
315
|
end
|
308
316
|
end
|
309
317
|
|
@@ -345,7 +353,7 @@ module ActiveRecord
|
|
345
353
|
end
|
346
354
|
|
347
355
|
if join_array.present?
|
348
|
-
left_outer_joins!(join_array)
|
356
|
+
left_outer_joins!(join_array)
|
349
357
|
# Without working from a duplicate, touching the AREL ast tree sets the @arel instance variable, which causes the relation to be immutable.
|
350
358
|
(rel_dupe = dup)._arel_alias_names
|
351
359
|
core_selects = selects.dup
|
@@ -356,12 +364,12 @@ module ActiveRecord
|
|
356
364
|
v.last.each do |k1, v1| # k1 is class, v1 is array of columns to snag
|
357
365
|
next if chains[k1].nil?
|
358
366
|
|
359
|
-
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
|
360
368
|
field_tbl_name = nil
|
361
369
|
v1.map { |x| [translations[x[0..-2].map(&:to_s).join('.')], x.last] }.each_with_index do |sel_col, idx|
|
362
|
-
field_tbl_name = field_tbl_names[v.first][sel_col.first] ||= shift_or_first(chains[sel_col.first])
|
370
|
+
field_tbl_name = (field_tbl_names[v.first][sel_col.first] ||= shift_or_first(chains[sel_col.first])).split('.').last
|
363
371
|
|
364
|
-
selects << "#{"#{field_tbl_name}
|
372
|
+
selects << "#{"\"#{field_tbl_name}\".\"#{sel_col.last}\""} AS \"#{(col_alias = "_brfk_#{v.first}__#{sel_col.last}")}\""
|
365
373
|
v1[idx] << col_alias
|
366
374
|
end
|
367
375
|
|
@@ -369,7 +377,7 @@ module ActiveRecord
|
|
369
377
|
# Accommodate composite primary key by allowing id_col to come in as an array
|
370
378
|
((id_col = k1.primary_key).is_a?(Array) ? id_col : [id_col]).each do |id_part|
|
371
379
|
id_for_tables[v.first] << if id_part
|
372
|
-
selects << "#{"#{tbl_name}
|
380
|
+
selects << "#{"\"#{tbl_name}\".\"#{id_part}\""} AS \"#{(id_alias = "_brfk_#{v.first}__#{id_part}")}\""
|
373
381
|
id_alias
|
374
382
|
end
|
375
383
|
end
|
@@ -416,6 +424,7 @@ JOIN (SELECT #{selects.join(', ')}, COUNT(#{count_column}) AS _ct_ FROM #{associ
|
|
416
424
|
joins!("#{join_clause} ON #{on_clause.join(' AND ')}")
|
417
425
|
end
|
418
426
|
where!(wheres) unless wheres.empty?
|
427
|
+
limit(1000) # Don't want to get too carried away just yet
|
419
428
|
wheres unless wheres.empty? # Return the specific parameters that we did use
|
420
429
|
end
|
421
430
|
|
@@ -480,7 +489,7 @@ if ActiveSupport::Dependencies.respond_to?(:autoload_module!) # %%% Only works w
|
|
480
489
|
::Brick.sti_models[qualified_name] = { base: base_class }
|
481
490
|
# Build subclass and place it into the specially STI-namespaced module
|
482
491
|
into.const_set(const_name.to_sym, klass = Class.new(base_class))
|
483
|
-
# %%% used to also have: autoload_once_paths.include?(base_path) ||
|
492
|
+
# %%% used to also have: autoload_once_paths.include?(base_path) ||
|
484
493
|
autoloaded_constants << qualified_name unless autoloaded_constants.include?(qualified_name)
|
485
494
|
klass
|
486
495
|
elsif (base_class = ::Brick.config.sti_namespace_prefixes&.fetch("::#{const_name}", nil)&.constantize)
|
@@ -494,75 +503,209 @@ if ActiveSupport::Dependencies.respond_to?(:autoload_module!) # %%% Only works w
|
|
494
503
|
end
|
495
504
|
end
|
496
505
|
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
return
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
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
|
530
|
+
|
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)
|
543
|
+
end
|
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
|
521
577
|
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
if (model = plural_class_name.singularize.constantize)
|
526
|
-
# 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.
|
527
|
-
build_controller(class_name, plural_class_name, model, relations)
|
528
|
-
end
|
529
|
-
elsif ::Brick.enable_models?
|
530
|
-
# See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
531
|
-
# checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
532
|
-
plural_class_name = ActiveSupport::Inflector.pluralize(model_name = class_name)
|
533
|
-
singular_table_name = ActiveSupport::Inflector.underscore(model_name)
|
534
|
-
|
535
|
-
# Adjust for STI if we know of a base model for the requested model name
|
536
|
-
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil) || ::Brick.existing_stis[model_name]&.constantize)
|
537
|
-
base_model.table_name
|
538
|
-
else
|
539
|
-
ActiveSupport::Inflector.pluralize(singular_table_name)
|
540
|
-
end
|
541
|
-
|
542
|
-
# Maybe, just maybe there's a database table that will satisfy this need
|
543
|
-
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(m) })
|
544
|
-
build_model(model_name, singular_table_name, table_name, relations, matching)
|
545
|
-
end
|
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)
|
546
581
|
end
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
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
|
552
588
|
# module_prefixes = type_name.split('::')
|
553
589
|
# path = self.name.split('::')[0..-2] + []
|
554
590
|
# module_prefixes.unshift('') unless module_prefixes.first.blank?
|
555
591
|
# candidate_file = Rails.root.join('app/models' + module_prefixes.map(&:underscore).join('/') + '.rb')
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
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)
|
561
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
|
562
697
|
|
563
698
|
private
|
564
699
|
|
565
|
-
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
|
+
|
566
709
|
return if ((is_view = (relation = relations[matching]).key?(:isView)) && ::Brick.config.skip_database_views) ||
|
567
710
|
::Brick.config.exclude_tables.include?(matching)
|
568
711
|
|
@@ -574,14 +717,15 @@ class Object
|
|
574
717
|
return
|
575
718
|
end
|
576
719
|
|
577
|
-
if (base_model = ::Brick.sti_models[
|
720
|
+
if (base_model = ::Brick.sti_models[full_name]&.fetch(:base, nil) || ::Brick.existing_stis[full_name]&.constantize)
|
578
721
|
is_sti = true
|
579
722
|
else
|
580
723
|
base_model = ::Brick.config.models_inherit_from || ActiveRecord::Base
|
581
724
|
end
|
582
|
-
|
725
|
+
hmts = nil
|
726
|
+
code = +"class #{full_name} < #{base_model.name}\n"
|
583
727
|
built_model = Class.new(base_model) do |new_model_class|
|
584
|
-
Object.const_set(model_name.to_sym, new_model_class)
|
728
|
+
(schema_module || Object).const_set(model_name.to_sym, new_model_class)
|
585
729
|
# Accommodate singular or camel-cased table names such as "order_detail" or "OrderDetails"
|
586
730
|
code << " self.table_name = '#{self.table_name = matching}'\n" unless table_name == matching
|
587
731
|
|
@@ -610,9 +754,14 @@ class Object
|
|
610
754
|
new_model_class.primary_key = (pk_sym = our_pks.first.to_sym)
|
611
755
|
code << " self.primary_key = #{pk_sym.inspect}\n"
|
612
756
|
end
|
757
|
+
_brick_primary_key(relation) # Set the newly-found PK in the instance variable
|
613
758
|
else
|
614
759
|
code << " # Could not identify any column(s) to use as a primary key\n" unless is_view
|
615
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
|
616
765
|
|
617
766
|
unless is_sti
|
618
767
|
fks = relation[:fks] || {}
|
@@ -630,7 +779,21 @@ class Object
|
|
630
779
|
build_bt_or_hm(relations, model_name, relation, hmts, assoc, inverse_assoc_name, invs, code) unless invs.is_a?(Array)
|
631
780
|
hmts
|
632
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
|
633
795
|
hmts.each do |hmt_fk, fks|
|
796
|
+
hmt_fk = hmt_fk.tr('.', '_')
|
634
797
|
fks.each do |fk|
|
635
798
|
through = fk.first[:assoc_name]
|
636
799
|
hmt_name = if fks.length > 1
|
@@ -651,18 +814,8 @@ class Object
|
|
651
814
|
self.send(:has_many, hmt_name.to_sym, **options)
|
652
815
|
end
|
653
816
|
end
|
654
|
-
# # Not NULLables
|
655
|
-
# # %%% For the minute we've had to pull this out because it's been troublesome implementing the NotNull validator
|
656
|
-
# relation[:cols].each do |col, datatype|
|
657
|
-
# if (datatype[3] && _brick_primary_key.exclude?(col) && ::Brick.config.metadata_columns.exclude?(col)) ||
|
658
|
-
# ::Brick.config.not_nullables.include?("#{matching}.#{col}")
|
659
|
-
# code << " validates :#{col}, not_null: true\n"
|
660
|
-
# self.send(:validates, col.to_sym, { not_null: true })
|
661
|
-
# end
|
662
|
-
# end
|
663
817
|
end
|
664
|
-
code << "end # model #{
|
665
|
-
end # class definition
|
818
|
+
code << "end # model #{full_name}\n\n"
|
666
819
|
[built_model, code]
|
667
820
|
end
|
668
821
|
|
@@ -673,7 +826,7 @@ class Object
|
|
673
826
|
# Try to take care of screwy names if this is a belongs_to going to an STI subclass
|
674
827
|
assoc_name = if (primary_class = assoc.fetch(:primary_class, nil)) &&
|
675
828
|
sti_inverse_assoc = primary_class.reflect_on_all_associations.find do |a|
|
676
|
-
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
|
677
830
|
end
|
678
831
|
sti_inverse_assoc.options[:inverse_of]&.to_s || assoc_name
|
679
832
|
else
|
@@ -705,7 +858,7 @@ class Object
|
|
705
858
|
if assoc.key?(:polymorphic)
|
706
859
|
options[:as] = assoc[:fk].to_sym
|
707
860
|
else
|
708
|
-
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]
|
709
862
|
end
|
710
863
|
# fks[table_name].find { |other_assoc| other_assoc.object_id != assoc.object_id && other_assoc[:assoc_name] == assoc[assoc_name] }
|
711
864
|
if (has_ones = ::Brick.config.has_ones&.fetch(model_name, nil))&.key?(singular_assoc_name = ActiveSupport::Inflector.singularize(assoc_name))
|
@@ -722,7 +875,7 @@ class Object
|
|
722
875
|
end
|
723
876
|
# Figure out if we need to specially call out the class_name and/or foreign key
|
724
877
|
# (and if either of those then definitely also a specific inverse_of)
|
725
|
-
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
|
726
879
|
# Work around a bug in CPK where self-referencing belongs_to associations double up their foreign keys
|
727
880
|
if need_fk # Funky foreign key?
|
728
881
|
options[:foreign_key] = if assoc[:fk].is_a?(Array)
|
@@ -732,10 +885,11 @@ class Object
|
|
732
885
|
assoc[:fk].to_sym
|
733
886
|
end
|
734
887
|
end
|
735
|
-
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)
|
736
889
|
|
737
890
|
# Prepare a list of entries for "has_many :through"
|
738
891
|
if macro == :has_many
|
892
|
+
puts [inverse_table, relations[inverse_table].length].inspect
|
739
893
|
relations[inverse_table][:hmt_fks].each do |k, hmt_fk|
|
740
894
|
next if k == assoc[:fk]
|
741
895
|
|
@@ -743,19 +897,20 @@ class Object
|
|
743
897
|
end
|
744
898
|
end
|
745
899
|
# And finally create a has_one, has_many, or belongs_to for this association
|
746
|
-
assoc_name = assoc_name.to_sym
|
900
|
+
assoc_name = assoc_name.tr('.', '_').to_sym
|
747
901
|
code << " #{macro} #{assoc_name.inspect}#{options.map { |k, v| ", #{k}: #{v.inspect}" }.join}\n"
|
748
902
|
self.send(macro, assoc_name, **options)
|
749
903
|
end
|
750
904
|
|
751
|
-
def build_controller(class_name, plural_class_name, model, relations)
|
905
|
+
def build_controller(namespace, class_name, plural_class_name, model, relations)
|
752
906
|
table_name = ActiveSupport::Inflector.underscore(plural_class_name)
|
753
907
|
singular_table_name = ActiveSupport::Inflector.singularize(table_name)
|
754
|
-
pk = model._brick_primary_key(relations
|
908
|
+
pk = model._brick_primary_key(relations.fetch(table_name, nil))
|
755
909
|
|
756
|
-
|
910
|
+
namespace_name = "#{namespace.name}::" if namespace
|
911
|
+
code = +"class #{namespace_name}#{class_name} < ApplicationController\n"
|
757
912
|
built_controller = Class.new(ActionController::Base) do |new_controller_class|
|
758
|
-
Object.const_set(class_name.to_sym, new_controller_class)
|
913
|
+
(namespace || Object).const_set(class_name.to_sym, new_controller_class)
|
759
914
|
|
760
915
|
code << " def index\n"
|
761
916
|
code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{pk.inspect})" : '.all'}\n"
|
@@ -782,9 +937,10 @@ class Object
|
|
782
937
|
# %%% Add custom HM count columns
|
783
938
|
# %%% What happens when the PK is composite?
|
784
939
|
counts = hm_counts.each_with_object([]) { |v, s| s << "_br_#{v.first}._ct_ AS _br_#{v.first}_ct" }
|
785
|
-
# *selects,
|
786
940
|
instance_variable_set("@#{table_name}".to_sym, ar_relation.dup._select!(*selects, *counts))
|
787
|
-
|
941
|
+
if namespace && (idx = lookup_context.prefixes.index(table_name))
|
942
|
+
lookup_context.prefixes[idx] = "#{namespace.name.underscore}/#{lookup_context.prefixes[idx]}"
|
943
|
+
end
|
788
944
|
@_brick_bt_descrip = bt_descrip
|
789
945
|
@_brick_hm_counts = hm_counts
|
790
946
|
@_brick_join_array = join_array
|
@@ -809,9 +965,9 @@ class Object
|
|
809
965
|
code << " # (Define :new, :create)\n"
|
810
966
|
|
811
967
|
if model.primary_key
|
812
|
-
if (schema = ::Brick.config.schema_to_analyse) && ::Brick.db_schemas&.include?(schema)
|
813
|
-
|
814
|
-
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
|
815
971
|
|
816
972
|
is_need_params = true
|
817
973
|
# code << " # (Define :edit, and :destroy)\n"
|
@@ -822,7 +978,6 @@ class Object
|
|
822
978
|
code << " end\n"
|
823
979
|
self.define_method :update do
|
824
980
|
::Brick.set_db_schema(params)
|
825
|
-
|
826
981
|
if request.format == :csv # Importing CSV?
|
827
982
|
require 'csv'
|
828
983
|
# See if internally it's likely a TSV file (tab-separated)
|
@@ -846,7 +1001,7 @@ class Object
|
|
846
1001
|
|
847
1002
|
if is_need_params
|
848
1003
|
code << "private\n"
|
849
|
-
code << " def
|
1004
|
+
code << " def #{params_name}\n"
|
850
1005
|
code << " params.require(:#{singular_table_name}).permit(#{model.columns_hash.keys.map { |c| c.to_sym.inspect }.join(', ')})\n"
|
851
1006
|
code << " end\n"
|
852
1007
|
self.define_method(params_name) do
|
@@ -856,7 +1011,7 @@ class Object
|
|
856
1011
|
# Get column names for params from relations[model.table_name][:cols].keys
|
857
1012
|
end
|
858
1013
|
end
|
859
|
-
code << "end # #{class_name}\n\n"
|
1014
|
+
code << "end # #{namespace_name}#{class_name}\n\n"
|
860
1015
|
end # class definition
|
861
1016
|
[built_controller, code]
|
862
1017
|
end
|
@@ -866,7 +1021,8 @@ class Object
|
|
866
1021
|
plural = ActiveSupport::Inflector.pluralize(hm_assoc[:alternate_name])
|
867
1022
|
[hm_assoc[:alternate_name] == name.underscore ? "#{hm_assoc[:assoc_name].singularize}_#{plural}" : plural, true]
|
868
1023
|
else
|
869
|
-
|
1024
|
+
assoc_name = hm_assoc[:inverse_table].pluralize
|
1025
|
+
[assoc_name, assoc_name.include?('.')]
|
870
1026
|
end
|
871
1027
|
end
|
872
1028
|
end
|
@@ -886,16 +1042,22 @@ module ActiveRecord::ConnectionHandling
|
|
886
1042
|
|
887
1043
|
def _brick_reflect_tables
|
888
1044
|
if (relations = ::Brick.relations).empty?
|
889
|
-
|
890
|
-
|
1045
|
+
# Hopefully our initializer is named exactly this!
|
1046
|
+
if File.exist?(brick_initializer = Rails.root.join('config/initializers/brick.rb'))
|
1047
|
+
load brick_initializer
|
1048
|
+
end
|
1049
|
+
# Only for Postgres? (Doesn't work in sqlite3)
|
1050
|
+
# puts ActiveRecord::Base.execute_sql("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
891
1051
|
|
892
|
-
|
893
|
-
|
1052
|
+
schema_sql = 'SELECT NULL AS table_schema;'
|
1053
|
+
case ActiveRecord::Base.connection.adapter_name
|
894
1054
|
when 'PostgreSQL'
|
895
|
-
|
1055
|
+
if (::Brick.default_schema = schema = ::Brick.config.schema_behavior&.[](:multitenant)&.[](:schema_to_analyse))
|
1056
|
+
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", schema)
|
1057
|
+
end
|
896
1058
|
schema_sql = 'SELECT DISTINCT table_schema FROM INFORMATION_SCHEMA.tables;'
|
897
1059
|
when 'Mysql2'
|
898
|
-
schema = ActiveRecord::Base.connection.current_database
|
1060
|
+
::Brick.default_schema = schema = ActiveRecord::Base.connection.current_database
|
899
1061
|
when 'SQLite'
|
900
1062
|
sql = "SELECT m.name AS relation_name, UPPER(m.type) AS table_type,
|
901
1063
|
p.name AS column_name, p.type AS data_type,
|
@@ -908,8 +1070,7 @@ module ActiveRecord::ConnectionHandling
|
|
908
1070
|
puts "Unfamiliar with connection adapter #{ActiveRecord::Base.connection.adapter_name}"
|
909
1071
|
end
|
910
1072
|
|
911
|
-
sql ||=
|
912
|
-
"SELECT t.table_name AS relation_name, t.table_type,
|
1073
|
+
sql ||= "SELECT t.table_schema AS schema, t.table_name AS relation_name, t.table_type,
|
913
1074
|
c.column_name, c.data_type,
|
914
1075
|
COALESCE(c.character_maximum_length, c.numeric_precision) AS max_length,
|
915
1076
|
tc.constraint_type AS const, kcu.constraint_name AS \"key\",
|
@@ -927,18 +1088,23 @@ module ActiveRecord::ConnectionHandling
|
|
927
1088
|
ON kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
|
928
1089
|
AND kcu.TABLE_NAME = tc.TABLE_NAME
|
929
1090
|
AND kcu.CONSTRAINT_NAME = tc.constraint_name
|
930
|
-
WHERE t.table_schema
|
1091
|
+
WHERE t.table_schema NOT IN ('information_schema', 'pg_catalog')#{"
|
1092
|
+
AND t.table_schema = COALESCE(current_setting('SEARCH_PATH'), 'public')" if schema }
|
931
1093
|
-- AND t.table_type IN ('VIEW') -- 'BASE TABLE', 'FOREIGN TABLE'
|
932
1094
|
AND t.table_name NOT IN ('pg_stat_statements', 'ar_internal_metadata', 'schema_migrations')
|
933
|
-
ORDER BY 1, t.table_type DESC, c.ordinal_position"
|
934
|
-
])
|
935
|
-
|
1095
|
+
ORDER BY 1, t.table_type DESC, c.ordinal_position"
|
936
1096
|
measures = []
|
937
1097
|
case ActiveRecord::Base.connection.adapter_name
|
938
1098
|
when 'PostgreSQL', 'SQLite' # These bring back a hash for each row because the query uses column aliases
|
1099
|
+
schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
939
1100
|
ActiveRecord::Base.execute_sql(sql).each do |r|
|
940
1101
|
# next if internal_views.include?(r['relation_name']) # Skip internal views such as v_all_assessments
|
941
|
-
|
1102
|
+
relation_name = if r['schema'] != schema
|
1103
|
+
"#{schema_name = r['schema']}.#{r['relation_name']}"
|
1104
|
+
else
|
1105
|
+
r['relation_name']
|
1106
|
+
end
|
1107
|
+
relation = relations[relation_name]
|
942
1108
|
relation[:isView] = true if r['table_type'] == 'VIEW'
|
943
1109
|
col_name = r['column_name']
|
944
1110
|
key = case r['const']
|
@@ -995,11 +1161,11 @@ module ActiveRecord::ConnectionHandling
|
|
995
1161
|
# end
|
996
1162
|
# end
|
997
1163
|
# end
|
998
|
-
|
1164
|
+
schema = ::Brick.default_schema # Reset back for this next round of fun
|
999
1165
|
case ActiveRecord::Base.connection.adapter_name
|
1000
1166
|
when 'PostgreSQL', 'Mysql2'
|
1001
|
-
sql =
|
1002
|
-
|
1167
|
+
sql = "SELECT kcu1.CONSTRAINT_SCHEMA, kcu1.TABLE_NAME, kcu1.COLUMN_NAME,
|
1168
|
+
kcu2.CONSTRAINT_SCHEMA AS primary_schema, kcu2.TABLE_NAME AS primary_table, kcu1.CONSTRAINT_NAME AS CONSTRAINT_SCHEMA_FK
|
1003
1169
|
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS rc
|
1004
1170
|
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu1
|
1005
1171
|
ON kcu1.CONSTRAINT_CATALOG = rc.CONSTRAINT_CATALOG
|
@@ -1009,10 +1175,9 @@ module ActiveRecord::ConnectionHandling
|
|
1009
1175
|
ON kcu2.CONSTRAINT_CATALOG = rc.UNIQUE_CONSTRAINT_CATALOG
|
1010
1176
|
AND kcu2.CONSTRAINT_SCHEMA = rc.UNIQUE_CONSTRAINT_SCHEMA
|
1011
1177
|
AND kcu2.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
|
1012
|
-
AND kcu2.ORDINAL_POSITION = kcu1.ORDINAL_POSITION
|
1013
|
-
|
1178
|
+
AND kcu2.ORDINAL_POSITION = kcu1.ORDINAL_POSITION#{"
|
1179
|
+
WHERE kcu1.CONSTRAINT_SCHEMA = COALESCE(current_setting('SEARCH_PATH'), 'public')" if schema }"
|
1014
1180
|
# AND kcu2.TABLE_NAME = ?;", Apartment::Tenant.current, table_name
|
1015
|
-
])
|
1016
1181
|
when 'SQLite'
|
1017
1182
|
sql = "SELECT m.name, fkl.\"from\", fkl.\"table\", m.name || '_' || fkl.\"from\" AS constraint_name
|
1018
1183
|
FROM sqlite_master m
|
@@ -1021,10 +1186,17 @@ module ActiveRecord::ConnectionHandling
|
|
1021
1186
|
else
|
1022
1187
|
end
|
1023
1188
|
if sql
|
1024
|
-
::Brick.
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1189
|
+
::Brick.default_schema ||= schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
1190
|
+
unless (db_schemas = ActiveRecord::Base.execute_sql(schema_sql)).is_a?(Array)
|
1191
|
+
db_schemas = db_schemas.to_a
|
1192
|
+
end
|
1193
|
+
unless db_schemas.empty?
|
1194
|
+
::Brick.db_schemas = db_schemas.each_with_object({}) do |row, s|
|
1195
|
+
row = row.is_a?(String) ? row : row['table_schema']
|
1196
|
+
# Remove whatever default schema we're using and other system schemas
|
1197
|
+
s[row] = nil unless ['information_schema', 'pg_catalog', schema].include?(row)
|
1198
|
+
end
|
1199
|
+
end
|
1028
1200
|
ActiveRecord::Base.execute_sql(sql).each do |fk|
|
1029
1201
|
fk = fk.values unless fk.is_a?(Array)
|
1030
1202
|
::Brick._add_bt_and_hm(fk, relations)
|
@@ -1071,34 +1243,46 @@ module Brick
|
|
1071
1243
|
|
1072
1244
|
class << self
|
1073
1245
|
def _add_bt_and_hm(fk, relations, is_polymorphic = false)
|
1074
|
-
bt_assoc_name = fk[
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1246
|
+
if (bt_assoc_name = fk[2].underscore).end_with?('_id')
|
1247
|
+
bt_assoc_name = bt_assoc_name[0..-4]
|
1248
|
+
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
|
1249
|
+
bt_assoc_name = bt_assoc_name[0..-3]
|
1250
|
+
else
|
1251
|
+
bt_assoc_name = "#{bt_assoc_name}_bt"
|
1252
|
+
end
|
1253
|
+
# %%% Temporary schema patch
|
1254
|
+
fk[1] = "#{fk[0]}.#{for_tbl = fk[1]}" if fk[0] && fk[0] != ::Brick.default_schema
|
1255
|
+
for_tbl << '_' if for_tbl
|
1256
|
+
bts = (relation = relations.fetch(fk[1], nil))&.fetch(:fks) { relation[:fks] = {} }
|
1078
1257
|
# %%% Do we miss out on has_many :through or even HM based on constantizing this model early?
|
1079
1258
|
# Maybe it's already gotten this info because we got as far as to say there was a unique class
|
1080
|
-
primary_table = (is_class = fk[
|
1259
|
+
primary_table = if (is_class = fk[4].is_a?(Hash) && fk[4].key?(:class))
|
1260
|
+
pri_tbl = (primary_class = fk[4][:class].constantize).table_name
|
1261
|
+
else
|
1262
|
+
pri_tbl = fk[4]
|
1263
|
+
(fk[3] && fk[3] != ::Brick.default_schema) ? "#{fk[3]}.#{pri_tbl}" : pri_tbl
|
1264
|
+
end
|
1081
1265
|
hms = (relation = relations.fetch(primary_table, nil))&.fetch(:fks) { relation[:fks] = {} } unless is_class
|
1082
1266
|
|
1083
|
-
unless (cnstr_name = fk[
|
1267
|
+
unless (cnstr_name = fk[5])
|
1084
1268
|
# For any appended references (those that come from config), arrive upon a definitely unique constraint name
|
1085
|
-
cnstr_base = cnstr_name = "(brick) #{
|
1269
|
+
cnstr_base = cnstr_name = "(brick) #{for_tbl}#{is_class ? fk[4][:class].underscore : pri_tbl}"
|
1086
1270
|
cnstr_added_num = 1
|
1087
1271
|
cnstr_name = "#{cnstr_base}_#{cnstr_added_num += 1}" while bts&.key?(cnstr_name) || hms&.key?(cnstr_name)
|
1088
1272
|
missing = []
|
1089
|
-
missing << fk[
|
1273
|
+
missing << fk[1] unless relations.key?(fk[1])
|
1090
1274
|
missing << primary_table unless is_class || relations.key?(primary_table)
|
1091
1275
|
unless missing.empty?
|
1092
1276
|
tables = relations.reject { |_k, v| v.fetch(:isView, nil) }.keys.sort
|
1093
1277
|
puts "Brick: Additional reference #{fk.inspect} refers to non-existent #{'table'.pluralize(missing.length)} #{missing.join(' and ')}. (Available tables include #{tables.join(', ')}.)"
|
1094
1278
|
return
|
1095
1279
|
end
|
1096
|
-
unless (cols = relations[fk[
|
1280
|
+
unless (cols = relations[fk[1]][:cols]).key?(fk[2]) || (is_polymorphic && cols.key?("#{fk[2]}_id") && cols.key?("#{fk[2]}_type"))
|
1097
1281
|
columns = cols.map { |k, v| "#{k} (#{v.first.split(' ').first})" }
|
1098
|
-
puts "Brick: Additional reference #{fk.inspect} refers to non-existent column #{fk[
|
1282
|
+
puts "Brick: Additional reference #{fk.inspect} refers to non-existent column #{fk[2]}. (Columns present in #{fk[1]} are #{columns.join(', ')}.)"
|
1099
1283
|
return
|
1100
1284
|
end
|
1101
|
-
if (redundant = bts.find { |_k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[
|
1285
|
+
if (redundant = bts.find { |_k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[1] && v[:fk] == fk[2] && v[:inverse_table] == primary_table })
|
1102
1286
|
if is_class && !redundant.last.key?(:class)
|
1103
1287
|
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
|
1104
1288
|
else
|
@@ -1110,18 +1294,18 @@ module Brick
|
|
1110
1294
|
if (assoc_bt = bts[cnstr_name])
|
1111
1295
|
if is_polymorphic
|
1112
1296
|
# Assuming same fk (don't yet support composite keys for polymorphics)
|
1113
|
-
assoc_bt[:inverse_table] << fk[
|
1297
|
+
assoc_bt[:inverse_table] << fk[4]
|
1114
1298
|
else # Expect we could have a composite key going
|
1115
1299
|
if assoc_bt[:fk].is_a?(String)
|
1116
|
-
assoc_bt[:fk] = [assoc_bt[:fk], fk[
|
1117
|
-
elsif assoc_bt[:fk].exclude?(fk[
|
1118
|
-
assoc_bt[:fk] << fk[
|
1300
|
+
assoc_bt[:fk] = [assoc_bt[:fk], fk[2]] unless fk[2] == assoc_bt[:fk]
|
1301
|
+
elsif assoc_bt[:fk].exclude?(fk[2])
|
1302
|
+
assoc_bt[:fk] << fk[2]
|
1119
1303
|
end
|
1120
|
-
assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[
|
1304
|
+
assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[2]}"
|
1121
1305
|
end
|
1122
1306
|
else
|
1123
1307
|
inverse_table = [primary_table] if is_polymorphic
|
1124
|
-
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[
|
1308
|
+
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[2], assoc_name: bt_assoc_name, inverse_table: inverse_table || primary_table }
|
1125
1309
|
assoc_bt[:polymorphic] = true if is_polymorphic
|
1126
1310
|
end
|
1127
1311
|
if is_class
|
@@ -1131,21 +1315,21 @@ module Brick
|
|
1131
1315
|
# assoc_bt[:inverse_of] = primary_class.reflect_on_all_associations.find { |a| a.foreign_key == bt[1] }
|
1132
1316
|
end
|
1133
1317
|
|
1134
|
-
return if is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[
|
1318
|
+
return if is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[1] == exclusion[0] && fk[2] == exclusion[1] && primary_table == exclusion[2] } || hms.nil?
|
1135
1319
|
|
1136
1320
|
if (assoc_hm = hms.fetch((hm_cnstr_name = "hm_#{cnstr_name}"), nil))
|
1137
1321
|
if assoc_hm[:fk].is_a?(String)
|
1138
|
-
assoc_hm[:fk] = [assoc_hm[:fk], fk[
|
1139
|
-
elsif assoc_hm[:fk].exclude?(fk[
|
1140
|
-
assoc_hm[:fk] << fk[
|
1322
|
+
assoc_hm[:fk] = [assoc_hm[:fk], fk[2]] unless fk[2] == assoc_hm[:fk]
|
1323
|
+
elsif assoc_hm[:fk].exclude?(fk[2])
|
1324
|
+
assoc_hm[:fk] << fk[2]
|
1141
1325
|
end
|
1142
1326
|
assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
|
1143
1327
|
assoc_hm[:inverse] = assoc_bt
|
1144
1328
|
else
|
1145
|
-
assoc_hm = hms[hm_cnstr_name] = { is_bt: false, fk: fk[
|
1329
|
+
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 }
|
1146
1330
|
assoc_hm[:polymorphic] = true if is_polymorphic
|
1147
1331
|
hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
|
1148
|
-
hm_counts[fk[
|
1332
|
+
hm_counts[fk[1]] = hm_counts.fetch(fk[1]) { 0 } + 1
|
1149
1333
|
end
|
1150
1334
|
assoc_bt[:inverse] = assoc_hm
|
1151
1335
|
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
|
|
@@ -83,9 +85,10 @@ module Brick
|
|
83
85
|
return _brick_find_template(*args, **options) unless @_brick_model
|
84
86
|
|
85
87
|
model_name = @_brick_model.name
|
86
|
-
pk = @_brick_model._brick_primary_key(::Brick.relations
|
87
|
-
obj_name = model_name.underscore
|
88
|
-
|
88
|
+
pk = @_brick_model._brick_primary_key(::Brick.relations.fetch(model_name, nil))
|
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;
|
@@ -207,7 +210,13 @@ input[type=submit] {
|
|
207
210
|
val.is_a?(String) && val.length == 60 && val.start_with?('$2a$')
|
208
211
|
end
|
209
212
|
def hide_bcrypt(val)
|
210
|
-
is_bcrypt?(val)
|
213
|
+
if is_bcrypt?(val)
|
214
|
+
'(hidden)'
|
215
|
+
elsif val.is_a?(String) && val.encoding.name != 'UTF-8'
|
216
|
+
val[0..1000].force_encoding('UTF-8')
|
217
|
+
else
|
218
|
+
val
|
219
|
+
end
|
211
220
|
end %>"
|
212
221
|
|
213
222
|
if ['index', 'show', 'update'].include?(args.first)
|
@@ -255,7 +264,8 @@ if (schemaSelect) {
|
|
255
264
|
|
256
265
|
var tblSelect = document.getElementById(\"tbl\");
|
257
266
|
if (tblSelect) {
|
258
|
-
tblSelect.value = changeout(location.href);
|
267
|
+
tblSelect.value = changeout(location.href)[0];
|
268
|
+
if (tblSelect.selectedIndex < 0) tblSelect.value = changeout(location.href)[1];
|
259
269
|
tblSelect.addEventListener(\"change\", function () {
|
260
270
|
var lhr = changeout(location.href, null, this.value);
|
261
271
|
if (brickSchema)
|
@@ -270,7 +280,8 @@ function changeout(href, param, value) {
|
|
270
280
|
hrefParts = hrefParts[0].split(\"://\");
|
271
281
|
var pathParts = hrefParts[hrefParts.length - 1].split(\"/\");
|
272
282
|
if (value === undefined)
|
273
|
-
|
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)];
|
274
285
|
else
|
275
286
|
return hrefParts[0] + \"://\" + pathParts[0] + \"/\" + value;
|
276
287
|
}
|
@@ -307,7 +318,7 @@ function changeout(href, param, value) {
|
|
307
318
|
btnImport.style.display = droppedTSV.length > 0 ? \"block\" : \"none\";
|
308
319
|
});
|
309
320
|
btnImport.addEventListener(\"click\", function () {
|
310
|
-
fetch(changeout(<%= #{
|
321
|
+
fetch(changeout(<%= #{path_obj_name}_path(-1, format: :csv).inspect.html_safe %>, \"_brick_schema\", brickSchema), {
|
311
322
|
method: 'PATCH',
|
312
323
|
headers: { 'Content-Type': 'text/tab-separated-values' },
|
313
324
|
body: droppedTSV
|
@@ -378,17 +389,32 @@ function changeout(href, param, value) {
|
|
378
389
|
<script async defer src=\"https://apis.google.com/js/api.js\" onload=\"gapiLoaded()\"></script>
|
379
390
|
"
|
380
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(', ') %>)
|
381
394
|
"#{css}
|
382
395
|
<p style=\"color: green\"><%= notice %></p>#{"
|
383
|
-
<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}
|
384
397
|
<select id=\"tbl\">#{table_options}</select>
|
385
|
-
<h1>#{model_name.pluralize}</h1>#{template_link}
|
386
|
-
|
387
|
-
<% 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 %>
|
388
413
|
<table id=\"#{table_name}\">
|
389
414
|
<thead><tr>#{'<th></th>' if pk.present?}
|
390
415
|
<% @#{table_name}.columns.map(&:name).each do |col| %>
|
391
|
-
<% 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) %>
|
392
418
|
<th>
|
393
419
|
<% if (bt = bts[col]) %>
|
394
420
|
BT <%
|
@@ -401,15 +427,16 @@ function changeout(href, param, value) {
|
|
401
427
|
</th>
|
402
428
|
<% end %>
|
403
429
|
<%# Consider getting the name from the association -- h.first.name -- if a more \"friendly\" alias should be used for a screwy table name %>
|
404
|
-
#{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}
|
405
431
|
</tr></thead>
|
406
432
|
|
407
433
|
<tbody>
|
408
434
|
<% @#{table_name}.each do |#{obj_name}| %>
|
409
435
|
<tr>#{"
|
410
|
-
<td><%= link_to '⇛', #{
|
436
|
+
<td><%= link_to '⇛', #{path_obj_name}_path(#{obj_pk}), { class: 'big-arrow' } %></td>" if obj_pk}
|
411
437
|
<% #{obj_name}.attributes.each do |k, val| %>
|
412
|
-
<% next if #{obj_pk
|
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'))) %>
|
413
440
|
<td>
|
414
441
|
<% if (bt = bts[k]) %>
|
415
442
|
<%# binding.pry # Postgres column names are limited to 63 characters %>
|
@@ -424,7 +451,7 @@ function changeout(href, param, value) {
|
|
424
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)
|
425
452
|
)
|
426
453
|
bt_id = #{obj_name}.send(*bt_id_col) if bt_id_col&.present? %>
|
427
|
-
<%= bt_id ? link_to(bt_txt, send(\"#\{bt_class.base_class.name.underscore\}_path\".to_sym, bt_id)) : bt_txt %>
|
454
|
+
<%= bt_id ? link_to(bt_txt, send(\"#\{bt_class.base_class.name.underscore.tr('/', '_')\}_path\".to_sym, bt_id)) : bt_txt %>
|
428
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 %>
|
429
456
|
<% end %>
|
430
457
|
<% else %>
|
@@ -434,19 +461,19 @@ function changeout(href, param, value) {
|
|
434
461
|
<% end %>
|
435
462
|
#{hms_columns.each_with_object(+'') { |hm_col, s| s << "<td>#{hm_col}</td>" }}
|
436
463
|
</tr>
|
437
|
-
</tbody>
|
438
464
|
<% end %>
|
465
|
+
</tbody>
|
439
466
|
</table>
|
440
467
|
|
441
|
-
#{"<hr><%= link_to \"New #{obj_name}\", new_#{
|
468
|
+
#{"<hr><%= link_to \"New #{obj_name}\", new_#{path_obj_name}_path %>" unless @_brick_model.is_view?}
|
442
469
|
#{script}"
|
443
470
|
when 'show', 'update'
|
444
471
|
"#{css}
|
445
472
|
<p style=\"color: green\"><%= notice %></p>#{"
|
446
|
-
<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}
|
447
474
|
<select id=\"tbl\">#{table_options}</select>
|
448
475
|
<h1>#{model_name}: <%= (obj = @#{obj_name})&.brick_descrip || controller_name %></h1>
|
449
|
-
<%= link_to '(See all #{obj_name.pluralize})', #{
|
476
|
+
<%= link_to '(See all #{obj_name.pluralize})', #{path_obj_name.pluralize}_path %>
|
450
477
|
<% if obj %>
|
451
478
|
<%= # path_options = [obj.#{pk}]
|
452
479
|
# path_options << { '_brick_schema': } if
|
@@ -456,8 +483,8 @@ function changeout(href, param, value) {
|
|
456
483
|
<% has_fields = false
|
457
484
|
@#{obj_name}.attributes.each do |k, val| %>
|
458
485
|
<tr>
|
459
|
-
|
460
|
-
|
486
|
+
<% next if (#{(pk || []).inspect}.include?(k) && !bts.key?(k)) ||
|
487
|
+
::Brick.config.metadata_columns.include?(k) %>
|
461
488
|
<th class=\"show-field\">
|
462
489
|
<% has_fields = true
|
463
490
|
if (bt = bts[k])
|
@@ -499,7 +526,7 @@ function changeout(href, param, value) {
|
|
499
526
|
html_options = { prompt: \"Select #\{bt_name\}\" }
|
500
527
|
html_options[:class] = 'dimmed' unless val %>
|
501
528
|
<%= f.select k.to_sym, bt[3], { value: val || '^^^brick_NULL^^^' }, html_options %>
|
502
|
-
<%= bt_obj = bt_class&.find_by(bt_pair[1] => val); link_to('⇛', send(\"#\{bt_class.base_class.name.underscore\}_path\".to_sym, bt_obj.send(bt_class.primary_key.to_sym)), { class: 'show-arrow' }) if bt_obj %>
|
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 %>
|
503
530
|
<% else case #{model_name}.column_for_attribute(k).type
|
504
531
|
when :string, :text %>
|
505
532
|
<% if is_bcrypt?(val) # || .readonly? %>
|
@@ -541,7 +568,7 @@ function changeout(href, param, value) {
|
|
541
568
|
<tr><td>(none)</td></tr>
|
542
569
|
<% else %>
|
543
570
|
<% collection.uniq.each do |#{hm_singular_name}| %>
|
544
|
-
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm.first.klass.name.underscore}_path([#{obj_pk}])) %></td></tr>
|
571
|
+
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm.first.klass.name.underscore.tr('/', '_')}_path([#{obj_pk}])) %></td></tr>
|
545
572
|
<% end %>
|
546
573
|
<% end %>
|
547
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.31
|
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
|