brick 1.0.25 → 1.0.26

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: b0dfa64d7a8a148b4c2ad990f5af1604d5855b80c6e95f39cd371a43a591b7f1
4
- data.tar.gz: bc7644a08678136d91696a68e0ad670f4f04d41772f36ba1bd806ebe45f23b04
3
+ metadata.gz: 922a55ec392e2ea9cef11c7297e4c2e4883e918130a23e49102b5a7e2442140e
4
+ data.tar.gz: 7cda213ec2e7cdccae096ef4ad8091a53a91359a1e6fabeb753cc2b579ac5c16
5
5
  SHA512:
6
- metadata.gz: a39a8adc0c72288db5bd08e2483cefb701db9c2122fe1b4012c19bef07c7d048fed7f6e051473a9b4b6c80e321e4a781a16543d40f3e569f23c31f420a5117b6
7
- data.tar.gz: 3ddcc914e3143a02a4582d6f525417c3012ae4eafc9ae889d5935223f6173afb4fb5ff3a40dc6c9caf95dfbf7a3c5be8e7bc4890efb385867ef5fa6f3ccbfb9e
6
+ metadata.gz: 7b817257f9c2aedd47bc2471b12e13edc8b370b71920258755f0f9a7594060e065d022e8d4b2b360cdc41f2a60def02cfc406889d526d80088670bd64acd58be
7
+ data.tar.gz: 1ea923301103dcc53853940770861ab5c2635f1280026e0740287274c0e9bb7d566ddcf96db0d3858467b788bc9ee4b73e7bcfc61145be1bbb43775cfb859b80
data/lib/brick/config.rb CHANGED
@@ -122,6 +122,14 @@ 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 }
127
+ end
128
+
129
+ def schema_to_analyse=(schema)
130
+ @mutex.synchronize { @schema_to_analyse = schema }
131
+ end
132
+
125
133
  def skip_database_views
126
134
  @mutex.synchronize { @skip_database_views }
127
135
  end
@@ -52,14 +52,6 @@ module Arel
52
52
  end
53
53
  end
54
54
 
55
- # module ActiveModel
56
- # class NotNullValidator < EachValidator
57
- # def validate_each(record, attribute, value)
58
- # record.errors[attribute] << "must not be null" if value.nil?
59
- # end
60
- # end
61
- # end
62
-
63
55
  module ActiveRecord
64
56
  class Base
65
57
  def self._assoc_names
@@ -82,7 +74,6 @@ module ActiveRecord
82
74
  dsl
83
75
  end
84
76
 
85
- # Pass in true for build_array, or just pass in a JoinArray
86
77
  def self.brick_parse_dsl(build_array = nil, prefix = [], translations = {}, is_polymorphic = false)
87
78
  build_array = ::Brick::JoinArray.new.tap { |ary| ary.replace([build_array]) } if build_array.is_a?(::Brick::JoinHash)
88
79
  build_array = ::Brick::JoinArray.new unless build_array.nil? || build_array.is_a?(Array)
@@ -308,6 +299,8 @@ module ActiveRecord
308
299
  if is_add_bts || is_add_hms
309
300
  bts, hms, associatives = ::Brick.get_bts_and_hms(klass)
310
301
  bts.each do |_k, bt|
302
+ next if bt[2] # Polymorphic?
303
+
311
304
  # join_array will receive this relation name when calling #brick_parse_dsl
312
305
  bt_descrip[bt.first] = if bt[1].is_a?(Array)
313
306
  bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations, true) }
@@ -363,15 +356,16 @@ module ActiveRecord
363
356
  end
364
357
  # end
365
358
 
366
- if (id_col = k1.primary_key) && !id_for_tables.key?(v.first) # was tbl_name
359
+ unless id_for_tables.key?(v.first)
367
360
  # Accommodate composite primary key by allowing id_col to come in as an array
368
- (id_col.is_a?(Array) ? id_col : [id_col]).each do |id_part|
369
- selects << "#{"#{tbl_name}.#{id_part}"} AS \"#{(id_alias = "_brfk_#{v.first}__#{id_part}")}\""
370
- id_for_tables[v.first] << id_alias
361
+ ((id_col = k1.primary_key).is_a?(Array) ? id_col : [id_col]).each do |id_part|
362
+ id_for_tables[v.first] << if id_part
363
+ selects << "#{"#{tbl_name}.#{id_part}"} AS \"#{(id_alias = "_brfk_#{v.first}__#{id_part}")}\""
364
+ id_alias
365
+ end
371
366
  end
