brick 1.0.99 → 1.0.101

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: 431e2aee4c3c199095d8475eeedcc46bd95e86a47c51623e3cea7fbbde59edcd
4
- data.tar.gz: 32144a9a9ab6ec60ef297f27ae73e35cc891e5f9462b9beae4e7a0534ab77135
3
+ metadata.gz: eba38d653370b0cc3e98f2adc82d8c475854440745bd35caab9a9a4253fc152c
4
+ data.tar.gz: 18ca517b90266cf5db711040a7f40c8e848600135ef0a39ad91da9cd2a0b1c89
5
5
  SHA512:
6
- metadata.gz: f471bc993d97e92deae3c03e6cac9db0e350e72bd070afe5bcde1d0ca18d650b546db0d018a101009e429e9870a7e2c5e566f980c9e5b1319bc18873271c5550
7
- data.tar.gz: 95f128f0f2a29cf0d1af1b5001eae27885b7e11ce23194e4a4da0141a0bfc059edd1917036f4addf1ee2aa4b69f763962e821ca31f9473048a6934dbecd8518e
6
+ metadata.gz: 859f48999fb0e8d1010a701e29c9f0275f716b20f54b4348576f4dbff7f68da1eb83709a51a1a7acd7debaf4dc1f82e863048ef16cda6b5f59862246e0c50787
7
+ data.tar.gz: d93c3db79715f62705c0b8f2f5bd2640a2756e1fbcb6aa6a7cf017a18bc31fa5e98496ea41539611c149f84870f24596c105d146484519b7f040d2f36b25ce45
@@ -404,21 +404,9 @@ module ActiveRecord
404
404
  def brick_select(params, selects = [], order_by = nil, translations = {}, join_array = ::Brick::JoinArray.new)
405
405
  is_add_bts = is_add_hms = true
406
406
 
407
- if selects.empty?
408
- # Build out cust_cols, bt_descrip and hm_counts now so that they are available on the
409
- # model early in case the user wants to do an ORDER BY based on any of that.
410
- model._brick_calculate_bts_hms(translations, join_array) if is_add_bts || is_add_hms
411
- else
412
- is_api = true
413
- # If there are any provided selects, treat them as API columns and build them as cust_cols since they
414
- # can be built using DSL.
415
- # false = not polymorphic, and true = yes -- please emit_dsl
416
- selects.each do |api_col|
417
- pieces, my_dsl = brick_parse_dsl(join_array, [], translations, false, "[#{api_col}]", true)
418
- _br_cust_cols[api_col.tr('.', '_')] = [pieces, my_dsl]
419
- end
420
- selects.clear # Now these have become custom columns
421
- end
407
+ # Build out cust_cols, bt_descrip and hm_counts now so that they are available on the
408
+ # model early in case the user wants to do an ORDER BY based on any of that.
409
+ model._brick_calculate_bts_hms(translations, join_array) if is_add_bts || is_add_hms
422
410
 
423
411
  is_postgres = ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
424
412
  is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2'
@@ -426,7 +414,7 @@ module ActiveRecord
426
414
  is_distinct = nil
427
415
  wheres = {}
428
416
  params.each do |k, v|
429
- next if ['_brick_schema', '_brick_order', '_brick_api', 'controller', 'action'].include?(k)
417
+ next if ['_brick_schema', '_brick_order', 'controller', 'action'].include?(k)
430
418
 
431
419
  if (where_col = (ks = k.split('.')).last)[-1] == '!'
432
420
  where_col = where_col[0..-2]
@@ -454,14 +442,17 @@ module ActiveRecord
454
442
  # ActiveRecord::StatementInvalid (TinyTds::Error: DBPROCESS is dead or not enabled)
455
443
  # Relevant info here: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/402
456
444
  columns.each do |col|
457
- next if is_api && id_parts.exclude?(col.name) # Only keep the ID columns if this is an API request
458
-
459
445
  col_alias = " AS #{col.name}_" if (col_name = col.name) == 'class'
460
446
  selects << if is_mysql
461
447
  "`#{tbl_no_schema}`.`#{col_name}`#{col_alias}"
462
448
  elsif is_postgres || is_mssql
463
- # Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
464
- cast_as_text = '::text' if is_distinct && Brick.relations[klass.table_name]&.[](:cols)&.[](col_name)&.first&.start_with?('xml')
449
+ if is_distinct # Postgres can not use DISTINCT with any columns that are XML or JSON
450
+ cast_as_text = if Brick.relations[klass.table_name]&.[](:cols)&.[](col_name)&.first == 'json'
451
+ '::jsonb' # Convert JSON to JSONB
452
+ elsif Brick.relations[klass.table_name]&.[](:cols)&.[](col_name)&.first&.start_with?('xml')
453
+ '::text' # Convert XML to text
454
+ end
455
+ end
465
456
  "\"#{tbl_no_schema}\".\"#{col_name}\"#{cast_as_text}#{col_alias}"
466
457
  elsif col.type # Could be Sqlite or Oracle
467
458
  if col_alias || !(/^[a-z0-9_]+$/ =~ col_name)
@@ -505,6 +496,7 @@ module ActiveRecord
505
496
  # binding.pry
506
497
  next
507
498
  end
499
+
508
500
  key_klass = nil
509
501
  key_tbl_name = nil
510
502
  dest_pk = nil
