brick 1.0.97 → 1.0.98
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/brick/extensions.rb +165 -178
- data/lib/brick/frameworks/rails/controller.rb +1 -1
- data/lib/brick/frameworks/rails/engine.rb +269 -197
- data/lib/brick/frameworks/rails/form_tags.rb +219 -0
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +7 -6
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4285ef6fb81d75bd90ceffc7d66c7fa096f04fb998da590f291f28fc9dfb45d
|
4
|
+
data.tar.gz: 2d031b156fd9c82d19af9b7818387eaecdd8cfadf939cbcf3b3b479474a77b54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5967ede119f86cd92f88abb0f43bd2049eda4272c14ae7fa1c3f9ecc54c813c4c227be2d99aaa501363e42ef34a3eb5d0a0924ac4e540ab668e40a9a9d28311e
|
7
|
+
data.tar.gz: ab01005e485b9335c67ba48bb01a13db78fed3ce31293122fae8fbf1317b3bc0c3197adb087ec4b125e2ff7153e52fb55d8b7fc675b1e7759233cf8d42769ac7
|
data/lib/brick/extensions.rb
CHANGED
@@ -540,7 +540,11 @@ module ActiveRecord
|
|
540
540
|
tbl_name = "\"#{tbl_name}\"" if ::Brick.is_oracle && rel_dupe.brick_links.values.include?(tbl_name)
|
541
541
|
field_tbl_name = nil
|
542
542
|
v1.map { |x| [x[0..-2].map(&:to_s).join('.'), x.last] }.each_with_index do |sel_col, idx|
|
543
|
-
|
543
|
+
# %%% Strangely in Rails 7.1 on a slower system then very rarely brick_link comes back nil...
|
544
|
+
brick_link = rel_dupe.brick_links[sel_col.first]
|
545
|
+
field_tbl_name = brick_link&.split('.')&.last ||
|
546
|
+
# ... so here's a best-effort guess for what the table name might be.
|
547
|
+
rel_dupe.klass.reflect_on_association(sel_col.first).klass.table_name
|
544
548
|
# If it's Oracle, quote any AREL aliases that had been applied
|
545
549
|
field_tbl_name = "\"#{field_tbl_name}\"" if ::Brick.is_oracle && rel_dupe.brick_links.values.include?(field_tbl_name)
|
546
550
|
|
@@ -825,80 +829,9 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
|
|
825
829
|
end
|
826
830
|
|
827
831
|
if Object.const_defined?('ActionView')
|
832
|
+
require 'brick/frameworks/rails/form_tags'
|
828
833
|
module ActionView::Helpers::FormTagHelper
|
829
|
-
|
830
|
-
return unless ::Brick.config.mode == :on
|
831
|
-
|
832
|
-
text = (args.first.is_a?(String) && args.first) || args[1]
|
833
|
-
klass_or_obj = ((args.first.is_a?(ActiveRecord::Relation) ||
|
834
|
-
args.first.is_a?(ActiveRecord::Base) ||
|
835
|
-
args.first.is_a?(Class)) &&
|
836
|
-
args.first) ||
|
837
|
-
@_brick_model
|
838
|
-
# If not provided, do a best-effort to automatically determine the resource class or object
|
839
|
-
filter_parts = []
|
840
|
-
klass_or_obj ||= begin
|
841
|
-
klass, sti_type = ::Brick.ctrl_to_klass(controller_path)
|
842
|
-
if klass
|
843
|
-
type_col = klass.inheritance_column # Usually 'type'
|
844
|
-
filter_parts << "#{type_col}=#{sti_type}" if sti_type && klass.column_names.include?(type_col)
|
845
|
-
path_params = request.path_parameters.dup
|
846
|
-
path_params.delete(:controller)
|
847
|
-
path_params.delete(:action)
|
848
|
-
pk = (klass.primary_key || ActiveRecord::Base.primary_key).to_sym
|
849
|
-
# Used to also have this but it's a bit too permissive to identify a primary key: (path_params.length == 1 && path_params.values.first) ||
|
850
|
-
if ((id = (path_params[pk] || path_params[:id] || path_params["#{klass.name.underscore}_id".to_sym])) && (obj = klass.find_by(pk => id))) ||
|
851
|
-
(['show', 'edit', 'update', 'destroy'].include?(action_name) && (obj = klass.first))
|
852
|
-
obj
|
853
|
-
else
|
854
|
-
# %%% If there is a HMT that refers to some ___id then try to identify an appropriate filter
|
855
|
-
# %%% If there is a polymorphic association that might relate to stuff in the path_params,
|
856
|
-
# try to identify an appropriate ___able_id and ___able_type filter
|
857
|
-
((klass.column_names - [pk.to_s]) & path_params.keys.map(&:to_s)).each do |path_param|
|
858
|
-
filter_parts << "#{path_param}=#{path_params[path_param.to_sym]}"
|
859
|
-
end
|
860
|
-
klass
|
861
|
-
end
|
862
|
-
end
|
863
|
-
rescue
|
864
|
-
end
|
865
|
-
if klass_or_obj
|
866
|
-
if klass_or_obj.is_a?(ActiveRecord::Relation)
|
867
|
-
klass_or_obj.where_values_hash.each do |whr|
|
868
|
-
filter_parts << "#{whr.first}=#{whr.last}" if whr.last && !whr.last.is_a?(Array)
|
869
|
-
end
|
870
|
-
klass_or_obj = klass_or_obj.klass
|
871
|
-
type_col = klass_or_obj.inheritance_column
|
872
|
-
if klass_or_obj.column_names.include?(type_col) && klass_or_obj.name != klass_or_obj.base_class.name
|
873
|
-
filter_parts << "#{type_col}=#{klass_or_obj.name}"
|
874
|
-
end
|
875
|
-
elsif klass_or_obj.is_a?(ActiveRecord::Base) && klass_or_obj.new_record?
|
876
|
-
klass_or_obj = klass_or_obj.class
|
877
|
-
end
|
878
|
-
filter = "?#{filter_parts.join('&')}" if filter_parts.present?
|
879
|
-
if (klass_or_obj&.is_a?(Class) && klass_or_obj < ActiveRecord::Base) ||
|
880
|
-
(klass_or_obj&.is_a?(ActiveRecord::Base) && klass_or_obj.new_record? && (klass_or_obj = klass_or_obj.class))
|
881
|
-
lt_args = [text || "Index for #{klass_or_obj.name.pluralize}",
|
882
|
-
"#{send("#{klass_or_obj._brick_index}_path")}#{filter}"]
|
883
|
-
else
|
884
|
-
# 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
|
885
|
-
lt_args = [text || "Show this #{klass_or_obj.class.name}",
|
886
|
-
"#{send("#{klass_or_obj.class._brick_index(:singular)}_path", klass_or_obj)}#{filter}"]
|
887
|
-
end
|
888
|
-
link_to(*lt_args, **kwargs)
|
889
|
-
else
|
890
|
-
# puts "Warning: link_to_brick could not find a class for \"#{controller_path}\" -- consider setting @_brick_model within that controller."
|
891
|
-
# if (hits = res_names.keys & instance_variables.map { |v| v.to_s[1..-1] }).present?
|
892
|
-
links = instance_variables.each_with_object([]) do |name, s|
|
893
|
-
iv_name = name.to_s[1..-1]
|
894
|
-
case (val = instance_variable_get(name))
|
895
|
-
when ActiveRecord::Relation, ActiveRecord::Base
|
896
|
-
s << link_to_brick(val, iv_name) if val
|
897
|
-
end
|
898
|
-
end
|
899
|
-
links.join(' ').html_safe
|
900
|
-
end
|
901
|
-
end
|
834
|
+
include ::Brick::Rails::FormTags
|
902
835
|
end
|
903
836
|
end
|
904
837
|
|
@@ -1002,6 +935,7 @@ Module.class_exec do
|
|
1002
935
|
end
|
1003
936
|
class_name = ::Brick.namify(requested)
|
1004
937
|
relations = ::Brick.relations
|
938
|
+
# CONTROLLER
|
1005
939
|
result = if ::Brick.enable_controllers? &&
|
1006
940
|
is_controller && (plural_class_name = class_name[0..-11]).length.positive?
|
1007
941
|
# Otherwise now it's up to us to fill in the gaps
|
@@ -1020,6 +954,8 @@ Module.class_exec do
|
|
1020
954
|
# if it's a controller and no match or a model doesn't really use the same table name, eager load all models and try to find a model class of the right name.
|
1021
955
|
Object.send(:build_controller, self, class_name, plural_class_name, model, relations)
|
1022
956
|
end
|
957
|
+
|
958
|
+
# MODULE
|
1023
959
|
elsif (::Brick.enable_models? || ::Brick.enable_controllers?) && # Schema match?
|
1024
960
|
base_module == Object && # %%% This works for Person::Person -- but also limits us to not being able to allow more than one level of namespacing
|
1025
961
|
(schema_name = [(singular_table_name = class_name.underscore),
|
@@ -1036,6 +972,50 @@ Module.class_exec do
|
|
1036
972
|
|
1037
973
|
[built_module, "module #{schema_name}; end\n"]
|
1038
974
|
# # %%% Perhaps an option to use the first module just as schema, and additional modules as namespace with a table name prefix applied
|
975
|
+
|
976
|
+
# AVO Resource
|
977
|
+
elsif base_module == Object && Object.const_defined?('Avo') && requested.end_with?('Resource') &&
|
978
|
+
['MotorResource'].exclude?(requested) # Expect that anything called MotorResource could be from that administrative gem
|
979
|
+
if (model = Object.const_get(requested[0..-9]))
|
980
|
+
require 'generators/avo/resource_generator'
|
981
|
+
field_generator = Generators::Avo::ResourceGenerator.new([''])
|
982
|
+
field_generator.instance_variable_set(:@model, model)
|
983
|
+
fields = field_generator.send(:generate_fields).split("\n")
|
984
|
+
.each_with_object([]) do |f, s|
|
985
|
+
if (f = f.strip).start_with?('field ')
|
986
|
+
f = f[6..-1].split(',')
|
987
|
+
s << [f.first[1..-1].to_sym, [f[1][1..-1].split(': :').map(&:to_sym)].to_h]
|
988
|
+
end
|
989
|
+
end
|
990
|
+
built_resource = Class.new(Avo::BaseResource) do |new_resource_class|
|
991
|
+
self.model_class = model
|
992
|
+
self.title = :brick_descrip
|
993
|
+
self.includes = []
|
994
|
+
if (!model.is_view? && mod_pk = model.primary_key)
|
995
|
+
field((mod_pk.is_a?(Array) ? mod_pk.first : mod_pk).to_sym, { as: :id })
|
996
|
+
end
|
997
|
+
# Create a call such as: field :name, as: :text
|
998
|
+
fields.each do |f|
|
999
|
+
# Add proper types if this is a polymorphic belongs_to
|
1000
|
+
if f.last == { as: :belongs_to } &&
|
1001
|
+
(fk = ::Brick.relations[model.table_name][:fks].find { |k, v| v[:assoc_name] == f.first.to_s }) &&
|
1002
|
+
fk.last.fetch(:polymorphic, nil)
|
1003
|
+
poly_types = fk.last.fetch(:inverse_table, nil)&.each_with_object([]) do |poly_table, s|
|
1004
|
+
s << Object.const_get(::Brick.relations[poly_table][:class_name])
|
1005
|
+
end
|
1006
|
+
if poly_types.present?
|
1007
|
+
f.last[:polymorphic_as] = f.first
|
1008
|
+
f.last[:types] = poly_types
|
1009
|
+
end
|
1010
|
+
end
|
1011
|
+
self.send(:field, *f)
|
1012
|
+
end
|
1013
|
+
end
|
1014
|
+
Object.const_set(requested.to_sym, built_resource)
|
1015
|
+
[built_resource, nil]
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
# MODEL
|
1039
1019
|
elsif ::Brick.enable_models?
|
1040
1020
|
# Custom inheritable Brick base model?
|
1041
1021
|
class_name = (inheritable_name = class_name)[5..-1] if class_name.start_with?('Brick')
|
@@ -1397,14 +1377,19 @@ class Object
|
|
1397
1377
|
end
|
1398
1378
|
|
1399
1379
|
def build_controller(namespace, class_name, plural_class_name, model, relations)
|
1380
|
+
if (is_avo = (namespace.name == 'Avo' && Object.const_defined?('Avo')))
|
1381
|
+
# Basic Avo functionality is available via its own generic controller.
|
1382
|
+
# (More information on https://docs.avohq.io/2.0/controllers.html)
|
1383
|
+
controller_base = Avo::ResourcesController
|
1384
|
+
end
|
1400
1385
|
table_name = ActiveSupport::Inflector.underscore(plural_class_name)
|
1401
1386
|
singular_table_name = ActiveSupport::Inflector.singularize(table_name)
|
1402
1387
|
pk = model&._brick_primary_key(relations.fetch(table_name, nil))
|
1403
1388
|
is_postgres = ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
1404
1389
|
is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2'
|
1405
1390
|
|
1406
|
-
code = +"class #{class_name} < ApplicationController\n"
|
1407
|
-
built_controller = Class.new(ActionController::Base) do |new_controller_class|
|
1391
|
+
code = +"class #{class_name} < #{controller_base&.name || 'ApplicationController'}\n"
|
1392
|
+
built_controller = Class.new(controller_base || ActionController::Base) do |new_controller_class|
|
1408
1393
|
(namespace || Object).const_set(class_name.to_sym, new_controller_class)
|
1409
1394
|
|
1410
1395
|
# Brick-specific pages
|
@@ -1501,118 +1486,120 @@ class Object
|
|
1501
1486
|
end
|
1502
1487
|
|
1503
1488
|
self.protect_from_forgery unless: -> { self.request.format.js? }
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1489
|
+
unless is_avo
|
1490
|
+
self.define_method :index do
|
1491
|
+
if (is_openapi || request.env['REQUEST_PATH'].start_with?(::Brick.api_root)) &&
|
1492
|
+
!params&.key?('_brick_schema') &&
|
1493
|
+
(referrer_params = request.env['HTTP_REFERER']&.split('?')&.last&.split('&')&.map { |x| x.split('=') }).present?
|
1494
|
+
if params
|
1495
|
+
referrer_params.each { |k, v| params.send(:parameters)[k] = v }
|
1496
|
+
else
|
1497
|
+
api_params = referrer_params&.to_h
|
1498
|
+
end
|
1512
1499
|
end
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
}
|
1541
|
-
}
|
1542
|
-
|
1543
|
-
s["#{::Brick.config.api_root}#{relation.first.tr('.', '/')}/{id}"] = {
|
1544
|
-
'patch': {
|
1545
|
-
'summary': "update a #{relation.first.singularize}",
|
1546
|
-
'description': table_description,
|
1547
|
-
'parameters': relation.last[:cols].reject { |k, v| Brick.config.metadata_columns.include?(k) }.map do |k, v|
|
1548
|
-
param = { 'name' => k, 'schema': { 'type': v.first } }
|
1549
|
-
if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k, nil))
|
1550
|
-
param['description'] = col_descrip
|
1551
|
-
end
|
1552
|
-
param
|
1553
|
-
end,
|
1554
|
-
'responses': { '200': { 'description': 'successful' } }
|
1500
|
+
_schema, @_is_show_schema_list = ::Brick.set_db_schema(params || api_params)
|
1501
|
+
|
1502
|
+
if is_openapi
|
1503
|
+
json = { 'openapi': '3.0.1', 'info': { 'title': Rswag::Ui.config.config_object[:urls].last&.fetch(:name, 'API documentation'), 'version': ::Brick.config.api_version },
|
1504
|
+
'servers': [
|
1505
|
+
{ 'url': '{scheme}://{defaultHost}', 'variables': {
|
1506
|
+
'scheme': { 'default': request.env['rack.url_scheme'] },
|
1507
|
+
'defaultHost': { 'default': request.env['HTTP_HOST'] }
|
1508
|
+
} }
|
1509
|
+
]
|
1510
|
+
}
|
1511
|
+
json['paths'] = relations.inject({}) do |s, relation|
|
1512
|
+
unless ::Brick.config.enable_api == false
|
1513
|
+
table_description = relation.last[:description]
|
1514
|
+
s["#{::Brick.config.api_root}#{relation.first.tr('.', '/')}"] = {
|
1515
|
+
'get': {
|
1516
|
+
'summary': "list #{relation.first}",
|
1517
|
+
'description': table_description,
|
1518
|
+
'parameters': relation.last[:cols].map do |k, v|
|
1519
|
+
param = { 'name' => k, 'schema': { 'type': v.first } }
|
1520
|
+
if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k, nil))
|
1521
|
+
param['description'] = col_descrip
|
1522
|
+
end
|
1523
|
+
param
|
1524
|
+
end,
|
1525
|
+
'responses': { '200': { 'description': 'successful' } }
|
1526
|
+
}
|
1555
1527
|
}
|
1556
|
-
|
1557
|
-
|
1528
|
+
|
1529
|
+
s["#{::Brick.config.api_root}#{relation.first.tr('.', '/')}/{id}"] = {
|
1530
|
+
'patch': {
|
1531
|
+
'summary': "update a #{relation.first.singularize}",
|
1532
|
+
'description': table_description,
|
1533
|
+
'parameters': relation.last[:cols].reject { |k, v| Brick.config.metadata_columns.include?(k) }.map do |k, v|
|
1534
|
+
param = { 'name' => k, 'schema': { 'type': v.first } }
|
1535
|
+
if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k, nil))
|
1536
|
+
param['description'] = col_descrip
|
1537
|
+
end
|
1538
|
+
param
|
1539
|
+
end,
|
1540
|
+
'responses': { '200': { 'description': 'successful' } }
|
1541
|
+
}
|
1542
|
+
} unless relation.last.fetch(:isView, nil)
|
1543
|
+
s
|
1544
|
+
end
|
1558
1545
|
end
|
1546
|
+
render inline: json.to_json, content_type: request.format
|
1547
|
+
return
|
1559
1548
|
end
|
1560
|
-
render inline: json.to_json, content_type: request.format
|
1561
|
-
return
|
1562
|
-
end
|
1563
1549
|
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1550
|
+
if request.format == :csv # Asking for a template?
|
1551
|
+
require 'csv'
|
1552
|
+
exported_csv = CSV.generate(force_quotes: false) do |csv_out|
|
1553
|
+
model.df_export(model.brick_import_template).each { |row| csv_out << row }
|
1554
|
+
end
|
1555
|
+
render inline: exported_csv, content_type: request.format
|
1556
|
+
return
|
1557
|
+
elsif request.format == :js || request.path.start_with?('/api/') # Asking for JSON?
|
1558
|
+
data = (model.is_view? || !Object.const_defined?('DutyFree')) ? model.limit(1000) : model.df_export(model.brick_import_template)
|
1559
|
+
render inline: data.to_json, content_type: request.format == '*/*' ? 'application/json' : request.format
|
1560
|
+
return
|
1568
1561
|
end
|
1569
|
-
render inline: exported_csv, content_type: request.format
|
1570
|
-
return
|
1571
|
-
elsif request.format == :js || request.path.start_with?('/api/') # Asking for JSON?
|
1572
|
-
data = (model.is_view? || !Object.const_defined?('DutyFree')) ? model.limit(1000) : model.df_export(model.brick_import_template)
|
1573
|
-
render inline: data.to_json, content_type: request.format == '*/*' ? 'application/json' : request.format
|
1574
|
-
return
|
1575
|
-
end
|
1576
1562
|
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1563
|
+
# Normal (not swagger or CSV) request
|
1564
|
+
|
1565
|
+
# %%% Allow params to define which columns to use for order_by
|
1566
|
+
# Overriding the default by providing a querystring param?
|
1567
|
+
ordering = params['_brick_order']&.split(',')&.map(&:to_sym) || Object.send(:default_ordering, table_name, pk)
|
1568
|
+
order_by, _ = model._brick_calculate_ordering(ordering, true) # Don't do the txt part
|
1569
|
+
|
1570
|
+
ar_relation = ActiveRecord.version < Gem::Version.new('4') ? model.preload : model.all
|
1571
|
+
@_brick_params = ar_relation.brick_select(params, (selects = []), order_by,
|
1572
|
+
translations = {},
|
1573
|
+
join_array = ::Brick::JoinArray.new)
|
1574
|
+
# %%% Add custom HM count columns
|
1575
|
+
# %%% What happens when the PK is composite?
|
1576
|
+
counts = model._br_hm_counts.each_with_object([]) do |v, s|
|
1577
|
+
s << if is_mysql
|
1578
|
+
"`b_r_#{v.first}`.c_t_ AS \"b_r_#{v.first}_ct\""
|
1579
|
+
elsif is_postgres
|
1580
|
+
"\"b_r_#{v.first}\".c_t_ AS \"b_r_#{v.first}_ct\""
|
1581
|
+
else
|
1582
|
+
"b_r_#{v.first}.c_t_ AS \"b_r_#{v.first}_ct\""
|
1583
|
+
end
|
1584
|
+
end
|
1585
|
+
ar_select = ar_relation.respond_to?(:_select!) ? ar_relation.dup._select!(*selects, *counts) : ar_relation.select(selects + counts)
|
1586
|
+
instance_variable_set("@#{table_name.pluralize}".to_sym, ar_select)
|
1587
|
+
if namespace && (idx = lookup_context.prefixes.index(table_name))
|
1588
|
+
lookup_context.prefixes[idx] = "#{namespace.name.underscore}/#{lookup_context.prefixes[idx]}"
|
1589
|
+
end
|
1590
|
+
@_brick_excl = session[:_brick_exclude]&.split(',')&.each_with_object([]) do |excl, s|
|
1591
|
+
if (excl_parts = excl.split('.')).first == table_name
|
1592
|
+
s << excl_parts.last
|
1593
|
+
end
|
1607
1594
|
end
|
1608
|
-
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1595
|
+
@_brick_bt_descrip = model._br_bt_descrip
|
1596
|
+
@_brick_hm_counts = model._br_hm_counts
|
1597
|
+
@_brick_join_array = join_array
|
1598
|
+
@_brick_erd = params['_brick_erd']&.to_i
|
1599
|
+
end
|
1613
1600
|
end
|
1614
1601
|
|
1615
|
-
unless is_openapi
|
1602
|
+
unless is_openapi || is_avo
|
1616
1603
|
_, order_by_txt = model._brick_calculate_ordering(default_ordering(table_name, pk)) if pk
|
1617
1604
|
code << " def index\n"
|
1618
1605
|
code << " @#{table_name.pluralize} = #{model.name}#{pk&.present? ? ".order(#{order_by_txt.join(', ')})" : '.all'}\n"
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Brick
|
4
4
|
module Rails
|
5
5
|
# Extensions to rails controllers. Provides convenient ways to pass certain
|
6
|
-
# information to the model layer, with `controller_info
|
6
|
+
# information to the model layer, with `controller_info`.
|
7
7
|
# Also includes a convenient on/off switch,
|
8
8
|
# `brick_enabled_for_controller`.
|
9
9
|
module Controller
|
@@ -4,6 +4,33 @@ module Brick
|
|
4
4
|
module Rails
|
5
5
|
# See http://guides.rubyonrails.org/engines.html
|
6
6
|
class Engine < ::Rails::Engine
|
7
|
+
JS_CHANGEOUT = "function changeout(href, param, value, trimAfter) {
|
8
|
+
var hrefParts = href.split(\"?\");
|
9
|
+
var params = hrefParts.length > 1 ? hrefParts[1].split(\"&\") : [];
|
10
|
+
if (param === undefined || param === null || param === -1) {
|
11
|
+
hrefParts = hrefParts[0].split(\"://\");
|
12
|
+
var pathParts = hrefParts[hrefParts.length - 1].split(\"/\").filter(function (pp) {return pp !== \"\";});
|
13
|
+
if (value === undefined)
|
14
|
+
// A couple possibilities if it's namespaced, starting with two parts in the path -- and then try just one
|
15
|
+
return [pathParts.slice(1, 3).join('/'), pathParts.slice(1, 2)[0]];
|
16
|
+
else {
|
17
|
+
var queryString = param ? \"?\" + params.join(\"&\") : \"\";
|
18
|
+
return hrefParts[0] + \"://\" + pathParts[0] + \"/\" + value + queryString;
|
19
|
+
}
|
20
|
+
}
|
21
|
+
if (trimAfter) {
|
22
|
+
var pathParts = hrefParts[0].split(\"/\");
|
23
|
+
while (pathParts.lastIndexOf(trimAfter) !== pathParts.length - 1) pathParts.pop();
|
24
|
+
hrefParts[0] = pathParts.join(\"/\");
|
25
|
+
}
|
26
|
+
params = params.reduce(function (s, v) { var parts = v.split(\"=\"); if (parts[1]) s[parts[0]] = parts[1]; return s; }, {});
|
27
|
+
if (value === undefined) return params[param];
|
28
|
+
params[param] = value;
|
29
|
+
var finalParams = Object.keys(params).reduce(function (s, v) { if (params[v]) s.push(v + \"=\" + params[v]); return s; }, []).join(\"&\");
|
30
|
+
return hrefParts[0] + (finalParams.length > 0 ? \"?\" + finalParams : \"\");
|
31
|
+
}
|
32
|
+
"
|
33
|
+
|
7
34
|
# paths['app/models'] << 'lib/brick/frameworks/active_record/models'
|
8
35
|
config.brick = ActiveSupport::OrderedOptions.new
|
9
36
|
ActiveSupport.on_load(:before_initialize) do |app|
|
@@ -61,6 +88,167 @@ module Brick
|
|
61
88
|
(app_config.assets.precompile ||= []) << "#{assets_path}/images/brick_erd.png"
|
62
89
|
(app.config.assets.paths ||= []) << assets_path
|
63
90
|
end
|
91
|
+
|
92
|
+
# Smarten up Avo so it recognises Brick's querystring option for Apartment multi-tenancy
|
93
|
+
if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace)
|
94
|
+
module ::Avo
|
95
|
+
class ApplicationController
|
96
|
+
# Make Avo tenant-compatible when a querystring param is included such as: ?_brick_schema=globex_corp
|
97
|
+
alias _brick_avo_init init_app
|
98
|
+
def init_app
|
99
|
+
_brick_avo_init
|
100
|
+
::Brick.set_db_schema(params)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
module UrlHelpers
|
105
|
+
alias _brick_resource_path resource_path
|
106
|
+
# Accommodate STI resources
|
107
|
+
def resource_path(model:, resource:, **args)
|
108
|
+
resource ||= if (klass = model&.class)
|
109
|
+
Avo::App.resources.find { |r| r.model_class > klass }
|
110
|
+
end
|
111
|
+
_brick_resource_path(model: model, resource: resource, **args)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class Fields::BelongsToField
|
116
|
+
# When there is no Resource created for the target of a belongs_to, defer to the description that Brick would use
|
117
|
+
alias _brick_label label
|
118
|
+
def label
|
119
|
+
target_resource ? _brick_label : value.send(:brick_descrip)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class App
|
124
|
+
class << self
|
125
|
+
alias _brick_eager_load eager_load
|
126
|
+
def eager_load(entity)
|
127
|
+
_brick_eager_load(entity)
|
128
|
+
if entity == :resources
|
129
|
+
# %%% This useful logic can be DRYd up since it's very similar to what's around extensions.rb:1894
|
130
|
+
if (possible_schemas = (multitenancy = ::Brick.config.schema_behavior&.[](:multitenant)) &&
|
131
|
+
multitenancy&.[](:schema_to_analyse))
|
132
|
+
possible_schemas = [possible_schemas] unless possible_schemas.is_a?(Array)
|
133
|
+
if (possible_schema = possible_schemas.find { |ps| ::Brick.db_schemas.key?(ps) })
|
134
|
+
orig_tenant = Apartment::Tenant.current
|
135
|
+
Apartment::Tenant.switch!(possible_schema)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
existing = Avo::BaseResource.descendants.each_with_object({}) do |r, s|
|
139
|
+
s[r.name[0..-9]] = nil if r.name.end_with?('Resource')
|
140
|
+
end
|
141
|
+
::Brick.relations.each do |k, v|
|
142
|
+
unless existing.key?(class_name = v[:class_name]) || Brick.config.exclude_tables.include?(k) || class_name.blank?
|
143
|
+
Object.const_get("#{class_name}Resource")
|
144
|
+
end
|
145
|
+
end
|
146
|
+
Apartment::Tenant.switch!(orig_tenant) if orig_tenant
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Add our schema link Javascript code when the TurboFrameWrapper is rendered so it ends up on all index / show / etc
|
153
|
+
TurboFrameWrapperComponent.class_exec do
|
154
|
+
alias _brick_content content
|
155
|
+
def content
|
156
|
+
# Avo's logo partial fails if there is not a URL helper called exactly "root_path"
|
157
|
+
# (Finicky line over there is: avo/app/views/avo/partials/_logo.html.erb:1)
|
158
|
+
if ::Brick.instance_variable_get(:@_brick_avo_js) == view_renderer.object_id
|
159
|
+
_brick_content
|
160
|
+
else
|
161
|
+
::Brick.instance_variable_set(:@_brick_avo_js, view_renderer.object_id)
|
162
|
+
unless ::Rails.application.routes.named_routes.names.include?(:root) || ActionView::Base.respond_to?(:root_path)
|
163
|
+
ActionView::Base.class_exec do
|
164
|
+
def root_path
|
165
|
+
Avo::App.root_path
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
"<script>
|
170
|
+
#{JS_CHANGEOUT}
|
171
|
+
window.addEventListener(\"load\", linkSchemas);
|
172
|
+
document.addEventListener(\"turbo:render\", linkSchemas);
|
173
|
+
window.addEventListener(\"popstate\", linkSchemas);
|
174
|
+
// [... document.getElementsByTagName('turbo-frame')].forEach(function (a) { a.addEventListener(\"turbo:frame-render\", linkSchemas); });
|
175
|
+
function linkSchemas() {
|
176
|
+
brickSchema = changeout(location.href, \"_brick_schema\");
|
177
|
+
if (brickSchema) {
|
178
|
+
[... document.getElementsByTagName(\"A\")].forEach(function (a) { a.href = changeout(a.href, \"_brick_schema\", brickSchema); });
|
179
|
+
[... document.getElementsByTagName(\"FORM\")].forEach(function (form) { form.action = changeout(form.action, \"_brick_schema\", brickSchema); });
|
180
|
+
}
|
181
|
+
}
|
182
|
+
</script>
|
183
|
+
#{_brick_content}".html_safe
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# When available, add a clickable brick icon to go to the Brick version of the page
|
189
|
+
PanelComponent.class_exec do
|
190
|
+
alias _brick_init initialize
|
191
|
+
def initialize(*args)
|
192
|
+
_brick_init(*args)
|
193
|
+
@name = BrickTitle.new(@name, self)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
class BrickTitle
|
198
|
+
def initialize(name, view_component)
|
199
|
+
@vc = view_component
|
200
|
+
@_name = name
|
201
|
+
end
|
202
|
+
def to_s
|
203
|
+
@_name.html_safe + @vc.instance_variable_get(:@__vc_helpers)&.link_to_brick(nil,
|
204
|
+
"<svg version=\"1.1\" style=\"display: inline; padding-left: 0.5em;\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"
|
205
|
+
viewBox=\"0 0 58 58\" height=\"1.4em\" xml:space=\"preserve\">
|
206
|
+
<g>
|
207
|
+
<polygon style=\"fill:#C2615F;\" points=\"58,15.831 19.106,35.492 0,26.644 40,6\"/>
|
208
|
+
<polygon style=\"fill:#6D4646;\" points=\"19,52 0,43.356 0,26.644 19,35\"/>
|
209
|
+
<polygon style=\"fill:#894747;\" points=\"58,31.559 19,52 19,35 58,15.831\"/>
|
210
|
+
</g>
|
211
|
+
</svg>
|
212
|
+
".html_safe,
|
213
|
+
{ title: "#{@_name} in Brick" }
|
214
|
+
)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
class Fields::IndexComponent
|
219
|
+
alias _brick_resource_view_path resource_view_path
|
220
|
+
def resource_view_path
|
221
|
+
return if @resource.model&.class&.is_view?
|
222
|
+
|
223
|
+
_brick_resource_view_path
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end # module Avo
|
227
|
+
|
228
|
+
# Steer any Avo-related controller/action based URL lookups to the Avo RouteSet
|
229
|
+
class ActionDispatch::Routing::RouteSet
|
230
|
+
alias _brick_url_for url_for
|
231
|
+
def url_for(options, *args)
|
232
|
+
if self != ::Avo.railtie_routes_url_helpers._routes && # This URL lookup is not on the Avo RouteSet ...
|
233
|
+
(options[:controller]&.start_with?('avo/') || # ... but it is based on an Avo controller and action?
|
234
|
+
options[:_recall]&.fetch(:controller, nil)&.start_with?('avo/')
|
235
|
+
)
|
236
|
+
options[:script_name] = ::Avo.configuration.root_path if options[:script_name].blank?
|
237
|
+
::Avo.railtie_routes_url_helpers._routes.url_for(options, *args) # Go get the answer from the real Avo RouteSet
|
238
|
+
# Views currently do not support show / new / edit
|
239
|
+
elsif options[:controller]&.start_with?('avo/') &&
|
240
|
+
['show', 'new', 'edit'].include?(options[:action]) &&
|
241
|
+
((options[:id].is_a?(ActiveRecord::Base) && options[:id].class.is_view?) ||
|
242
|
+
::Brick.relations.fetch(options[:controller][4..-1], nil)&.fetch(:isView, nil)
|
243
|
+
)
|
244
|
+
nil
|
245
|
+
else # This is either a non-Avo request or a proper Avo request, so carry on
|
246
|
+
_brick_url_for(options, *args)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end # Avo compatibility
|
251
|
+
|
64
252
|
# ====================================
|
65
253
|
# Dynamically create generic templates
|
66
254
|
# ====================================
|
@@ -93,15 +281,15 @@ module Brick
|
|
93
281
|
end
|
94
282
|
end
|
95
283
|
|
96
|
-
def path_keys(hm_assoc, fk_name,
|
284
|
+
def path_keys(hm_assoc, fk_name, pk)
|
285
|
+
pk.map!(&:to_sym)
|
97
286
|
keys = if fk_name.is_a?(Array) && pk.is_a?(Array) # Composite keys?
|
98
|
-
fk_name.zip(pk
|
287
|
+
fk_name.zip(pk)
|
99
288
|
else
|
100
|
-
pk = pk.map { |pk_part| "#{obj_name}.#{pk_part}" }
|
101
289
|
[[fk_name, pk.length == 1 ? pk.first : pk.inspect]]
|
102
290
|
end
|
103
291
|
keys << [hm_assoc.inverse_of.foreign_type, hm_assoc.active_record.name] if hm_assoc.options.key?(:as)
|
104
|
-
keys.
|
292
|
+
keys.to_h
|
105
293
|
end
|
106
294
|
|
107
295
|
alias :_brick_find_template :find_template
|
@@ -138,8 +326,6 @@ module Brick
|
|
138
326
|
"H#{hm_assoc.macro == :has_one ? 'O' : 'M'}#{'T' if hm_assoc.options[:through]}",
|
139
327
|
(assoc_name = hm.first)]
|
140
328
|
hm_fk_name = if (through = hm_assoc.options[:through])
|
141
|
-
next unless @_brick_model._br_hm_counts.key?(hm_assoc.name) # Skip any weird HMTs that go through a HM with a source_type
|
142
|
-
|
143
329
|
next unless @_brick_model.instance_methods.include?(through) &&
|
144
330
|
(associative = @_brick_model._br_associatives.fetch(hm.first, nil))
|
145
331
|
|
@@ -154,33 +340,30 @@ module Brick
|
|
154
340
|
tbl_nm.slice!(0) if tbl_nm[0] == '/'
|
155
341
|
tbl_nm = tbl_nm.tr('/', '_').pluralize
|
156
342
|
end
|
157
|
-
"
|
343
|
+
"#{tbl_nm}.#{associative.foreign_key}"
|
158
344
|
else
|
159
345
|
hm_assoc.foreign_key
|
160
346
|
end
|
161
347
|
case args.first
|
162
348
|
when 'index'
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
else # has_one
|
175
|
-
# 0..62 because Postgres column names are limited to 63 characters
|
176
|
-
", nil, #{path_keys(hm_assoc, hm_fk_name, obj_name, pk)}"
|
177
|
-
end
|
178
|
-
hm_entry << ']'
|
179
|
-
hms_columns << hm_entry
|
349
|
+
unless skip_klass_hms.key?(assoc_name.to_sym) || hm_assoc.options[:source]
|
350
|
+
hm_entry = +"'#{hm_assoc.name}' => [#{assoc_name.inspect}, "
|
351
|
+
hm_entry << if hm_assoc.macro == :has_many
|
352
|
+
# Postgres column names are limited to 63 characters
|
353
|
+
"'" + "b_r_#{assoc_name}_ct"[0..62] + "'"
|
354
|
+
else # has_one
|
355
|
+
'nil'
|
356
|
+
end
|
357
|
+
hm_entry << ", #{path_keys(hm_assoc, hm_fk_name, pk).inspect}]"
|
358
|
+
hms_columns << hm_entry
|
359
|
+
end
|
180
360
|
when 'show', 'new', 'update'
|
181
361
|
hm_stuff << if hm_fk_name
|
182
362
|
if hm_assoc.klass.column_names.include?(hm_fk_name)
|
183
|
-
|
363
|
+
predicates = path_keys(hm_assoc, hm_fk_name, pk).map do |k, v|
|
364
|
+
v.is_a?(String) ? "#{k}: '#{v}'" : "#{k}: @#{obj_name}.#{v}"
|
365
|
+
end.join(', ')
|
366
|
+
"<%= link_to '#{assoc_name}', #{hm_assoc.klass._brick_index}_path({ #{predicates} }) %>\n"
|
184
367
|
else
|
185
368
|
puts "Warning: has_many :#{hm_assoc.name} in model #{hm_assoc.active_record.name} currently looks for a foreign key called \"#{hm_assoc.foreign_key}\". "\
|
186
369
|
"Instead it should use the clause \"foreign_key: :#{hm_assoc.inverse_of&.foreign_key}\"."
|
@@ -437,7 +620,18 @@ end
|
|
437
620
|
def slashify(*vals)
|
438
621
|
vals.map { |val_part| val_part.is_a?(String) ? val_part.gsub('/', '^^sl^^') : val_part }
|
439
622
|
end
|
440
|
-
callbacks = {} %>
|
623
|
+
callbacks = {} %>
|
624
|
+
|
625
|
+
<% avo_svg = \"#{
|
626
|
+
"<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 84 90\" height=\"30\" fill=\"#3096F7\">
|
627
|
+
<path d=\"M83.8304 81.0201C83.8343 82.9343 83.2216 84.7996 82.0822 86.3423C80.9427 87.8851 79.3363 89.0244 77.4984 89.5931C75.6606 90.1618 73.6878 90.1302 71.8694 89.5027C70.0509 88.8753 68.4823 87.6851 67.3935 86.1065L67.0796 85.6029C66.9412 85.378 66.8146 85.1463 66.6998 84.9079L66.8821 85.3007C64.1347 81.223 60.419 77.8817 56.0639 75.5723C51.7087 73.263 46.8484 72.057 41.9129 72.0609C31.75 72.0609 22.372 77.6459 16.9336 85.336C17.1412 84.7518 17.7185 83.6137 17.9463 83.0446L19.1059 80.5265L19.1414 80.456C25.2533 68.3694 37.7252 59.9541 52.0555 59.9541C53.1949 59.9541 54.3241 60.0095 55.433 60.1102C60.748 60.6134 65.8887 62.2627 70.4974 64.9433C75.1061 67.6238 79.0719 71.2712 82.1188 75.6314C82.1188 75.6314 82.1441 75.6717 82.1593 75.6868C82.1808 75.717 82.1995 75.749 82.215 75.7825C82.2821 75.8717 82.3446 75.9641 82.4024 76.0595C82.4682 76.1653 82.534 76.4221 82.5999 76.5279C82.6657 76.6336 82.772 76.82 82.848 76.9711L83.1822 77.7063C83.6094 78.7595 83.8294 79.8844 83.8304 81.0201V81.0201Z\" fill=\"currentColor\" fill-opacity=\"0.22\"></path>
|
628
|
+
<path opacity=\"0.25\" d=\"M83.8303 81.015C83.8354 82.9297 83.2235 84.7956 82.0844 86.3393C80.9453 87.8829 79.339 89.0229 77.5008 89.5923C75.6627 90.1617 73.6895 90.1304 71.8706 89.5031C70.0516 88.8758 68.4826 87.6854 67.3935 86.1065L67.0796 85.6029C66.9412 85.3746 66.8146 85.1429 66.6998 84.9079L66.8821 85.3007C64.1353 81.222 60.4199 77.8797 56.0647 75.5695C51.7095 73.2593 46.8488 72.0524 41.9129 72.0558C31.75 72.0558 22.372 77.6408 16.9336 85.3309C17.1412 84.7467 17.7185 83.6086 17.9463 83.0395L19.1059 80.5214L19.1414 80.4509C22.1906 74.357 26.8837 69.2264 32.6961 65.6326C38.5086 62.0387 45.2114 60.1232 52.0555 60.1001C53.1949 60.1001 54.3241 60.1555 55.433 60.2562C60.7479 60.7594 65.8887 62.4087 70.4974 65.0893C75.1061 67.7698 79.0719 71.4172 82.1188 75.7775C82.1188 75.7775 82.1441 75.8177 82.1593 75.8328C82.1808 75.863 82.1995 75.895 82.215 75.9285C82.2821 76.0177 82.3446 76.1101 82.4024 76.2055L82.5999 76.5228C82.6859 76.6638 82.772 76.8149 82.848 76.966L83.1822 77.7012C83.6093 78.7544 83.8294 79.8793 83.8303 81.015Z\" fill=\"currentColor\" fill-opacity=\"0.22\"></path>
|
629
|
+
<path d=\"M42.1155 30.2056L35.3453 45.0218C35.2161 45.302 35.0189 45.5458 34.7714 45.7313C34.5239 45.9168 34.2338 46.0382 33.9274 46.0844C27.3926 47.1694 21.1567 49.5963 15.617 53.2105C15.279 53.4302 14.8783 53.5347 14.4753 53.5083C14.0723 53.4819 13.6889 53.326 13.3827 53.0641C13.0765 52.8022 12.8642 52.4485 12.7777 52.0562C12.6911 51.6638 12.7351 51.2542 12.9029 50.8889L32.2311 8.55046L33.6894 5.35254C32.8713 7.50748 32.9166 9.89263 33.816 12.0153L33.9983 12.4131L42.1155 30.2056Z\" fill=\"currentColor\" fill-opacity=\"0.22\"></path>
|
630
|
+
<path d=\"M82.812 76.8753C82.6905 76.694 82.3715 76.2207 82.2449 76.0444C82.2044 75.9739 82.2044 75.8782 82.1588 75.8127C82.1132 75.7473 82.1335 75.7724 82.1183 75.7573C79.0714 71.3971 75.1056 67.7497 70.4969 65.0692C65.8882 62.3886 60.7474 60.7393 55.4325 60.2361C54.3236 60.1354 53.1943 60.08 52.055 60.08C45.2173 60.1051 38.5214 62.022 32.7166 65.6161C26.9118 69.2102 22.2271 74.3397 19.1864 80.4308L19.151 80.5013C18.7358 81.3323 18.3458 82.1784 17.9914 83.0194L16.9786 85.2655C16.9077 85.3662 16.8419 85.472 16.771 85.5828C16.6647 85.7389 16.5584 85.9 16.4621 86.0612C15.3778 87.6439 13.8123 88.8397 11.995 89.4732C10.1776 90.1068 8.20406 90.1448 6.36344 89.5817C4.52281 89.0186 2.9119 87.884 1.76676 86.3442C0.621625 84.8044 0.00246102 82.9403 0 81.0251C0.00604053 80.0402 0.177178 79.0632 0.506372 78.1344L1.22036 76.5681C1.25084 76.5034 1.28639 76.4411 1.32669 76.3818C1.40265 76.2559 1.47861 76.135 1.56469 76.0192C1.58531 75.9789 1.60901 75.9401 1.63558 75.9034C7.06401 67.6054 14.947 61.1866 24.1977 57.5317C33.4485 53.8768 43.6114 53.166 53.2855 55.4971L48.9155 45.9286L41.9276 30.6188L33.8256 12.8263L33.6433 12.4285C32.7439 10.3058 32.6986 7.92067 33.5167 5.76573L34.0231 4.69304C34.8148 3.24136 35.9941 2.03525 37.431 1.20762C38.868 0.379997 40.5068 -0.0370045 42.1668 0.0025773C43.8268 0.0421591 45.4436 0.536787 46.839 1.43195C48.2345 2.32711 49.3543 3.58804 50.0751 5.07578L50.2523 5.47363L51.8474 8.96365L74.0974 57.708L82.812 76.8753Z\" fill=\"currentColor\" fill-opacity=\"0.22\"></path>
|
631
|
+
<path opacity=\"0.25\" d=\"M41.9129 30.649L35.3301 45.0422C35.2023 45.3204 35.0074 45.563 34.7627 45.7484C34.518 45.9337 34.2311 46.0562 33.9274 46.1048C27.3926 47.1897 21.1567 49.6166 15.617 53.2308C15.279 53.4505 14.8783 53.555 14.4753 53.5286C14.0723 53.5022 13.6889 53.3463 13.3827 53.0844C13.0765 52.8225 12.8642 52.4688 12.7777 52.0765C12.6911 51.6842 12.7351 51.2745 12.9029 50.9092L32.0285 8.99382L33.4869 5.7959C32.6687 7.95084 32.7141 10.336 33.6135 12.4586L33.7958 12.8565L41.9129 30.649Z\" fill=\"currentColor\" fill-opacity=\"0.22\"></path>
|
632
|
+
</svg>
|
633
|
+
".gsub('"', '\"')
|
634
|
+
}\".html_safe %>"
|
441
635
|
|
442
636
|
if ['index', 'show', 'new', 'update'].include?(args.first)
|
443
637
|
poly_cols = []
|
@@ -520,31 +714,7 @@ document.querySelectorAll(\"input[type=submit][data-confirm]\").forEach(function
|
|
520
714
|
});
|
521
715
|
});
|
522
716
|
|
523
|
-
|
524
|
-
var hrefParts = href.split(\"?\");
|
525
|
-
var params = hrefParts.length > 1 ? hrefParts[1].split(\"&\") : [];
|
526
|
-
if (param === undefined || param === null || param === -1) {
|
527
|
-
hrefParts = hrefParts[0].split(\"://\");
|
528
|
-
var pathParts = hrefParts[hrefParts.length - 1].split(\"/\").filter(function (pp) {return pp !== \"\";});
|
529
|
-
if (value === undefined)
|
530
|
-
// A couple possibilities if it's namespaced, starting with two parts in the path -- and then try just one
|
531
|
-
return [pathParts.slice(1, 3).join('/'), pathParts.slice(1, 2)[0]];
|
532
|
-
else {
|
533
|
-
var queryString = param ? \"?\" + params.join(\"&\") : \"\";
|
534
|
-
return hrefParts[0] + \"://\" + pathParts[0] + \"/\" + value + queryString;
|
535
|
-
}
|
536
|
-
}
|
537
|
-
if (trimAfter) {
|
538
|
-
var pathParts = hrefParts[0].split(\"/\");
|
539
|
-
while (pathParts.lastIndexOf(trimAfter) !== pathParts.length - 1) pathParts.pop();
|
540
|
-
hrefParts[0] = pathParts.join(\"/\");
|
541
|
-
}
|
542
|
-
params = params.reduce(function (s, v) { var parts = v.split(\"=\"); if (parts[1]) s[parts[0]] = parts[1]; return s; }, {});
|
543
|
-
if (value === undefined) return params[param];
|
544
|
-
params[param] = value;
|
545
|
-
var finalParams = Object.keys(params).reduce(function (s, v) { if (params[v]) s.push(v + \"=\" + params[v]); return s; }, []).join(\"&\");
|
546
|
-
return hrefParts[0] + (finalParams.length > 0 ? \"?\" + finalParams : \"\");
|
547
|
-
}
|
717
|
+
#{JS_CHANGEOUT}
|
548
718
|
|
549
719
|
// Snag first TR for sticky header
|
550
720
|
var grid = document.getElementById(\"#{table_name}\");
|
@@ -730,11 +900,6 @@ erDiagram
|
|
730
900
|
end
|
731
901
|
inline = case args.first
|
732
902
|
when 'index'
|
733
|
-
obj_pk = if pk&.is_a?(Array) # Composite primary key?
|
734
|
-
"#{pk.map { |pk_part| "#{obj_name}.#{pk_part}" }.join(', ')}" unless pk.empty?
|
735
|
-
elsif pk
|
736
|
-
"#{obj_name}.#{pk}"
|
737
|
-
end
|
738
903
|
if Object.const_defined?('DutyFree')
|
739
904
|
template_link = "
|
740
905
|
<%= link_to 'CSV', #{@_brick_model._brick_index}_path(format: :csv) %> <a href=\"#\" id=\"sheetsLink\">Sheets</a>
|
@@ -842,6 +1007,15 @@ erDiagram
|
|
842
1007
|
<table id=\"resourceName\"><tr>
|
843
1008
|
<td><h1>#{model_name}</h1></td>
|
844
1009
|
<td id=\"imgErd\" title=\"Show ERD\"></td>
|
1010
|
+
<% if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) %>
|
1011
|
+
<td><%= link_to_brick(
|
1012
|
+
avo_svg,
|
1013
|
+
{ index_proc: Proc.new do |model|
|
1014
|
+
::Avo.railtie_routes_url_helpers.send(\"resources_#\{model.base_class.model_name.route_key}_path\".to_sym)
|
1015
|
+
end,
|
1016
|
+
title: '#{model_name} in Avo' }
|
1017
|
+
) %></td>
|
1018
|
+
<% end %>
|
845
1019
|
</tr></table>#{template_link}<%
|
846
1020
|
if description.present? %><%=
|
847
1021
|
description %><br><%
|
@@ -875,149 +1049,36 @@ erDiagram
|
|
875
1049
|
</script>
|
876
1050
|
<% end %>
|
877
1051
|
#{erd_markup}
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
['@cache_hit', '@marked_for_same_origin_verification', '@view_renderer', '@view_flow', '@output_buffer', '@virtual_path'].include?(entry.to_s)
|
891
|
-
end
|
892
|
-
msg = \"Can't find resource \\\"#{table_name}\\\".\"
|
893
|
-
# Can't be sure otherwise of what is up, so check DidYouMean and offer a suggestion.
|
894
|
-
if (dym = DidYouMean::SpellChecker.new(dictionary: dym_list).correct('@#{table_name}')).present?
|
895
|
-
msg << \"\nIf you meant \\\"#\{found_dym = dym.first[1..-1]}\\\" then to avoid this message add this entry into inflections.rb:\n\"
|
896
|
-
msg << \" inflect.singular('#\{found_dym}', '#{obj_name}')\"
|
897
|
-
puts
|
898
|
-
puts \"WARNING: #\{msg}\"
|
899
|
-
puts
|
900
|
-
@#{table_name} = instance_variable_get(dym.first.to_sym)
|
901
|
-
else
|
902
|
-
raise ActiveRecord::RecordNotFound.new(msg)
|
1052
|
+
|
1053
|
+
<%= # Consider getting the name from the association -- hm.first.name -- if a more \"friendly\" alias should be used for a screwy table name
|
1054
|
+
cols = {#{hms_keys = []
|
1055
|
+
hms_headers.map do |hm|
|
1056
|
+
hms_keys << (assoc_name = (assoc = hm.first).name.to_s)
|
1057
|
+
"#{assoc_name.inspect} => [#{(assoc.options[:through] && !assoc.through_reflection).inspect}, #{assoc.klass.name}, #{hm[1].inspect}, #{hm[2].inspect}]"
|
1058
|
+
end.join(', ')}}
|
1059
|
+
|
1060
|
+
# If the resource is missing, has the user simply created an inappropriately pluralised name for a table?
|
1061
|
+
@#{table_name} ||= if dym_list = instance_variables.reject do |entry|
|
1062
|
+
entry.to_s.start_with?('@_') ||
|
1063
|
+
['@cache_hit', '@marked_for_same_origin_verification', '@view_renderer', '@view_flow', '@output_buffer', '@virtual_path'].include?(entry.to_s)
|
903
1064
|
end
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
end
|
914
|
-
unless @_brick_sequence # If no sequence is defined, start with all inclusions
|
915
|
-
cust_cols = #{model_name}._br_cust_cols
|
916
|
-
# HOT columns, kept as symbols
|
917
|
-
hots = #{model_name}._br_bt_descrip.keys.select { |k| bts.key?(k) }
|
918
|
-
@_brick_sequence = col_keys + cust_cols.keys + hots + #{(hms_keys).inspect}.reject { |assoc_name| @_brick_incl&.exclude?(assoc_name) }
|
919
|
-
end
|
920
|
-
@_brick_sequence.reject! { |nm| @_brick_excl.include?(nm) } if @_brick_excl # Reject exclusions
|
921
|
-
@_brick_sequence.each_with_object(+'') do |col_name, s|
|
922
|
-
if (col = cols[col_name]).is_a?(ActiveRecord::ConnectionAdapters::Column)
|
923
|
-
s << '<th'
|
924
|
-
s << \" title=\\\"#\{col.comment}\\\"\" if col.respond_to?(:comment) && !col.comment.blank?
|
925
|
-
s << if (bt = bts[col_name])
|
926
|
-
# Allow sorting for any BT except polymorphics
|
927
|
-
\"#\{' x-order=\"' + bt.first.to_s + '\"' unless bt[2]}>BT \" +
|
928
|
-
bt[1].map { |bt_pair| bt_pair.first.bt_link(bt.first) }.join(' ')
|
929
|
-
else # Normal column
|
930
|
-
\"#\{' x-order=\"' + col_name + '\"' if true}>#\{col_name}\"
|
931
|
-
end
|
932
|
-
elsif col # HM column
|
933
|
-
s << \"<th#\{' x-order=\"' + col_name + '\"' if true}>#\{col[2]} \"
|
934
|
-
s << (col.first ? \"#\{col[3]}\" : \"#\{link_to(col[3], send(\"#\{col[1]._brick_index}_path\"))}\")
|
935
|
-
elsif cust_cols.key?(col_name) # Custom column
|
936
|
-
s << \"<th x-order=\\\"#\{col_name}\\\">#\{col_name}\"
|
937
|
-
elsif col_name.is_a?(Symbol) && (hot = bts[col_name]) # has_one :through
|
938
|
-
s << \"<th x-order=\\\"#\{hot.first.to_s}\\\">HOT \" +
|
939
|
-
hot[1].map { |hot_pair| hot_pair.first.bt_link(col_name) }.join(' ')
|
940
|
-
hot[1].first
|
941
|
-
else # Bad column name!
|
942
|
-
s << \"<th title=\\\"<< Unknown column >>\\\">#\{col_name}\"
|
943
|
-
end
|
944
|
-
s << '</th>'
|
945
|
-
end.html_safe
|
946
|
-
%></tr></thead>
|
947
|
-
<tbody>
|
948
|
-
<% # %%% Have once gotten this error with MSSQL referring to http://localhost:3000/warehouse/cold_room_temperatures__archive
|
949
|
-
# ActiveRecord::StatementTimeout in Warehouse::ColdRoomTemperatures_Archive#index
|
950
|
-
# TinyTds::Error: Adaptive Server connection timed out
|
951
|
-
# (After restarting the server it worked fine again.)
|
952
|
-
@#{table_name}.each do |#{obj_name}|
|
953
|
-
hms_cols = {#{hms_columns.join(', ')}} %>
|
954
|
-
<tr>#{"
|
955
|
-
<td><%= link_to '⇛', #{path_obj_name}_path(slashify(#{obj_pk})), { class: 'big-arrow' } %></td>" if obj_pk}
|
956
|
-
<% @_brick_sequence.each do |col_name|
|
957
|
-
val = #{obj_name}.attributes[col_name] %>
|
958
|
-
<td<%= ' class=\"dimmed\"'.html_safe unless cols.key?(col_name) || (cust_col = cust_cols[col_name]) ||
|
959
|
-
(col_name.is_a?(Symbol) && bts.key?(col_name)) # HOT
|
960
|
-
%>><%
|
961
|
-
if (bt = bts[col_name])
|
962
|
-
if bt[2] # Polymorphic?
|
963
|
-
bt_class = #{obj_name}.send(\"#\{bt.first}_type\")
|
964
|
-
base_class_underscored = (::Brick.existing_stis[bt_class] || bt_class).constantize.base_class._brick_index(:singular)
|
965
|
-
poly_id = #{obj_name}.send(\"#\{bt.first}_id\")
|
966
|
-
%><%= link_to(\"#\{bt_class} ##\{poly_id}\", send(\"#\{base_class_underscored}_path\".to_sym, poly_id)) if poly_id %><%
|
967
|
-
else # BT or HOT
|
968
|
-
bt_class = bt[1].first.first
|
969
|
-
descrips = @_brick_bt_descrip[bt.first][bt_class]
|
970
|
-
bt_id_col = if descrips.nil?
|
971
|
-
puts \"Caught it in the act for #{obj_name} / #\{col_name}!\"
|
972
|
-
# binding.pry
|
973
|
-
elsif descrips.length == 1
|
974
|
-
[#{obj_name}.class.reflect_on_association(bt.first)&.foreign_key]
|
1065
|
+
msg = \"Can't find resource \\\"#{table_name}\\\".\"
|
1066
|
+
# Can't be sure otherwise of what is up, so check DidYouMean and offer a suggestion.
|
1067
|
+
if (dym = DidYouMean::SpellChecker.new(dictionary: dym_list).correct('@#{table_name}')).present?
|
1068
|
+
msg << \"\nIf you meant \\\"#\{found_dym = dym.first[1..-1]}\\\" then to avoid this message add this entry into inflections.rb:\n\"
|
1069
|
+
msg << \" inflect.singular('#\{found_dym}', '#{obj_name}')\"
|
1070
|
+
puts
|
1071
|
+
puts \"WARNING: #\{msg}\"
|
1072
|
+
puts
|
1073
|
+
@#{table_name} = instance_variable_get(dym.first.to_sym)
|
975
1074
|
else
|
976
|
-
|
1075
|
+
raise ActiveRecord::RecordNotFound.new(msg)
|
977
1076
|
end
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
bt_id = bt_id_col&.map { |id_col| #{obj_name}.respond_to?(id_sym = id_col.to_sym) ? #{obj_name}.send(id_sym) : id_col } %>
|
984
|
-
<%= bt_id&.first ? link_to(bt_txt, send(\"#\{bt_class.base_class._brick_index(:singular)}_path\".to_sym, bt_id)) : bt_txt %>
|
985
|
-
<% end
|
986
|
-
elsif (hms_col = hms_cols[col_name])
|
987
|
-
if hms_col.length == 1 %>
|
988
|
-
<%= hms_col.first %>
|
989
|
-
<% else
|
990
|
-
%><%= klass = (col = cols[col_name])[1]
|
991
|
-
if col[2] == 'HO'
|
992
|
-
descrips = @_brick_bt_descrip[col_name.to_sym][klass]
|
993
|
-
if (ho_id = (ho_id_col = descrips.last).map { |id_col| #{obj_name}.send(id_col.to_sym) })&.first
|
994
|
-
ho_txt = klass.brick_descrip(#{obj_name}, descrips[0..-2].map { |id| #{obj_name}.send(id.last[0..62]) }, ho_id_col)
|
995
|
-
link_to(ho_txt, send(\"#\{klass.base_class._brick_index(:singular)}_path\".to_sym, ho_id))
|
996
|
-
end
|
997
|
-
elsif hms_col[1]&.positive?
|
998
|
-
link_to \"#\{hms_col[1] || 'View'} #\{hms_col.first}\", send(\"#\{klass._brick_index}_path\".to_sym, hms_col[2])
|
999
|
-
end %>
|
1000
|
-
<% end
|
1001
|
-
elsif (col = cols[col_name])
|
1002
|
-
col_type = col&.sql_type == 'geography' ? col.sql_type : col&.type
|
1003
|
-
%><%= display_value(col_type || col&.sql_type, val) %><%
|
1004
|
-
elsif cust_col
|
1005
|
-
data = cust_col.first.map { |cc_part| #{obj_name}.send(cc_part.last) }
|
1006
|
-
cust_txt = #{model_name}.brick_descrip(cust_col[-2], data)
|
1007
|
-
if (link_id = #{obj_name}.send(cust_col.last[1]) if cust_col.last)
|
1008
|
-
%><%= link_to(cust_txt, send(\"#\{cust_col.last.first._brick_index(:singular)}_path\", link_id)) %><%
|
1009
|
-
else
|
1010
|
-
%><%= cust_txt %><%
|
1011
|
-
end
|
1012
|
-
else # Bad column name!
|
1013
|
-
%>?<%
|
1014
|
-
end
|
1015
|
-
%></td>
|
1016
|
-
<% end %>
|
1017
|
-
</tr>
|
1018
|
-
<% end %>
|
1019
|
-
</tbody>
|
1020
|
-
</table>
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
# Write out the mega-grid
|
1080
|
+
brick_grid(@#{table_name}, @_brick_bt_descrip, @_brick_sequence, @_brick_incl, @_brick_excl,
|
1081
|
+
cols, poly_cols, bts, #{hms_keys.inspect}, {#{hms_columns.join(', ')}}) %>
|
1021
1082
|
|
1022
1083
|
#{"<hr><%= link_to \"New #{obj_name}\", new_#{path_obj_name}_path %>" unless @_brick_model.is_view?}
|
1023
1084
|
#{script}
|
@@ -1125,7 +1186,18 @@ erDiagram
|
|
1125
1186
|
<p style=\"color: green\"><%= notice %></p>#{"
|
1126
1187
|
#{schema_options}" if schema_options}
|
1127
1188
|
<select id=\"tbl\">#{table_options}</select>
|
1128
|
-
<h1><%= page_title %></h1
|
1189
|
+
<table><td><h1><%= page_title %></h1></td>
|
1190
|
+
<% if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) %>
|
1191
|
+
<td><%= link_to_brick(
|
1192
|
+
avo_svg,
|
1193
|
+
{ show_proc: Proc.new do |obj|
|
1194
|
+
::Avo.railtie_routes_url_helpers.send(\"resources_#\{obj.class.base_class.model_name.singular_route_key}_path\".to_sym, obj)
|
1195
|
+
end,
|
1196
|
+
title: \"#\{page_title} in Avo\" }
|
1197
|
+
) %></td>
|
1198
|
+
<% end %>
|
1199
|
+
</table>
|
1200
|
+
<%
|
1129
1201
|
if (description = (relation = Brick.relations[#{model_name}.table_name])&.fetch(:description, nil)) %><%=
|
1130
1202
|
description %><br><%
|
1131
1203
|
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
module Brick::Rails::FormTags
|
2
|
+
# Our super speedy grid
|
3
|
+
def brick_grid(relation, bt_descrip, sequence = nil, inclusions, exclusions,
|
4
|
+
cols, poly_cols, bts, hms_keys, hms_cols)
|
5
|
+
out = "<table id=\"headerTop\"></table>
|
6
|
+
<table id=\"#{relation.table_name}\" class=\"shadow\">
|
7
|
+
<thead><tr>"
|
8
|
+
pk = (klass = relation.klass).primary_key || []
|
9
|
+
pk = [pk] unless pk.is_a?(Array)
|
10
|
+
if pk.present?
|
11
|
+
out << "<th x-order=\"#{pk.join(',')}\"></th>"
|
12
|
+
end
|
13
|
+
|
14
|
+
col_keys = relation.columns.each_with_object([]) do |col, s|
|
15
|
+
col_name = col.name
|
16
|
+
next if inclusions&.exclude?(col_name) ||
|
17
|
+
(pk.include?(col_name) && [:integer, :uuid].include?(col.type) && !bts.key?(col_name)) ||
|
18
|
+
::Brick.config.metadata_columns.include?(col_name) || poly_cols.include?(col_name)
|
19
|
+
|
20
|
+
s << col_name
|
21
|
+
cols[col_name] = col
|
22
|
+
end
|
23
|
+
unless sequence # If no sequence is defined, start with all inclusions
|
24
|
+
cust_cols = klass._br_cust_cols
|
25
|
+
# HOT columns, kept as symbols
|
26
|
+
hots = klass._br_bt_descrip.keys.select { |k| bts.key?(k) }
|
27
|
+
sequence = col_keys + cust_cols.keys + hots + hms_keys.reject { |assoc_name| inclusions&.exclude?(assoc_name) }
|
28
|
+
end
|
29
|
+
sequence.reject! { |nm| exclusions.include?(nm) } if exclusions
|
30
|
+
out << sequence.each_with_object(+'') do |col_name, s|
|
31
|
+
if (col = cols[col_name]).is_a?(ActiveRecord::ConnectionAdapters::Column)
|
32
|
+
s << '<th'
|
33
|
+
s << " title=\"#{col.comment}\"" if col.respond_to?(:comment) && !col.comment.blank?
|
34
|
+
s << if (bt = bts[col_name])
|
35
|
+
# Allow sorting for any BT except polymorphics
|
36
|
+
"#{' x-order="' + bt.first.to_s + '"' unless bt[2]}>BT " +
|
37
|
+
bt[1].map { |bt_pair| bt_pair.first.bt_link(bt.first) }.join(' ')
|
38
|
+
else # Normal column
|
39
|
+
"#{' x-order="' + col_name + '"' if true}>#{col_name}"
|
40
|
+
end
|
41
|
+
elsif col # HM column
|
42
|
+
s << "<th#{' x-order="' + col_name + '"' if true}>#{col[2]} "
|
43
|
+
s << (col.first ? "#{col[3]}" : "#{link_to(col[3], send("#{col[1]._brick_index}_path"))}")
|
44
|
+
elsif cust_cols.key?(col_name) # Custom column
|
45
|
+
s << "<th x-order=\"#{col_name}\">#{col_name}"
|
46
|
+
elsif col_name.is_a?(Symbol) && (hot = bts[col_name]) # has_one :through
|
47
|
+
s << "<th x-order=\"#{hot.first.to_s}\">HOT " +
|
48
|
+
hot[1].map { |hot_pair| hot_pair.first.bt_link(col_name) }.join(' ')
|
49
|
+
hot[1].first
|
50
|
+
else # Bad column name!
|
51
|
+
s << "<th title=\"<< Unknown column >>\">#{col_name}"
|
52
|
+
end
|
53
|
+
s << '</th>'
|
54
|
+
end
|
55
|
+
out << "</tr></thead>
|
56
|
+
<tbody>"
|
57
|
+
# %%% Have once gotten this error with MSSQL referring to http://localhost:3000/warehouse/cold_room_temperatures__archive
|
58
|
+
# ActiveRecord::StatementTimeout in Warehouse::ColdRoomTemperatures_Archive#index
|
59
|
+
# TinyTds::Error: Adaptive Server connection timed out
|
60
|
+
# (After restarting the server it worked fine again.)
|
61
|
+
relation.each do |obj|
|
62
|
+
out << "<tr>\n"
|
63
|
+
out << "<td>#{link_to('⇛', send("#{klass._brick_index(:singular)}_path".to_sym,
|
64
|
+
pk.map { |pk_part| obj.send(pk_part.to_sym) }), { class: 'big-arrow' })}</td>\n" if pk.present?
|
65
|
+
sequence.each do |col_name|
|
66
|
+
val = obj.attributes[col_name]
|
67
|
+
out << '<td'
|
68
|
+
out << ' class=\"dimmed\"' unless cols.key?(col_name) || (cust_col = cust_cols[col_name]) ||
|
69
|
+
(col_name.is_a?(Symbol) && bts.key?(col_name)) # HOT
|
70
|
+
out << '>'
|
71
|
+
if (bt = bts[col_name])
|
72
|
+
if bt[2] # Polymorphic?
|
73
|
+
bt_class = obj.send("#{bt.first}_type")
|
74
|
+
base_class_underscored = (::Brick.existing_stis[bt_class] || bt_class).constantize.base_class._brick_index(:singular)
|
75
|
+
poly_id = obj.send("#{bt.first}_id")
|
76
|
+
out << link_to("#{bt_class} ##{poly_id}", send("#{base_class_underscored}_path".to_sym, poly_id)) if poly_id
|
77
|
+
else # BT or HOT
|
78
|
+
bt_class = bt[1].first.first
|
79
|
+
descrips = bt_descrip[bt.first][bt_class]
|
80
|
+
bt_id_col = if descrips.nil?
|
81
|
+
puts "Caught it in the act for obj / #{col_name}!"
|
82
|
+
elsif descrips.length == 1
|
83
|
+
[obj.class.reflect_on_association(bt.first)&.foreign_key]
|
84
|
+
else
|
85
|
+
descrips.last
|
86
|
+
end
|
87
|
+
bt_txt = bt_class.brick_descrip(
|
88
|
+
# 0..62 because Postgres column names are limited to 63 characters
|
89
|
+
obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, bt_id_col
|
90
|
+
)
|
91
|
+
bt_txt ||= "<span class=\"orphan\"><< Orphaned ID: #{val} >></span>" if val
|
92
|
+
bt_id = bt_id_col&.map { |id_col| obj.respond_to?(id_sym = id_col.to_sym) ? obj.send(id_sym) : id_col }
|
93
|
+
out << (bt_id&.first ? link_to(bt_txt, send("#{bt_class.base_class._brick_index(:singular)}_path".to_sym, bt_id)) : bt_txt || '')
|
94
|
+
end
|
95
|
+
elsif (hms_col = hms_cols[col_name])
|
96
|
+
if hms_col.length == 1
|
97
|
+
out << hms_col.first
|
98
|
+
else
|
99
|
+
klass = (col = cols[col_name])[1]
|
100
|
+
if col[2] == 'HO'
|
101
|
+
descrips = bt_descrip[col_name.to_sym][klass]
|
102
|
+
if (ho_id = (ho_id_col = descrips.last).map { |id_col| obj.send(id_col.to_sym) })&.first
|
103
|
+
ho_txt = klass.brick_descrip(obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, ho_id_col)
|
104
|
+
out << link_to(ho_txt, send("#{klass.base_class._brick_index(:singular)}_path".to_sym, ho_id))
|
105
|
+
end
|
106
|
+
else
|
107
|
+
if (ct = obj.send(hms_col[1].to_sym)&.to_i)&.positive?
|
108
|
+
out << "#{link_to("#{ct || 'View'} #{hms_col.first}",
|
109
|
+
send("#{klass._brick_index}_path".to_sym,
|
110
|
+
hms_col[2].each_with_object({}) { |v, s| s[v.first] = v.last.is_a?(String) ? v.last : obj.send(v.last) })
|
111
|
+
)}\n"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
elsif (col = cols[col_name]).is_a?(ActiveRecord::ConnectionAdapters::Column)
|
116
|
+
binding.pry if col.is_a?(Array)
|
117
|
+
col_type = col&.sql_type == 'geography' ? col.sql_type : col&.type
|
118
|
+
out << display_value(col_type || col&.sql_type, val).to_s
|
119
|
+
elsif cust_col
|
120
|
+
data = cust_col.first.map { |cc_part| obj.send(cc_part.last) }
|
121
|
+
cust_txt = klass.brick_descrip(cust_col[-2], data)
|
122
|
+
if (link_id = obj.send(cust_col.last[1]) if cust_col.last)
|
123
|
+
out << link_to(cust_txt, send("#{cust_col.last.first._brick_index(:singular)}_path", link_id))
|
124
|
+
else
|
125
|
+
out << cust_txt
|
126
|
+
end
|
127
|
+
else # Bad column name!
|
128
|
+
out << '?'
|
129
|
+
end
|
130
|
+
out << '</td>'
|
131
|
+
end
|
132
|
+
out << '</tr>'
|
133
|
+
end
|
134
|
+
out << " </tbody>
|
135
|
+
</table>
|
136
|
+
"
|
137
|
+
out.html_safe
|
138
|
+
end # brick_grid
|
139
|
+
|
140
|
+
def link_to_brick(*args, **kwargs)
|
141
|
+
return unless ::Brick.config.mode == :on
|
142
|
+
|
143
|
+
text = ((args.first.is_a?(String) || args.first.is_a?(Proc)) && args.shift) || args[1]
|
144
|
+
text = text.call if text.is_a?(Proc)
|
145
|
+
klass_or_obj = ((args.first.is_a?(ActiveRecord::Relation) ||
|
146
|
+
args.first.is_a?(ActiveRecord::Base) ||
|
147
|
+
args.first.is_a?(Class)) &&
|
148
|
+
args.first) ||
|
149
|
+
@_brick_model
|
150
|
+
# If not provided, do a best-effort to automatically determine the resource class or object
|
151
|
+
filter_parts = []
|
152
|
+
klass_or_obj ||= begin
|
153
|
+
klass, sti_type = ::Brick.ctrl_to_klass(controller_path)
|
154
|
+
if klass
|
155
|
+
type_col = klass.inheritance_column # Usually 'type'
|
156
|
+
filter_parts << "#{type_col}=#{sti_type}" if sti_type && klass.column_names.include?(type_col)
|
157
|
+
path_params = request.path_parameters.dup
|
158
|
+
path_params.delete(:controller)
|
159
|
+
path_params.delete(:action)
|
160
|
+
pk = (klass.primary_key || ActiveRecord::Base.primary_key).to_sym
|
161
|
+
# Used to also have this but it's a bit too permissive to identify a primary key: (path_params.length == 1 && path_params.values.first) ||
|
162
|
+
if ((id = (path_params[pk] || path_params[:id] || path_params["#{klass.name.underscore}_id".to_sym])) && (obj = klass.find_by(pk => id))) ||
|
163
|
+
(['show', 'edit', 'update', 'destroy'].include?(action_name) && (obj = klass.first))
|
164
|
+
obj
|
165
|
+
else
|
166
|
+
# %%% If there is a HMT that refers to some ___id then try to identify an appropriate filter
|
167
|
+
# %%% If there is a polymorphic association that might relate to stuff in the path_params,
|
168
|
+
# try to identify an appropriate ___able_id and ___able_type filter
|
169
|
+
((klass.column_names - [pk.to_s]) & path_params.keys.map(&:to_s)).each do |path_param|
|
170
|
+
filter_parts << "#{path_param}=#{path_params[path_param.to_sym]}"
|
171
|
+
end
|
172
|
+
klass
|
173
|
+
end
|
174
|
+
end
|
175
|
+
rescue
|
176
|
+
end
|
177
|
+
if klass_or_obj
|
178
|
+
if klass_or_obj.is_a?(ActiveRecord::Relation)
|
179
|
+
klass_or_obj.where_values_hash.each do |whr|
|
180
|
+
filter_parts << "#{whr.first}=#{whr.last}" if whr.last && !whr.last.is_a?(Array)
|
181
|
+
end
|
182
|
+
klass_or_obj = klass_or_obj.klass
|
183
|
+
type_col = klass_or_obj.inheritance_column
|
184
|
+
if klass_or_obj.column_names.include?(type_col) && klass_or_obj.name != klass_or_obj.base_class.name
|
185
|
+
filter_parts << "#{type_col}=#{klass_or_obj.name}"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
filter = "?#{filter_parts.join('&')}" if filter_parts.present?
|
189
|
+
if (klass_or_obj&.is_a?(Class) && klass_or_obj < ActiveRecord::Base) ||
|
190
|
+
(klass_or_obj&.is_a?(ActiveRecord::Base) && klass_or_obj.new_record? && (klass_or_obj = klass_or_obj.class))
|
191
|
+
path = (proc = kwargs[:index_proc]) ? proc.call(klass_or_obj) : "#{send("#{klass_or_obj.base_class._brick_index}_path")}#{filter}"
|
192
|
+
lt_args = [text || "Index for #{klass_or_obj.name.pluralize}", path]
|
193
|
+
else
|
194
|
+
# 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
|
195
|
+
path = (proc = kwargs[:show_proc]) ? proc.call(klass_or_obj) : "#{send("#{klass_or_obj.class.base_class._brick_index(:singular)}_path", klass_or_obj)}#{filter}"
|
196
|
+
lt_args = [text || "Show this #{klass_or_obj.class.name}", path]
|
197
|
+
end
|
198
|
+
link_to(*lt_args, **kwargs)
|
199
|
+
else
|
200
|
+
# puts "Warning: link_to_brick could not find a class for \"#{controller_path}\" -- consider setting @_brick_model within that controller."
|
201
|
+
# if (hits = res_names.keys & instance_variables.map { |v| v.to_s[1..-1] }).present?
|
202
|
+
links = instance_variables.each_with_object(Hash.new { |h, k| h[k] = [] }) do |name, s|
|
203
|
+
iv_name = name.to_s[1..-1]
|
204
|
+
case (val = instance_variable_get(name))
|
205
|
+
when ActiveRecord::Relation
|
206
|
+
s[val.klass] << iv_name
|
207
|
+
when ActiveRecord::Base
|
208
|
+
s[val] << iv_name
|
209
|
+
end
|
210
|
+
end
|
211
|
+
if links.length == 1 # If there's only one match then use any text that was supplied
|
212
|
+
link_to_brick(text || links.first.last.join('/'), links.first.first, **kwargs)
|
213
|
+
else
|
214
|
+
links.map { |k, v| link_to_brick(v.join('/'), v, **kwargs) }.join(' ').html_safe
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end # link_to_brick
|
218
|
+
|
219
|
+
end
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -138,15 +138,16 @@ module Brick
|
|
138
138
|
def set_db_schema(params = nil)
|
139
139
|
# If Apartment::Tenant.current is not still the default (usually 'public') then an elevator has brought us into
|
140
140
|
# a different tenant. If so then don't allow schema navigation.
|
141
|
-
chosen = if
|
141
|
+
chosen = if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' &&
|
142
|
+
(current_schema = (ActiveRecord::Base.execute_sql('SELECT current_schemas(true)').first['current_schemas'][1..-2]
|
143
|
+
.split(',') - ['pg_catalog', 'pg_toast', 'heroku_ext']).first) &&
|
144
|
+
(is_show_schema_list = (apartment_multitenant && current_schema == ::Brick.default_schema)) &&
|
142
145
|
(schema = (params ? params['_brick_schema'] : ::Brick.default_schema)) &&
|
143
146
|
::Brick.db_schemas&.key?(schema)
|
144
147
|
Apartment::Tenant.switch!(schema)
|
145
148
|
schema
|
146
|
-
|
147
|
-
# Just return the current schema
|
148
|
-
current_schema = ActiveRecord::Base.execute_sql('SELECT current_schemas(true)').first['current_schemas'][1..-2].split(',')
|
149
|
-
(current_schema - ['pg_catalog', 'pg_toast', 'heroku_ext']).first
|
149
|
+
else
|
150
|
+
current_schema # Just return the current schema
|
150
151
|
end
|
151
152
|
[chosen == ::Brick.default_schema ? nil : chosen, is_show_schema_list]
|
152
153
|
end
|
@@ -1262,8 +1263,8 @@ module ActiveRecord
|
|
1262
1263
|
# entry in your .select().
|
1263
1264
|
# More information: https://discuss.rubyonrails.org/t/includes-and-select-for-joined-data/81640
|
1264
1265
|
def apply_column_aliases(relation)
|
1265
|
-
used_cols = {}
|
1266
1266
|
if (sel_vals = relation.select_values.map(&:to_s)).first == '_brick_eager_load'
|
1267
|
+
used_cols = {}
|
1267
1268
|
# Find and expand out all column names being used in select(...)
|
1268
1269
|
new_select_values = sel_vals.each_with_object([]) do |col, s|
|
1269
1270
|
next if col == '_brick_eager_load'
|
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.98
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lorin Thwaits
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -216,6 +216,7 @@ files:
|
|
216
216
|
- lib/brick/frameworks/rails/controller.rb
|
217
217
|
- lib/brick/frameworks/rails/crosstab.brk
|
218
218
|
- lib/brick/frameworks/rails/engine.rb
|
219
|
+
- lib/brick/frameworks/rails/form_tags.rb
|
219
220
|
- lib/brick/frameworks/rspec.rb
|
220
221
|
- lib/brick/join_array.rb
|
221
222
|
- lib/brick/serializers/json.rb
|