372
- v1 << id_for_tables[v.first]
367
+ v1 << id_for_tables[v.first].compact
373
368
  end
374
-
375
369
  end
376
370
  end
377
371
  join_array.each do |assoc_name|
@@ -454,8 +448,7 @@ JOIN (SELECT #{selects.join(', ')}, COUNT(#{count_column}) AS _ct_ FROM #{associ
454
448
  this_module.const_get(class_name)
455
449
  else
456
450
  # Build STI subclass and place it into the namespace module
457
- # %%% Does this ever get used???
458
- puts [this_module.const_set(class_name, klass = Class.new(self)).name, class_name].inspect
451
+ this_module.const_set(class_name, klass = Class.new(self))
459
452
  klass
460
453
  end
461
454
  end
@@ -530,7 +523,7 @@ class Object
530
523
  singular_table_name = ActiveSupport::Inflector.underscore(model_name)
531
524
 
532
525
  # Adjust for STI if we know of a base model for the requested model name
533
- table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
526
+ table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, ::Brick.existing_stis[model_name]&.constantize))
534
527
  base_model.table_name
535
528
  else
536
529
  ActiveSupport::Inflector.pluralize(singular_table_name)
@@ -571,7 +564,7 @@ class Object
571
564
  return
572
565
  end
573
566
 
574
- if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
567
+ if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, ::Brick.existing_stis[model_name]&.constantize))
575
568
  is_sti = true
576
569
  else
577
570
  base_model = ::Brick.config.models_inherit_from || ActiveRecord::Base
@@ -698,9 +691,8 @@ class Object
698
691
  # need_class_name = ActiveSupport::Inflector.singularize(assoc_name) == ActiveSupport::Inflector.singularize(table_name.underscore)
699
692
  # Are there multiple foreign keys out to the same table?
700
693
  assoc_name, need_class_name = _brick_get_hm_assoc_name(relation, assoc)
701
- # binding.pry if assoc.key?(:polymorphic)
702
694
  if assoc.key?(:polymorphic)
703
- options[:as] = assoc[:fk].to_sym if assoc.key?(:polymorphic)
695
+ options[:as] = assoc[:fk].to_sym
704
696
  else
705
697
  need_fk = "#{ActiveSupport::Inflector.singularize(assoc[:inverse][:inverse_table])}_id" != assoc[:fk]
706
698
  end
@@ -1064,12 +1056,8 @@ module Brick
1064
1056
  bts = (relation = relations.fetch(fk[0], nil))&.fetch(:fks) { relation[:fks] = {} }
1065
1057
  # %%% Do we miss out on has_many :through or even HM based on constantizing this model early?
1066
1058
  # Maybe it's already gotten this info because we got as far as to say there was a unique class
1067
- # if is_polymorphic
1068
- # primary_table = fk[]
1069
- # else
1070
- primary_table = (is_class = fk[2].is_a?(Hash) && fk[2].key?(:class)) ? (primary_class = fk[2][:class].constantize).table_name : fk[2]
1071
- hms = (relation = relations.fetch(primary_table, nil))&.fetch(:fks) { relation[:fks] = {} } unless is_class
1072
- # end
1059
+ primary_table = (is_class = fk[2].is_a?(Hash) && fk[2].key?(:class)) ? (primary_class = fk[2][:class].constantize).table_name : fk[2]
1060
+ hms = (relation = relations.fetch(primary_table, nil))&.fetch(:fks) { relation[:fks] = {} } unless is_class
1073
1061
 
1074
1062
  unless (cnstr_name = fk[3])
1075
1063
  # For any appended references (those that come from config), arrive upon a definitely unique constraint name
@@ -1102,8 +1090,12 @@ module Brick
1102
1090
  if is_polymorphic
1103
1091
  # Assuming same fk (don't yet support composite keys for polymorphics)
1104
1092
  assoc_bt[:inverse_table] << fk[2]
1105
- else # Expect we've got a composite key going
1106
- assoc_bt[:fk] = assoc_bt[:fk].is_a?(String) ? [assoc_bt[:fk], fk[1]] : assoc_bt[:fk].concat(fk[1])
1093
+ else # Expect we could have a composite key going
1094
+ if assoc_bt[:fk].is_a?(String)
1095
+ assoc_bt[:fk] = [assoc_bt[:fk], fk[1]] unless fk[1] == assoc_bt[:fk]
1096
+ elsif assoc_bt[:fk].exclude?(fk[1])
1097
+ assoc_bt[:fk] << fk[1]
1098
+ end
1107
1099
  assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[1]}"