@@ -644,19 +636,26 @@ module ActiveRecord
644
636
  through_sources.map do |a|
645
637
  from_clause << "\n LEFT OUTER JOIN #{a.table_name} br_t#{idx += 1} "
646
638
  from_clause << if (src_ref = a.source_reflection).macro == :belongs_to
647
- (nm = hmt_assoc.source_reflection.inverse_of&.name)
648
- # binding.pry unless nm
639
+ nm = hmt_assoc.source_reflection.inverse_of&.name
649
640
  link_back << nm
650
641
  "ON br_t#{idx}.id = br_t#{idx - 1}.#{a.foreign_key}"
651
642
  elsif src_ref.options[:as]
652
643
  "ON br_t#{idx}.#{src_ref.type} = '#{src_ref.active_record.name}'" + # "polymorphable_type"
653
644
  " AND br_t#{idx}.#{src_ref.foreign_key} = br_t#{idx - 1}.id"
654
645
  elsif src_ref.options[:source_type]
655
- print "Skipping #{hm.name} --HMT-> #{hm.source_reflection.name} as it uses source_type which is not yet supported"
656
- nix << k
657
- bail_out = true
658
- break
659
- else # Standard has_many
646
+ if a == hm.source_reflection
647
+ print "Skipping #{hm.name} --HMT-> #{hm.source_reflection.name} as it uses source_type in a way which is not yet supported"
648
+ nix << k
649
+ bail_out = true
650
+ break
651
+ # "ON br_t#{idx}.#{a.foreign_type} = '#{src_ref.options[:source_type]}' AND " \
652
+ # "br_t#{idx}.#{a.foreign_key} = br_t#{idx - 1}.id"
653
+ else # Works for HMT through a polymorphic HO
654
+ link_back << hmt_assoc.source_reflection.inverse_of&.name # Some polymorphic "_able" thing
655
+ "ON br_t#{idx - 1}.#{a.foreign_type} = '#{src_ref.options[:source_type]}' AND " \
656
+ "br_t#{idx - 1}.#{a.foreign_key} = br_t#{idx}.id"
657
+ end
658
+ else # Standard has_many or has_one
660
659
  # binding.pry unless (
661
660
  nm = hmt_assoc.source_reflection.inverse_of&.name
662
661
  # )
@@ -719,12 +718,13 @@ module ActiveRecord
719
718
  on_clause << "#{tbl_alias}.#{poly_type} = '#{name}'"
720
719
  end
721
720
  unless from_clause
721
+ tbl_nm = hm.macro == :has_and_belongs_to_many ? hm.join_table : hm.table_name
722
722
  hm_table_name = if is_mysql
723
- "`#{hm.table_name}`"
723
+ "`#{tbl_nm}`"
724
724
  elsif is_postgres || is_mssql
725
- "\"#{(hm.table_name).gsub('.', '"."')}\""
725
+ "\"#{(tbl_nm).gsub('.', '"."')}\""
726
726
  else
727
- hm.table_name
727
+ tbl_nm
728
728
  end
729
729
  end
730
730
  group_bys = ::Brick.is_oracle || is_mssql ? hm_selects : (1..hm_selects.length).to_a
@@ -984,7 +984,7 @@ Module.class_exec do
984
984
  ::Brick.is_oracle ? class_name.upcase : class_name,
985
985
  (plural_class_name = class_name.pluralize)].find { |s| Brick.db_schemas&.include?(s) }&.camelize ||
986
986
  (::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}::") && class_name) ||
987
- (::Brick.config.table_name_prefixes&.values.include?(class_name) && class_name))
987
+ (::Brick.config.table_name_prefixes.values.include?(class_name) && class_name))
988
988
  return self.const_get(schema_name) if self.const_defined?(schema_name)
989
989
 
990
990
  # Build out a module for the schema if it's namespaced
@@ -1403,8 +1403,8 @@ class Object
1403
1403
  # (More information on https://docs.avohq.io/2.0/controllers.html)
1404
1404
  controller_base = Avo::ResourcesController
1405
1405
  end
1406
- table_name = ActiveSupport::Inflector.underscore(plural_class_name)
1407
- singular_table_name = ActiveSupport::Inflector.singularize(table_name)
1406
+ table_name = model&.table_name || ActiveSupport::Inflector.underscore(plural_class_name)
1407
+ singular_table_name = ActiveSupport::Inflector.singularize(ActiveSupport::Inflector.underscore(plural_class_name))
1408
1408
  pk = model&._brick_primary_key(relations.fetch(table_name, nil))
1409
1409
  is_postgres = ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
1410
1410
  is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2'
@@ -1589,14 +1589,6 @@ class Object
1589
1589
  order_by, _ = model._brick_calculate_ordering(ordering, true) # Don't do the txt part
1590
1590
 
1591
1591
  ar_relation = ActiveRecord.version < Gem::Version.new('4') ? model.preload : model.all
1592
-
1593
- if (cc = params['_brick_api']&.split(','))
1594
- is_api = true
1595
- selects = cc
1596
- counts = [] # No need for any extra HM count columns
1597
- model._br_cust_cols.clear
1598
- end
1599
-
1600
1592
  @_brick_params = ar_relation.brick_select(params, (selects ||= []), order_by,
1601
1593
  translations = {},
1602
1594
  join_array = ::Brick::JoinArray.new)
