brick 1.0.211 → 1.0.212
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/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
|