1108
1100
  end
1109
1101
  else
@@ -1121,7 +1113,11 @@ module Brick
1121
1113
  return if is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[0] == exclusion[0] && fk[1] == exclusion[1] && primary_table == exclusion[2] }
1122
1114
 
1123
1115
  if (assoc_hm = hms.fetch((hm_cnstr_name = "hm_#{cnstr_name}"), nil))
1124
- assoc_hm[:fk] = assoc_hm[:fk].is_a?(String) ? [assoc_hm[:fk], fk[1]] : assoc_hm[:fk].concat(fk[1])
1116
+ if assoc_bt[:fk].is_a?(String)
1117
+ assoc_bt[:fk] = [assoc_bt[:fk], fk[1]] unless fk[1] == assoc_bt[:fk]
1118
+ elsif assoc_bt[:fk].exclude?(fk[1])
1119
+ assoc_bt[:fk] << fk[1]
1120
+ end
1125
1121
  assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
1126
1122
  assoc_hm[:inverse] = assoc_bt
1127
1123
  else
@@ -210,16 +210,19 @@ def hide_bcrypt(val)
210
210
  end %>"
211
211
 
212
212
  if ['index', 'show', 'update'].include?(args.first)
213
+ poly_cols = []
213
214
  css << "<% bts = { #{
214
215
  bts.each_with_object([]) do |v, s|
215
- foreign_models = if v.last[1].is_a?(Array)
216
+ foreign_models = if v.last[2] # Polymorphic?
217
+ poly_cols << @_brick_model.reflect_on_association(v[1].first).foreign_type
216
218
  v.last[1].each_with_object([]) { |x, s| s << "[#{x.name}, #{x.primary_key.inspect}]" }.join(', ')
217
219
  else
218
220
  "[#{v.last[1].name}, #{v.last[1].primary_key.inspect}]"
219
221
  end
220
- s << "#{v.first.inspect} => [#{v.last.first.inspect}, [#{foreign_models}]]"
222
+ s << "#{v.first.inspect} => [#{v.last.first.inspect}, [#{foreign_models}], #{v.last[2].inspect}]"
221
223
  end.join(', ')
222
- } } %>"
224
+ } }
225
+ poly_cols = #{poly_cols.inspect} %>"
223
226
  end
224
227
 
225
228
  # %%% When doing schema select, if there's an ID then remove it, or if we're on a new page go to index