@@ -1611,14 +1603,14 @@ class Object
1611
1603
  "b_r_#{v.first}.c_t_ AS \"b_r_#{v.first}_ct\""
1612
1604
  end
1613
1605
  end
1614
-
1615
1606
  ar_select = ar_relation.respond_to?(:_select!) ? ar_relation.dup._select!(*selects, *counts) : ar_relation.select(selects + counts)
1616
- instance_variable_set("@#{table_name.pluralize}".to_sym, ar_select)
1617
- if namespace && (idx = lookup_context.prefixes.index(table_name))
1607
+ instance_variable_set("@#{table_name.split('.').last}".to_sym, ar_select)
1608
+ table_name_no_schema = singular_table_name.pluralize
1609
+ if namespace && (idx = lookup_context.prefixes.index(table_name_no_schema))
1618
1610
  lookup_context.prefixes[idx] = "#{namespace.name.underscore}/#{lookup_context.prefixes[idx]}"
1619
1611
  end
1620
1612
  @_brick_excl = session[:_brick_exclude]&.split(',')&.each_with_object([]) do |excl, s|
1621
- if (excl_parts = excl.split('.')).first == table_name
1613
+ if (excl_parts = excl.split('.')).first == table_name_no_schema
1622
1614
  s << excl_parts.last
1623
1615
  end
1624
1616
  end
@@ -1749,7 +1741,12 @@ class Object
1749
1741
  val_part.gsub('^^sl^^', '/')
1750
1742
  end
1751
1743
  end
1752
- model.find(id.is_a?(Array) && id.length == 1 ? id.first : id)
1744
+ # Support friendly_id gem
1745
+ if Object.const_defined?('FriendlyId') && model.instance_variable_get(:@friendly_id_config)
1746
+ model.friendly.find(id.is_a?(Array) && id.length == 1 ? id.first : id)
1747
+ else
1748
+ model.find(id.is_a?(Array) && id.length == 1 ? id.first : id)
1749
+ end
1753
1750
  end
1754
1751
  end
1755
1752
 
@@ -54,7 +54,7 @@ module Brick
54
54
  end
55
55
 
56
56
  # When table names have specific prefixes, automatically place them in their own module with a table_name_prefix.
57
- ::Brick.table_name_prefixes = app.config.brick.fetch(:table_name_prefixes, [])
57
+ ::Brick.table_name_prefixes = app.config.brick.fetch(:table_name_prefixes, {})
58
58
 
59
59
  # Columns to treat as being metadata for purposes of identifying associative tables for has_many :through
60
60
  ::Brick.metadata_columns = app.config.brick.fetch(:metadata_columns, ['created_at', 'updated_at', 'deleted_at'])
