brick 1.0.103 → 1.0.105
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 +4 -4
- data/lib/brick/extensions.rb +36 -12
- data/lib/brick/frameworks/rails/engine.rb +32 -10
- data/lib/brick/frameworks/rails/form_tags.rb +87 -76
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +69 -14
- data/lib/generators/brick/install_generator.rb +9 -6
- 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: 48fc69654d7cbdf5c7feb9ba7803b7a953c6c0ccd3c176614fe9a61994bb39e1
|
4
|
+
data.tar.gz: f3a4837d334cf97cb491cacd658cb6e6d8e3f3a5c640f80c151a083bf6811b7a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e51134710bae78f033b9dcf7827cd7c087cfd642ef8bc6244e2429f22158a80e9f804022bc703f55fa55f85cbc009ff25440289205ade1b415f5ba0af3d44c77
|
7
|
+
data.tar.gz: d6069676ad70e8401f7597b92b641f0f95e3c28fffa54747f630932500c778d7b4ac967c5bbd70059647d11d398598362d9c0bb5c82ef0500b1025371104a578
|
data/lib/brick/config.rb
CHANGED
@@ -94,13 +94,13 @@ module Brick
|
|
94
94
|
@mutex.synchronize { @enable_api = enable }
|
95
95
|
end
|
96
96
|
|
97
|
-
def
|
97
|
+
def api_roots
|
98
98
|
ver = api_version
|
99
|
-
@mutex.synchronize { @
|
99
|
+
@mutex.synchronize { @api_roots || ["/api/#{ver}/"] }
|
100
100
|
end
|
101
101
|
|
102
|
-
def
|
103
|
-
@mutex.synchronize { @
|
102
|
+
def api_roots=(path)
|
103
|
+
@mutex.synchronize { @api_roots = path }
|
104
104
|
end
|
105
105
|
|
106
106
|
def api_version
|
data/lib/brick/extensions.rb
CHANGED
@@ -257,11 +257,11 @@ module ActiveRecord
|
|
257
257
|
assoc_html_name ? "#{assoc_name}-#{link}".html_safe : link
|
258
258
|
end
|
259
259
|
|
260
|
-
def self._brick_index(mode = nil)
|
260
|
+
def self._brick_index(mode = nil, separator = '_')
|
261
261
|
tbl_parts = ((mode == :singular) ? table_name.singularize : table_name).split('.')
|
262
262
|
tbl_parts.shift if ::Brick.apartment_multitenant && tbl_parts.length > 1 && tbl_parts.first == ::Brick.apartment_default_tenant
|
263
263
|
tbl_parts.unshift(::Brick.config.path_prefix) if ::Brick.config.path_prefix
|
264
|
-
index = tbl_parts.map(&:underscore).join(
|
264
|
+
index = tbl_parts.map(&:underscore).join(separator)
|
265
265
|
# Rails applies an _index suffix to that route when the resource name is singular
|
266
266
|
index << '_index' if mode != :singular && index == index.singularize
|
267
267
|
index
|
@@ -396,6 +396,8 @@ module ActiveRecord
|
|
396
396
|
end
|
397
397
|
|
398
398
|
class Relation
|
399
|
+
attr_accessor :_brick_page_num
|
400
|
+
|
399
401
|
# Links from ActiveRecord association pathing names over to real table correlation names
|
400
402
|
# that get chosen when the AREL AST tree is walked.
|
401
403
|
def brick_links
|
@@ -785,8 +787,21 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
|
|
785
787
|
end
|
786
788
|
self.order_values |= final_order_by # Same as: order!(*final_order_by)
|
787
789
|
end
|
788
|
-
|
789
|
-
|
790
|
+
if (page = params['_brick_page']&.to_i)
|
791
|
+
page = 1 if page < 1
|
792
|
+
limit = params['_brick_page_size'] || 1000
|
793
|
+
offset = (page - 1) * limit.to_i
|
794
|
+
else
|
795
|
+
offset = params['_brick_offset']
|
796
|
+
limit = params['_brick_limit']
|
797
|
+
end
|
798
|
+
if offset.is_a?(Numeric) || offset&.present?
|
799
|
+
offset = offset.to_i
|
800
|
+
self.offset_value = offset unless offset == 0
|
801
|
+
@_brick_page_num = (offset / limit.to_i) + 1 if limit&.!= 0 && (offset % limit.to_i) == 0
|
802
|
+
end
|
803
|
+
# By default just 1000 rows (Like doing: limit!(1000) but this way is compatible with AR <= 4.2)
|
804
|
+
self.limit_value = limit&.to_i || 1000 unless limit.is_a?(String) && limit.empty?
|
790
805
|
wheres unless wheres.empty? # Return the specific parameters that we did use
|
791
806
|
end
|
792
807
|
|
@@ -1511,7 +1526,11 @@ class Object
|
|
1511
1526
|
self.protect_from_forgery unless: -> { self.request.format.js? }
|
1512
1527
|
unless is_avo
|
1513
1528
|
self.define_method :index do
|
1514
|
-
|
1529
|
+
current_api_root = ::Brick.config.api_roots.find do |ar|
|
1530
|
+
request.path.start_with?(ar) || # Exact match?
|
1531
|
+
request.path.split('/')[-2] == ar.split('/').last # Version at least matches?
|
1532
|
+
end
|
1533
|
+
if (current_api_root || is_openapi) &&
|
1515
1534
|
!params&.key?('_brick_schema') &&
|
1516
1535
|
(referrer_params = request.env['HTTP_REFERER']&.split('?')&.last&.split('&')&.map { |x| x.split('=') }).present?
|
1517
1536
|
if params
|
@@ -1523,6 +1542,7 @@ class Object
|
|
1523
1542
|
_schema, @_is_show_schema_list = ::Brick.set_db_schema(params || api_params)
|
1524
1543
|
|
1525
1544
|
if is_openapi
|
1545
|
+
current_api_ver = current_api_root.split('/').last&.[](1..-1).to_i
|
1526
1546
|
json = { 'openapi': '3.0.1', 'info': { 'title': Rswag::Ui.config.config_object[:urls].last&.fetch(:name, 'API documentation'), 'version': ::Brick.config.api_version },
|
1527
1547
|
'servers': [
|
1528
1548
|
{ 'url': '{scheme}://{defaultHost}', 'variables': {
|
@@ -1531,15 +1551,19 @@ class Object
|
|
1531
1551
|
} }
|
1532
1552
|
]
|
1533
1553
|
}
|
1534
|
-
json['paths'] = relations.
|
1554
|
+
json['paths'] = relations.each_with_object({}) do |relation, s|
|
1535
1555
|
unless ::Brick.config.enable_api == false
|
1556
|
+
next if (api_vers = relation.last.fetch(:api, nil)) &&
|
1557
|
+
!(api_ver_path = api_vers[current_api_ver])
|
1558
|
+
|
1559
|
+
relation_name = api_ver_path || relation.first.tr('.', '/')
|
1536
1560
|
table_description = relation.last[:description]
|
1537
|
-
s["#{
|
1561
|
+
s["#{current_api_root}#{relation_name}"] = {
|
1538
1562
|
'get': {
|
1539
1563
|
'summary': "list #{relation.first}",
|
1540
1564
|
'description': table_description,
|
1541
1565
|
'parameters': relation.last[:cols].map do |k, v|
|
1542
|
-
param = { 'name' => k, 'schema': { 'type': v.first } }
|
1566
|
+
param = { in: 'query', 'name' => k, 'schema': { 'type': v.first } }
|
1543
1567
|
if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k, nil))
|
1544
1568
|
param['description'] = col_descrip
|
1545
1569
|
end
|
@@ -1549,7 +1573,7 @@ class Object
|
|
1549
1573
|
}
|
1550
1574
|
}
|
1551
1575
|
|
1552
|
-
s["#{
|
1576
|
+
s["#{current_api_root}#{relation_name}/{id}"] = {
|
1553
1577
|
'patch': {
|
1554
1578
|
'summary': "update a #{relation.first.singularize}",
|
1555
1579
|
'description': table_description,
|
@@ -1563,7 +1587,6 @@ class Object
|
|
1563
1587
|
'responses': { '200': { 'description': 'successful' } }
|
1564
1588
|
}
|
1565
1589
|
} unless relation.last.fetch(:isView, nil)
|
1566
|
-
s
|
1567
1590
|
end
|
1568
1591
|
end
|
1569
1592
|
render inline: json.to_json, content_type: request.format
|
@@ -1577,9 +1600,10 @@ class Object
|
|
1577
1600
|
end
|
1578
1601
|
render inline: exported_csv, content_type: request.format
|
1579
1602
|
return
|
1580
|
-
elsif request.format == :js ||
|
1603
|
+
elsif request.format == :js || current_api_root # Asking for JSON?
|
1604
|
+
# %%% Add: where, order, page, page_size, offset, limit
|
1581
1605
|
data = (model.is_view? || !Object.const_defined?('DutyFree')) ? model.limit(1000) : model.df_export(model.brick_import_template)
|
1582
|
-
render inline: data.to_json, content_type: request.format == '*/*' ? 'application/json' : request.format
|
1606
|
+
render inline: { data: data }.to_json, content_type: request.format == '*/*' ? 'application/json' : request.format
|
1583
1607
|
return
|
1584
1608
|
end
|
1585
1609
|
|
@@ -228,8 +228,8 @@ window.addEventListener(\"popstate\", linkSchemas);
|
|
228
228
|
# When available, add a clickable brick icon to go to the Brick version of the page
|
229
229
|
PanelComponent.class_exec do
|
230
230
|
alias _brick_init initialize
|
231
|
-
def initialize(*args)
|
232
|
-
_brick_init(*args)
|
231
|
+
def initialize(*args, **kwargs)
|
232
|
+
_brick_init(*args, **kwargs)
|
233
233
|
@name = BrickTitle.new(@name, self)
|
234
234
|
end
|
235
235
|
end
|
@@ -263,6 +263,16 @@ window.addEventListener(\"popstate\", linkSchemas);
|
|
263
263
|
_brick_resource_view_path
|
264
264
|
end
|
265
265
|
end
|
266
|
+
|
267
|
+
module Concerns::HasFields
|
268
|
+
class_methods do
|
269
|
+
alias _brick_field field
|
270
|
+
def field(name, *args, **kwargs, &block)
|
271
|
+
kwargs.merge!(args.pop) if args.last.is_a?(Hash)
|
272
|
+
_brick_field(name, **kwargs, &block)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
266
276
|
end # module Avo
|
267
277
|
|
268
278
|
# Steer any Avo-related controller/action based URL lookups to the Avo RouteSet
|
@@ -454,6 +464,12 @@ window.addEventListener(\"popstate\", linkSchemas);
|
|
454
464
|
table_options << "<option value=\"#{prefix}brick_orphans\">(Orphans)</option>".html_safe if is_orphans
|
455
465
|
table_options << "<option value=\"#{prefix}brick_orphans\">(Crosstab)</option>".html_safe if is_crosstab
|
456
466
|
css = +"<style>
|
467
|
+
#titleSticky {
|
468
|
+
position: sticky;
|
469
|
+
display: inline-block;
|
470
|
+
left: 0;
|
471
|
+
}
|
472
|
+
|
457
473
|
h1, h3 {
|
458
474
|
margin-bottom: 0;
|
459
475
|
}
|
@@ -465,7 +481,6 @@ h1, h3 {
|
|
465
481
|
cursor: pointer;
|
466
482
|
}
|
467
483
|
#mermaidErd {
|
468
|
-
position: relative;
|
469
484
|
display: none;
|
470
485
|
}
|
471
486
|
#mermaidErd .exclude {
|
@@ -866,6 +881,7 @@ if (grid) {
|
|
866
881
|
// });
|
867
882
|
}
|
868
883
|
function setHeaderSizes() {
|
884
|
+
document.getElementById(\"titleBox\").style.width = grid.clientWidth;
|
869
885
|
// console.log(\"start\");
|
870
886
|
// See if the headerTop is already populated
|
871
887
|
// %%% Grab the TRs from headerTop, clear it out, do this stuff, add them back
|
@@ -1112,13 +1128,16 @@ erDiagram
|
|
1112
1128
|
%></title>
|
1113
1129
|
</head>
|
1114
1130
|
<body>
|
1131
|
+
<div id=\"titleBox\"><div id=\"titleSticky\">
|
1115
1132
|
<p style=\"color: green\"><%= notice %></p>#{"
|
1116
1133
|
#{schema_options}" if schema_options}
|
1117
1134
|
<select id=\"tbl\">#{table_options}</select>
|
1118
1135
|
<table id=\"resourceName\"><tr>
|
1119
|
-
<td><h1><%=
|
1136
|
+
<td><h1><%= td_count = 2
|
1137
|
+
model.name %></h1></td>
|
1120
1138
|
<td id=\"imgErd\" title=\"Show ERD\"></td>
|
1121
|
-
<% if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace)
|
1139
|
+
<% if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace)
|
1140
|
+
td_count += 1 %>
|
1122
1141
|
<td><%= link_to_brick(
|
1123
1142
|
avo_svg,
|
1124
1143
|
{ index_proc: Proc.new do |avo_model|
|
@@ -1127,7 +1146,9 @@ erDiagram
|
|
1127
1146
|
title: \"#\{model.name} in Avo\" }
|
1128
1147
|
) %></td>
|
1129
1148
|
<% end %>
|
1130
|
-
</tr
|
1149
|
+
</tr><%= if (page_num = @#{table_name}._brick_page_num)
|
1150
|
+
\"<tr><td colspan=\\\"#\{td_count}\\\">Page #\{page_num}</td></tr>\".html_safe
|
1151
|
+
end %></table>#{template_link}<%
|
1131
1152
|
if description.present? %><%=
|
1132
1153
|
description %><br><%
|
1133
1154
|
end
|
@@ -1162,6 +1183,7 @@ erDiagram
|
|
1162
1183
|
});
|
1163
1184
|
</script>
|
1164
1185
|
<% end %>
|
1186
|
+
</div></div>
|
1165
1187
|
#{erd_markup}
|
1166
1188
|
|
1167
1189
|
<%= # Consider getting the name from the association -- hm.first.name -- if a more \"friendly\" alias should be used for a screwy table name
|
@@ -1327,7 +1349,7 @@ end
|
|
1327
1349
|
options[:url] = send(\"#\{#{model_name}._brick_index(:singular)}_path\".to_sym, obj) if ::Brick.config.path_prefix
|
1328
1350
|
%>
|
1329
1351
|
<br><br>
|
1330
|
-
|
1352
|
+
<%= form_for(obj.becomes(#{model_name}), options) do |f| %>
|
1331
1353
|
<table class=\"shadow\">
|
1332
1354
|
<% has_fields = false
|
1333
1355
|
@#{obj_name}.attributes.each do |k, val|
|
@@ -1432,7 +1454,7 @@ end
|
|
1432
1454
|
is_revert = false %>
|
1433
1455
|
<% else %>
|
1434
1456
|
<%= is_revert = false
|
1435
|
-
display_value(col_type, val) %>
|
1457
|
+
display_value(col_type, val).html_safe %>
|
1436
1458
|
<% end
|
1437
1459
|
end
|
1438
1460
|
if is_revert
|
@@ -1449,7 +1471,7 @@ end
|
|
1449
1471
|
<tr><td colspan=\"2\">(No displayable fields)</td></tr>
|
1450
1472
|
<% end %>
|
1451
1473
|
</table>
|
1452
|
-
<%
|
1474
|
+
<% end %>
|
1453
1475
|
|
1454
1476
|
#{unless args.first == 'new'
|
1455
1477
|
# Was: confirm_are_you_sure = ActionView.version < ::Gem::Version.new('7.0') ? "data: { confirm: 'Delete #\{model_name} -- Are you sure?' }" : "form: { data: { turbo_confirm: 'Delete #\{model_name} -- Are you sure?' } }"
|
@@ -1538,7 +1560,7 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
|
|
1538
1560
|
if (imgErd) imgErd.addEventListener(\"click\", showErd);
|
1539
1561
|
function showErd() {
|
1540
1562
|
imgErd.style.display = \"none\";
|
1541
|
-
mermaidErd.style.display = \"
|
1563
|
+
mermaidErd.style.display = \"block\";
|
1542
1564
|
if (mermaidCode) return; // Cut it short if we've already rendered the diagram
|
1543
1565
|
|
1544
1566
|
mermaidCode = document.createElement(\"SCRIPT\");
|
@@ -56,85 +56,85 @@ module Brick::Rails::FormTags
|
|
56
56
|
end
|
57
57
|
out << "</tr></thead>
|
58
58
|
<tbody>"
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
else
|
102
|
-
hm_klass = (col = cols[col_name])[1]
|
103
|
-
if col[2] == 'HO'
|
104
|
-
descrips = bt_descrip[col_name.to_sym][hm_klass]
|
105
|
-
if (ho_id = (ho_id_col = descrips.last).map { |id_col| obj.send(id_col.to_sym) })&.first
|
106
|
-
ho_txt = hm_klass.brick_descrip(obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, ho_id_col)
|
107
|
-
out << link_to(ho_txt, send("#{hm_klass.base_class._brick_index(:singular)}_path".to_sym, ho_id))
|
108
|
-
end
|
59
|
+
# %%% Have once gotten this error with MSSQL referring to http://localhost:3000/warehouse/cold_room_temperatures__archive
|
60
|
+
# ActiveRecord::StatementTimeout in Warehouse::ColdRoomTemperatures_Archive#index
|
61
|
+
# TinyTds::Error: Adaptive Server connection timed out
|
62
|
+
# (After restarting the server it worked fine again.)
|
63
|
+
relation.each do |obj|
|
64
|
+
out << "<tr>\n"
|
65
|
+
out << "<td>#{link_to('⇛', send("#{klass._brick_index(:singular)}_path".to_sym,
|
66
|
+
pk.map { |pk_part| obj.send(pk_part.to_sym) }), { class: 'big-arrow' })}</td>\n" if pk.present?
|
67
|
+
sequence.each do |col_name|
|
68
|
+
val = obj.attributes[col_name]
|
69
|
+
out << '<td'
|
70
|
+
out << ' class=\"dimmed\"' unless cols.key?(col_name) || (cust_col = cust_cols[col_name]) ||
|
71
|
+
(col_name.is_a?(Symbol) && bts.key?(col_name)) # HOT
|
72
|
+
out << '>'
|
73
|
+
if (bt = bts[col_name])
|
74
|
+
if bt[2] # Polymorphic?
|
75
|
+
bt_class = obj.send("#{bt.first}_type")
|
76
|
+
base_class_underscored = (::Brick.existing_stis[bt_class] || bt_class).constantize.base_class._brick_index(:singular)
|
77
|
+
poly_id = obj.send("#{bt.first}_id")
|
78
|
+
out << link_to("#{bt_class} ##{poly_id}", send("#{base_class_underscored}_path".to_sym, poly_id)) if poly_id
|
79
|
+
else # BT or HOT
|
80
|
+
bt_class = bt[1].first.first
|
81
|
+
descrips = bt_descrip[bt.first][bt_class]
|
82
|
+
bt_id_col = if descrips.nil?
|
83
|
+
puts "Caught it in the act for obj / #{col_name}!"
|
84
|
+
elsif descrips.length == 1
|
85
|
+
[obj.class.reflect_on_association(bt.first)&.foreign_key]
|
86
|
+
else
|
87
|
+
descrips.last
|
88
|
+
end
|
89
|
+
bt_txt = bt_class.brick_descrip(
|
90
|
+
# 0..62 because Postgres column names are limited to 63 characters
|
91
|
+
obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, bt_id_col
|
92
|
+
)
|
93
|
+
bt_txt = display_binary(bt_txt).html_safe if bt_txt&.encoding&.name == 'ASCII-8BIT'
|
94
|
+
bt_txt ||= "<span class=\"orphan\"><< Orphaned ID: #{val} >></span>" if val
|
95
|
+
bt_id = bt_id_col&.map { |id_col| obj.respond_to?(id_sym = id_col.to_sym) ? obj.send(id_sym) : id_col }
|
96
|
+
out << (bt_id&.first ? link_to(bt_txt, send("#{bt_class.base_class._brick_index(:singular)}_path".to_sym, bt_id)) : bt_txt || '')
|
97
|
+
end
|
98
|
+
elsif (hms_col = hms_cols[col_name])
|
99
|
+
if hms_col.length == 1
|
100
|
+
out << hms_col.first
|
109
101
|
else
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
102
|
+
hm_klass = (col = cols[col_name])[1]
|
103
|
+
if col[2] == 'HO'
|
104
|
+
descrips = bt_descrip[col_name.to_sym][hm_klass]
|
105
|
+
if (ho_id = (ho_id_col = descrips.last).map { |id_col| obj.send(id_col.to_sym) })&.first
|
106
|
+
ho_txt = hm_klass.brick_descrip(obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, ho_id_col)
|
107
|
+
out << link_to(ho_txt, send("#{hm_klass.base_class._brick_index(:singular)}_path".to_sym, ho_id))
|
108
|
+
end
|
109
|
+
else
|
110
|
+
if (ct = obj.send(hms_col[1].to_sym)&.to_i)&.positive?
|
111
|
+
out << "#{link_to("#{ct || 'View'} #{hms_col.first}",
|
112
|
+
send("#{hm_klass._brick_index}_path".to_sym,
|
113
|
+
hms_col[2].each_with_object({}) { |v, s| s[v.first] = v.last.is_a?(String) ? v.last : obj.send(v.last) })
|
114
|
+
)}\n"
|
115
|
+
end
|
115
116
|
end
|
116
117
|
end
|
118
|
+
elsif (col = cols[col_name]).is_a?(ActiveRecord::ConnectionAdapters::Column)
|
119
|
+
binding.pry if col.is_a?(Array)
|
120
|
+
col_type = col&.sql_type == 'geography' ? col.sql_type : col&.type
|
121
|
+
out << display_value(col_type || col&.sql_type, val).to_s
|
122
|
+
elsif cust_col
|
123
|
+
data = cust_col.first.map { |cc_part| obj.send(cc_part.last) }
|
124
|
+
cust_txt = klass.brick_descrip(cust_col[-2], data)
|
125
|
+
if (link_id = obj.send(cust_col.last[1]) if cust_col.last)
|
126
|
+
out << link_to(cust_txt, send("#{cust_col.last.first._brick_index(:singular)}_path", link_id))
|
127
|
+
else
|
128
|
+
out << (cust_txt || '')
|
129
|
+
end
|
130
|
+
else # Bad column name!
|
131
|
+
out << '?'
|
117
132
|
end
|
118
|
-
|
119
|
-
binding.pry if col.is_a?(Array)
|
120
|
-
col_type = col&.sql_type == 'geography' ? col.sql_type : col&.type
|
121
|
-
out << display_value(col_type || col&.sql_type, val).to_s
|
122
|
-
elsif cust_col
|
123
|
-
data = cust_col.first.map { |cc_part| obj.send(cc_part.last) }
|
124
|
-
cust_txt = klass.brick_descrip(cust_col[-2], data)
|
125
|
-
if (link_id = obj.send(cust_col.last[1]) if cust_col.last)
|
126
|
-
out << link_to(cust_txt, send("#{cust_col.last.first._brick_index(:singular)}_path", link_id))
|
127
|
-
else
|
128
|
-
out << (cust_txt || '')
|
129
|
-
end
|
130
|
-
else # Bad column name!
|
131
|
-
out << '?'
|
133
|
+
out << '</td>'
|
132
134
|
end
|
133
|
-
out << '</
|
135
|
+
out << '</tr>'
|
134
136
|
end
|
135
|
-
|
136
|
-
end
|
137
|
-
out << " </tbody>
|
137
|
+
out << " </tbody>
|
138
138
|
</table>
|
139
139
|
"
|
140
140
|
out.html_safe
|
@@ -143,6 +143,16 @@ module Brick::Rails::FormTags
|
|
143
143
|
def link_to_brick(*args, **kwargs)
|
144
144
|
return unless ::Brick.config.mode == :on
|
145
145
|
|
146
|
+
kwargs.merge!(args.pop) if args.last.is_a?(Hash)
|
147
|
+
# Avoid infinite recursion
|
148
|
+
if (visited = kwargs.fetch(:visited, nil))
|
149
|
+
return if visited.key?(object_id)
|
150
|
+
|
151
|
+
kwargs[:visited][object_id] = nil
|
152
|
+
else
|
153
|
+
kwargs[:visited] = {}
|
154
|
+
end
|
155
|
+
|
146
156
|
text = ((args.first.is_a?(String) || args.first.is_a?(Proc)) && args.shift) || args[1]
|
147
157
|
text = text.call if text.is_a?(Proc)
|
148
158
|
klass_or_obj = ((args.first.is_a?(ActiveRecord::Relation) ||
|
@@ -192,13 +202,14 @@ module Brick::Rails::FormTags
|
|
192
202
|
app_routes = Rails.application.routes # In case we're operating in another engine, reference the application since Brick routes are placed there.
|
193
203
|
if (klass_or_obj&.is_a?(Class) && klass_or_obj < ActiveRecord::Base) ||
|
194
204
|
(klass_or_obj&.is_a?(ActiveRecord::Base) && klass_or_obj.new_record? && (klass_or_obj = klass_or_obj.class))
|
195
|
-
path = (proc = kwargs[:index_proc]) ? proc.call(klass_or_obj) : "#{app_routes.path_for(controller: klass_or_obj.base_class._brick_index, action: :index)}#{filter}"
|
205
|
+
path = (proc = kwargs[:index_proc]) ? proc.call(klass_or_obj) : "#{app_routes.path_for(controller: klass_or_obj.base_class._brick_index(nil, '/'), action: :index)}#{filter}"
|
196
206
|
lt_args = [text || "Index for #{klass_or_obj.name.pluralize}", path]
|
197
207
|
else
|
198
208
|
# 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
|
199
|
-
path = (proc = kwargs[:show_proc]) ? proc.call(klass_or_obj) : "#{app_routes.path_for(controller: klass_or_obj.class.base_class._brick_index, action: :show, id: klass_or_obj)}#{filter}"
|
209
|
+
path = (proc = kwargs[:show_proc]) ? proc.call(klass_or_obj) : "#{app_routes.path_for(controller: klass_or_obj.class.base_class._brick_index(nil, '/'), action: :show, id: klass_or_obj)}#{filter}"
|
200
210
|
lt_args = [text || "Show this #{klass_or_obj.class.name}", path]
|
201
211
|
end
|
212
|
+
kwargs.delete(:visited)
|
202
213
|
link_to(*lt_args, **kwargs)
|
203
214
|
else
|
204
215
|
# puts "Warning: link_to_brick could not find a class for \"#{controller_path}\" -- consider setting @_brick_model within that controller."
|
@@ -215,7 +226,7 @@ module Brick::Rails::FormTags
|
|
215
226
|
if links.length == 1 # If there's only one match then use any text that was supplied
|
216
227
|
link_to_brick(text || links.first.last.join('/'), links.first.first, **kwargs)
|
217
228
|
else
|
218
|
-
links.
|
229
|
+
links.each_with_object([]) { |v, s| s << link if link = link_to_brick(v.join('/'), v, **kwargs) }.join(' ').html_safe
|
219
230
|
end
|
220
231
|
end
|
221
232
|
end # link_to_brick
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -334,12 +334,17 @@ module Brick
|
|
334
334
|
|
335
335
|
# @api public
|
336
336
|
def api_root=(path)
|
337
|
-
Brick.config.
|
337
|
+
Brick.config.api_roots = [path]
|
338
338
|
end
|
339
339
|
|
340
340
|
# @api public
|
341
|
-
def
|
342
|
-
Brick.config.
|
341
|
+
def api_roots=(paths)
|
342
|
+
Brick.config.api_roots = paths
|
343
|
+
end
|
344
|
+
|
345
|
+
# @api public
|
346
|
+
def api_roots
|
347
|
+
Brick.config.api_roots
|
343
348
|
end
|
344
349
|
|
345
350
|
# @api public
|
@@ -657,19 +662,65 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
657
662
|
# Turn something like {"::Spouse"=>"Person", "::Friend"=>"Person"} into {"Person"=>["Spouse", "Friend"]}
|
658
663
|
s[v.last] << v.first[2..-1] unless v.first.end_with?('::')
|
659
664
|
end
|
665
|
+
versioned_views = {} # Track which views have already been done for each api_root
|
660
666
|
::Brick.relations.each do |k, v|
|
661
667
|
next if !(controller_name = v.fetch(:resource, nil)&.pluralize) || existing_controllers.key?(controller_name)
|
662
668
|
|
669
|
+
schema_name = v.fetch(:schema, nil)
|
663
670
|
options = {}
|
664
671
|
options[:only] = [:index, :show] if v.key?(:isView)
|
665
|
-
# First do the API routes
|
672
|
+
# First do the API routes if necessary
|
666
673
|
full_resource = nil
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
#
|
672
|
-
|
674
|
+
::Brick.api_roots&.each do |api_root|
|
675
|
+
api_done_views = (versioned_views[api_root] ||= {})
|
676
|
+
found = nil
|
677
|
+
view_relation = nil
|
678
|
+
# If it's a view then see if there's a versioned one available by searching for resource names
|
679
|
+
# versioned with the closest number (equal to or less than) compared with our API version number.
|
680
|
+
if v.key?(:isView) && (ver = k.match(/^v([\d_]*)/).captures.first)[-1] == '_'
|
681
|
+
next if api_done_views.key?(unversioned = k[ver.length + 1..-1])
|
682
|
+
|
683
|
+
# # if ().length.positive? # Does it have a version number?
|
684
|
+
# try_num = (ver_num = (ver = ver[1..-1].gsub('_', '.')).to_d)
|
685
|
+
|
686
|
+
# Expect that the last item in the path generally holds versioning information
|
687
|
+
api_ver = api_root.split('/')[-1]&.gsub('_', '.')
|
688
|
+
vn_idx = api_ver.rindex(/[^\d._]/) # Position of the first numeric digit at the end of the version number
|
689
|
+
# Was: .to_d
|
690
|
+
api_ver_num = api_ver[vn_idx + 1..-1].gsub('_', '.').to_i # Attempt to turn something like "v3" into the decimal value 3
|
691
|
+
# puts [api_ver, vn_idx, api_ver_num, unversioned].inspect
|
692
|
+
|
693
|
+
next if ver.to_i > api_ver_num # Don't surface any newer views in an older API
|
694
|
+
|
695
|
+
api_ver_num -= 1 until api_ver_num.zero? ||
|
696
|
+
(view_relation = ::Brick.relations.fetch(
|
697
|
+
found = "v#{api_ver_num}_#{k[ver.length + 1..-1]}", nil
|
698
|
+
))
|
699
|
+
api_done_views[unversioned] = nil # Mark that for this API version this view is done
|
700
|
+
|
701
|
+
# puts "Found #{found}" if view_relation
|
702
|
+
# If we haven't found "v3_view_name" or "v2_view_name" or so forth, at the last
|
703
|
+
# fall back to simply looking for "v_view_name", and then finally "view_name".
|
704
|
+
unversioned = "v_#{unversioned}"
|
705
|
+
view_relation ||= ::Brick.relations.fetch(found = unversioned,
|
706
|
+
::Brick.relations.fetch(found = unversioned, nil)
|
707
|
+
)
|
708
|
+
if found && view_relation && k != (found = unversioned)
|
709
|
+
view_relation[:api][api_ver_num] = found
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
# view_ver_num = if (first_part = k.split('_').first) =~ /^v[\d_]+/
|
714
|
+
# first_part[1..-1].gsub('_', '.').to_i
|
715
|
+
# end
|
716
|
+
controller_name = view_relation.fetch(:resource, nil)&.pluralize if view_relation
|
717
|
+
if schema_name
|
718
|
+
full_resource = "#{schema_name}/#{found || v[:resource]}"
|
719
|
+
send(:get, "#{api_root}#{full_resource}", { to: "#{controller_prefix}#{schema_name}/#{controller_name}#index" })
|
720
|
+
else
|
721
|
+
# Normally goes to something like: /api/v1/employees
|
722
|
+
send(:get, "#{api_root}#{found || v[:resource]}", { to: "#{controller_prefix}#{controller_name}#index" })
|
723
|
+
end
|
673
724
|
end
|
674
725
|
|
675
726
|
# Track routes being built
|
@@ -716,15 +767,19 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
716
767
|
unless ::Brick.routes_done
|
717
768
|
if Object.const_defined?('Rswag::Ui')
|
718
769
|
rswag_path = ::Rails.application.routes.routes.find { |r| r.app.app == Rswag::Ui::Engine }&.instance_variable_get(:@path_formatter)&.instance_variable_get(:@parts)&.join
|
719
|
-
|
770
|
+
first_endpoint_parts = nil
|
771
|
+
(doc_endpoints = Rswag::Ui.config.config_object[:urls]&.uniq!)&.each do |doc_endpoint|
|
720
772
|
puts "Mounting OpenApi 3.0 documentation endpoint for \"#{doc_endpoint[:name]}\" on #{doc_endpoint[:url]}"
|
721
773
|
send(:get, doc_endpoint[:url], { to: 'brick_openapi#index' })
|
722
774
|
endpoint_parts = doc_endpoint[:url]&.split('/')
|
723
|
-
|
724
|
-
|
775
|
+
first_endpoint_parts ||= endpoint_parts
|
776
|
+
end
|
777
|
+
if doc_endpoints.present?
|
778
|
+
if rswag_path && first_endpoint_parts
|
779
|
+
puts "API documentation now available when navigating to: /#{first_endpoint_parts&.find(&:present?)}/index.html"
|
725
780
|
else
|
726
781
|
puts "In order to make documentation available you can put this into your routes.rb:"
|
727
|
-
puts " mount Rswag::Ui::Engine => '/#{
|
782
|
+
puts " mount Rswag::Ui::Engine => '/#{first_endpoint_parts&.find(&:present?) || 'api-docs'}'"
|
728
783
|
end
|
729
784
|
else
|
730
785
|
sample_path = rswag_path || '/api-docs'
|
@@ -159,8 +159,9 @@ if ActiveRecord::Base.respond_to?(:brick_select)
|
|
159
159
|
# Brick.enable_views = true # Setting this to \"false\" will disable views in development
|
160
160
|
|
161
161
|
# # If The Brick sees that RSwag gem is present, it allows for API resources to be automatically served out.
|
162
|
-
# # You can configure
|
163
|
-
#
|
162
|
+
# # You can configure one or more root path(s) for these resources, and when there are multiple then an attempt
|
163
|
+
# # is made to return data from that version of the view or table name, or the most recent prior to that version:
|
164
|
+
# ::Brick.api_roots = ['/api/v1/']
|
164
165
|
# # You may also want to add an OpenAPI 3.0 documentation endpoint using Rswag::Ui:
|
165
166
|
# Rswag::Ui.configure do |config|
|
166
167
|
# config.swagger_endpoint '/api-docs/v1/swagger.json', 'API V1 Docs'
|
@@ -288,16 +289,18 @@ if ActiveRecord::Base.respond_to?(:brick_select)
|
|
288
289
|
|
289
290
|
# # POLYMORPHIC ASSOCIATIONS
|
290
291
|
|
291
|
-
# #
|
292
|
-
|
292
|
+
# # Polymorphic associations are set up by providing a model name and polymorphic association name#{poly}
|
293
|
+
|
294
|
+
# # For multi-tenant databases that use a separate schema for each tenant, a single representative database schema
|
295
|
+
# # can be analysed to determine the range of polymorphic classes that can be used for each association. Hopefully
|
296
|
+
# # the schema chosen is one loaded with existing data that is representative of all possible polymorphic
|
297
|
+
# # associations.
|
293
298
|
# Brick.schema_behavior = :namespaced
|
294
299
|
#{Brick.config.schema_behavior.present? ? " Brick.schema_behavior = { multitenant: { schema_to_analyse: #{
|
295
300
|
Brick.config.schema_behavior[:multitenant]&.fetch(:schema_to_analyse, nil).inspect}" :
|
296
301
|
" # Brick.schema_behavior = { multitenant: { schema_to_analyse: 'engineering'"
|
297
302
|
} } }
|
298
303
|
|
299
|
-
# # Polymorphic associations are set up by providing a model name and polymorphic association name#{poly}
|
300
|
-
|
301
304
|
# # DEFAULT ROOT ROUTE
|
302
305
|
|
303
306
|
# # If a default route is not supplied, Brick attempts to find the most \"central\" table and wires up the default
|
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.105
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lorin Thwaits
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-01-
|
11
|
+
date: 2023-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|