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 +4 -4
- data/lib/brick/extensions.rb +96 -47
- data/lib/brick/frameworks/rails/engine.rb +17 -7
- data/lib/brick/frameworks/rails/form_tags.rb +268 -229
- data/lib/brick/frameworks/rails.rb +0 -1
- data/lib/brick/join_array.rb +6 -0
- data/lib/brick/reflect_tables.rb +1 -1
- data/lib/brick/version_number.rb +1 -1
- data/lib/generators/brick/seeds_generator.rb +6 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: afed5350a655b7eca570609ff4d15f998ecfe746345c4771b7eb910274df14ae
|
4
|
+
data.tar.gz: 45d5cccb07240fe0995bf812dc4b0cc34d21edd1bb0ab3cf212cd255c8a0f6a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca420002527953167f6ba1112e43d197bea259bf360c89ff92a01c9753c53da1b7060c15cd021c27e6106c4e77e7d3c30f589c89509ae21c2a2fe69d42e6f079
|
7
|
+
data.tar.gz: e6b1fd3745b5c69bfaa9486a4158b7a8efc03c7c43aa884a13f630e9b023e40e0893e1862e6e7fee545049efb46c5b3686cd4f05363d19eb3dfcf0d8f6b1cef3
|
data/lib/brick/extensions.rb
CHANGED
@@ -71,15 +71,24 @@ module ActiveRecord
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def real_model(params)
|
74
|
-
if params && (
|
75
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
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
|
-
|
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
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =
|
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
|
-
|
2484
|
-
|
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(:#{
|
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(
|
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) .
|
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) .
|
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><%=
|
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})
|
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
|
-
|
1514
|
-
|
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
|
-
|
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
|
-
|
20
|
-
out <<
|
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
|
-
|
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} \";
|
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
|
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
|
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 << "
|
540
|
-
"
|
352
|
+
out << "<th>#{x_item.first}</th>"
|
541
353
|
end
|
542
|
-
out << "
|
354
|
+
out << "</tr></thead>
|
543
355
|
<tbody>
|
544
356
|
"
|
545
|
-
obj_path = "#{
|
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 =
|
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: \"#{
|
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: \"#{
|
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} \";
|
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
|
-
|
694
|
-
|
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
|
-
|
754
|
-
|
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',
|
data/lib/brick/join_array.rb
CHANGED
@@ -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
|
data/lib/brick/reflect_tables.rb
CHANGED
@@ -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
|
data/lib/brick/version_number.rb
CHANGED
@@ -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 = +
|
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.
|
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-
|
11
|
+
date: 2024-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|