@@ -384,7 +387,7 @@ function changeout(href, param, value) {
384
387
  <table id=\"#{table_name}\">
385
388
  <thead><tr>#{'<th></th>' if pk}
386
389
  <% @#{table_name}.columns.map(&:name).each do |col| %>
387
- <% next if col == '#{pk}' || ::Brick.config.metadata_columns.include?(col) %>
390
+ <% next if col == '#{pk}' || ::Brick.config.metadata_columns.include?(col) || poly_cols.include?(col) %>
388
391
  <th>
389
392
  <% if (bt = bts[col]) %>
390
393
  BT <%
@@ -405,22 +408,22 @@ function changeout(href, param, value) {
405
408
  <tr>#{"
406
409
  <td><%= link_to '⇛', #{obj_name}_path(#{obj_pk}), { class: 'big-arrow' } %></td>" if obj_pk}
407
410
  <% #{obj_name}.attributes.each do |k, val| %>
408
- <% next if k == '#{pk}' || ::Brick.config.metadata_columns.include?(k) || k.start_with?('_brfk_') || (k.start_with?('_br_') && (k.length == 63 || k.end_with?('_ct'))) %>
411
+ <% next if k == '#{pk}' || ::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'))) %>
409
412
  <td>
410
413
  <% if (bt = bts[k]) %>
411
414
  <%# binding.pry # Postgres column names are limited to 63 characters %>
412
- <% if (pairs = bt[1].length > 1)
415
+ <% if bt[2] # Polymorphic?
413
416
  bt_class = #{obj_name}.send(\"#\{bt.first\}_type\")
414
- # descrips = @_brick_bt_descrip[bt.first][bt_class]
417
+ base_class = (::Brick.existing_stis[bt_class] || bt_class).constantize.base_class.name.underscore
415
418
  poly_id = #{obj_name}.send(\"#\{bt.first\}_id\")
416
419
  %><%= link_to(\"#\{bt_class\} ##\{poly_id\}\",
417
- send(\"#\{bt_class.underscore\}_path\".to_sym, poly_id)) if poly_id %><%
418
- else # We should do something other than [0..-2] for when there is no primary key (or maybe have an empty final array there in that case?)
419
- bt_txt = (bt_class = bt[1].first.first).brick_descrip(
420
- #{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)
421
- )
422
- bt_id = #{obj_name}.send(*bt_id_col) if bt_id_col&.present? %>
423
- <%= bt_id ? link_to(bt_txt, send(\"#\{bt_class.name.underscore\}_path\".to_sym, bt_id)) : bt_txt %>
420
+ send(\"#\{base_class\}_path\".to_sym, poly_id)) if poly_id %><%
421
+ else
422
+ bt_txt = (bt_class = bt[1].first.first).brick_descrip(
423
+ #{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)
424
+ )
425
+ bt_id = #{obj_name}.send(*bt_id_col) if bt_id_col&.present? %>
426
+ <%= bt_id ? link_to(bt_txt, send(\"#\{bt_class.base_class.name.underscore\}_path\".to_sym, bt_id)) : bt_txt %>
424
427
  <%#= 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 %>
425
428
  <% end %>
426
429
  <% else %>
@@ -460,9 +463,9 @@ function changeout(href, param, value) {
460
463
  # Add a final member in this array with descriptive options to be used in <select> drop-downs
461
464
  bt_name = bt[1].map { |x| x.first.name }.join('/')
462
465
  # %%% Only do this if the user has permissions to edit this bt field
463
- if (pairs = bt[1]).length > 1
466
+ if bt[2] # Polymorphic?
464
467
  poly_class_name = @#{obj_name}.first.send(\"#\{bt.first\}_type\")
465
- bt_pair = pairs.find { |pair| pair.first.name == poly_class_name }
468
+ bt_pair = bt[1].find { |pair| pair.first.name == poly_class_name }
466
469
  # descrips = @_brick_bt_descrip[bt.first][bt_class]
467
470
  poly_id = @#{obj_name}.first.send(\"#\{bt.first\}_id\")
468
471
  # bt_class.order(obj_pk = bt_class.primary_key).each { |obj| option_detail << [obj.brick_descrip(nil, obj_pk), obj.send(obj_pk)] }
@@ -470,7 +473,7 @@ function changeout(href, param, value) {
470
473
  bt_pair = bt[1].first
471
474
  end
472
475
  bt_class = bt_pair.first
473
- if bt.length < 3
476
+ if bt.length < 4
474
477
  bt << (option_detail = [[\"(No #\{bt_name\} chosen)\", '^^^brick_NULL^^^']])
475
478
  # %%% Accommodate composite keys for obj.pk at the end here
476
479
  bt_class.order(obj_pk = bt_class.primary_key).each { |obj| option_detail << [obj.brick_descrip(nil, obj_pk), obj.send(obj_pk)] }
@@ -484,8 +487,8 @@ function changeout(href, param, value) {
484
487
  <% if bt
485
488
  html_options = { prompt: \"Select #\{bt_name\}\" }
486
489
  html_options[:class] = 'dimmed' unless val %>
487
- <%= f.select k.to_sym, bt[2], { value: val || '^^^brick_NULL^^^' }, html_options %>
488
- <%= bt_obj = bt_class.find_by(bt_pair[1] => val); link_to('⇛', send(\"#\{bt_class.name.underscore\}_path\".to_sym, bt_obj.send(bt_class.primary_key.to_sym)), { class: 'show-arrow' }) if bt_obj %>
490
+ <%= f.select k.to_sym, bt[3], { value: val || '^^^brick_NULL^^^' }, html_options %>
491
+ <%= 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 %>
489
492
  <% else case #{model_name}.column_for_attribute(k).type
490
493
  when :string, :text %>
491
494
  <% if is_bcrypt?(val) # || .readonly? %>
@@ -538,7 +541,6 @@ function changeout(href, param, value) {
538
541
  #{script}"
539
542
 
540
543
  end
541
- puts inline
542
544
  # As if it were an inline template (see #determine_template in actionview-5.2.6.2/lib/action_view/renderer/template_renderer.rb)
543
545
  keys = options.has_key?(:locals) ? options[:locals].keys : []
544
546
  handler = ActionView::Template.handler_for_extension(options[:type] || 'erb')
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 25
8
+ TINY = 26
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
@@ -85,12 +85,16 @@ module Brick
85
85
  @sti_models ||= {}
86
86
  end
87
87
 
88
+ def self.existing_stis
89
+ @existing_stis ||= Brick.config.sti_namespace_prefixes.each_with_object({}) { |snp, s| s[snp.first[2..-1]] = snp.last unless snp.first.end_with?('::') }
90
+ end
91
+
88
92
  class << self
89
93
  attr_accessor :db_schemas
90
94
 
91
95
  def set_db_schema(params)
92
96
  schema = params['_brick_schema'] || 'public'
93
- ActiveRecord::Base.connection.execute("SET SEARCH_PATH='#{schema}';") if schema && ::Brick.db_schemas&.include?(schema)
97
+ ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema) if schema && ::Brick.db_schemas&.include?(schema)
94
98
  end
95
99
 
96
100
  # All tables and views (what Postgres calls "relations" including column and foreign key info)
@@ -103,14 +107,14 @@ module Brick
103
107
 
104
108
  def get_bts_and_hms(model)
105
109
  bts, hms = model.reflect_on_all_associations.each_with_object([{}, {}]) do |a, s|
106
- next if (!const_defined?(a.name.to_s.singularize.camelize) && ::Brick.config.exclude_tables.include?(a.plural_name))
110
+ next if !const_defined?(a.name.to_s.singularize.camelize) && ::Brick.config.exclude_tables.include?(a.plural_name)
107
111
 
108
112
  case a.macro
109
113
  when :belongs_to
110
114
  s.first[a.foreign_key] = if a.polymorphic?
111
115
  primary_tables = relations[model.table_name][:fks].find { |_k, fk| fk[:assoc_name] == a.name.to_s }&.last&.fetch(:inverse_table, [])
112
116
  models = primary_tables&.map { |table| table.singularize.camelize.constantize }
113
- [a.name, models]
117
+ [a.name, models, true]
114
118
  else
115
119
  [a.name, a.klass]
116
120
  end
@@ -264,6 +268,7 @@ module Brick
264
268
 
265
269
  # Polymorphic associations
266
270
  def polymorphics=(polys)
271
+ polys = polys.each_with_object({}) { |poly, s| s[poly] = nil } if polys.is_a?(Array)
267
272
  Brick.config.polymorphics = polys || {}
268
273
  end
269
274
 
@@ -279,6 +284,13 @@ module Brick
279
284
  Brick.config.sti_namespace_prefixes = snp
280
285
  end
281
286
 
287
+ # Database schema to use when analysing existing data, such as deriving a list of polymorphic classes
288
+ # for polymorphics in which it wasn't originally specified.
289
+ # @api public
290
+ def schema_to_analyse=(schema)
291
+ Brick.config.schema_to_analyse = schema
292
+ end
293
+
282
294
  # Load additional references (virtual foreign keys)
283
295
  # This is attempted early if a brick initialiser file is found, and then again as a failsafe at the end of our engine's initialisation
284
296
  # %%% Maybe look for differences the second time 'round and just add new stuff instead of entirely deferring
@@ -289,13 +301,30 @@ module Brick
289
301
  if (ars = ::Brick.config.additional_references) || ::Brick.config.polymorphics
290
302
  ars.each { |fk| ::Brick._add_bt_and_hm(fk[0..2], relations) } if ars
291
303
  if (polys = ::Brick.config.polymorphics)
304
+ if (schema = ::Brick.config.schema_to_analyse) && ::Brick.db_schemas&.include?(schema)
305
+ ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
306
+ end
307
+ missing_stis = {}
292
308
  polys.each do |k, v|
293
309
  table_name, poly = k.split('.')
294
310
  v ||= ActiveRecord::Base.execute_sql("SELECT DISTINCT #{poly}_type AS typ FROM #{table_name}").map { |result| result['typ'] }
295
311
  v.each do |type|
296
- ::Brick._add_bt_and_hm([table_name, poly, type.underscore.pluralize, "(brick) #{table_name}_#{poly}"], relations, true)
312
+ if relations.key?(primary_table = type.underscore.pluralize)
313
+ ::Brick._add_bt_and_hm([table_name, poly, primary_table, "(brick) #{table_name}_#{poly}"], relations, true)
314
+ else
315
+ missing_stis[primary_table] = type unless ::Brick.existing_stis.key?(type)
316
+ end
297
317
  end
298
318
  end
319
+ unless missing_stis.empty?
320
+ print "
321
+ You might be missing an STI namespace prefix entry for these tables: #{missing_stis.keys.join(', ')}.
322
+ In config/initializers/brick.rb appropriate entries would look something like:
323
+ Brick.sti_namespace_prefixes = {"
324
+ puts missing_stis.map { |_k, missing_sti| "\n '::#{missing_sti}' => 'YourParentModel'" }.join(',')
325
+ puts " }
326
+ (Just trade out YourParentModel with some more appropriate one.)"
327
+ end
299
328
  end
300
329
  @_additional_references_loaded = true
301
330
  end
@@ -19,11 +19,31 @@ module Brick
19
19
 
20
20
  def create_initializer_file
21
21
  unless File.exist?(filename = 'config/initializers/brick.rb')
22
- # See if we can make suggestions for additional_references
23
- resembles_fks = []
24
- possible_additional_references = (relations = ::Brick.relations).each_with_object([]) do |v, s|
25
- v.last[:cols].each do |col, _type|
22
+ # See if we can make suggestions for additional_references and polymorphic associations
23
+ resembles_fks = Hash.new { |h, k| h[k] = [] }
24
+ possible_polymorphics = {}
25
+ possible_additional_references = (relations = ::Brick.relations).each_with_object(Hash.new { |h, k| h[k] = [] }) do |v, s|
26
+ model_filename = "app/models/#{ActiveSupport::Inflector.singularize(v.first)}.rb"
27
+ v.last[:cols].each do |col, type|
26
28
  col_down = col.downcase
29
+
30
+ if (is_possible_poly = ['character varying', 'text'].include?(type.first))
31
+ if col_down.end_with?('_type') &&
32
+ poly_type_cut_length = -6
33
+ col_down = col_down[0..-6]
34
+ elsif col_down.end_with?('type')
35
+ poly_type_cut_length = -5
36
+ col_down = col_down[0..-5]
37
+ else
38
+ is_possible_poly = false
39
+ end
40
+ is_possible_poly = false if col_down.length < 6 # Was it simply called "type" or something else really short?
41
+ if is_possible_poly && !File.exist?(model_filename) # Make sure a model file isn't present
42
+ possible_polymorphics["#{v.first}.#{col_down}"] = "'#{v.first}.#{col[0..poly_type_cut_length]}'"
43
+ next
44
+ end
45
+ end
46
+
27
47
  is_possible = true
28
48
  if col_down.end_with?('_id')
29
49
  col_down = col_down[0..-4]
@@ -40,30 +60,48 @@ module Brick
40
60
  if col_down.start_with?('fk_')
41
61
  is_possible = true
42
62
  col_down = col_down[3..-1]
63
+ elsif col_down.start_with?('fk')
64
+ is_possible = true
65
+ col_down = col_down[2..-1]
43
66
  end
44
67
  # This possible key not really a primary key and not yet used as a foreign key?
45
68
  if is_possible && !(relation = relations.fetch(v.first, {}))[:pkey].first&.last&.include?(col) &&
46
69
  !relations.fetch(v.first, {})[:fks]&.any? { |_k, v| v[:is_bt] && v[:fk] == col }
47
- if (relations.fetch(f_table = col_down, nil) ||
48
- relations.fetch(f_table = ActiveSupport::Inflector.pluralize(col_down), nil)) &&
49
- # Looks pretty promising ... just make sure a model file isn't present
50
- !File.exist?("app/models/#{ActiveSupport::Inflector.singularize(v.first)}.rb")
51
- s << "['#{v.first}', '#{col}', '#{f_table}']"
52
- else
53
- resembles_fks << "#{v.first}.#{col}"
70
+ # Starting to look promising ... make sure a model file isn't present
71
+ if !File.exist?(model_filename)
72
+ if (relations.fetch(f_table = col_down, nil) ||
73
+ relations.fetch(f_table = ActiveSupport::Inflector.pluralize(col_down), nil)) &&
74
+ s["#{v.first}.#{col_down}"] << "['#{v.first}', '#{col}', '#{f_table}']"
75
+ else
76
+ resembles_fks["#{v.first}.#{col_down}"] << "#{v.first}.#{col}"
77
+ end
54
78
  end
55
79
  end
56
80
  end
57
- s
58
81
  end
59
82
 
60
- bar = case possible_additional_references.length
83
+ possible_polymorphics.each_key do |k|
84
+ # Also matching one of the FK suggestions means it could be polymorphic,
85
+ # so delete any suggestions for a FK of the same name and only recommend
86
+ # the polymorphic association.
87
+ if resembles_fks.key?(k)
88
+ resembles_fks.delete(k)
89
+ elsif possible_additional_references.key?(k)
90
+ possible_additional_references.delete(k)
91
+ else
92
+ # While this one has a type, it's missing a corresponding ID column so it isn't polymorphic
93
+ possible_polymorphics.delete(k)
94
+ end
95
+ end
96
+ resembles_fks = resembles_fks.values.flatten
97
+
98
+ bar = case (possible_additional_references = possible_additional_references.values.flatten).length
61
99
  when 0
62
100
  +"# Brick.additional_references = [['orders', 'customer_id', 'customer'],
63
101
  # ['customer', 'region_id', 'regions']]"
64
102
  when 1
65
103
  +"# # Here is a possible additional reference that has been auto-identified for the #{ActiveRecord::Base.connection.current_database} database:
66
- # Brick.additional_references = [[#{possible_additional_references.first}]"
104
+ # Brick.additional_references = [#{possible_additional_references.first}]"
67
105
  else
68
106
  +"# # Here are possible additional references that have been auto-identified for the #{ActiveRecord::Base.connection.current_database} database:
69
107
  # Brick.additional_references = [
@@ -75,6 +113,26 @@ module Brick
75
113
  # # #{resembles_fks.join(', ')}"
76
114
  end
77
115
 
116
+ poly = case (possible_polymorphics = possible_polymorphics.values.flatten).length
117
+ when 0
118
+ " like this:
119
+ # Brick.polymorphics = [
120
+ # 'comments.commentable',
121
+ # 'images.imageable'
122
+ # ]"
123
+ when 1
124
+ ".
125
+ # # Here is a possible polymorphic association that has been auto-identified for the #{ActiveRecord::Base.connection.current_database} database:
126
+ # Brick.polymorphics = [#{possible_additional_references.first}]"
127
+
128
+ else
129
+ ".
130
+ # # Here are possible polymorphic associations that have been auto-identified for the #{ActiveRecord::Base.connection.current_database} database:
131
+ # Brick.polymorphics = [
132
+ # #{possible_polymorphics.join(",\n# ")}
133
+ # ]"
134
+ end
135
+
78
136
  create_file(filename, "# frozen_string_literal: true
79
137
 
80
138
  # # Settings for the Brick gem
@@ -119,7 +177,7 @@ module Brick
119
177
  # # Skip showing counts for these specific has_many associations when building auto-generated #index views.
120
178
  # # When there are related tables with a significant number of records, this can lessen the load on the database
121
179
  # # considerably, sometimes fixing what might appear to be an index page that just \"hangs\" for no apparent reason.
122
- Brick.skip_index_hms = ['User.litany_of_woes']
180
+ # Brick.skip_index_hms = ['User.litany_of_woes']
123
181
 
124
182
  # # By default primary tables involved in a foreign key relationship will indicate a \"has_many\" relationship pointing
125
183
  # # back to the foreign table. In order to represent a \"has_one\" association instead, an override can be provided
@@ -157,9 +215,11 @@ Brick.skip_index_hms = ['User.litany_of_woes']
157
215
  # Brick.sti_namespace_prefixes = { '::Animals::' => 'Animal',
158
216
  # '::Snake' => 'Reptile' }
159
217
 
160
- # # Polymorphic associations must be explicitly specified, which is as easy as providing a model name and polymorphic
161
- # # association name like this:
162
- # Brick.polymorphics = ['Comment.commentable', 'Image.imageable']
218
+ # # Database schema to use when analysing existing data, such as deriving a list of polymorphic classes in the case that
219
+ # # it wasn't originally specified.
220
+ # Brick.schema_to_analyse = 'engineering'
221
+
222
+ # # Polymorphic associations are set up by providing a model name and polymorphic association name#{poly}
163
223
 
164
224
  # # If a default route is not supplied, Brick attempts to find the most \"central\" table and wires up the default
165
225
  # # route to go to the :index action for what would be a controller for that table. You can specify any controller
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.25
4
+ version: 1.0.26
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-21 00:00:00.000000000 Z
11
+ date: 2022-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord