brick 1.0.211 → 1.0.212
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/brick/config.rb +26 -1
- data/lib/brick/extensions.rb +24 -7
- data/lib/brick/frameworks/rails/engine.rb +26 -8
- data/lib/brick/frameworks/rails/form_builder.rb +1 -1
- data/lib/brick/frameworks/rails/form_tags.rb +182 -25
- data/lib/brick/frameworks/rails.rb +12 -1
- data/lib/brick/route_mapper.rb +9 -0
- data/lib/brick/version_number.rb +1 -1
- data/lib/generators/brick/install_generator.rb +16 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e954e525ea7a17eea2b33a125283f8af47a7bad1e457780815a8f53e06ed8fc
|
4
|
+
data.tar.gz: 9ded8bfa75d407294eb3e34895d1fc36ffa17767b9cbdeee0c961d6d9e821653
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c2d33f2a6247cc001071929731d2b2543d2b91ba0e8c22afeda7d9fa40e37ae9c3957d4046f8157a34b1549dca8e542de97a58f22b25a312532eed775d3989c
|
7
|
+
data.tar.gz: 4e63b7986eb7b683421ef92d00fe61a3970ed0d24fb57403a89d05c114760ad00310e6c635d66bc0388e5ca26b6c6fd2061574c83e1f70b60f90baa22babcb92
|
data/lib/brick/config.rb
CHANGED
@@ -238,7 +238,24 @@ module Brick
|
|
238
238
|
end
|
239
239
|
|
240
240
|
def treat_as_associative=(tables)
|
241
|
-
@mutex.synchronize
|
241
|
+
@mutex.synchronize do
|
242
|
+
@treat_as_associative = if tables.is_a?(Hash)
|
243
|
+
tables.each_with_object({}) do |v, s|
|
244
|
+
# If it's :constellation, or anything else in a hash, we'll take its value
|
245
|
+
# (and hopefully in this case that would be either a string or nil)
|
246
|
+
dsl = ((v.last.is_a?(Symbol) && v.last) || v.last&.values&.last)
|
247
|
+
unless (dsl ||= '').is_a?(String) || dsl.is_a?(Symbol)
|
248
|
+
puts "Was really expecting #{v.first} / #{v.last.first&.first} / #{dsl} to be a string, " +
|
249
|
+
"so will disregard #{dsl} and just turn on simple constellation view for #{v.first}."
|
250
|
+
end
|
251
|
+
s[v.first] = v.last.is_a?(Hash) ? dsl : v.last
|
252
|
+
end
|
253
|
+
elsif tables.is_a?(String) # comma-separated list?
|
254
|
+
tables.split(',').each_with_object({}) { |v, s| s[v.trim] = nil }
|
255
|
+
else # Expecting an Array, and have no special presentation
|
256
|
+
tables&.each_with_object({}) { |v, s| s[v] = nil }
|
257
|
+
end
|
258
|
+
end
|
242
259
|
end
|
243
260
|
|
244
261
|
# Polymorphic associations
|
@@ -286,6 +303,14 @@ module Brick
|
|
286
303
|
@mutex.synchronize { @model_descrips = descrips }
|
287
304
|
end
|
288
305
|
|
306
|
+
def erd_show_columns
|
307
|
+
@mutex.synchronize { @erd_show_columns ||= [] }
|
308
|
+
end
|
309
|
+
|
310
|
+
def erd_show_columns=(descrips)
|
311
|
+
@mutex.synchronize { @erd_show_columns = descrips }
|
312
|
+
end
|
313
|
+
|
289
314
|
def sti_namespace_prefixes
|
290
315
|
@mutex.synchronize { @sti_namespace_prefixes ||= {} }
|
291
316
|
end
|
data/lib/brick/extensions.rb
CHANGED
@@ -107,7 +107,7 @@ module ActiveRecord
|
|
107
107
|
# has_one_attached, has_many_attached, and has_rich_text
|
108
108
|
def _activestorage_actiontext_fields
|
109
109
|
fields = [[], [], {}]
|
110
|
-
if
|
110
|
+
if Object.const_defined?('ActiveStorage') && respond_to?(:generated_association_methods) && !(self <= ::ActiveStorage::Blob) # ActiveStorage
|
111
111
|
generated_association_methods.instance_methods.each do |method_sym|
|
112
112
|
method_str = method_sym.to_s
|
113
113
|
fields[0] << method_str[0..-13].to_sym if method_str.end_with?('_attachment=') # has_one_attached
|
@@ -124,7 +124,7 @@ module ActiveRecord
|
|
124
124
|
end
|
125
125
|
|
126
126
|
def _active_storage_name(col_name)
|
127
|
-
if Object.const_defined?('ActiveStorage') && (self <= ActiveStorage::Attachment || self <= ActiveStorage::Blob)
|
127
|
+
if Object.const_defined?('ActiveStorage') && (self <= ::ActiveStorage::Attachment || self <= ::ActiveStorage::Blob)
|
128
128
|
if (col_str = col_name.to_s).end_with?('_attachments')
|
129
129
|
col_str[0..-13]
|
130
130
|
elsif col_str.end_with?('_blobs')
|
@@ -880,7 +880,7 @@ module ActiveRecord
|
|
880
880
|
through_sources.push(src_ref) unless src_ref.belongs_to?
|
881
881
|
from_clause = +"#{_br_quoted_name(through_sources.first.table_name)} br_t0"
|
882
882
|
# ActiveStorage will not get the correct count unless we do some extra filtering later
|
883
|
-
tbl_nm = 'br_t0' if Object.const_defined?('ActiveStorage') && through_sources.first.klass <= ActiveStorage::Attachment
|
883
|
+
tbl_nm = 'br_t0' if Object.const_defined?('ActiveStorage') && through_sources.first.klass <= ::ActiveStorage::Attachment
|
884
884
|
fk_col = through_sources.shift.foreign_key
|
885
885
|
|
886
886
|
idx = 0
|
@@ -1069,8 +1069,8 @@ JOIN (SELECT #{hm_selects.map { |s| _br_quoted_name("#{'br_t0.' if from_clause}#
|
|
1069
1069
|
end
|
1070
1070
|
|
1071
1071
|
# ActiveStorage compatibility
|
1072
|
-
selects << 'service_name' if klass.name == 'ActiveStorage::Blob' && ActiveStorage::Blob.columns_hash.key?('service_name')
|
1073
|
-
selects << 'blob_id' if klass.name == 'ActiveStorage::Attachment' && ActiveStorage::Attachment.columns_hash.key?('blob_id')
|
1072
|
+
selects << 'service_name' if klass.name == 'ActiveStorage::Blob' && ::ActiveStorage::Blob.columns_hash.key?('service_name')
|
1073
|
+
selects << 'blob_id' if klass.name == 'ActiveStorage::Attachment' && ::ActiveStorage::Attachment.columns_hash.key?('blob_id')
|
1074
1074
|
# Pay gem compatibility
|
1075
1075
|
selects << 'processor' if klass.name == 'Pay::Customer' && Pay::Customer.columns_hash.key?('processor')
|
1076
1076
|
selects << 'customer_id' if klass.name == 'Pay::Subscription' && Pay::Subscription.columns_hash.key?('customer_id')
|
@@ -2078,6 +2078,23 @@ class Object
|
|
2078
2078
|
# add_csp_hash
|
2079
2079
|
# end
|
2080
2080
|
# end
|
2081
|
+
|
2082
|
+
# Associate and unassociate in an N:M relation
|
2083
|
+
self.define_method :associate do
|
2084
|
+
if (base_class = (model = params['modelName']&.constantize).base_class)
|
2085
|
+
args = params['args']
|
2086
|
+
record = base_class.create(args[0] => args[1], args[2] => args[3])
|
2087
|
+
add_csp_hash
|
2088
|
+
render json: { data: record.id }
|
2089
|
+
end
|
2090
|
+
end
|
2091
|
+
self.define_method :unassociate do
|
2092
|
+
if (base_class = (model = params['modelName']&.constantize).base_class)
|
2093
|
+
base_class.find_by(base_class._pk_as_array&.first => params['id']).delete
|
2094
|
+
add_csp_hash
|
2095
|
+
end
|
2096
|
+
end
|
2097
|
+
|
2081
2098
|
self.define_method :orphans do
|
2082
2099
|
instance_variable_set(:@orphans, ::Brick.find_orphans(::Brick.set_db_schema(params).first))
|
2083
2100
|
add_csp_hash
|
@@ -2440,7 +2457,7 @@ class Object
|
|
2440
2457
|
if (new_obj = model.new(new_params)).respond_to?(:serializable_hash)
|
2441
2458
|
# Convert any Filename objects with nil into an empty string so that #encode can be called on them
|
2442
2459
|
new_obj.serializable_hash.each do |k, v|
|
2443
|
-
new_obj.send("#{k}=", ActiveStorage::Filename.new('')) if v.is_a?(ActiveStorage::Filename) && !v.instance_variable_get(:@filename)
|
2460
|
+
new_obj.send("#{k}=", ::ActiveStorage::Filename.new('')) if v.is_a?(::ActiveStorage::Filename) && !v.instance_variable_get(:@filename)
|
2444
2461
|
end if Object.const_defined?('ActiveStorage')
|
2445
2462
|
end
|
2446
2463
|
instance_variable_set("@#{singular_table_name}".to_sym, new_obj)
|
@@ -3087,7 +3104,7 @@ module Brick
|
|
3087
3104
|
else
|
3088
3105
|
res_name = (tbl_name_parts = tbl_name.split('.'))[0..-2].first
|
3089
3106
|
res_name << '.' if res_name
|
3090
|
-
(res_name ||= +'') << relation&.fetch(:resource, nil) || tbl_name_parts.last
|
3107
|
+
(res_name ||= +'') << (relation&.fetch(:resource, nil) || tbl_name_parts.last)
|
3091
3108
|
end
|
3092
3109
|
|
3093
3110
|
res_parts = ((mode == :singular) ? res_name.singularize : res_name).split('.')
|
@@ -198,7 +198,7 @@ function linkSchemas() {
|
|
198
198
|
|
199
199
|
# Treat ActiveStorage::Blob metadata as JSON
|
200
200
|
if ::Brick.config.table_name_prefixes.fetch('active_storage_', nil) == 'ActiveStorage' &&
|
201
|
-
ActiveStorage.const_defined?('Blob')
|
201
|
+
::ActiveStorage.const_defined?('Blob')
|
202
202
|
unless (md = (::Brick.config.model_descrips ||= {})).key?('ActiveStorage::Blob')
|
203
203
|
md['ActiveStorage::Blob'] = '[filename]'
|
204
204
|
end
|
@@ -643,7 +643,7 @@ window.addEventListener(\"popstate\", linkSchemas);
|
|
643
643
|
end
|
644
644
|
# ActiveStorage has_one_attached and has_many_attached needs additional filtering on the name
|
645
645
|
if (as_name = hm_assoc.klass&._active_storage_name(hm_assoc.name)) # ActiveStorage HMT
|
646
|
-
prefix = 'attachments.' if hm_assoc.through_reflection&.klass&.<= ActiveStorage::Attachment
|
646
|
+
prefix = 'attachments.' if hm_assoc.through_reflection&.klass&.<= ::ActiveStorage::Attachment
|
647
647
|
keys << ["#{prefix}name", as_name]
|
648
648
|
end
|
649
649
|
keys.to_h
|
@@ -1315,9 +1315,26 @@ end
|
|
1315
1315
|
# or
|
1316
1316
|
# Rails.application.reloader.to_prepare do ... end
|
1317
1317
|
self.class.class_exec { include ::Brick::Rails::FormTags } unless respond_to?(:brick_grid)
|
1318
|
-
|
1318
|
+
|
1319
|
+
#{# Determine if we should render an N:M representation or the standard "mega_grid"
|
1320
|
+
taa = ::Brick.config.treat_as_associative&.fetch(res_name, nil)
|
1321
|
+
options = {}
|
1322
|
+
options[:prefix] = prefix unless prefix.blank?
|
1323
|
+
if taa.is_a?(String) # Write out a constellation
|
1324
|
+
representation = :constellation
|
1325
|
+
"
|
1326
|
+
brick_constellation(@#{res_name}, #{options.inspect}, bt_descrip: @_brick_bt_descrip, bts: bts)"
|
1327
|
+
elsif taa.is_a?(Symbol) # Write out a bezier representation
|
1328
|
+
"
|
1329
|
+
brick_bezier(@#{res_name}, #{options.inspect}, bt_descrip: @_brick_bt_descrip, bts: bts)"
|
1330
|
+
else # Write out the mega-grid
|
1331
|
+
representation = :grid
|
1332
|
+
"
|
1319
1333
|
brick_grid(@#{res_name}, @_brick_sequence, @_brick_incl, @_brick_excl,
|
1320
|
-
cols, bt_descrip: @_brick_bt_descrip,
|
1334
|
+
cols, bt_descrip: @_brick_bt_descrip,
|
1335
|
+
poly_cols: poly_cols, bts: bts, hms_keys: #{hms_keys.inspect}, hms_cols: {#{hms_columns.join(', ')}})"
|
1336
|
+
end}
|
1337
|
+
%>
|
1321
1338
|
|
1322
1339
|
#{"<hr><%= link_to(\"New #{new_path_name = "new_#{path_obj_name}_path"
|
1323
1340
|
obj_name}\", #{new_path_name}, { class: '__brick' }) if respond_to?(:#{new_path_name}) %>" unless @_brick_model.is_view?}
|
@@ -1727,10 +1744,11 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
|
|
1727
1744
|
}
|
1728
1745
|
<%= \" showErd();\n\" if (@_brick_erd || 0) > 0
|
1729
1746
|
%></script>
|
1730
|
-
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
1747
|
+
<% end %>
|
1748
|
+
"
|
1749
|
+
end
|
1750
|
+
if representation == :grid
|
1751
|
+
"<script>
|
1734
1752
|
<% # Make column headers sort when clicked
|
1735
1753
|
# %%% Create a smart javascript routine which can do this client-side %>
|
1736
1754
|
[... document.getElementsByTagName(\"TH\")].forEach(function (th) {
|
@@ -84,7 +84,7 @@ module Brick::Rails::FormBuilder
|
|
84
84
|
opts = enum_type.send(:mapping)&.each_with_object([]) { |v, s| s << [v.first, v.first] } || []
|
85
85
|
out << self.select(method.to_sym, [["(No #{method} chosen)", '^^^brick_NULL^^^']] + opts, { value: val || '^^^brick_NULL^^^' }, options)
|
86
86
|
else
|
87
|
-
digit_pattern = col_type == :integer ? '\d*' : '\d*(?:\.\d*|)'
|
87
|
+
digit_pattern = col_type == :integer ? '(?:-|)\d*' : '(?:-|)\d*(?:\.\d*|)'
|
88
88
|
# Used to do this for float / decimal: self.number_field method.to_sym
|
89
89
|
out << self.text_field(method.to_sym, { pattern: digit_pattern, class: 'check-validity' })
|
90
90
|
end
|
@@ -3,27 +3,9 @@ module Brick::Rails::FormTags
|
|
3
3
|
def brick_grid(relation = nil, sequence = nil, inclusions = nil, exclusions = nil,
|
4
4
|
cols = {}, bt_descrip: nil, poly_cols: nil, bts: {}, hms_keys: [], hms_cols: {},
|
5
5
|
show_header: nil, show_row_count: nil, show_erd_button: nil, show_in_app_button: nil, show_new_button: nil, show_avo_button: nil, show_aa_button: nil)
|
6
|
-
# When a relation is not provided, first see if one exists which matches the controller name
|
7
|
-
|
8
|
-
|
9
|
-
case (collections = _brick_resource_from_iv).length
|
10
|
-
when 0
|
11
|
-
puts '#brick_grid: Not having been provided with a collection to work from, searched through all instance variables to find an ActiveRecord::Relation. None could be found.'
|
12
|
-
return
|
13
|
-
when 1 # If there's only one type match then simply get the first one, hoping that this is what they intended
|
14
|
-
relation = instance_variable_get(iv = (chosen = collections.first).last.first)
|
15
|
-
puts "#brick_grid: Not having been provided with a collection to work from, first tried @#{controller_name}.
|
16
|
-
Failing that, have searched through instance variables and found #{iv} of type #{chosen.first.name}.
|
17
|
-
Running with it!"
|
18
|
-
else
|
19
|
-
myriad = collections.each_with_object([]) { |c, s| c.last.each { |iv| s << "#{iv} (#{c.first.name})" } }
|
20
|
-
puts "#brick_grid: Not having been provided with a collection to work from, first tried @#{controller_name}, and then searched through all instance variables.
|
21
|
-
Found ActiveRecord::Relation objects of multiple types:
|
22
|
-
#{myriad.inspect}
|
23
|
-
Not knowing which of these to render, have erred on the side of caution and simply provided this warning message."
|
24
|
-
return
|
25
|
-
end
|
26
|
-
end
|
6
|
+
# When a relation is not provided, first see if one exists which matches the controller name or
|
7
|
+
# something has turned up in the instance variables.
|
8
|
+
relation ||= (instance_variable_get("@#{controller_name}".to_sym) || _brick_resource_from_iv)
|
27
9
|
|
28
10
|
nfc = Brick.config.sidescroll.fetch(relation.table_name, nil)&.fetch(:num_frozen_columns, nil) ||
|
29
11
|
Brick.config.sidescroll.fetch(:num_frozen_columns, nil) ||
|
@@ -458,8 +440,8 @@ function onImagesLoaded(event) {
|
|
458
440
|
elsif hma.include?(k) # has_many_attached
|
459
441
|
{ sql_type: 'binary', type: :files }
|
460
442
|
elsif rtans&.key?(k) # has_rich_text
|
461
|
-
|
462
|
-
|
443
|
+
k = rtans[k]
|
444
|
+
{ sql_type: 'varchar', type: :text }
|
463
445
|
end
|
464
446
|
col = (ActiveRecord::ConnectionAdapters::Column.new(
|
465
447
|
'', nil, ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(**kwargs)
|
@@ -541,6 +523,159 @@ function onImagesLoaded(event) {
|
|
541
523
|
end
|
542
524
|
end # brick_form_for
|
543
525
|
|
526
|
+
# ------------------------------------------
|
527
|
+
# Our cool N:M checkbox constellation editor
|
528
|
+
def brick_constellation(relation = nil, options = {}, x_axis: nil, y_axis: nil, bt_descrip: nil, bts: {})
|
529
|
+
x_axis, x_list, y_axis, y_list, existing = _n_m_prep(relation, x_axis, y_axis)
|
530
|
+
|
531
|
+
# HTML for constellation
|
532
|
+
prefix = options[:prefix]
|
533
|
+
out = +"<form action=\"#{"#{prefix}/" if prefix}brick_constellation\">
|
534
|
+
<table id=\"#{table_name = relation.table_name.split('.').last}\" class=\"shadow\">
|
535
|
+
<thead><tr><td></td>
|
536
|
+
"
|
537
|
+
# Header row with X axis values
|
538
|
+
x_list.each do |x_item|
|
539
|
+
out << " <th>#{x_item.first}</th>
|
540
|
+
"
|
541
|
+
end
|
542
|
+
out << " </tr></thead>
|
543
|
+
<tbody>
|
544
|
+
"
|
545
|
+
obj_path = "#{relation.klass._brick_index(:singular)}_path".to_sym
|
546
|
+
link_arrow = link_to('⇛', send(obj_path, '____'), { class: 'big-arrow' })
|
547
|
+
pk_as_array = relation.klass._pk_as_array
|
548
|
+
y_list.each do |y_item|
|
549
|
+
out << " <tr><th>#{y_item.first}</th>
|
550
|
+
"
|
551
|
+
x_list.each do |x_item|
|
552
|
+
checked = existing.find { |e| e[1] == x_item.last && e[2] == y_item.last }
|
553
|
+
item_id = pk_as_array.map { |pk_part| checked.first }.join('%2F') if checked
|
554
|
+
out << " <td><input type=\"checkbox\" name=\"#{table_name}\" #{"x-id=\"#{item_id}\" " if checked
|
555
|
+
}\" value=\"#{x_item.last}_#{y_item.last}\"#{' checked' if checked}>
|
556
|
+
#{link_arrow.gsub('____', item_id) if checked}</td>
|
557
|
+
"
|
558
|
+
end
|
559
|
+
out << " </tr>
|
560
|
+
"
|
561
|
+
end
|
562
|
+
out << " </tbody>
|
563
|
+
</table>
|
564
|
+
<script>
|
565
|
+
var constellation = document.getElementById(\"#{table_name}\");
|
566
|
+
var nextSib,
|
567
|
+
_this;
|
568
|
+
[... constellation.getElementsByTagName(\"INPUT\")].forEach(function (x) {
|
569
|
+
x.addEventListener(\"change\", function (y) {
|
570
|
+
_this = this;
|
571
|
+
if (this.checked) {
|
572
|
+
var ids = this.value.split(\"_\");
|
573
|
+
doFetch(\"POST\", {modelName: \"#{relation.klass.name}\",
|
574
|
+
args: [#{x_axis[1].inspect}, ids[0], #{y_axis[1].inspect}, ids[1]],
|
575
|
+
_brick_action: \"/#{prefix}brick_associate\"},
|
576
|
+
function (p) { // If it returns successfully, create an <a> element
|
577
|
+
p.text().then(function (response) {
|
578
|
+
var recordId = JSON.parse(response).data;
|
579
|
+
if (recordId) {
|
580
|
+
console.log(_this.getAttribute(\"x-id\"));
|
581
|
+
var tmp = document.createElement(\"DIV\");
|
582
|
+
tmp.innerHTML = \"#{link_arrow.gsub('"', '\"')}\".replace(\"____\", recordId);
|
583
|
+
_this.parentElement.append(tmp.firstChild);
|
584
|
+
}
|
585
|
+
});
|
586
|
+
}
|
587
|
+
);
|
588
|
+
} else if (nextSib = this.nextElementSibling) {
|
589
|
+
doFetch(\"DELETE\", {modelName: \"#{relation.klass.name}\",
|
590
|
+
id: this.getAttribute(\"x-id\"),
|
591
|
+
_brick_action: \"/#{prefix}brick_associate\"},
|
592
|
+
function (p) { // If it returns successfully, remove the an <a> element
|
593
|
+
_this.parentElement.removeChild(nextSib);
|
594
|
+
}
|
595
|
+
);
|
596
|
+
}
|
597
|
+
});
|
598
|
+
});
|
599
|
+
</script>
|
600
|
+
</form>
|
601
|
+
"
|
602
|
+
out.html_safe
|
603
|
+
end # brick_constellation
|
604
|
+
|
605
|
+
# ---------------------------------
|
606
|
+
# Our cool N:M bezier visualisation
|
607
|
+
# (...... work in progress .......)
|
608
|
+
def brick_bezier(relation = nil, options = {}, x_axis: nil, y_axis: nil, bt_descrip: nil, bts: {})
|
609
|
+
x_axis, x_list, y_axis, y_list, existing = _n_m_prep(relation, x_axis, y_axis)
|
610
|
+
# HTML for constellation
|
611
|
+
# X axis (List on left side)
|
612
|
+
out = +"<table id=\"#{x_axis.first}\" class=\"shadow\">
|
613
|
+
<tbody>
|
614
|
+
"
|
615
|
+
x_list.each_with_index { |x_item, idx| out << " <tr>#{"<th rowspan=\"#{x_list.length}\">#{x_axis.first}</th>" if idx.zero?}<td>#{x_item.first}</td></tr>" }
|
616
|
+
out << " </tbody>
|
617
|
+
</table>
|
618
|
+
"
|
619
|
+
|
620
|
+
# Y axis (List on right side)
|
621
|
+
out << "<table id=\"#{y_axis.first}\" class=\"shadow\">
|
622
|
+
<tbody>
|
623
|
+
"
|
624
|
+
y_list.each_with_index { |y_item, idx| out << " <tr><td>#{y_item.first}</td>#{"<th rowspan=\"#{y_list.length}\">#{y_axis.first}</th>" if idx.zero?}</tr>" }
|
625
|
+
out << " </tbody>
|
626
|
+
</table>
|
627
|
+
"
|
628
|
+
|
629
|
+
out.html_safe
|
630
|
+
end # brick_bezier
|
631
|
+
|
632
|
+
def _n_m_prep(relation, x_axis, y_axis)
|
633
|
+
relation ||= (instance_variable_get("@#{controller_name}".to_sym) || _brick_resource_from_iv)
|
634
|
+
# Just find the first two BT things at this point
|
635
|
+
|
636
|
+
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
|
+
fk_assocs = klass.reflect_on_all_associations.each_with_object([]) do |assoc, s|
|
640
|
+
s << [assoc.name.to_s, assoc.foreign_key, assoc.klass] if assoc.belongs_to?
|
641
|
+
end
|
642
|
+
|
643
|
+
if (x_axis = fk_assocs.find { |assoc| assoc.include?(x_axis) })
|
644
|
+
fk_assocs -= x_axis
|
645
|
+
end
|
646
|
+
if (y_axis = fk_assocs.find { |assoc| assoc.include?(y_axis) })
|
647
|
+
fk_assocs -= y_axis
|
648
|
+
end
|
649
|
+
y_axis = fk_assocs.shift unless y_axis
|
650
|
+
x_axis = fk_assocs.shift unless x_axis
|
651
|
+
puts "FK Leftovers: #{fk_assocs.join(', ')}" unless fk_assocs.empty?
|
652
|
+
|
653
|
+
existing = relation.each_with_object([]) do |row, s|
|
654
|
+
row_id = row.send(klass.primary_key)
|
655
|
+
if (x_id = row.send(x_axis[1])) && (y_id = row.send(y_axis[1]))
|
656
|
+
s << [row_id, x_id, y_id]
|
657
|
+
end
|
658
|
+
end
|
659
|
+
x_list = _expand_collection(x_axis.last.all)
|
660
|
+
y_list = _expand_collection(y_axis.last.all)
|
661
|
+
[x_axis, x_list, y_axis, y_list, existing]
|
662
|
+
end
|
663
|
+
|
664
|
+
def _expand_collection(relation)
|
665
|
+
collection, descrip_cols = relation.brick_list
|
666
|
+
details = []
|
667
|
+
obj_pk = relation.klass.primary_key
|
668
|
+
collection&.brick_(:each) do |obj|
|
669
|
+
details << [
|
670
|
+
obj.brick_descrip(
|
671
|
+
descrip_cols&.first&.map { |col2| obj.send(col2.last) },
|
672
|
+
obj_pk
|
673
|
+
), obj.send(obj_pk)
|
674
|
+
]
|
675
|
+
end
|
676
|
+
details
|
677
|
+
end
|
678
|
+
|
544
679
|
# --------------------------------
|
545
680
|
def link_to_brick(*args, **kwargs)
|
546
681
|
return unless ::Brick.config.mode == :on
|
@@ -627,7 +762,7 @@ function onImagesLoaded(event) {
|
|
627
762
|
else
|
628
763
|
# puts "Warning: link_to_brick could not find a class for \"#{controller_path}\" -- consider setting @_brick_model within that controller."
|
629
764
|
# if (hits = res_names.keys & instance_variables.map { |v| v.to_s[1..-1] }).present?
|
630
|
-
if (links =
|
765
|
+
if (links = _brick_relation_from_iv(true)).length == 1 # If there's only one match then use any text that was supplied
|
631
766
|
link_to_brick(text || links.first.last.join('/'), links.first.first, **kwargs)
|
632
767
|
else
|
633
768
|
links.each_with_object([]) do |v, s|
|
@@ -677,7 +812,7 @@ btnAddCol.addEventListener(\"click\", function () {
|
|
677
812
|
private
|
678
813
|
|
679
814
|
# Dig through all instance variables with hopes to find any that appear related to ActiveRecord
|
680
|
-
def
|
815
|
+
def _brick_relation_from_iv(trim_ampersand = false)
|
681
816
|
instance_variables.each_with_object(Hash.new { |h, k| h[k] = [] }) do |name, s|
|
682
817
|
iv_name = trim_ampersand ? name.to_s[1..-1] : name
|
683
818
|
case (val = instance_variable_get(name))
|
@@ -688,4 +823,26 @@ private
|
|
688
823
|
end
|
689
824
|
end
|
690
825
|
end
|
826
|
+
|
827
|
+
def _brick_resource_from_iv
|
828
|
+
# Failing that, dig through the instance variables with hopes to find something that is an ActiveRecord::Relation
|
829
|
+
case (collections = _brick_relation_from_iv).length
|
830
|
+
when 0
|
831
|
+
puts '#brick_grid: Not having been provided with a collection to work from, searched through all instance variables to find an ActiveRecord::Relation. None could be found.'
|
832
|
+
return
|
833
|
+
when 1 # If there's only one type match then simply get the first one, hoping that this is what they intended
|
834
|
+
relation = instance_variable_get(iv = (chosen = collections.first).last.first)
|
835
|
+
puts "#brick_grid: Not having been provided with a collection to work from, first tried @#{controller_name}.
|
836
|
+
Failing that, have searched through instance variables and found #{iv} of type #{chosen.first.name}.
|
837
|
+
Running with it!"
|
838
|
+
relation
|
839
|
+
else
|
840
|
+
myriad = collections.each_with_object([]) { |c, s| c.last.each { |iv| s << "#{iv} (#{c.first.name})" } }
|
841
|
+
puts "#brick_grid: Not having been provided with a collection to work from, first tried @#{controller_name}, and then searched through all instance variables.
|
842
|
+
Found ActiveRecord::Relation objects of multiple types:
|
843
|
+
#{myriad.inspect}
|
844
|
+
Not knowing which of these to render, have erred on the side of caution and simply provided this warning message."
|
845
|
+
return
|
846
|
+
end
|
847
|
+
end
|
691
848
|
end
|
@@ -173,7 +173,12 @@ erDiagram
|
|
173
173
|
<%= erd_sidelinks(shown_classes, hm_class).html_safe %>
|
174
174
|
<% end
|
175
175
|
def dt_lookup(dt)
|
176
|
-
|
176
|
+
puts dt.inspect
|
177
|
+
{ 'integer' => 'int', 'character varying' => 'varchar', 'double precision' => 'float',
|
178
|
+
'timestamp without time zone' => 'timestamp',
|
179
|
+
'timestamp with time zone' => 'timestamp',
|
180
|
+
'time without time zone' => 'time',
|
181
|
+
'time with time zone' => 'time' }[dt] || dt&.tr(' ', '_') || 'int'
|
177
182
|
end
|
178
183
|
callbacks.merge({#{model_short_name.inspect} => #{model.name}}).each do |cb_k, cb_class|
|
179
184
|
cb_relation = ::Brick.relations[cb_class.table_name]
|
@@ -193,6 +198,12 @@ erDiagram
|
|
193
198
|
%>
|
194
199
|
<%= \"#\{dt_lookup(cols[fk]&.first)} #\{fk} \\\" fk\\\"\".html_safe unless pkeys&.include?(fk) %><%
|
195
200
|
end
|
201
|
+
end %><%
|
202
|
+
if (erd_sc = Brick.config.erd_show_columns) == true || erd_sc&.include?(cb_class.name)
|
203
|
+
cols&.each do |col|
|
204
|
+
next if pkeys.include?(col.first) || fkeys.include?(col.first) %>
|
205
|
+
<%= \"#\{dt_lookup(col[1]&.first&.to_s)} #\{col.first}\".html_safe %><%
|
206
|
+
end
|
196
207
|
end %>
|
197
208
|
}
|
198
209
|
<% end
|
data/lib/brick/route_mapper.rb
CHANGED
@@ -287,6 +287,15 @@ module Brick
|
|
287
287
|
# post("/#{controller_prefix}brick_schema", to: 'brick_gem#schema_create', as: schema_as.to_s)
|
288
288
|
# end
|
289
289
|
|
290
|
+
if (associate_as = "#{controller_prefix.tr('/', '_')}brick_associate".to_sym)
|
291
|
+
(
|
292
|
+
!(associate_route = instance_variable_get(:@set).named_routes.find { |route| route.first == associate_as }&.last) ||
|
293
|
+
!associate_route.ast.to_s.include?("/#{controller_prefix}brick_associate/")
|
294
|
+
)
|
295
|
+
post("/#{controller_prefix}brick_associate", to: 'brick_gem#associate', as: associate_as.to_s)
|
296
|
+
delete("/#{controller_prefix}brick_associate", to: 'brick_gem#unassociate')
|
297
|
+
end
|
298
|
+
|
290
299
|
if ::Brick.config.add_orphans && (orphans_as = "#{controller_prefix.tr('/', '_')}brick_orphans".to_sym)
|
291
300
|
(
|
292
301
|
!(orphans_route = instance_variable_get(:@set).named_routes.find { |route| route.first == orphans_as }&.last) ||
|
data/lib/brick/version_number.rb
CHANGED
@@ -310,6 +310,14 @@ if ActiveRecord::Base.respond_to?(:brick_select) && !::Brick.initializer_loaded
|
|
310
310
|
# # have a table to still be treated as associative, causing HMTs to be auto-generated.)
|
311
311
|
# Brick.treat_as_associative = ['flights']
|
312
312
|
|
313
|
+
# # Further, if you want to present a given associative table in various ways then you can choose a 2D
|
314
|
+
# # constellation map of checkboxes, or bezier curves showing the association between a list at the left and at
|
315
|
+
# # the right. Indicating just :bezier is the same as :bezier_full, which shows the full list of all possible
|
316
|
+
# # things that can be associated. :bezier_union shows just the ones that are currently wired up, and
|
317
|
+
# # :bezier_excluded, :bezier_excluded_left, or :bezier_excluded_right shows the ones not yet wired up.
|
318
|
+
# Brick.treat_as_associative = { 'flights' => [:bezier, 'departure.code', 'arrival.code'],
|
319
|
+
# 'crew' => [:constellation, 'flight', 'personnel', '[used ? [used it!] : []]'] }
|
320
|
+
|
313
321
|
# # We normally don't show the timestamp columns \"created_at\", \"updated_at\", and \"deleted_at\", and also do
|
314
322
|
# # not consider them when finding associative tables to support an N:M association. (That is, ones that can be a
|
315
323
|
# # part of a has_many :through association.) If you want to use different exclusion columns than our defaults
|
@@ -334,6 +342,14 @@ if ActiveRecord::Base.respond_to?(:brick_select) && !::Brick.initializer_loaded
|
|
334
342
|
# # user, then you can use model_descrips like this, putting expressions with property references in square brackets:
|
335
343
|
# Brick.model_descrips = { 'User' => '[profile.firstname] [profile.lastname]' }
|
336
344
|
|
345
|
+
# # ERD SETTINGS
|
346
|
+
|
347
|
+
# # By default the Entity Relationship Diagram fragment which is available to be shown on the Grid page includes
|
348
|
+
# # primary and foreign keys. In order for it to show all columns in all cases, set this value to +true+:
|
349
|
+
# Brick.config.erd_show_columns = true
|
350
|
+
# # or to show all columns for specific tables, supply an array of model names:
|
351
|
+
# Brick.config.erd_show_columns = ['User', 'OrderDetail']
|
352
|
+
|
337
353
|
# # SINGLE TABLE INHERITANCE
|
338
354
|
|
339
355
|
# # Specify STI subclasses either directly by name or as a general module prefix that should always relate to a specific
|
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.212
|
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-
|
11
|
+
date: 2024-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -285,7 +285,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
285
285
|
- !ruby/object:Gem::Version
|
286
286
|
version: 1.3.6
|
287
287
|
requirements: []
|
288
|
-
rubygems_version: 3.
|
288
|
+
rubygems_version: 3.2.33
|
289
289
|
signing_key:
|
290
290
|
specification_version: 4
|
291
291
|
summary: Create a Rails app from data alone
|