@@ -197,7 +197,7 @@ function linkSchemas() {
197
197
  class BrickTitle
198
198
  def initialize(name, view_component)
199
199
  @vc = view_component
200
- @_name = name
200
+ @_name = name || ''
201
201
  end
202
202
  def to_s
203
203
  @_name.html_safe + @vc.instance_variable_get(:@__vc_helpers)&.link_to_brick(nil,
@@ -313,10 +313,10 @@ function linkSchemas() {
313
313
  end
314
314
 
315
315
  if @_brick_model
316
- pk = @_brick_model._brick_primary_key(::Brick.relations.fetch(@_brick_model&.table_name, nil))
316
+ pk = @_brick_model._brick_primary_key(::Brick.relations.fetch((table_name = @_brick_model.table_name.split('.').last), nil))
317
317
  obj_name = model_name.split('::').last.underscore
318
318
  path_obj_name = @_brick_model._brick_index(:singular)
319
- table_name = obj_name.pluralize
319
+ table_name ||= obj_name.pluralize
320
320
  template_link = nil
321
321
  bts, hms = ::Brick.get_bts_and_hms(@_brick_model) # This gets BT and HM and also has_many :through (HMT)
322
322
  hms_columns = [] # Used for 'index'
@@ -346,13 +346,16 @@ function linkSchemas() {
346
346
  end
347
347
  case args.first
348
348
  when 'index'
349
- unless skip_klass_hms.key?(assoc_name.to_sym) || hm_assoc.options[:source]
349
+ if !skip_klass_hms.key?(assoc_name.to_sym) && (
350
+ @_brick_model._br_hm_counts.key?(assoc_name) ||
351
+ @_brick_model._br_bt_descrip.key?(assoc_name) # Will end up here if it's a has_one
352
+ )
350
353
  hm_entry = +"'#{hm_assoc.name}' => [#{assoc_name.inspect}, "
351
- hm_entry << if hm_assoc.macro == :has_many
354
+ hm_entry << if hm_assoc.macro == :has_one
355
+ 'nil'
356
+ else # :has_many or :has_and_belongs_to_many
352
357
  # Postgres column names are limited to 63 characters
353
358
  "'" + "b_r_#{assoc_name}_ct"[0..62] + "'"
354
- else # has_one
355
- 'nil'
356
359
  end
357
360
  hm_entry << ", #{path_keys(hm_assoc, hm_fk_name, pk).inspect}]"
358
361
  hms_columns << hm_entry
@@ -424,14 +427,6 @@ h1, h3 {
424
427
  cursor: pointer;
425
428
  }
426
429
 
427
- #apiToggle {
428
- border: 2px solid purple;
429
- }
430
-
431
- #apiToggle, .apiColName {
432
- cursor: pointer;
433
- }
434
-
435
430
  #dropper {
436
431
  background-color: #eee;
437
432
  }
@@ -572,10 +567,58 @@ def hide_bcrypt(val, max_len = 200)
572
567
  '(hidden)'
573
568
  else
574
569
  if val.is_a?(String)
575
- val = \"#\{val[0...max_len]}...\" if val.length > max_len
570
+ if (val = val.dup.strip).length > max_len
571
+ if val[0] == '<' # Seems to be HTML?
572
+ cur_len = 0
573
+ cur_idx = 0
574
+ # Find which HTML tags we might be inside so we can apply ending tags to balance
575
+ element_name = nil
576
+ in_closing = nil
577
+ elements = []
578
+ val.each_char do |ch|
579
+ case ch
580
+ when '<'
581
+ element_name = +''
582
+ when '/' # First character of tag is '/'?
583
+ in_closing = true if element_name == ''
584
+ when '>'
585
+ if element_name
586
+ if in_closing
587
+ if (idx = elements.index { |tag| tag.downcase == element_name.downcase })
588
+ elements.delete_at(idx)
589
+ end
590
+ elsif (tag_name = element_name.split.first).present?
591
+ elements.unshift(tag_name)
592
+ end
593
+ element_name = nil
594
+ in_closing = nil
595
+ end
596
+ else
597
+ element_name << ch if element_name
598
+ end
599
+ cur_idx += 1
600
+ # Unless it's inside wickets then this is real text content, and see if we're at the limit
601
+ break if element_name.nil? && ((cur_len += 1) > max_len)
602
+ end
603
+ val = val[0..cur_idx]
604
+ # Somehow still in the middle of an opening tag right at the end? (Should never happen)
605
+ if !in_closing && (tag_name = element_name&.split&.first)&.present?
606
+ elements.unshift(tag_name)
607
+ val << '>'
608
+ end
609
+ elements.each do |closing_tag|
610
+ val << \"</#\{closing_tag}>\"
611
+ end
612
+ else # Not HTML, just cut it at the length
613
+ val = val[0...max_len]
614
+ end
615
+ val = \"#\{val}...\"
616
+ end
576
617
  val = val.dup.force_encoding('UTF-8') unless val.encoding.name == 'UTF-8'
618
+ val
619
+ else
620
+ val.to_s
577
621
  end
578
- val
579
622
  end
580
623
  end
581
624
  def display_value(col_type, val)
@@ -1002,8 +1045,12 @@ erDiagram
1002
1045
  +"<html>
1003
1046
  <head>
1004
1047
  #{css}
1005
- <title>#{model_name} <%
1006
- if (description = (relation = Brick.relations[#{model_name}.table_name])&.fetch(:description, nil)).present?
1048
+ <title><% model = #{model_name}
1049
+ if sub_model = @_brick_params&.fetch(type_col = model.inheritance_column, nil)&.first
1050
+ model = Object.const_get(sub_model.to_sym)
1051
+ end
1052
+ %><%= model.name %><%
1053
+ if (description = (relation = Brick.relations[model.table_name])&.fetch(:description, nil)).present?
1007
1054
  %> - <%= description
1008
1055
  %><% end
1009
1056
  %></title>
@@ -1012,19 +1059,16 @@ erDiagram
1012
1059
  <p style=\"color: green\"><%= notice %></p>#{"
1013
1060
  #{schema_options}" if schema_options}
1014
1061
  <select id=\"tbl\">#{table_options}</select>
1015
-
1016
- <%= pick_api(#{model_name}).html_safe %>
1017
-
1018
1062
  <table id=\"resourceName\"><tr>
1019
- <td><h1>#{model_name}</h1></td>
1063
+ <td><h1><%= model.name %></h1></td>
1020
1064
  <td id=\"imgErd\" title=\"Show ERD\"></td>
1021
1065
  <% if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) %>
1022
1066
  <td><%= link_to_brick(
1023
1067
  avo_svg,
1024
- { index_proc: Proc.new do |model|
1025
- ::Avo.railtie_routes_url_helpers.send(\"resources_#\{model.base_class.model_name.route_key}_path\".to_sym)
1068
+ { index_proc: Proc.new do |avo_model|
1069
+ ::Avo.railtie_routes_url_helpers.send(\"resources_#\{model.model_name.route_key}_path\".to_sym)
1026
1070
  end,
1027
- title: '#{model_name} in Avo' }
1071
+ title: \"#\{model.name} in Avo\" }
1028
1072
  ) %></td>
1029
1073
  <% end %>
1030
1074
  </tr></table>#{template_link}<%
@@ -1036,13 +1080,13 @@ erDiagram
1036
1080
  <% if @_brick_params.length == 1 # %%% Does not yet work with composite keys
1037
1081
  k, id = @_brick_params.first
1038
1082
  id = id.first if id.is_a?(Array) && id.length == 1
1039
- origin = (key_parts = k.split('.')).length == 1 ? #{model_name} : #{model_name}.reflect_on_association(key_parts.first).klass
1083
+ origin = (key_parts = k.split('.')).length == 1 ? model : model.reflect_on_association(key_parts.first).klass
1040
1084
  if (destination_fk = Brick.relations[origin.table_name][:fks].values.find { |fk| fk[:fk] == key_parts.last }) &&
1041
1085
  (obj = (destination = origin.reflect_on_association(destination_fk[:assoc_name])&.klass)&.find(id)) %>
1042
1086
  <h3>for <%= link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination._brick_index(:singular)\}_path\".to_sym, id) %></h3><%
1043
1087
  end
1044
1088
  end %>
1045
- (<%= link_to 'See all #{model_name.split('::').last.pluralize}', #{@_brick_model._brick_index}_path %>)
1089
+ (<%= link_to \"See all #\{model.base_class.name.split('::').last.pluralize}\", #{@_brick_model._brick_index}_path %>)
1046
1090
  <% end
1047
1091
  # COLUMN EXCLUSIONS
1048
1092
  if @_brick_excl&.present? %>
@@ -1069,10 +1113,10 @@ erDiagram
1069
1113
  end.join(', ')}}
1070
1114
 
1071
1115
  # If the resource is missing, has the user simply created an inappropriately pluralised name for a table?
1072
- @#{table_name} ||= if dym_list = instance_variables.reject do |entry|
1073
- entry.to_s.start_with?('@_') ||
1074
- ['@cache_hit', '@marked_for_same_origin_verification', '@view_renderer', '@view_flow', '@output_buffer', '@virtual_path'].include?(entry.to_s)
1075
- end
1116
+ @#{table_name} ||= if (dym_list = instance_variables.reject do |entry|
1117
+ entry.to_s.start_with?('@_') ||
1118
+ ['@cache_hit', '@marked_for_same_origin_verification', '@view_renderer', '@view_flow', '@output_buffer', '@virtual_path'].include?(entry.to_s)
1119
+ end).present?
1076
1120
  msg = \"Can't find resource \\\"#{table_name}\\\".\"
1077
1121
  # Can't be sure otherwise of what is up, so check DidYouMean and offer a suggestion.
1078
1122
  if (dym = DidYouMean::SpellChecker.new(dictionary: dym_list).correct('@#{table_name}')).present?
@@ -1088,13 +1132,8 @@ erDiagram
1088
1132
  end
1089
1133
 
1090
1134
  # Write out the mega-grid
1091
- if params['_brick_api'] # API response?
1092
- brick_grid(@#{table_name}, @_brick_bt_descrip, @_brick_sequence, [], @_brick_excl,
1093
- cols, poly_cols, {}, [], {})
1094
- else
1095
- brick_grid(@#{table_name}, @_brick_bt_descrip, @_brick_sequence, @_brick_incl, @_brick_excl,
1096
- cols, poly_cols, bts, #{hms_keys.inspect}, {#{hms_columns.join(', ')}})
1097
- end %>
1135
+ brick_grid(@#{table_name}, @_brick_bt_descrip, @_brick_sequence, @_brick_incl, @_brick_excl,
1136
+ cols, poly_cols, bts, #{hms_keys.inspect}, {#{hms_columns.join(', ')}}) %>
1098
1137
 
1099
1138
  #{"<hr><%= link_to \"New #{obj_name}\", new_#{path_obj_name}_path %>" unless @_brick_model.is_view?}
1100
1139
  #{script}
@@ -11,7 +11,7 @@ module Brick::Rails::FormTags
11
11
  out << "<th x-order=\"#{pk.join(',')}\"></th>"
12
12
  end
13
13
 
14
- col_keys ||= relation.columns.each_with_object([]) do |col, s|
14
+ col_keys = relation.columns.each_with_object([]) do |col, s|
15
15
  col_name = col.name
16
16
  next if inclusions&.exclude?(col_name) ||
17
17
  (pk.include?(col_name) && [:integer, :uuid].include?(col.type) && !bts.key?(col_name)) ||
@@ -39,8 +39,10 @@ module Brick::Rails::FormTags
39
39
  "#{' x-order="' + col_name + '"' if true}>#{col_name}"
40
40
  end
41
41
  elsif col # HM column
42
+ options = {}
43
+ options[col[1].inheritance_column] = col[1].name unless col[1] == col[1].base_class
42
44
  s << "<th#{' x-order="' + col_name + '"' if true}>#{col[2]} "
43
- s << (col.first ? "#{col[3]}" : "#{link_to(col[3], send("#{col[1]._brick_index}_path"))}")
45
+ s << (col.first ? "#{col[3]}" : "#{link_to(col[3], send("#{col[1]._brick_index}_path", options))}")
44
46
  elsif cust_cols.key?(col_name) # Custom column
45
47
  s << "<th x-order=\"#{col_name}\">#{col_name}"
46
48
  elsif col_name.is_a?(Symbol) && (hot = bts[col_name]) # has_one :through
@@ -96,17 +98,17 @@ module Brick::Rails::FormTags
96
98
  if hms_col.length == 1
97
99
  out << hms_col.first
98
100
  else
99
- klass = (col = cols[col_name])[1]
101
+ hm_klass = (col = cols[col_name])[1]
100
102
  if col[2] == 'HO'
101
- descrips = bt_descrip[col_name.to_sym][klass]
103
+ descrips = bt_descrip[col_name.to_sym][hm_klass]
102
104
  if (ho_id = (ho_id_col = descrips.last).map { |id_col| obj.send(id_col.to_sym) })&.first
103
- ho_txt = klass.brick_descrip(obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, ho_id_col)
104
- out << link_to(ho_txt, send("#{klass.base_class._brick_index(:singular)}_path".to_sym, ho_id))
105
+ ho_txt = hm_klass.brick_descrip(obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, ho_id_col)
106
+ out << link_to(ho_txt, send("#{hm_klass.base_class._brick_index(:singular)}_path".to_sym, ho_id))
105
107
  end
106
108
  else
107
109
  if (ct = obj.send(hms_col[1].to_sym)&.to_i)&.positive?
108
110
  out << "#{link_to("#{ct || 'View'} #{hms_col.first}",
109
- send("#{klass._brick_index}_path".to_sym,
111
+ send("#{hm_klass._brick_index}_path".to_sym,
110
112
  hms_col[2].each_with_object({}) { |v, s| s[v.first] = v.last.is_a?(String) ? v.last : obj.send(v.last) })
111
113
  )}\n"
112
114
  end
@@ -137,51 +139,6 @@ module Brick::Rails::FormTags
137
139
  out.html_safe
138
140
  end # brick_grid
139
141
 
140
- # Recursively render UL and LI to choose columns to include
141
- def pick_api(model, is_last = nil, visited = [])
142
- out = +''
143
- if visited.empty?
144
- out << '<span id="apiToggle">API buffet</span><div id="apiBuffet" style="display: none;">'
145
- out << '<textarea id="apiCode" cols="100"></textarea>'
146
- out << '<input type="button" id="apiRender" value="Render">'
147
- end
148
- out << "\n#{indent = ' ' * visited.length}<ul>"
149
- model.column_names.each { |col_name| out << "\n#{indent} <li class=\"apiColName\" x-nm=\"#{visited.map { |v| "#{v.last}." }.join}#{col_name}\">#{col_name}</li>" }
150
- unless is_last || visited.length == 2
151
- model.reflect_on_all_associations.each_with_object({}) do |v, s|
152
- next if v.macro == :has_many || v.polymorphic?
153
-
154
- out << "\n#{indent} <li><b>#{v.name}</b>#{pick_api(v.klass, visited.map(&:first).include?(v.klass),
155
- visited.dup << [v.klass, v.name]
156
- )}"
157
- out << "\n#{indent} </li>"
158
- s[v.name] = nil
159
- end
160
- end
161
- out << "\n#{indent}</ul>"
162
- if visited.empty?
163
- out << '</div><script>[... document.getElementsByClassName("apiColName")].forEach(function (li) {li.addEventListener("click", apiColClick);});'
164
- out << "
165
- var colList = [];
166
- var apiBuffet = document.getElementById(\"apiBuffet\");
167
- var apiCode = document.getElementById(\"apiCode\");
168
- function apiColClick(evt) {
169
- apiCode.innerHTML += this.getAttribute(\"x-nm\") + \",\\n\";
170
- }
171
- document.getElementById(\"apiToggle\").addEventListener(\"click\", function () {
172
- apiBuffet.style.display = (apiBuffet.style.display === \"block\" ? \"none\" : \"block\");
173
- });
174
- // Cheap and cheerful way to render a list of columns just for the demo
175
- document.getElementById(\"apiRender\").addEventListener(\"click\", function () {
176
- var changecolList = apiCode.innerHTML.substring(0, apiCode.innerHTML.length - 2);
177
- location.href = changeout(location.href, \"_brick_api\", changecolList);
178
- });
179
- </script>
180
- "
181
- end
182
- out
183
- end
184
-
185
142
  def link_to_brick(*args, **kwargs)
186
143
  return unless ::Brick.config.mode == :on
187
144
 
@@ -231,13 +188,14 @@ document.getElementById(\"apiRender\").addEventListener(\"click\", function () {
231
188
  end
232
189
  end
233
190
  filter = "?#{filter_parts.join('&')}" if filter_parts.present?
191
+ app_routes = Rails.application.routes # In case we're operating in another engine, reference the application since Brick routes are placed there.
234
192
  if (klass_or_obj&.is_a?(Class) && klass_or_obj < ActiveRecord::Base) ||
235
193
  (klass_or_obj&.is_a?(ActiveRecord::Base) && klass_or_obj.new_record? && (klass_or_obj = klass_or_obj.class))
236
- path = (proc = kwargs[:index_proc]) ? proc.call(klass_or_obj) : "#{send("#{klass_or_obj.base_class._brick_index}_path")}#{filter}"
194
+ path = (proc = kwargs[:index_proc]) ? proc.call(klass_or_obj) : "#{app_routes.path_for(controller: klass_or_obj.base_class._brick_index, action: :index)}#{filter}"
237
195
  lt_args = [text || "Index for #{klass_or_obj.name.pluralize}", path]
238
196
  else
239
197
  # If there are multiple incoming parameters then last one is probably the actual ID, and first few might be some nested tree of stuff leading up to it
240
- path = (proc = kwargs[:show_proc]) ? proc.call(klass_or_obj) : "#{send("#{klass_or_obj.class.base_class._brick_index(:singular)}_path", klass_or_obj)}#{filter}"
198
+ path = (proc = kwargs[:show_proc]) ? proc.call(klass_or_obj) : "#{app_routes.path_for(controller: klass_or_obj.class.base_class._brick_index, action: :show, id: klass_or_obj)}#{filter}"
241
199
  lt_args = [text || "Show this #{klass_or_obj.class.name}", path]
242
200
  end
243
201
  link_to(*lt_args, **kwargs)
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 99
8
+ TINY = 101
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
@@ -640,50 +640,62 @@ In config/initializers/brick.rb appropriate entries would look something like:
640
640
  table_class_length = 38 # Length of "Classes that can be built from tables:"
641
641
  view_class_length = 37 # Length of "Classes that can be built from views:"
642
642
 
643
- brick_routes_create = lambda do |schema_name, controller_name, v, options|
643
+ brick_routes_create = lambda do |schema_name, res_name, options|
644
644
  if schema_name # && !Object.const_defined('Apartment')
645
645
  send(:namespace, schema_name) do
646
- send(:resources, v[:resource].to_sym, **options)
646
+ send(:resources, res_name.to_sym, **options)
647
647
  end
648
648
  else
649
- send(:resources, v[:resource].to_sym, **options)
649
+ send(:resources, res_name.to_sym, **options)
650
650
  end
651
651
  end
652
652
 
653
653
  # %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
654
654
  # If auto-controllers and auto-models are both enabled then this makes sense:
655
655
  controller_prefix = (path_prefix ? "#{path_prefix}/" : '')
656
+ sti_subclasses = ::Brick.config.sti_namespace_prefixes.each_with_object(Hash.new { |h, k| h[k] = [] }) do |v, s|
657
+ # Turn something like {"::Spouse"=>"Person", "::Friend"=>"Person"} into {"Person"=>["Spouse", "Friend"]}
658
+ s[v.last] << v.first[2..-1] unless v.first.end_with?('::')
659
+ end
656
660
  ::Brick.relations.each do |k, v|
657
- unless !(controller_name = v.fetch(:resource, nil)&.pluralize) || existing_controllers.key?(controller_name)
658
- options = {}
659
- options[:only] = [:index, :show] if v.key?(:isView)
660
- # First do the API routes
661
- full_resource = nil
662
- if (schema_name = v.fetch(:schema, nil))
663
- full_resource = "#{schema_name}/#{v[:resource]}"
664
- send(:get, "#{::Brick.api_root}#{full_resource}", { to: "#{controller_prefix}#{schema_name}/#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
661
+ next if !(controller_name = v.fetch(:resource, nil)&.pluralize) || existing_controllers.key?(controller_name)
662
+
663
+ options = {}
664
+ options[:only] = [:index, :show] if v.key?(:isView)
665
+ # First do the API routes
666
+ full_resource = nil
667
+ if (schema_name = v.fetch(:schema, nil))
668
+ full_resource = "#{schema_name}/#{v[:resource]}"
669
+ send(:get, "#{::Brick.api_root}#{full_resource}", { to: "#{controller_prefix}#{schema_name}/#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
670
+ else
671
+ # Normally goes to something like: /api/v1/employees
672
+ send(:get, "#{::Brick.api_root}#{v[:resource]}", { to: "#{controller_prefix}#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
673
+ end
674
+
675
+ # Track routes being built
676
+ if (class_name = v.fetch(:class_name, nil))
677
+ if v.key?(:isView)
678
+ view_class_length = class_name.length if class_name.length > view_class_length
679
+ views
665
680
  else
666
- # Normally goes to something like: /api/v1/employees
667
- send(:get, "#{::Brick.api_root}#{v[:resource]}", { to: "#{controller_prefix}#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
668
- end
669
- # Now the normal routes
670
- if path_prefix
671
- # Was: send(:scope, path: path_prefix) do
672
- send(:namespace, path_prefix) do
673
- brick_routes_create.call(schema_name, controller_name, v, options)
681
+ table_class_length = class_name.length if class_name.length > table_class_length
682
+ tables
683
+ end << [class_name, full_resource || v[:resource]]
684
+ end
685
+
686
+ # Now the normal routes
687
+ if path_prefix
688
+ # Was: send(:scope, path: path_prefix) do
689
+ send(:namespace, path_prefix) do
690
+ brick_routes_create.call(schema_name, v[:resource], options)
691
+ sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
692
+ brick_routes_create.call(schema_name, sc.underscore.tr('/', '_').pluralize, options)
674
693
  end
675
- else
676
- brick_routes_create.call(schema_name, controller_name, v, options)
677
694
  end
678
-
679
- if (class_name = v.fetch(:class_name, nil))
680
- if v.key?(:isView)
681
- view_class_length = class_name.length if class_name.length > view_class_length
682
- views
683
- else
684
- table_class_length = class_name.length if class_name.length > table_class_length
685
- tables
686
- end << [class_name, full_resource || v[:resource]]
695
+ else
696
+ brick_routes_create.call(schema_name, v[:resource], options)
697
+ sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
698
+ brick_routes_create.call(schema_name, sc.underscore.tr('/', '_').pluralize, options)
687
699
  end
688
700
  end
689
701
  end
@@ -1322,24 +1334,26 @@ module ActiveRecord
1322
1334
  private
1323
1335
 
1324
1336
  # %%% Pretty much have to flat-out replace this guy (I think anyway)
1325
- # Good with Rails 5.24 and 7 on this
1326
- def build(associations, base_klass, root = nil, path = '')
1327
- root ||= associations
1328
- associations.map do |name, right|
1329
- reflection = find_reflection base_klass, name
1330
- reflection.check_validity!
1331
- reflection.check_eager_loadable!
1332
-
1333
- if reflection.polymorphic?
1334
- raise EagerLoadPolymorphicError.new(reflection)
1335
- end
1337
+ # Good with Rails 5.24 through 7 on this
1338
+ # Ransack gem includes Polyamorous which replaces #build in a different way (which we handle below)
1339
+ unless Gem::Specification.all_names.any? { |g| g.start_with?('ransack-') }
1340
+ def build(associations, base_klass, root = nil, path = '')
1341
+ root ||= associations
1342
+ associations.map do |name, right|
1343
+ reflection = find_reflection base_klass, name
1344
+ reflection.check_validity!
1345
+ reflection.check_eager_loadable!
1346
+
1347
+ if reflection.polymorphic?
1348
+ raise EagerLoadPolymorphicError.new(reflection)
1349
+ end
1336
1350
 
1337
- # %%% The path
1338
- link_path = path.blank? ? name.to_s : path + ".#{name}"
1339
- ja = JoinAssociation.new(reflection, build(right, reflection.klass, root, link_path))
1340
- ja.instance_variable_set(:@link_path, link_path) # Make note on the JoinAssociation of its AR path
1341
- ja.instance_variable_set(:@assocs, root)
1342
- ja
1351
+ link_path = path.blank? ? name.to_s : path + ".#{name}"
1352
+ ja = JoinAssociation.new(reflection, build(right, reflection.klass, root, link_path))
1353
+ ja.instance_variable_set(:@link_path, link_path) # Make note on the JoinAssociation of its AR path
1354
+ ja.instance_variable_set(:@assocs, root)
1355
+ ja
1356
+ end
1343
1357
  end
1344
1358
  end
1345
1359
 
@@ -1348,10 +1362,9 @@ module ActiveRecord
1348
1362
  alias _brick_table_aliases_for table_aliases_for
1349
1363
  def table_aliases_for(parent, node)
1350
1364
  result = _brick_table_aliases_for(parent, node)
1351
-
1352
1365
  # Capture the table alias name that was chosen
1353
- link_path = node.instance_variable_get(:@link_path)
1354
1366
  if (relation = node.instance_variable_get(:@assocs)&.instance_variable_get(:@relation))
1367
+ link_path = node.instance_variable_get(:@link_path)
1355
1368
  relation.brick_links[link_path] = result.first.table_alias || result.first.table_name
1356
1369
  end
1357
1370
  result
@@ -1360,11 +1373,14 @@ module ActiveRecord
1360
1373
  alias _brick_make_constraints make_constraints
1361
1374
  def make_constraints(parent, child, join_type)
1362
1375
  result = _brick_make_constraints(parent, child, join_type)
1363
-
1364
1376
  # Capture the table alias name that was chosen
1365
- link_path = child.instance_variable_get(:@link_path)
1366
1377
  if (relation = child.instance_variable_get(:@assocs)&.instance_variable_get(:@relation))
1367
- relation.brick_links[link_path] = result.first.left.table_alias || result.first.left.table_name
1378
+ link_path = child.instance_variable_get(:@link_path)
1379
+ relation.brick_links[link_path] = if child.table.is_a?(Arel::Nodes::TableAlias)
1380
+ child.table.right
1381
+ else
1382
+ result.first&.left&.table_alias || child.table_name
1383
+ end
1368
1384
  end
1369
1385
  result
1370
1386
  end
@@ -1374,4 +1390,47 @@ module ActiveRecord
1374
1390
  end
1375
1391
  end
1376
1392
 
1393
+ # Now the Ransack Polyamorous version of #build
1394
+ if Gem::Specification.all_names.any? { |g| g.start_with?('ransack-') }
1395
+ require "polyamorous/activerecord_#{::ActiveRecord::VERSION::STRING[0, 3]}_ruby_2/join_dependency"
1396
+ module Polyamorous::JoinDependencyExtensions
1397
+ def build(associations, base_klass, root = nil, path = '')
1398
+ root ||= associations
1399
+ puts associations.map(&:first)
1400
+
1401
+ associations.map do |name, right|
1402
+ link_path = path.blank? ? name.to_s : path + ".#{name}"
1403
+ ja = if name.is_a? ::Polyamorous::Join
1404
+ reflection = find_reflection base_klass, name.name
1405
+ reflection.check_validity!
1406
+ reflection.check_eager_loadable!
1407
+
1408
+ klass = if reflection.polymorphic?
1409
+ name.klass || base_klass
1410
+ else
1411
+ reflection.klass
1412
+ end
1413
+ ::ActiveRecord::Associations::JoinDependency::JoinAssociation.new(
1414
+ reflection, build(right, klass, root, link_path), name.klass, name.type
1415
+ )
1416
+ else
1417
+ reflection = find_reflection base_klass, name
1418
+ reflection.check_validity!
1419
+ reflection.check_eager_loadable!
1420
+
1421
+ if reflection.polymorphic?
1422
+ raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
1423
+ end
1424
+ ::ActiveRecord::Associations::JoinDependency::JoinAssociation.new(
1425
+ reflection, build(right, reflection.klass, root, link_path)
1426
+ )
1427
+ end
1428
+ ja.instance_variable_set(:@link_path, link_path) # Make note on the JoinAssociation of its AR path
1429
+ ja.instance_variable_set(:@assocs, root)
1430
+ ja
1431
+ end
1432
+ end
1433
+ end
1434
+ end
1435
+
1377
1436
  require 'brick/extensions'
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.99
4
+ version: 1.0.101
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-12-06 00:00:00.000000000 Z
11
+ date: 2022-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord