brick 1.0.212 → 1.0.214

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e954e525ea7a17eea2b33a125283f8af47a7bad1e457780815a8f53e06ed8fc
4
- data.tar.gz: 9ded8bfa75d407294eb3e34895d1fc36ffa17767b9cbdeee0c961d6d9e821653
3
+ metadata.gz: afed5350a655b7eca570609ff4d15f998ecfe746345c4771b7eb910274df14ae
4
+ data.tar.gz: 45d5cccb07240fe0995bf812dc4b0cc34d21edd1bb0ab3cf212cd255c8a0f6a4
5
5
  SHA512:
6
- metadata.gz: 6c2d33f2a6247cc001071929731d2b2543d2b91ba0e8c22afeda7d9fa40e37ae9c3957d4046f8157a34b1549dca8e542de97a58f22b25a312532eed775d3989c
7
- data.tar.gz: 4e63b7986eb7b683421ef92d00fe61a3970ed0d24fb57403a89d05c114760ad00310e6c635d66bc0388e5ca26b6c6fd2061574c83e1f70b60f90baa22babcb92
6
+ metadata.gz: ca420002527953167f6ba1112e43d197bea259bf360c89ff92a01c9753c53da1b7060c15cd021c27e6106c4e77e7d3c30f589c89509ae21c2a2fe69d42e6f079
7
+ data.tar.gz: e6b1fd3745b5c69bfaa9486a4158b7a8efc03c7c43aa884a13f630e9b023e40e0893e1862e6e7fee545049efb46c5b3686cd4f05363d19eb3dfcf0d8f6b1cef3
@@ -71,15 +71,24 @@ module ActiveRecord
71
71
  end
72
72
 
73
73
  def real_model(params)
74
- if params && (sub_model = params.fetch(type_col = inheritance_column, nil))
75
- sub_model = sub_model.first if sub_model.is_a?(Array) # Support the params style that gets returned from #_brick_querying
74
+ if params && ((sub_name = params.fetch(inheritance_column, nil)).present? ||
75
+ (sub_name = params[name.underscore]&.fetch(inheritance_column, nil)))
76
+ sub_name = sub_name.first if sub_name.is_a?(Array) # Support the params style that gets returned from #_brick_querying
76
77
  # Make sure the chosen model is really the same or a subclass of this model
77
- (possible_model = sub_model.constantize) <= self ? possible_model : self
78
+ return self if sub_name.blank?
79
+
80
+ (possible_model = sub_name.constantize) <= self ? possible_model : self
78
81
  else
79
82
  self
80
83
  end
81
84
  end
82
85
 
86
+ # Accommodate STI
87
+ def real_singular(params)
88
+ real_model = real_model(params)
89
+ [real_model, real_model.name.underscore.split('/').last]
90
+ end
91
+
83
92
  def json_column?(col)
84
93
  col.type == :json || ::Brick.config.json_columns[table_name]&.include?(col.name) ||
85
94
  (
@@ -230,11 +239,7 @@ module ActiveRecord
230
239
  end
231
240
  if first_parts
232
241
  if (parts = prefix + first_parts + [parts[-1]]).length > 1 && klass
233
- unless is_polymorphic
234
- s = join_array
235
- parts[0..-3].each { |v| s = s[v.to_sym] }
236
- s[parts[-2]] = nil # unless parts[-2].empty? # Using []= will "hydrate" any missing part(s) in our whole series
237
- end
242
+ join_array.add_parts(parts) unless is_polymorphic
238
243
  translations[parts[0..-2].join('.')] = klass
239
244
  end
240
245
  if klass&.column_names.exclude?(parts.last) &&
@@ -538,7 +543,11 @@ module ActiveRecord
538
543
  end
539
544
 
540
545
  def self.brick_where(*args)
541
- (relation = all).brick_where(*args)
546
+ all.brick_where(*args)
547
+ end
548
+
549
+ def self.brick_group(*args, withhold_ids: true, **kwargs)
550
+ all.brick_group(*args, withhold_ids: withhold_ids, **kwargs)
542
551
  end
543
552
 
544
553
  private
@@ -612,27 +621,36 @@ module ActiveRecord
612
621
  pluck(selects)
613
622
  end
614
623
 
615
- def _brick_querying(*args, withhold_ids: nil, params: {}, order_by: nil, translations: {},
624
+ def brick_group(*args, **kwargs)
625
+ grouping = args[0].is_a?(Array) ? args[0] : args
626
+ _brick_querying(select_values.frozen? ? select_values.dup : select_values,
627
+ grouping: grouping, **kwargs)
628
+ self
629
+ end
630
+
631
+ def _brick_querying(*args, grouping: nil, withhold_ids: nil, params: {}, order_by: nil, translations: {},
616
632
  join_array: ::Brick::JoinArray.new,
617
633
  cust_col_override: nil,
618
634
  brick_col_names: nil)
619
635
  selects = args[0].is_a?(Array) ? args[0] : args
620
- if selects.present? && cust_col_override.nil? # See if there's any fancy ones in the select list
621
- idx = 0
622
- while idx < selects.length
623
- v = selects[idx]
624
- if v.is_a?(String) && v.index('.')
625
- # No prefixes and not polymorphic
626
- pieces = self.brick_parse_dsl(join_array, [], translations, false, dsl = "[#{v}]")
627
- (cust_col_override ||= {})[v.tr('.', '_').to_sym] = [pieces, dsl, true]
628
- selects.delete_at(idx)
629
- else
630
- idx += 1
636
+ unless cust_col_override
637
+ if selects.present? # See if there's any fancy ones in the select list
638
+ idx = 0
639
+ while idx < selects.length
640
+ v = selects[idx]
641
+ if v.is_a?(String) && v.index('.')
642
+ # No prefixes and not polymorphic
643
+ pieces = self.brick_parse_dsl(join_array, [], translations, false, dsl = "[#{v}]")
644
+ (cust_col_override ||= {})[v.tr('.', '_').to_sym] = [pieces, dsl, true]
645
+ selects.delete_at(idx)
646
+ else
647
+ idx += 1
648
+ end
631
649
  end
650
+ elsif selects.is_a?(Hash) && params.empty? # Make sense of things if they've passed in only params
651
+ params = selects
652
+ selects = []
632
653
  end
633
- elsif selects.is_a?(Hash) && params.empty? && cust_col_override.nil? # Make sense of things if they've passed in only params
634
- params = selects
635
- selects = []
636
654
  end
637
655
  is_add_bts = is_add_hms = !cust_col_override
638
656
 
@@ -710,6 +728,14 @@ module ActiveRecord
710
728
  end
711
729
  end
712
730
 
731
+ # Establish necessary JOINs for any custom GROUP BY columns
732
+ grouping&.each do |group_item|
733
+ # JOIN in all the same ways as the pathing describes
734
+ if group_item.is_a?(String) && (ref_parts = group_item.split('.')).length > 1
735
+ join_array.add_parts(ref_parts)
736
+ end
737
+ end
738
+
713
739
  if join_array.present?
714
740
  if ActiveRecord.version < Gem::Version.new('4.2')
715
741
  self.joins_values += join_array # Same as: joins!(join_array)
@@ -725,7 +751,9 @@ module ActiveRecord
725
751
 
726
752
  # CUSTOM COLUMNS
727
753
  # ==============
728
- (cust_col_override || (!withhold_ids && klass._br_cust_cols))&.each do |k, cc|
754
+ cust_cols = cust_col_override
755
+ cust_cols ||= klass._br_cust_cols unless withhold_ids
756
+ cust_cols&.each do |k, cc|
729
757
  brick_links # Intentionally create a relation duplicate
730
758
  if @_brick_rel_dup.respond_to?(k) # Name already taken?
731
759
  # %%% Use ensure_unique here in this kind of fashion:
@@ -753,7 +781,7 @@ module ActiveRecord
753
781
  col_alias = "#{col_prefix}#{k}__#{table_name.tr('.', '_')}_#{cc_part.first}"
754
782
  elsif brick_col_names ||
755
783
  used_col_aliases.key?(col_alias = k.to_s) # This sets a simpler custom column name if possible
756
- while cc_part_idx > 0 &&
784
+ while cc_part_idx >= 0 &&
757
785
  (col_alias = "#{col_prefix}#{k}__#{cc_part[cc_part_idx..-1].map(&:to_s).join('__').tr('.', '_')}") &&
758
786
  used_col_aliases.key?(col_alias)
759
787
  cc_part_idx -= 1
@@ -988,6 +1016,20 @@ JOIN (SELECT #{hm_selects.map { |s| _br_quoted_name("#{'br_t0.' if from_clause}#
988
1016
  klass._br_hm_counts.delete(n)
989
1017
  end
990
1018
 
1019
+ # Rewrite the group values to reference table and correlation names built out by AREL
1020
+ if grouping
1021
+ group2 = (gvgu = (group_values + grouping).uniq).each_with_object([]) do |v, s|
1022
+ if v.is_a?(Symbol) || (v_parts = v.split('.')).length == 1
1023
+ s << v
1024
+ elsif (tbl_name = brick_links[v_parts[0..-2].join('.')]&.split('.')&.last)
1025
+ s << "#{tbl_name}.#{v_parts.last}"
1026
+ else
1027
+ s << v
1028
+ end
1029
+ end
1030
+ group!(*group2)
1031
+ end
1032
+
991
1033
  unless wheres.empty?
992
1034
  # Rewrite the wheres to reference table and correlation names built out by AREL
993
1035
  where_nots = {}
@@ -998,7 +1040,7 @@ JOIN (SELECT #{hm_selects.map { |s| _br_quoted_name("#{'br_t0.' if from_clause}#
998
1040
  if (v_parts = v.first.split('.')).length == 1
999
1041
  (is_not ? where_nots : s)[v.first] = v.last
1000
1042
  else
1001
- tbl_name = brick_links[v_parts.first].split('.').last
1043
+ tbl_name = brick_links[v_parts[0..-2].join('.')].split('.').last
1002
1044
  (is_not ? where_nots : s)["#{tbl_name}.#{v_parts.last}"] = v.last
1003
1045
  end
1004
1046
  end
@@ -1095,13 +1137,9 @@ JOIN (SELECT #{hm_selects.map { |s| _br_quoted_name("#{'br_t0.' if from_clause}#
1095
1137
  # && joins_values.empty? # Make sure we don't step on any toes if they've already specified JOIN things
1096
1138
  ja = nil
1097
1139
  opts.each do |k, v|
1140
+ # JOIN in all the same ways as the pathing describes
1098
1141
  if k.is_a?(String) && (ref_parts = k.split('.')).length > 1
1099
- # JOIN in all the same ways as the pathing describes
1100
- linkage = (ja ||= ::Brick::JoinArray.new)
1101
- ref_parts[0..-3].each do |prefix_part|
1102
- linkage = linkage[prefix_part.to_sym]
1103
- end
1104
- linkage[ref_parts[-2].to_sym] = nil
1142
+ (ja ||= ::Brick::JoinArray.new).add_parts(ref_parts)
1105
1143
  end
1106
1144
  end
1107
1145
  if ja&.present?
@@ -1709,7 +1747,9 @@ class Object
1709
1747
  end
1710
1748
  # Accommodate singular or camel-cased table names such as "order_detail" or "OrderDetails"
1711
1749
  code << " self.table_name = '#{self.table_name = matching}'\n" if (inheritable_name || model_name).underscore.pluralize != matching
1712
- if (inh_col = ::Brick.config.sti_type_column.find { |_k, v| v.include?(matching) }&.first)
1750
+
1751
+ if (inh_col = relation.fetch(:sti_col, nil) ||
1752
+ ::Brick.config.sti_type_column.find { |_k, v| v.include?(matching) }&.first)
1713
1753
  new_model_class.inheritance_column = inh_col
1714
1754
  code << " self.inheritance_column = '#{inh_col}'\n"
1715
1755
  end
@@ -1760,11 +1800,6 @@ class Object
1760
1800
  end
1761
1801
  end
1762
1802
 
1763
- if (sti_col = relation.fetch(:sti_col, nil))
1764
- new_model_class.send(:'inheritance_column=', sti_col)
1765
- code << " self.inheritance_column = #{sti_col.inspect}\n"
1766
- end
1767
-
1768
1803
  unless is_sti
1769
1804
  fks = relation[:fks] || {}
1770
1805
  # Do the bulk of the has_many / belongs_to processing, and store details about HMT so they can be done at the very last
@@ -2418,13 +2453,15 @@ class Object
2418
2453
  code << " @#{plural_table_name}._brick_querying(params, brick_col_names: true)\n"
2419
2454
  code << " end\n"
2420
2455
 
2421
- is_pk_string = nil
2456
+ # ----------------------------------------------------------------------------------
2457
+
2422
2458
  if pk.present?
2423
2459
  code << " def show\n"
2424
2460
  code << " #{find_by_name = "find_#{singular_table_name}"}\n"
2425
2461
  code << " end\n"
2426
2462
  self.define_method :show do
2427
2463
  _schema, @_is_show_schema_list = ::Brick.set_db_schema(params)
2464
+ _, singular_table_name = model.real_singular(params)
2428
2465
  instance_variable_set("@#{singular_table_name}".to_sym, find_obj)
2429
2466
  add_csp_hash("'unsafe-inline'")
2430
2467
  end
@@ -2443,7 +2480,8 @@ class Object
2443
2480
  send(params_name_sym)
2444
2481
  rescue
2445
2482
  end
2446
- new_params ||= model.attribute_names.each_with_object({}) do |a, s|
2483
+ real_model, singular_table_name = model.real_singular(params)
2484
+ new_params ||= real_model.attribute_names.each_with_object({}) do |a, s|
2447
2485
  if (val = params["__#{a}"])
2448
2486
  # val = case new_obj.class.column_for_attribute(a).type
2449
2487
  # when :datetime, :date, :time, :timestamp
@@ -2454,7 +2492,7 @@ class Object
2454
2492
  s[a] = val
2455
2493
  end
2456
2494
  end
2457
- if (new_obj = model.new(new_params)).respond_to?(:serializable_hash)
2495
+ if (new_obj = real_model.new(new_params)).respond_to?(:serializable_hash)
2458
2496
  # Convert any Filename objects with nil into an empty string so that #encode can be called on them
2459
2497
  new_obj.serializable_hash.each do |k, v|
2460
2498
  new_obj.send("#{k}=", ::ActiveStorage::Filename.new('')) if v.is_a?(::ActiveStorage::Filename) && !v.instance_variable_get(:@filename)
@@ -2480,12 +2518,20 @@ class Object
2480
2518
  end
2481
2519
  render json: { result: ::Brick.unexclude_column(table_name, col) }
2482
2520
  else
2483
- created_obj = model.send(:create, send(params_name_sym))
2484
- @_lookup_context.instance_variable_set(:@_brick_model, model)
2521
+ real_model = model.real_model(params)
2522
+ singular_table_name = real_model.name.underscore.split('/').last
2523
+ created_obj = model.send(:new, send(params_name_sym))
2524
+ if created_obj.respond_to?(inh_col = model.inheritance_column) && created_obj.send(inh_col) == ''
2525
+ created_obj.send("#{inh_col}=", model.name)
2526
+ end
2527
+ created_obj.save
2528
+ @_lookup_context.instance_variable_set(:@_brick_model, real_model)
2485
2529
  if created_obj.errors.empty?
2530
+ instance_variable_set("@#{singular_table_name}".to_sym, created_obj)
2486
2531
  index
2487
2532
  render :index
2488
2533
  else # Surface errors to the user in a flash message
2534
+ instance_variable_set("@#{singular_table_name}".to_sym, created_obj)
2489
2535
  flash.now.alert = (created_obj.errors.errors.map { |err| "<b>#{err.attribute}</b> #{err.message}" }.join(', '))
2490
2536
  new
2491
2537
  render :new
@@ -2504,6 +2550,7 @@ class Object
2504
2550
  code << " end\n"
2505
2551
  self.define_method :edit do
2506
2552
  _schema, @_is_show_schema_list = ::Brick.set_db_schema(params)
2553
+ _, singular_table_name = model.real_singular(params)
2507
2554
  instance_variable_set("@#{singular_table_name}".to_sym, find_obj)
2508
2555
  add_csp_hash
2509
2556
  end
@@ -2531,6 +2578,7 @@ class Object
2531
2578
  # return
2532
2579
  end
2533
2580
 
2581
+ _, singular_table_name = model.real_singular(params)
2534
2582
  instance_variable_set("@#{singular_table_name}".to_sym, (obj = find_obj))
2535
2583
  upd_params = send(params_name_sym)
2536
2584
  json_overrides = ::Brick.config.json_columns&.fetch(table_name, nil)
@@ -2617,11 +2665,11 @@ class Object
2617
2665
  if is_need_params
2618
2666
  code << " def #{params_name}\n"
2619
2667
  permits_txt = model._brick_find_permits(model, permits = model._brick_all_fields(true))
2620
- code << " params.require(:#{require_name = model.name.underscore.tr('/', '_')
2668
+ code << " params.require(:#{model.base_class.name.underscore.tr('/', '_')
2621
2669
  }).permit(#{permits_txt.map(&:inspect).join(', ')})\n"
2622
2670
  code << " end\n"
2623
2671
  self.define_method(params_name) do
2624
- params.require(require_name.to_sym).permit(permits)
2672
+ params.require(model.base_class.name.underscore.tr('/', '_').to_sym).permit(permits)
2625
2673
  end
2626
2674
  private params_name
2627
2675
  # Get column names for params from relations[model.table_name][:cols].keys
@@ -2916,7 +2964,8 @@ module Brick
2916
2964
  else
2917
2965
  inverse_table = [primary_table] if polymorphic_class
2918
2966
  assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[2], assoc_name: bt_assoc_name, inverse_table: inverse_table || primary_table }
2919
- assoc_bt[:optional] = true if is_optional
2967
+ assoc_bt[:optional] = true if is_optional ||
2968
+ (is_optional.nil? && !relations[fk[1]][:cols][fk[2]][3])
2920
2969
  assoc_bt[:polymorphic] = [polymorphic_class] if polymorphic_class
2921
2970
  end
2922
2971
  if is_class
@@ -809,6 +809,7 @@ h1, h3 {
809
809
  background-repeat: no-repeat;
810
810
  background-size: 100% 100%;
811
811
  width: 28px;
812
+ height: 32px;
812
813
  cursor: pointer;
813
814
  }
814
815
  #mermaidErd {
@@ -942,10 +943,10 @@ table.shadow > tbody > tr {
942
943
  table tbody tr:nth-of-type(even) {
943
944
  background-color: #f3f3f3;
944
945
  }
945
- table tbody tr:nth-of-type(even) .col-sticky {
946
+ table tbody tr:nth-of-type(even) .alternating-gray {
946
947
  background-color: #fff;
947
948
  }
948
- table tbody tr:nth-of-type(odd) .col-sticky {
949
+ table tbody tr:nth-of-type(odd) .alternating-gray {
949
950
  background-color: #f3f3f3;
950
951
  }
951
952
 
@@ -991,6 +992,11 @@ a.big-arrow {
991
992
  background-color: red;
992
993
  color: white;
993
994
  }
995
+ .brick-note {
996
+ font-size: 0.7em;
997
+ color: #A0FFA0;
998
+ max-width: 0;
999
+ }
994
1000
 
995
1001
  #revertTemplate {
996
1002
  display: none;
@@ -1336,8 +1342,7 @@ end
1336
1342
  end}
1337
1343
  %>
1338
1344
 
1339
- #{"<hr><%= link_to(\"New #{new_path_name = "new_#{path_obj_name}_path"
1340
- obj_name}\", #{new_path_name}, { class: '__brick' }) if respond_to?(:#{new_path_name}) %>" unless @_brick_model.is_view?}
1345
+ #{"<hr><%= link_to_brick(model, new: true, class: '__brick') %>" unless @_brick_model.is_view?}
1341
1346
  #{script}
1342
1347
  </body>
1343
1348
  </html>
@@ -1437,7 +1442,7 @@ end
1437
1442
  <head>
1438
1443
  #{css}
1439
1444
  <title><%=
1440
- base_model = (model = (obj = @#{obj_name})&.class).base_class
1445
+ base_model = (model = (obj = @#{obj_name}).class).base_class
1441
1446
  see_all_path = send(\"#\{base_model._brick_index}_path\")
1442
1447
  #{(inh_col = @_brick_model.inheritance_column).present? &&
1443
1448
  " if obj.respond_to?(:#{inh_col}) && (model_name = @#{obj_name}.#{inh_col}) &&
@@ -1445,6 +1450,7 @@ end
1445
1450
  see_all_path << \"?#{inh_col}=#\{model_name}\"
1446
1451
  end
1447
1452
  model_name = base_model.name if model_name.is_a?(Numeric)"}
1453
+ model_name = nil if model_name == ''
1448
1454
  page_title = (\"#\{model_name ||= model.name}: #\{obj&.brick_descrip || controller_name}\")
1449
1455
  %></title>
1450
1456
  </head>
@@ -1510,8 +1516,12 @@ end
1510
1516
  # path_options = [obj.#{pk}]
1511
1517
  # path_options << { '_brick_schema': } if
1512
1518
  options = {}
1513
- path_helper = obj.new_record? ? #{model_name}._brick_index : #{model_name}._brick_index(:singular)
1514
- options[:url] = send(\"#\{path_helper}_path\".to_sym, obj) if ::Brick.config.path_prefix || (path_helper != obj.class.table_name)
1519
+ options[:url] = if obj.new_record?
1520
+ link_to_brick(obj.class, path_only: true) # Properly supports STI, but only works for :new
1521
+ else
1522
+ path_helper = obj.new_record? ? #{model_name}._brick_index : #{model_name}._brick_index(:singular)
1523
+ options[:url] = send(\"#\{path_helper}_path\".to_sym, obj) if ::Brick.config.path_prefix || (path_helper != obj.class.table_name)
1524
+ end
1515
1525
  %>
1516
1526
  <br><br>
1517
1527
 
@@ -6,70 +6,20 @@ module Brick::Rails::FormTags
6
6
  # When a relation is not provided, first see if one exists which matches the controller name or
7
7
  # something has turned up in the instance variables.
8
8
  relation ||= (instance_variable_get("@#{controller_name}".to_sym) || _brick_resource_from_iv)
9
+ klass = relation.klass
9
10
 
10
11
  nfc = Brick.config.sidescroll.fetch(relation.table_name, nil)&.fetch(:num_frozen_columns, nil) ||
11
12
  Brick.config.sidescroll.fetch(:num_frozen_columns, nil) ||
12
13
  0
13
14
 
14
- # HTML for brick_grid
15
- out = +"<div id=\"headerTopContainer\"><table id=\"headerTop\"></table>
16
- "
17
- klass = relation.klass
15
+ out = +''
18
16
  rel = ::Brick.relations&.fetch(relation.table_name, nil)
19
- unless show_header == false
20
- out << " <div id=\"headerTopAddNew\">
21
- <div id=\"headerButtonBox\">
22
- "
23
- unless show_row_count == false
24
- out << " <div id=\"rowCount\"></div>
25
- "
26
- end
27
- unless show_erd_button == false
28
- out << " <div id=\"imgErd\" title=\"Show ERD\"></div>
29
- "
30
- end
31
- if rel && show_in_app_button != false && (in_app = rel.fetch(:existing, nil)&.fetch(:index, nil))
32
- begin
33
- in_app = send("#{in_app}_path") if in_app.is_a?(Symbol)
34
- out << " <td title=\"Show in app\">#{link_to(::Brick::Rails::IN_APP.html_safe, in_app)}</td>
35
- "
36
- rescue ActionController::UrlGenerationError # Avoid snags like "No route matches {:action=>"index", :controller=>"categories/products"}, missing required keys: [:category_id]"
37
- end
38
- end
39
- if show_avo_button != false && Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) && klass.name.exclude?('::')
40
- out << "
41
- <td>#{link_to_brick(
42
- ::Brick::Rails::AVO_SVG.html_safe,
43
- { index_proc: Proc.new do |_avo_model, relation|
44
- path_helper = "resources_#{relation.fetch(:auto_prefixed_schema, nil)}#{klass.model_name.route_key}_path".to_sym
45
- ::Avo.railtie_routes_url_helpers.send(path_helper) if ::Avo.railtie_routes_url_helpers.respond_to?(path_helper)
46
- end,
47
- title: "#{klass.name} in Avo" }
48
- )}</td>
49
- "
50
- end
51
-
52
- if show_aa_button != false && Object.const_defined?('ActiveAdmin')
53
- ActiveAdmin.application.namespaces.names.each do |ns|
54
- out << "
55
- <td>#{link_to_brick(
56
- ::Brick::Rails::AA_PNG.html_safe,
57
- { index_proc: Proc.new do |aa_model, relation|
58
- path_helper = "#{ns}_#{relation.fetch(:auto_prefixed_schema, nil)}#{aa_model.model_name.route_key}_path".to_sym
59
- send(path_helper) if respond_to?(path_helper)
60
- end,
61
- title: "#{klass.name} in ActiveAdmin" }
62
- )}</td>
63
- "
64
- end
65
- end
66
- out << " </div>
67
- </div>
68
- "
17
+ if show_header != false
18
+ out << brick_header(rel, klass, show_row_count, show_erd_button, show_in_app_button, show_avo_button, show_aa_button)
69
19
  end
70
20
 
71
- out << "</div>
72
- <table id=\"#{table_name = relation.table_name.split('.').last}\" class=\"shadow\"#{ " x-num-frozen=\"#{nfc}\"" if nfc.positive? }>
21
+ # HTML for brick_grid
22
+ out << "<table id=\"#{table_name = relation.table_name.split('.').last}\" class=\"shadow\"#{ " x-num-frozen=\"#{nfc}\"" if nfc.positive? }>
73
23
  <thead><tr>"
74
24
  pk = klass.primary_key || []
75
25
  pk = [pk] unless pk.is_a?(Array)
@@ -145,13 +95,13 @@ module Brick::Rails::FormTags
145
95
  row_count = 0
146
96
  relation.each do |obj|
147
97
  out << "<tr>\n"
148
- out << "<td class=\"col-sticky\">#{link_to('⇛', send("#{klass._brick_index(:singular)}_path".to_sym,
98
+ out << "<td class=\"col-sticky alternating-gray\">#{link_to('⇛', send("#{klass._brick_index(:singular)}_path".to_sym,
149
99
  pk.map { |pk_part| obj.send(pk_part.to_sym) }), { class: 'big-arrow' })}</td>\n" if pk.present?
150
100
  sequence.each_with_index do |col_name, idx|
151
101
  val = obj.attributes[col_name]
152
102
  bt = bts[col_name] || composite_bt_names[col_name]
153
103
  out << '<td'
154
- (classes ||= []) << 'col-sticky' if idx < nfc
104
+ (classes ||= []) << 'col-sticky alternating-gray' if idx < nfc
155
105
  (classes ||= []) << 'dimmed' unless cols.key?(col_name) || (cust_col = cust_cols[col_name]) ||
156
106
  (col_name.is_a?(Symbol) && bts.key?(col_name)) # HOT
157
107
  (classes ||= []) << 'right' if val.is_a?(Numeric) && !bt
@@ -255,161 +205,14 @@ module Brick::Rails::FormTags
255
205
  out << '</tr>'
256
206
  row_count += 1
257
207
  end
258
- if rel && (total_row_count = rel.fetch(:rowcount, nil))
259
- total_row_count = total_row_count > row_count ? " (out of #{total_row_count})" : nil
260
- end
261
208
  out << " </tbody>
262
209
  </table>
263
- <script>
264
- var rowCount = document.getElementById(\"rowCount\");
265
- if (rowCount) rowCount.innerHTML = \"#{pluralize(row_count, "row")}#{total_row_count} &nbsp;\";
266
- </script>
267
- "
268
-
269
- # Javascript for brick_grid
270
- (@_brick_javascripts ||= {})[:grid_scripts] = "
271
- var #{table_name}HtColumns;
272
-
273
- // Snag first TR for sticky header
274
- var grid = document.getElementById(\"#{table_name}\");
275
- #{table_name}HtColumns = grid && [grid.getElementsByTagName(\"TR\")[0]];
276
- var headerTop = document.getElementById(\"headerTop\");
277
- var headerCols;
278
- if (grid) {
279
- // COLUMN HEADER AND TABLE CELL HIGHLIGHTING
280
- var gridHighHeader = null,
281
- gridHighCell = null;
282
- grid.addEventListener(\"mouseenter\", gridMove);
283
- grid.addEventListener(\"mousemove\", gridMove);
284
- grid.addEventListener(\"mouseleave\", function (evt) {
285
- if (gridHighCell) gridHighCell.classList.remove(\"highlight\");
286
- gridHighCell = null;
287
- if (gridHighHeader) gridHighHeader.classList.remove(\"highlight\");
288
- gridHighHeader = null;
289
- });
290
- function gridMove(evt) {
291
- var lastHighCell = gridHighCell;
292
- gridHighCell = document.elementFromPoint(evt.x, evt.y);
293
- while (gridHighCell && gridHighCell.tagName !== \"TD\" && gridHighCell.tagName !== \"TH\")
294
- gridHighCell = gridHighCell.parentElement;
295
- if (gridHighCell) {
296
- if (lastHighCell !== gridHighCell) {
297
- gridHighCell.classList.add(\"highlight\");
298
- if (lastHighCell) lastHighCell.classList.remove(\"highlight\");
299
- }
300
- var lastHighHeader = gridHighHeader;
301
- if ((gridHighHeader = headerCols[gridHighCell.cellIndex]) && lastHighHeader !== gridHighHeader) {
302
- if (gridHighHeader) gridHighHeader.classList.add(\"highlight\");
303
- if (lastHighHeader) lastHighHeader.classList.remove(\"highlight\");
304
- }
305
- }
306
- }
307
- // // LESS TOUCHY NAVIGATION BACK OR FORWARD IN HISTORY WHEN USING MOUSE WHEEL
308
- // grid.addEventListener(\"wheel\", function (evt) {
309
- // grid.scrollLeft += evt.deltaX;
310
- // document.body.scrollTop += (evt.deltaY * 0.6);
311
- // evt.preventDefault();
312
- // return false;
313
- // });
314
- }
315
- function setHeaderSizes() {
316
- if (grid.clientWidth > window.outerWidth)
317
- document.getElementById(\"titleBox\").style.width = grid.clientWidth;
318
- // console.log(\"start\");
319
- // See if the headerTop is already populated
320
- // %%% Grab the TRs from headerTop, clear it out, do this stuff, add them back
321
- headerTop.innerHTML = \"\"; // %%% Would love to not have to clear it out like this every time! (Currently doing this to support resize events.)
322
- var isEmpty = headerTop.childElementCount === 0;
323
- var numFixed = parseInt(grid.getAttribute(\"x-num-frozen\")) || 0;
324
- var fixedColLefts = [0];
325
-
326
- // Set up proper sizings of sticky column header
327
- var node;
328
- for (var j = 0; j < #{table_name}HtColumns.length; ++j) {
329
- var row = #{table_name}HtColumns[j];
330
- var tr = isEmpty ? document.createElement(\"TR\") : headerTop.childNodes[j];
331
- tr.innerHTML = row.innerHTML.trim();
332
- var curLeft = 0.0;
333
- // Match up widths from the original column headers
334
- for (var i = 0; i < row.childNodes.length; ++i) {
335
- node = row.childNodes[i];
336
- if (node.nodeType === 1) {
337
- var th = tr.childNodes[i];
338
- th.style.minWidth = th.style.maxWidth = getComputedStyle(node).width;
339
- // Add \"left: __px\" style to the fixed-width column THs
340
- if (i <= numFixed) {
341
- th.style.position = \"sticky\";
342
- th.style.backgroundColor = \"#008061\";
343
- th.style.zIndex = \"1\";
344
- th.style.left = curLeft + \"px\";
345
- fixedColLefts.push(curLeft += node.clientWidth);
346
- }
347
- if (#{pk&.present? ? 'i > 0' : 'true'}) {
348
- // Add <span> at the end
349
- var span = document.createElement(\"SPAN\");
350
- span.className = \"exclude\";
351
- span.innerHTML = \"X\";
352
- span.addEventListener(\"click\", function (e) {
353
- e.stopPropagation();
354
- doFetch(\"POST\", {_brick_exclude: this.parentElement.getAttribute(\"x-order\")});
355
- });
356
- th.appendChild(span);
357
- }
358
- }
359
- }
360
- headerCols = tr.childNodes;
361
- if (isEmpty) headerTop.appendChild(tr);
362
- }
363
- // Add \"left: __px\" style to all fixed-width column TDs
364
- [...grid.children[1].children].forEach(function (row) {
365
- for (var j = 1; j <= numFixed; ++j) {
366
- row.children[j].style.left = fixedColLefts[j] + 'px';
367
- }
368
- });
369
- grid.style.marginTop = \"-\" + getComputedStyle(headerTop).height;
370
- // console.log(\"end\");
371
- }
372
-
373
- if (headerTop) {
374
- onImagesLoaded(function() {
375
- setHeaderSizes();
376
- });
377
- window.addEventListener(\"resize\", function(event) {
378
- setHeaderSizes();
379
- }, true);#{
380
- if !klass.is_view? && respond_to?(new_path_name = "new_#{klass._brick_index(:singular)}_path")
381
- "
382
- var headerButtonBox = document.getElementById(\"headerButtonBox\");
383
- if (headerButtonBox) {
384
- var addNew = document.createElement(\"A\");
385
- addNew.id = \"addNew\";
386
- addNew.href = \"#{send(new_path_name)}\";
387
- addNew.title = \"New #{table_name.singularize}\";
388
- addNew.innerHTML = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"#fff\" d=\"M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z\"/></svg>';
389
- headerButtonBox.append(addNew);
390
- }
391
- "
392
- end}
393
- }
394
-
395
- function onImagesLoaded(event) {
396
- var images = document.getElementsByTagName(\"IMG\");
397
- var numLoaded = images.length;
398
- for (var i = 0; i < images.length; ++i) {
399
- if (images[i].complete)
400
- --numLoaded;
401
- else {
402
- images[i].addEventListener(\"load\", function() {
403
- if (--numLoaded <= 0)
404
- event();
405
- });
406
- }
407
- }
408
- if (numLoaded <= 0)
409
- event();
410
- }
411
210
  "
211
+ if rel && (total_row_count = rel.fetch(:rowcount, nil))
212
+ total_row_count = total_row_count > row_count ? " (out of #{total_row_count})" : nil
213
+ end
412
214
 
215
+ set_grid_javascript(klass, pk, show_new_button, row_count, total_row_count)
413
216
  out.html_safe
414
217
  end # brick_grid
415
218
 
@@ -418,7 +221,7 @@ function onImagesLoaded(event) {
418
221
  def brick_form_for(obj, options = {}, model = obj.class, bts = {}, pk = (obj.class.primary_key || []))
419
222
  pk = [pk] unless pk.is_a?(Array)
420
223
  pk.map!(&:to_s)
421
- form_for(obj.becomes(model), options) do |f|
224
+ form_for(obj.becomes(model.base_class), options) do |f|
422
225
  out = +'<table class="shadow">'
423
226
  has_fields = false
424
227
  # If it's a new record, set any default polymorphic types
@@ -525,28 +328,37 @@ function onImagesLoaded(event) {
525
328
 
526
329
  # ------------------------------------------
527
330
  # Our cool N:M checkbox constellation editor
528
- def brick_constellation(relation = nil, options = {}, x_axis: nil, y_axis: nil, bt_descrip: nil, bts: {})
331
+ def brick_constellation(relation = nil, options = {}, x_axis: nil, y_axis: nil, bt_descrip: nil, bts: {},
332
+ show_header: nil, show_erd_button: nil, show_in_app_button: nil, show_new_button: nil, show_avo_button: nil, show_aa_button: nil)
333
+ relation ||= (instance_variable_get("@#{controller_name}".to_sym) || _brick_resource_from_iv)
334
+ klass = relation.klass
529
335
  x_axis, x_list, y_axis, y_list, existing = _n_m_prep(relation, x_axis, y_axis)
530
336
 
337
+ out = +''
338
+ rel = ::Brick.relations&.fetch(relation.table_name, nil)
339
+ if show_header != false
340
+ out << brick_header(rel, klass, false, show_erd_button, show_in_app_button, show_avo_button, show_aa_button)
341
+ end
342
+
531
343
  # HTML for constellation
532
344
  prefix = options[:prefix]
533
- out = +"<form action=\"#{"#{prefix}/" if prefix}brick_constellation\">
345
+ out << "<form action=\"#{"#{prefix}/" if prefix}brick_constellation\">
534
346
  <table id=\"#{table_name = relation.table_name.split('.').last}\" class=\"shadow\">
535
- <thead><tr><td></td>
536
- "
347
+ <thead><tr><td class=\"brick-note\">Checkbox changes are saved immediately</td>"
537
348
  # Header row with X axis values
349
+ # (In order for grid highlighting to function, these TH elements must have no whitespace between them.
350
+ # In this way the Javascript headerCols array will be set properly.
538
351
  x_list.each do |x_item|
539
- out << " <th>#{x_item.first}</th>
540
- "
352
+ out << "<th>#{x_item.first}</th>"
541
353
  end
542
- out << " </tr></thead>
354
+ out << "</tr></thead>
543
355
  <tbody>
544
356
  "
545
- obj_path = "#{relation.klass._brick_index(:singular)}_path".to_sym
357
+ obj_path = "#{klass._brick_index(:singular)}_path".to_sym
546
358
  link_arrow = link_to('⇛', send(obj_path, '____'), { class: 'big-arrow' })
547
- pk_as_array = relation.klass._pk_as_array
359
+ pk_as_array = klass._pk_as_array
548
360
  y_list.each do |y_item|
549
- out << " <tr><th>#{y_item.first}</th>
361
+ out << " <tr><th class=\"col-sticky\">#{y_item.first}</th>
550
362
  "
551
363
  x_list.each do |x_item|
552
364
  checked = existing.find { |e| e[1] == x_item.last && e[2] == y_item.last }
@@ -570,7 +382,7 @@ function onImagesLoaded(event) {
570
382
  _this = this;
571
383
  if (this.checked) {
572
384
  var ids = this.value.split(\"_\");
573
- doFetch(\"POST\", {modelName: \"#{relation.klass.name}\",
385
+ doFetch(\"POST\", {modelName: \"#{klass.name}\",
574
386
  args: [#{x_axis[1].inspect}, ids[0], #{y_axis[1].inspect}, ids[1]],
575
387
  _brick_action: \"/#{prefix}brick_associate\"},
576
388
  function (p) { // If it returns successfully, create an <a> element
@@ -586,7 +398,7 @@ function onImagesLoaded(event) {
586
398
  }
587
399
  );
588
400
  } else if (nextSib = this.nextElementSibling) {
589
- doFetch(\"DELETE\", {modelName: \"#{relation.klass.name}\",
401
+ doFetch(\"DELETE\", {modelName: \"#{klass.name}\",
590
402
  id: this.getAttribute(\"x-id\"),
591
403
  _brick_action: \"/#{prefix}brick_associate\"},
592
404
  function (p) { // If it returns successfully, remove the an <a> element
@@ -599,6 +411,7 @@ function onImagesLoaded(event) {
599
411
  </script>
600
412
  </form>
601
413
  "
414
+ set_grid_javascript(klass, pk_as_array, false)
602
415
  out.html_safe
603
416
  end # brick_constellation
604
417
 
@@ -606,7 +419,9 @@ function onImagesLoaded(event) {
606
419
  # Our cool N:M bezier visualisation
607
420
  # (...... work in progress .......)
608
421
  def brick_bezier(relation = nil, options = {}, x_axis: nil, y_axis: nil, bt_descrip: nil, bts: {})
422
+ relation ||= (instance_variable_get("@#{controller_name}".to_sym) || _brick_resource_from_iv)
609
423
  x_axis, x_list, y_axis, y_list, existing = _n_m_prep(relation, x_axis, y_axis)
424
+ rel = ::Brick.relations&.fetch(relation.table_name, nil)
610
425
  # HTML for constellation
611
426
  # X axis (List on left side)
612
427
  out = +"<table id=\"#{x_axis.first}\" class=\"shadow\">
@@ -629,13 +444,222 @@ function onImagesLoaded(event) {
629
444
  out.html_safe
630
445
  end # brick_bezier
631
446
 
447
+ # ---------------------------------------------------------------------------------------------------------
448
+ def brick_header(rel, klass, show_row_count, show_erd_button, show_in_app_button, show_avo_button, show_aa_button)
449
+ out = +"<div id=\"headerTopContainer\"><table id=\"headerTop\"></table>
450
+ <div id=\"headerTopAddNew\">
451
+ <div id=\"headerButtonBox\">
452
+ "
453
+ unless show_row_count == false
454
+ out << " <div id=\"rowCount\"></div>
455
+ "
456
+ end
457
+ unless show_erd_button == false
458
+ out << " <div id=\"imgErd\" title=\"Show ERD\"></div>
459
+ "
460
+ end
461
+ if rel && show_in_app_button != false && (in_app = rel.fetch(:existing, nil)&.fetch(:index, nil))
462
+ begin
463
+ in_app = send("#{in_app}_path") if in_app.is_a?(Symbol)
464
+ out << " <td title=\"Show in app\">#{link_to(::Brick::Rails::IN_APP.html_safe, in_app)}</td>
465
+ "
466
+ rescue ActionController::UrlGenerationError # Avoid snags like "No route matches {:action=>"index", :controller=>"categories/products"}, missing required keys: [:category_id]"
467
+ end
468
+ end
469
+ if show_avo_button != false && Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) && klass.name.exclude?('::')
470
+ out << "
471
+ <td>#{link_to_brick(
472
+ ::Brick::Rails::AVO_SVG.html_safe,
473
+ { index_proc: Proc.new do |_avo_model, relation|
474
+ path_helper = "resources_#{relation.fetch(:auto_prefixed_schema, nil)}#{klass.model_name.route_key}_path".to_sym
475
+ ::Avo.railtie_routes_url_helpers.send(path_helper) if ::Avo.railtie_routes_url_helpers.respond_to?(path_helper)
476
+ end,
477
+ title: "#{klass.name} in Avo" }
478
+ )}</td>
479
+ "
480
+ end
481
+
482
+ if show_aa_button != false && Object.const_defined?('ActiveAdmin')
483
+ ActiveAdmin.application.namespaces.names.each do |ns|
484
+ out << "
485
+ <td>#{link_to_brick(
486
+ ::Brick::Rails::AA_PNG.html_safe,
487
+ { index_proc: Proc.new do |aa_model, relation|
488
+ path_helper = "#{ns}_#{relation.fetch(:auto_prefixed_schema, nil)}#{aa_model.model_name.route_key}_path".to_sym
489
+ send(path_helper) if respond_to?(path_helper)
490
+ end,
491
+ title: "#{rel[:class_name]} in ActiveAdmin" }
492
+ )}</td>
493
+ "
494
+ end
495
+ end
496
+ out << " </div>
497
+ </div>
498
+ </div>
499
+ "
500
+ out
501
+ end # brick_header
502
+
503
+ # -----------------------------------------------------------------------------------------------
504
+ def set_grid_javascript(klass, pk, show_new_button = nil, row_count = nil, total_row_count = nil)
505
+ table_name = klass.table_name.split('.').last
506
+
507
+ # Javascript for brick_grid and brick_constellation
508
+ grid_scripts = (@_brick_javascripts ||= {})[:grid_scripts] = +''
509
+
510
+ grid_scripts << "
511
+ // Plunk the row count in now that we know it
512
+ var rowCount = document.getElementById(\"rowCount\");
513
+ if (rowCount) rowCount.innerHTML = \"#{pluralize(row_count, "row")}#{total_row_count} &nbsp;\";
514
+ var #{table_name}HtColumns;
515
+ " unless row_count.nil?
516
+
517
+ grid_scripts << "
518
+ // Snag first TR for sticky header
519
+ var grid = document.getElementById(\"#{table_name}\");
520
+ #{table_name}HtColumns = grid && [grid.getElementsByTagName(\"TR\")[0]];
521
+ var headerTop = document.getElementById(\"headerTop\");
522
+ var headerCols;
523
+ if (grid) {
524
+ // COLUMN HEADER AND TABLE CELL HIGHLIGHTING
525
+ var gridHighHeader = null,
526
+ gridHighCell = null;
527
+ grid.addEventListener(\"mouseenter\", gridMove);
528
+ grid.addEventListener(\"mousemove\", gridMove);
529
+ grid.addEventListener(\"mouseleave\", function (evt) {
530
+ if (gridHighCell) gridHighCell.classList.remove(\"highlight\");
531
+ gridHighCell = null;
532
+ if (gridHighHeader) gridHighHeader.classList.remove(\"highlight\");
533
+ gridHighHeader = null;
534
+ });
535
+ function gridMove(evt) {
536
+ var lastHighCell = gridHighCell;
537
+ gridHighCell = document.elementFromPoint(evt.x, evt.y);
538
+ while (gridHighCell && gridHighCell.tagName !== \"TD\" && gridHighCell.tagName !== \"TH\")
539
+ gridHighCell = gridHighCell.parentElement;
540
+ if (gridHighCell) {
541
+ if (lastHighCell !== gridHighCell) {
542
+ gridHighCell.classList.add(\"highlight\");
543
+ if (lastHighCell) lastHighCell.classList.remove(\"highlight\");
544
+ }
545
+ var lastHighHeader = gridHighHeader;
546
+ if ((gridHighHeader = headerCols[gridHighCell.cellIndex]) && lastHighHeader !== gridHighHeader) {
547
+ if (gridHighHeader) gridHighHeader.classList.add(\"highlight\");
548
+ if (lastHighHeader) lastHighHeader.classList.remove(\"highlight\");
549
+ }
550
+ }
551
+ }
552
+ // // Less touchy navigation back or forward in history when using mouse wheel
553
+ // grid.addEventListener(\"wheel\", function (evt) {
554
+ // grid.scrollLeft += evt.deltaX;
555
+ // document.body.scrollTop += (evt.deltaY * 0.6);
556
+ // evt.preventDefault();
557
+ // return false;
558
+ // });
559
+ }
560
+ function setHeaderSizes() {
561
+ if (grid.clientWidth > window.outerWidth)
562
+ document.getElementById(\"titleBox\").style.width = grid.clientWidth;
563
+ // console.log(\"start\");
564
+ // See if the headerTop is already populated
565
+ // %%% Grab the TRs from headerTop, clear it out, do this stuff, add them back
566
+ headerTop.innerHTML = \"\"; // %%% Would love to not have to clear it out like this every time! (Currently doing this to support resize events.)
567
+ var isEmpty = headerTop.childElementCount === 0;
568
+ var numFixed = parseInt(grid.getAttribute(\"x-num-frozen\")) || 0;
569
+ var fixedColLefts = [0];
570
+
571
+ // Set up proper sizings of sticky column header
572
+ var node;
573
+ for (var j = 0; j < #{table_name}HtColumns.length; ++j) {
574
+ var row = #{table_name}HtColumns[j];
575
+ var tr = isEmpty ? document.createElement(\"TR\") : headerTop.childNodes[j];
576
+ tr.innerHTML = row.innerHTML.trim();
577
+ var curLeft = 0.0;
578
+ // Match up widths from the original column headers
579
+ for (var i = 0; i < row.childNodes.length; ++i) {
580
+ node = row.childNodes[i];
581
+ if (node.nodeType === 1) {
582
+ var th = tr.childNodes[i];
583
+ th.style.minWidth = th.style.maxWidth = getComputedStyle(node).width;
584
+ // Add \"left: __px\" style to the fixed-width column THs
585
+ if (i <= numFixed) {
586
+ th.style.position = \"sticky\";
587
+ th.style.backgroundColor = \"#008061\";
588
+ th.style.zIndex = \"1\";
589
+ th.style.left = curLeft + \"px\";
590
+ fixedColLefts.push(curLeft += node.clientWidth);
591
+ }
592
+ if (#{pk&.present? ? 'i > 0' : 'true'}) {
593
+ // Add <span> at the end
594
+ var span = document.createElement(\"SPAN\");
595
+ span.className = \"exclude\";
596
+ span.innerHTML = \"X\";
597
+ span.addEventListener(\"click\", function (e) {
598
+ e.stopPropagation();
599
+ doFetch(\"POST\", {_brick_exclude: this.parentElement.getAttribute(\"x-order\")});
600
+ });
601
+ th.appendChild(span);
602
+ }
603
+ }
604
+ }
605
+ headerCols = tr.childNodes;
606
+ if (isEmpty) headerTop.appendChild(tr);
607
+ }
608
+ // Add \"left: __px\" style to all fixed-width column TDs
609
+ [...grid.children[1].children].forEach(function (row) {
610
+ for (var j = 1; j <= numFixed; ++j) {
611
+ row.children[j].style.left = fixedColLefts[j] + 'px';
612
+ }
613
+ });
614
+ grid.style.marginTop = \"-\" + getComputedStyle(headerTop).height;
615
+ // console.log(\"end\");
616
+ }
617
+
618
+ if (headerTop) {
619
+ onImagesLoaded(function() {
620
+ setHeaderSizes();
621
+ });
622
+ window.addEventListener(\"resize\", function(event) {
623
+ setHeaderSizes();
624
+ }, true);#{
625
+ "
626
+ var headerButtonBox = document.getElementById(\"headerButtonBox\");
627
+ if (headerButtonBox) {
628
+ var addNew = document.createElement(\"A\");
629
+ addNew.id = \"addNew\";
630
+ addNew.href = \"#{link_to_brick(klass, new: true, path_only: true)}\";
631
+ addNew.title = \"New #{table_name.singularize}\";
632
+ addNew.innerHTML = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"#fff\" d=\"M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z\"/></svg>';
633
+ headerButtonBox.append(addNew);
634
+ }
635
+ " unless klass.is_view? || show_new_button == false
636
+ }
637
+ }
638
+
639
+ function onImagesLoaded(event) {
640
+ var images = document.getElementsByTagName(\"IMG\");
641
+ var numLoaded = images.length;
642
+ for (var i = 0; i < images.length; ++i) {
643
+ if (images[i].complete)
644
+ --numLoaded;
645
+ else {
646
+ images[i].addEventListener(\"load\", function() {
647
+ if (--numLoaded <= 0)
648
+ event();
649
+ });
650
+ }
651
+ }
652
+ if (numLoaded <= 0)
653
+ event();
654
+ }
655
+ "
656
+ end
657
+
658
+ # -------------------------------------
632
659
  def _n_m_prep(relation, x_axis, y_axis)
633
- relation ||= (instance_variable_get("@#{controller_name}".to_sym) || _brick_resource_from_iv)
634
660
  # Just find the first two BT things at this point
635
661
 
636
662
  klass = relation.klass
637
- rel = ::Brick.relations&.fetch(relation.table_name, nil)
638
- # fk_assocs = rel[:fks].map { |k, fk| [fk[:assoc_name], fk[:fk]] }
639
663
  fk_assocs = klass.reflect_on_all_associations.each_with_object([]) do |assoc, s|
640
664
  s << [assoc.name.to_s, assoc.foreign_key, assoc.klass] if assoc.belongs_to?
641
665
  end
@@ -690,8 +714,10 @@ function onImagesLoaded(event) {
690
714
  kwargs[:visited] = {}
691
715
  end
692
716
 
693
- text = ((args.first.is_a?(String) || args.first.is_a?(Proc)) && args.shift) || args[1]
694
- text = text.call if text.is_a?(Proc)
717
+ unless (is_path_only = kwargs.delete(:path_only))
718
+ text = ((args.first.is_a?(String) || args.first.is_a?(Proc)) && args.shift) || args[1]
719
+ text = text.call if text.is_a?(Proc)
720
+ end
695
721
  klass_or_obj = ((args.first.is_a?(ActiveRecord::Relation) ||
696
722
  args.first.is_a?(ActiveRecord::Base) ||
697
723
  args.first.is_a?(Class)) &&
@@ -739,6 +765,8 @@ function onImagesLoaded(event) {
739
765
  filter_parts << "#{whr.first}=#{whr.last}" if whr.last && !whr.last.is_a?(Array)
740
766
  end
741
767
  klass_or_obj = klass_or_obj.klass
768
+ end
769
+ if klass_or_obj.is_a?(Class) && klass_or_obj <= ActiveRecord::Base
742
770
  type_col = klass_or_obj.inheritance_column
743
771
  if klass_or_obj.column_names.include?(type_col) && klass_or_obj.name != klass_or_obj.base_class.name
744
772
  filter_parts << "#{type_col}=#{klass_or_obj.name}"
@@ -750,11 +778,22 @@ function onImagesLoaded(event) {
750
778
  relation = ::Brick.relations.fetch(rel_name || klass.table_name, nil)
751
779
  if (klass_or_obj&.is_a?(Class) && klass_or_obj < ActiveRecord::Base) ||
752
780
  (klass_or_obj&.is_a?(ActiveRecord::Base) && klass_or_obj.new_record? && (klass_or_obj = klass_or_obj.class))
753
- path = (proc = kwargs[:index_proc]) ? proc.call(klass_or_obj, relation) : "#{app_routes.path_for(controller: klass_or_obj.base_class._brick_index(nil, '/', relation, true), action: :index)}#{filter}"
754
- lt_args = [text || "Index for #{klass_or_obj.name.pluralize}", path]
781
+ if kwargs.delete(:new)
782
+ path = (proc = kwargs[:new_proc]) ? proc.call(klass_or_obj, relation) : "#{app_routes.path_for(controller: klass_or_obj.base_class._brick_index(:singular, '/', relation, true), action: :new)}#{filter}"
783
+ return path if is_path_only
784
+
785
+ lt_args = [text || "New #{klass_or_obj.name}", path]
786
+ else
787
+ path = (proc = kwargs[:index_proc]) ? proc.call(klass_or_obj, relation) : "#{app_routes.path_for(controller: klass_or_obj.base_class._brick_index(nil, '/', relation, true), action: :index)}#{filter}"
788
+ return path if is_path_only
789
+
790
+ lt_args = [text || "Index for #{klass_or_obj.name.pluralize}", path]
791
+ end
755
792
  else
756
793
  # 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
757
794
  path = (proc = kwargs[:show_proc]) ? proc.call(klass_or_obj, relation) : "#{app_routes.path_for(controller: klass_or_obj.class.base_class._brick_index(nil, '/', relation, true), action: :show, id: klass_or_obj)}#{filter}"
795
+ return path if is_path_only
796
+
758
797
  lt_args = [text || "Show this #{klass_or_obj.class.name}", path]
759
798
  end
760
799
  kwargs.delete(:visited)
@@ -173,7 +173,6 @@ erDiagram
173
173
  <%= erd_sidelinks(shown_classes, hm_class).html_safe %>
174
174
  <% end
175
175
  def dt_lookup(dt)
176
- puts dt.inspect
177
176
  { 'integer' => 'int', 'character varying' => 'varchar', 'double precision' => 'float',
178
177
  'timestamp without time zone' => 'timestamp',
179
178
  'timestamp with time zone' => 'timestamp',
@@ -165,6 +165,12 @@ module Brick
165
165
  end.tap { |member| push(member) }
166
166
  end
167
167
  end
168
+
169
+ def add_parts(parts)
170
+ s = self
171
+ parts[0..-3].each { |part| s = s[part.to_sym] }
172
+ s[parts[-2].to_sym] = nil # unless parts[-2].empty? # Using []= will "hydrate" any missing part(s) in our whole series
173
+ end
168
174
  end
169
175
 
170
176
  class JoinHash < Hash
@@ -341,7 +341,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
341
341
  fk[4].downcase! if fk[4] =~ /^[A-Z0-9_]+$/
342
342
  fk[2] = connection.send(:oracle_downcase, fk[2])
343
343
  end
344
- ::Brick._add_bt_and_hm(fk, relations)
344
+ ::Brick._add_bt_and_hm(fk, relations, nil, nil)
345
345
  end
346
346
  kcus = nil # Allow this large item to be garbage collected
347
347
  end
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 212
8
+ TINY = 214
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
@@ -53,7 +53,12 @@ module Brick
53
53
  s[v_parts.first] = nil unless [::Brick.default_schema, 'public'].include?(v_parts.first)
54
54
  end
55
55
  end
56
- seeds = +"# Seeds file for #{ActiveRecord::Base.connection.current_database}:\n"
56
+ seeds = +'# Seeds file for '
57
+ if (arbc = ActiveRecord::Base.connection).respond_to?(:current_database) # SQLite3 can't do this!
58
+ seeds << "#{arbc.current_database}:\n"
59
+ elsif (filename = arbc.instance_variable_get(:@connection_parameters)&.fetch(:database, nil))
60
+ seeds << "#{filename}:\n"
61
+ end
57
62
  done = []
58
63
  fks = {}
59
64
  stuck = {}
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.212
4
+ version: 1.0.214
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-28 00:00:00.000000000 Z
11
+ date: 2024-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord