brick 1.0.246 → 1.0.248

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3410ae5fca9b12aa31007df9d3fac95a39f09eee58f4ffc2089bada97bde7793
4
- data.tar.gz: 0da56c9311d8dd0ce2fd3a05039a6f85ca9910c64b303c25fc26f8d65fab512b
3
+ metadata.gz: ea1b54ee47db7637a4122bccb3fdcb613550aae57632bc6ce590851307085da2
4
+ data.tar.gz: 1f80b6c273b32df62b8b00f1251c9eea3322ecfd8cc308472cce4dbfecb85866
5
5
  SHA512:
6
- metadata.gz: 7a2e821dfee25a5df265caf5700e25003cbc7cab88e439102fc949f5dfbba2f9f7b73803c98068d373ffbf12cab27dac3859c76a7772ea19bbaf4ffdbfbddbe6
7
- data.tar.gz: 701d6d9e7d374cbe850806ce732f5b765c1ea3d051c111f316bcd29fdf45622d8999c04fa89ca058f13052670f56c77350fde24e00139ef40dc892b0c97ca896
6
+ metadata.gz: abb49248dc4466a54f892210db594acd481fe0292a21077fdc32e566855aabeca634b8ad5f9a4bef1d52c05e2aa798a4d24002f046e80bd285c19042db637d7e
7
+ data.tar.gz: a40eb65cfbd88e69e9b6dff165fbc4e83251b002cd445da9ad6a229841138311197fca25e22733c19e3ece186ecfbca882a35b7d64984ceaa3782571c65b181d
@@ -119,7 +119,7 @@ if Object.const_defined?('ActionPack') && !ActionPack.respond_to?(:version)
119
119
  end
120
120
  end
121
121
  end
122
- if Object.const_defined?('Bundler') && Bundler.locked_gems&.dependencies.key?('action_view')
122
+ if Object.const_defined?('Bundler') && Bundler.locked_gems&.dependencies&.key?('action_view')
123
123
  require 'action_view' # Needed for Rails <= 4.0
124
124
  module ::ActionView
125
125
  if Object.const_defined?('ActionView') && !ActionView.respond_to?(:version)
@@ -38,6 +38,8 @@
38
38
 
39
39
  # Upon creation of a new object, when going to the index page, highlight this new object and scroll it into view (likely to the very bottom of everything, although might be sorted differently)
40
40
 
41
+ # If there is a module defined with the same name as a schema found in a Postgres, MSSQL, or Oracle database then missing models that should exist underneath that module are not auto-generated.
42
+
41
43
  # ==========================================================
42
44
  # Dynamically create model or controller classes when needed
43
45
  # ==========================================================
@@ -105,13 +107,41 @@ module ActiveRecord
105
107
  col_names = columns_hash.keys
106
108
  # If it's a composite primary key then allow all the values through
107
109
  # TODO: Should disallow any autoincrement / SERIAL columns
108
- if skip_id && (pk_as_array = _pk_as_array).length == 1
109
- col_names -= _pk_as_array
110
+ if skip_id && (pk_as_array = _pk_as_array).length == 1 &&
111
+ [:string, :text].exclude?(columns_hash[pk_as_array.first]&.type)
112
+ col_names -= pk_as_array
110
113
  end
111
114
  hoa, hma, rtans = _activestorage_actiontext_fields
112
115
  col_names.map(&:to_sym) + hoa + hma.map { |as| { as => [] } } + rtans.values
113
116
  end
114
117
 
118
+ def _brick_set_wheres(params, join_array = nil)
119
+ is_distinct = nil
120
+ wheres = {}
121
+ params.each do |k, v|
122
+ k = k.to_s # Rails < 4.2 comes in as a symbol
123
+ next unless k.start_with?('__')
124
+
125
+ k = k[2..-1] # Take off leading "__"
126
+ if (where_col = (ks = k.split('.')).last)[-1] == '!'
127
+ where_col = where_col[0..-2]
128
+ end
129
+ case ks.length
130
+ when 1
131
+ next unless self.column_names.any?(where_col) || self._brick_get_fks.include?(where_col)
132
+ when 2
133
+ assoc_name = ks.first.to_sym
134
+ # Make sure it's a good association name and that the model has that column name
135
+ next unless self.reflect_on_association(assoc_name)&.klass&.column_names&.any?(where_col)
136
+
137
+ join_array&.[]=(assoc_name, nil) # Store this relation name in our special collection for .joins()
138
+ is_distinct = true
139
+ end
140
+ wheres[k] = v.is_a?(String) ? v.split(',') : v
141
+ end
142
+ [wheres, is_distinct]
143
+ end
144
+
115
145
  # Return three lists of fields for this model --
116
146
  # has_one_attached, has_many_attached, and has_rich_text
117
147
  def _activestorage_actiontext_fields
@@ -185,7 +215,7 @@ module ActiveRecord
185
215
  end
186
216
 
187
217
  def is_postgres
188
- @is_postgres ||= connection.adapter_name == 'PostgreSQL'
218
+ @is_postgres ||= ['PostgreSQL', 'PostGIS'].include?(connection.adapter_name)
189
219
  end
190
220
  def is_mysql
191
221
  @is_mysql ||= ['Mysql2', 'Trilogy'].include?(connection.adapter_name)
@@ -219,10 +249,17 @@ module ActiveRecord
219
249
  else
220
250
  # If there's no DSL yet specified, just try to find the first usable column on this model
221
251
  dsl = if table_exists?
222
- skip_columns = _brick_get_fks + (::Brick.config.metadata_columns || []) + [primary_key]
223
- if (descrip_col = columns.find { |c| c.type == :string && skip_columns.exclude?(c.name) })
252
+ skip_columns = _brick_get_fks + (::Brick.config.metadata_columns || [])
253
+ column_list = columns.dup
254
+ pk_idx = column_list.index { |c| c.name == primary_key }
255
+ if pk_idx
256
+ pk_column = column_list.delete_at(pk_idx)
257
+ # if the PK column is a string, put it at the end so it will get chosen as a last resort
258
+ column_list << pk_column if [:string, :text].include?(columns_hash[primary_key]&.type)
259
+ end
260
+ if (descrip_col = column_list.find { |c| c.type == :string && skip_columns.exclude?(c.name) })
224
261
  "[#{descrip_col.name}]"
225
- elsif (descrip_col = columns.find { |c| [:boolean, :binary, :xml].exclude?(c.type) && skip_columns.exclude?(c.name) })
262
+ elsif (descrip_col = column_list.find { |c| [:boolean, :binary, :xml].exclude?(c.type) && skip_columns.exclude?(c.name) })
226
263
  "[#{descrip_col.name}]"
227
264
  else
228
265
  "#{name} ##{_pk_as_array.map { |pk_part| "[#{pk_part}]" }.join(', ')}"
@@ -689,32 +726,10 @@ module ActiveRecord
689
726
  # model early in case the user wants to do an ORDER BY based on any of that.
690
727
  model._brick_calculate_bts_hms(translations, join_array) if is_add_bts || is_add_hms
691
728
 
692
- is_distinct = nil
693
- wheres = {}
694
729
  params = params.to_unsafe_h unless params.is_a?(Hash)
695
730
  params.merge!(args[1]) if args[1]
696
- params.each do |k, v|
697
- k = k.to_s # Rails < 4.2 comes in as a symbol
698
- next unless k.start_with?('__')
699
-
700
- k = k[2..-1] # Take off leading "__"
701
- if (where_col = (ks = k.split('.')).last)[-1] == '!'
702
- where_col = where_col[0..-2]
703
- end
704
- case ks.length
705
- when 1
706
- next unless klass.column_names.any?(where_col) || klass._brick_get_fks.include?(where_col)
707
- when 2
708
- assoc_name = ks.first.to_sym
709
- # Make sure it's a good association name and that the model has that column name
710
- next unless klass.reflect_on_association(assoc_name)&.klass&.column_names&.any?(where_col)
711
-
712
- join_array[assoc_name] = nil # Store this relation name in our special collection for .joins()
713
- is_distinct = true
714
- distinct!
715
- end
716
- wheres[k] = v.is_a?(String) ? v.split(',') : v
717
- end
731
+ wheres, is_distinct = klass._brick_set_wheres(params, join_array)
732
+ distinct! if is_distinct
718
733
 
719
734
  # %%% Skip the metadata columns
720
735
  if selects.empty? # Default to all columns
@@ -1756,7 +1771,7 @@ class Object
1756
1771
  end
1757
1772
  full_name = if relation || schema_name.blank?
1758
1773
  if singular_table_name != table_name.singularize && # %%% Try this with http://localhost:3000/brick/spree/property_translations
1759
- (schema_module = ::Brick.config.table_name_prefixes.find { |k, v| table_name.start_with?(k) }&.last&.constantize)
1774
+ (schema_module = ::Brick.config.table_name_prefixes.find { |k, v| table_name.length > k&.length && table_name.start_with?(k) }&.last&.constantize)
1760
1775
  "#{schema_module&.name}::#{inheritable_name || model_name}"
1761
1776
  else
1762
1777
  inheritable_name || model_name
@@ -1775,7 +1790,7 @@ class Object
1775
1790
  ::Brick.config.exclude_tables.include?(matching)
1776
1791
 
1777
1792
  # Are they trying to use a pluralised class name such as "Employees" instead of "Employee"?
1778
- if table_name == singular_table_name && !ActiveSupport::Inflector.inflections.uncountable.include?(table_name)
1793
+ if table_name == singular_table_name && model_name.pluralize == model_name && !ActiveSupport::Inflector.inflections.uncountable.include?(table_name)
1779
1794
  # unless ::Brick.config.sti_namespace_prefixes&.key?("::#{singular_table_name.camelize}::")
1780
1795
  # puts "Warning: Class name for a model that references table \"#{matching
1781
1796
  # }\" should be \"#{ActiveSupport::Inflector.singularize(inheritable_name || model_name)}\"."
@@ -2193,7 +2208,7 @@ class Object
2193
2208
  end
2194
2209
  table_name = model&.table_name || ActiveSupport::Inflector.underscore(plural_class_name)
2195
2210
  pk = model&._brick_primary_key(relations.fetch(table_name, nil))
2196
- is_postgres = ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
2211
+ is_postgres = ['PostgreSQL', 'PostGIS'].include?(ActiveRecord::Base.connection.adapter_name)
2197
2212
  is_mysql = ['Mysql2', 'Trilogy'].include?(ActiveRecord::Base.connection.adapter_name)
2198
2213
 
2199
2214
  namespace = nil if namespace == ::Object
@@ -2475,10 +2490,19 @@ class Object
2475
2490
 
2476
2491
  real_model = model.find_real_model(params)
2477
2492
 
2493
+ # %%% Allow params to define which columns to use for order_by
2494
+ # Overriding the default by providing a querystring param?
2495
+ order_by = params['_brick_order']&.split(',')&.map(&:to_sym) || Object.send(:default_ordering, table_name, pk)
2496
+
2478
2497
  if request.format == :csv # Asking for a template?
2479
2498
  require 'csv'
2480
2499
  exported_csv = CSV.generate(force_quotes: false) do |csv_out|
2481
- real_model.df_export(true, real_model.brick_import_template, false, wheres, order_by).each do |row|
2500
+ export_args = [true, real_model.brick_import_template, false]
2501
+ if real_model.method(:df_export).parameters.length > 3
2502
+ wheres, _is_distinct = real_model._brick_set_wheres(params)
2503
+ export_args += [wheres, order_by]
2504
+ end
2505
+ real_model.df_export(*export_args).each do |row|
2482
2506
  row.each do |d|
2483
2507
  row.each_with_index do |d, idx|
2484
2508
  # "false" disallows HTML encoding the descriptions of binary content
@@ -2499,11 +2523,6 @@ class Object
2499
2523
  end
2500
2524
 
2501
2525
  # Normal (not swagger or CSV) request
2502
-
2503
- # %%% Allow params to define which columns to use for order_by
2504
- # Overriding the default by providing a querystring param?
2505
- order_by = params['_brick_order']&.split(',')&.map(&:to_sym) || Object.send(:default_ordering, table_name, pk)
2506
-
2507
2526
  ar_relation = ActiveRecord.version < Gem::Version.new('4') ? real_model.preload : real_model.all
2508
2527
  params['_brick_is_api'] = true if (is_api = request.format == :js || current_api_root)
2509
2528
  @_brick_params = ar_relation._brick_querying((selects ||= []), params: params, order_by: order_by,
@@ -2696,7 +2715,14 @@ class Object
2696
2715
  render json: { result: es_result }
2697
2716
  else
2698
2717
  real_model, real_singular_table_name = model.real_singular(params)
2699
- created_obj = model.send(:new, send(params_name_sym))
2718
+ # If there are any foreign keys that are strings and we've received a blank value, set that to nil
2719
+ internal_params = (ac_params = send(params_name_sym))&.send(:parameters)
2720
+ real_model.reflect_on_all_associations.each do |a|
2721
+ if a.belongs_to? && internal_params.key?(fk_name = a.foreign_key.to_s) && [:string, :text].include?((fk_col = real_model.columns_hash[fk_name])&.type)
2722
+ internal_params[fk_name] = nil if internal_params[fk_name].blank?
2723
+ end
2724
+ end
2725
+ created_obj = model.send(:new, ac_params)
2700
2726
  if created_obj.respond_to?(inh_col = model.inheritance_column) && created_obj.send(inh_col) == ''
2701
2727
  created_obj.send("#{inh_col}=", model.name)
2702
2728
  end
@@ -2846,14 +2872,14 @@ class Object
2846
2872
  code << " def #{params_name}\n"
2847
2873
  require_txt = model.base_class.name.underscore.tr('/', '_')
2848
2874
  is_for_expects = ::ActiveSupport.version >= ::Gem::Version.new('8.0a')
2849
- permits_txt = model._brick_find_permits(model, permits = model._brick_all_fields(true), is_for_expects)
2875
+ permits = model._brick_find_permits(model, model._brick_all_fields(true), is_for_expects)
2850
2876
  if is_for_expects
2851
- code << " params.expect(#{require_txt}: #{permits_txt})\n"
2877
+ code << " params.expect(#{require_txt}: #{permits})\n"
2852
2878
  self.define_method(params_name) do
2853
2879
  params.expect({ model.base_class.name.underscore.tr('/', '_').to_sym => permits })
2854
2880
  end
2855
2881
  else
2856
- code << " params.require(:#{require_txt}).permit(#{permits_txt.map(&:inspect).join(', ')})\n"
2882
+ code << " params.require(:#{require_txt}).permit(#{permits.map(&:inspect).join(', ')})\n"
2857
2883
  self.define_method(params_name) do
2858
2884
  params.require(model.base_class.name.underscore.tr('/', '_').to_sym).permit(permits)
2859
2885
  end
@@ -828,13 +828,13 @@ window.addEventListener(\"popstate\", linkSchemas);
828
828
  end
829
829
  css = +"
830
830
  <%
831
- if @_request.respond_to?(:content_security_policy) && (csp = @_request.content_security_policy)&.directives&.present?
831
+ if @_request.respond_to?(:content_security_policy) && (csp = @_request.content_security_policy)&.directives&.present? %><%
832
832
  @_request.env['_is_brick'] = true
833
833
  if @_request.respond_to?(:content_security_policy_nonce_directives)
834
834
  @_request.content_security_policy_nonce_directives = %w[ script-src ]
835
835
  @_request.env['_brick_nonce'] = \" nonce=\\\"#\{@_request.content_security_policy_nonce}\\\"\".html_safe
836
836
  %><meta name=\"csp-nonce\" content=\"<%= @_request.content_security_policy_nonce %>\"><%
837
- end
837
+ end %><%
838
838
  if !@_request.respond_to?(:_brick_content_security_policy)
839
839
  if csp.instance_variables.exclude?(:@_brick_style_shas)
840
840
  csp.instance_variable_set(:@_brick_style_shas, [
@@ -950,7 +950,10 @@ if (window.brickFontFamily) {
950
950
  when 'index'
951
951
  if Object.const_defined?('DutyFree')
952
952
  template_link = "
953
- <%= link_to 'CSV', #{@_brick_model._brick_index}_path(format: :csv) %> &nbsp; <a href=\"#\" id=\"sheetsLink\">Sheets</a>
953
+ <%= csv_path = #{@_brick_model._brick_index}_path(format: :csv)
954
+ where_params = params.to_unsafe_h.each_with_object([]) { |v, s| s << \"#\{v.first}=#\{v[1]}\" if v.first.start_with?('__') }.join('&')
955
+ csv_path << \"?#\{where_params}\" unless where_params.blank?
956
+ link_to 'CSV', csv_path %> &nbsp; <a href=\"#\" id=\"sheetsLink\">Sheets</a>
954
957
  <div id=\"dropper\" contenteditable=\"true\"></div>
955
958
  <input type=\"button\" id=\"btnImport\" value=\"Import\">
956
959
 
@@ -1039,7 +1042,7 @@ if (window.brickFontFamily) {
1039
1042
  }
1040
1043
  </script>
1041
1044
  <script src=\"https://apis.google.com/js/api.js\"></script>
1042
- <script async defer src=\"https://accounts.google.com/gsi/client\" id=\"gapiScript\"<%= @_request.env['_brick_nonce'] %>></script>
1045
+ <script async defer src=\"https://accounts.google.com/gsi/client\" id=\"gapiScript\"></script>
1043
1046
  <script<%= @_request.env['_brick_nonce'] %>>
1044
1047
  document.getElementById(\"gapiScript\").addEventListener(\"onload\",
1045
1048
  function () { // gapiLoaded
@@ -1072,14 +1075,16 @@ if (window.brickFontFamily) {
1072
1075
  </head>
1073
1076
  <body>
1074
1077
  <div id=\"titleBox\"><div id=\"titleSticky\">
1075
- <% if request.respond_to?(:flash)
1076
- if (alert)
1077
- %><p class=\"flashAlert\"><%= alert.html_safe %></p><%
1078
- end
1079
- if (notice)
1080
- %><p class=\"flashNotice\"><%= notice.html_safe %></p><%
1081
- end
1082
- end %>#{"
1078
+ <%= if request.respond_to?(:flash)
1079
+ out = +''
1080
+ if (alert)
1081
+ out << \"<p class=\\\"flashAlert\\\">#\{alert}</p>\"
1082
+ end
1083
+ if (notice)
1084
+ out << \"<p class=\\\"flashNotice\\\">#\{notice}</p>\"
1085
+ end
1086
+ out.html_safe
1087
+ end %>#{"
1083
1088
  #{schema_options}" if schema_options}
1084
1089
  <select id=\"tbl\">#{table_options}</select>
1085
1090
  <table id=\"resourceName\"><tr>
@@ -1111,22 +1116,22 @@ end
1111
1116
  if description.present? %><span class=\"__brick\"><%=
1112
1117
  description %></span><br><%
1113
1118
  end
1114
- # FILTER PARAMETERS
1119
+ # FILTER PARAMETERS %><%
1115
1120
  if @_brick_params&.present? %>
1116
- <% if @_brick_params.length == 1 # %%% Does not yet work with composite keys
1121
+ <% if @_brick_params.length == 1 # %%% Does not yet work with composite keys %><%
1117
1122
  k, id = @_brick_params.first
1118
1123
  id = id.first if id.is_a?(Array) && id.length == 1
1119
- origin = (key_parts = k.split('.')).length == 1 ? model : model.reflect_on_association(key_parts.first).klass
1124
+ origin = (key_parts = k.split('.')).length == 1 ? model : model.reflect_on_association(key_parts.first).klass %><%
1120
1125
  if (destination_fk = Brick.relations[origin.table_name][:fks].values.find { |fk| fk[:fk] == key_parts.last }) &&
1121
- (objs = (destination = origin.reflect_on_association(destination_fk[:assoc_name])&.klass)&.find(id))
1126
+ (objs = (destination = origin.reflect_on_association(destination_fk[:assoc_name])&.klass)&.find(id)) %><%
1122
1127
  objs = [objs] unless objs.is_a?(Array) %>
1123
1128
  <h3 class=\"__brick\">for <% objs.each do |obj| %><%=
1124
1129
  link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination._brick_index(:singular)\}_path\".to_sym, id)
1125
1130
  %><% end %></h3><%
1126
- end
1131
+ end %><%
1127
1132
  end %>
1128
1133
  <span class=\"__brick\">(<%= link_to \"See all #\{model.base_class.name.split('::').last.pluralize}\", #{@_brick_model._brick_index}_path %>)</span>
1129
- <% end
1134
+ <% end %><%
1130
1135
  # COLUMN EXCLUSIONS
1131
1136
  if @_brick_excl&.present? %>
1132
1137
  <div id=\"exclusions\">Excluded columns:
@@ -1141,9 +1146,10 @@ end
1141
1146
  });
1142
1147
  });
1143
1148
  </script>
1144
- <% end
1149
+ <% end %><%
1145
1150
  # SEARCH BOX
1146
- if @_brick_es && @_brick_es&.index('r') # Must have at least Elasticsearch Read access %>
1151
+ if @_brick_es && @_brick_es&.index('r') # Must have at least Elasticsearch Read access
1152
+ %>
1147
1153
  <input type=\"text\" id=\"esSearch\" class=\"dimmed\">
1148
1154
  <script<%= @_request.env['_brick_nonce'] %>>
1149
1155
  var esSearch = document.getElementById(\"esSearch\");
@@ -1208,7 +1214,7 @@ end
1208
1214
  </div></div>
1209
1215
  #{::Brick::Rails.erd_markup(@_brick_model, prefix) if @_brick_model}
1210
1216
 
1211
- <%= # Consider getting the name from the association -- hm.first.name -- if a more \"friendly\" alias should be used for a screwy table name
1217
+ <%=# Consider getting the name from the association -- hm.first.name -- if a more \"friendly\" alias should be used for a screwy table name
1212
1218
  # If the resource is missing, has the user simply created an inappropriately pluralised name for a table?
1213
1219
  @#{res_name} ||= if (dym_list = instance_variables.reject do |entry|
1214
1220
  entry.to_s.start_with?('@_') ||
@@ -1340,21 +1346,21 @@ end
1340
1346
  ' class=\"orphan\"' unless ::Brick.relations.key?(r[1])
1341
1347
  else
1342
1348
  ' class=\"dimmed\"'
1343
- end&.html_safe %>><%= # Table
1349
+ end&.html_safe %>><%=# Table
1344
1350
  if (rowcount = rel&.fetch(:rowcount, nil))
1345
1351
  rowcount = (rowcount > 0 ? \" (#\{rowcount})\" : nil)
1346
1352
  end
1347
1353
  \"#\{r[1]}#\{rowcount}\" %></td>
1348
1354
  <td<%= lines = r[2]&.map { |line| \"#\{line.first}:#\{line.last}\" }
1349
- ' class=\"dimmed\"'.html_safe unless r[2] %>><%= # Migration
1355
+ ' class=\"dimmed\"'.html_safe unless r[2] %>><%=# Migration
1350
1356
  lines&.join('<br>')&.html_safe %></td>
1351
- <td<%= ' class=\"dimmed\"'.html_safe unless r[3] %>><%= # Model
1357
+ <td<%= ' class=\"dimmed\"'.html_safe unless r[3] %>><%=# Model
1352
1358
  r[3] %></td>
1353
- <td<%= ' class=\"dimmed\"'.html_safe unless r[4] %>><%= # Route
1359
+ <td<%= ' class=\"dimmed\"'.html_safe unless r[4] %>><%=# Route
1354
1360
  %></td>
1355
- <td<%= ' class=\"dimmed\"'.html_safe unless r[5] %>><%= # Controller
1361
+ <td<%= ' class=\"dimmed\"'.html_safe unless r[5] %>><%=# Controller
1356
1362
  %></td>
1357
- <td<%= ' class=\"dimmed\"'.html_safe unless r[6] %>><%= # Views
1363
+ <td<%= ' class=\"dimmed\"'.html_safe unless r[6] %>><%=# Views
1358
1364
  %></td>
1359
1365
  </tr>
1360
1366
  <% end %>
@@ -1369,14 +1375,14 @@ end
1369
1375
  #{schema_options}" if schema_options}
1370
1376
  <select id=\"tbl\">#{table_options}</select>
1371
1377
  <h1>Orphans<%= \" for #\{}\" if false %></h1>
1372
- <% @orphans.each do |o|
1373
- if (klass = ::Brick.relations[o[0]]&.fetch(:class_name, nil)&.constantize) %>
1374
- <%= via = \" (via #\{o[4]})\" unless \"#\{o[2].split('.').last.underscore.singularize}_id\" == o[4]
1378
+ <%= @orphans.each_with_object(+'') do |o, s|
1379
+ if (klass = ::Brick.relations[o[0]]&.fetch(:class_name, nil)&.constantize)
1380
+ s << \" via = \\\" (via #\{o[4]})\\\" unless \"#\{o[2].split('.').last.underscore.singularize}_id\" == o[4]
1375
1381
  link_to(\"#\{o[0]} #\{o[1]} refers#\{via} to non-existent #\{o[2]} #\{o[3]}#\{\" (in table \\\"#\{o[5]}\\\")\" if o[5]}\",
1376
- send(\"#\{klass._brick_index(:singular)\}_path\".to_sym, o[1])) %>
1377
- <br>
1378
- <% end
1379
- end %>
1382
+ send(\"#\{klass._brick_index(:singular)\}_path\".to_sym, o[1]))
1383
+ s << \"\\n <br>\\n\"
1384
+ end
1385
+ end.html_safe %>
1380
1386
  #{script}"
1381
1387
  end
1382
1388
 
@@ -1416,25 +1422,27 @@ end
1416
1422
  c23.141-70.188,89.141-120.906,167.063-120.906c97.25,0,176,78.813,176,176C511.828,227.078,404.391,119.641,271.844,119.641z\" />
1417
1423
  </svg>
1418
1424
 
1419
- <% if request.respond_to?(:flash)
1420
- if (alert)
1421
- %><p class=\"flashAlert\"><%= alert.html_safe %></p><%
1422
- end
1423
- if (notice)
1424
- %><p class=\"flashNotice\"><%= notice.html_safe %></p><%
1425
- end
1426
- end %>#{"
1425
+ <%= if request.respond_to?(:flash)
1426
+ out = +''
1427
+ if (alert)
1428
+ out << \"<p class=\\\"flashAlert\\\">#\{alert}</p>\"
1429
+ end
1430
+ if (notice)
1431
+ out << \"<p class=\\\"flashNotice\\\">#\{notice}</p>\"
1432
+ end
1433
+ out.html_safe
1434
+ end %>#{"
1427
1435
  #{schema_options}" if schema_options}
1428
1436
  <select id=\"tbl\">#{table_options}</select>
1429
1437
  <table id=\"resourceName\"><tr><td><h1><%= page_title %></h1></td>
1430
- <% rel = Brick.relations[#{model_name}.table_name]
1438
+ <%= rel = Brick.relations[#{model_name}.table_name]
1431
1439
  if (in_app = rel.fetch(:existing, nil)&.fetch(:show, nil))
1432
1440
  begin
1433
- in_app = send(\"#\{in_app}_path\", #{pk.is_a?(String) ? "obj.#{pk}" : '[' + pk.map { |pk_part| "obj.#{pk_part}" }.join(', ') + ']' }) if in_app.is_a?(Symbol) %>
1434
- <td><%= link_to(::Brick::Rails::IN_APP.html_safe, in_app) %></td>
1435
- <% rescue ActionController::UrlGenerationError
1441
+ in_app = send(\"#\{in_app}_path\", #{pk.is_a?(String) ? "obj.#{pk}" : '[' + pk.map { |pk_part| "obj.#{pk_part}" }.join(', ') + ']' }) if in_app.is_a?(Symbol)
1442
+ \"\\n <td>#\{link_to(::Brick::Rails::IN_APP.html_safe, in_app)}</td>\\n\".html_safe
1443
+ rescue ActionController::UrlGenerationError
1436
1444
  end
1437
- end
1445
+ end %><%
1438
1446
 
1439
1447
  if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) %>
1440
1448
  <td><%= link_to_brick(
@@ -1445,19 +1453,19 @@ end %>#{"
1445
1453
  end,
1446
1454
  title: \"#\{page_title} in Avo\" }
1447
1455
  ) %></td>
1448
- <% end
1456
+ <% end %><%=
1449
1457
 
1450
1458
  if Object.const_defined?('ActiveAdmin')
1451
- ActiveAdmin.application.namespaces.names.each do |ns| %>
1452
- <td><%= link_to_brick(
1453
- ::Brick::Rails::AA_PNG.html_safe,
1454
- { show_proc: Proc.new do |aa_model, relation|
1455
- path_helper = \"#\{ns}_#\{relation.fetch(:auto_prefixed_schema, nil)}#\{aa_model.model_name.singular_route_key}_path\".to_sym
1456
- send(path_helper, obj) if respond_to?(path_helper)
1457
- end,
1458
- title: \"#\{page_title} in ActiveAdmin\" }
1459
- ) %></td>
1460
- <% end
1459
+ ActiveAdmin.application.namespaces.names.each_with_object(+'') do |ns, s|
1460
+ s << \"\\n<td>#\{link_to_brick(
1461
+ ::Brick::Rails::AA_PNG.html_safe,
1462
+ { show_proc: Proc.new do |aa_model, relation|
1463
+ path_helper = \"#\{ns}_#\{relation.fetch(:auto_prefixed_schema, nil)}#\{aa_model.model_name.singular_route_key}_path\".to_sym
1464
+ send(path_helper, obj) if respond_to?(path_helper)
1465
+ end,
1466
+ title: \"#\{page_title} in ActiveAdmin\" }
1467
+ )}</td>\\n\"
1468
+ end
1461
1469
  end %>
1462
1470
  </tr></table>
1463
1471
  <%
@@ -1466,21 +1474,11 @@ if (description = rel&.fetch(:description, nil)) %>
1466
1474
  end
1467
1475
  %><%= link_to \"(See all #\{model_name.pluralize})\", see_all_path, { class: '__brick' } %>
1468
1476
  #{::Brick::Rails.erd_markup(@_brick_model, prefix) if @_brick_model}
1469
- <% if obj
1470
- # path_options = [obj.#{pk}]
1471
- # path_options << { '_brick_schema': } if
1472
- options = {}
1473
- options[:url] = if obj.new_record?
1474
- link_to_brick(obj.class, path_only: true) # Properly supports STI, but only works for :new
1475
- else
1476
- path_helper = obj.new_record? ? #{model_name}._brick_index : #{model_name}._brick_index(:singular)
1477
- options[:url] = send(\"#\{path_helper}_path\".to_sym, obj) if ::Brick.config.path_prefix || (path_helper != obj.class.table_name)
1478
- end
1479
- %>
1477
+ <% if obj %>
1480
1478
  <br><br>
1481
1479
 
1482
- <%= # Write out the mega-form
1483
- brick_form_for(obj, options, #{model_name}, bts, #{pk.inspect}) %>
1480
+ <%=# Write out the mega-form
1481
+ brick_form_with(model: obj, bts: bts, pk: #{pk.inspect}) %>
1484
1482
 
1485
1483
  #{unless args.first == 'new'
1486
1484
  # 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?\" } }"
@@ -1713,7 +1711,7 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
1713
1711
  end
1714
1712
  if representation == :grid
1715
1713
  inline << "<script<%= @_request.env['_brick_nonce'] %>>
1716
- <% # Make column headers sort when clicked
1714
+ <%# Make column headers sort when clicked
1717
1715
  # %%% Create a smart javascript routine which can do this client-side %>
1718
1716
  [... document.getElementsByTagName(\"TH\")].forEach(function (th) {
1719
1717
  th.addEventListener(\"click\", function (e) {
@@ -9,18 +9,19 @@ module Brick::Rails::FormBuilder
9
9
  def brick_field(method, html_options = {}, val = nil, col = nil,
10
10
  bt = nil, bt_class = nil, bt_name = nil, bt_pair = nil)
11
11
  model = self.object.class
12
- col ||= model.columns_hash[method]
12
+ col ||= model.columns_hash[method.to_s]
13
13
  out = +'<table><tr><td>'
14
14
  html_options[:class] = 'dimmed' unless val
15
15
  is_revert = true
16
16
  template = instance_variable_get(:@template)
17
+ method_sym = method.to_sym
17
18
  if bt
18
19
  bt_class ||= bt[1].first.first
19
20
  bt_name ||= bt[1].map { |x| x.first.name }.join('/')
20
21
  bt_pair ||= bt[1].first
21
22
 
22
23
  html_options[:prompt] = "Select #{bt_name}"
23
- out << self.select(method.to_sym, bt[3], { value: val || '^^^brick_NULL^^^' }, html_options)
24
+ out << self.select(method_sym, bt[3], { value: val || '^^^brick_NULL^^^' }, html_options)
24
25
  bt_obj = nil
25
26
  begin
26
27
  bt_obj = bt_class&.find_by(bt_pair[1] => val)
@@ -38,8 +39,8 @@ module Brick::Rails::FormBuilder
38
39
  "<span class=\"orphan\">Orphaned ID: #{val}</span>".html_safe
39
40
  end
40
41
  out << bt_link if bt_link
41
- elsif model._brick_monetized_attributes&.include?(method)
42
- out << self.text_field(method.to_sym, html_options.merge({ value: Money.new(val.to_i).format }))
42
+ elsif model._brick_monetized_attributes&.include?(method.to_s)
43
+ out << self.text_field(method_sym, html_options.merge({ value: Money.new(val.to_i).format }))
43
44
  else
44
45
  col_type = if model.json_column?(col) || val.is_a?(Array)
45
46
  :json
@@ -61,40 +62,40 @@ module Brick::Rails::FormBuilder
61
62
  out << "<img src=\"#{url}\" title=\"#{val}\">"
62
63
  elsif model.respond_to?(:enumerized_attributes) && (opts = (attr = model.enumerized_attributes[method])&.options).present?
63
64
  enum_html_options = attr.kind_of?(Enumerize::Multiple) ? html_options.merge({ multiple: true, size: opts.length + 1 }) : html_options
64
- out << self.select(method.to_sym, [["(No #{method} chosen)", '^^^brick_NULL^^^']] + opts, { value: val || '^^^brick_NULL^^^' }, enum_html_options)
65
+ out << self.select(method_sym, [["(No #{method} chosen)", '^^^brick_NULL^^^']] + opts, { value: val || '^^^brick_NULL^^^' }, enum_html_options)
65
66
  else
66
67
  spit_out_text_field = true
67
68
  end
68
69
  elsif col_type == :enum
69
70
  if col.respond_to?(:limit) && col.limit.present?
70
- out << self.select(method.to_sym, [["(No #{method} chosen)", '^^^brick_NULL^^^']] + col.limit, { value: val.to_s || '^^^brick_NULL^^^' }, html_options)
71
+ out << self.select(method_sym, [["(No #{method} chosen)", '^^^brick_NULL^^^']] + col.limit, { value: val.to_s || '^^^brick_NULL^^^' }, html_options)
71
72
  else
72
73
  spit_out_text_field = true
73
74
  end
74
75
  else
75
76
  template.instance_variable_set(:@_text_fields_present, true)
76
- out << self.hidden_field(method.to_sym, html_options)
77
+ out << self.hidden_field(method_sym, html_options)
77
78
  out << "<trix-editor input=\"#{self.field_id(method)}\"></trix-editor>"
78
79
  end
79
80
  if spit_out_text_field
80
81
  # %%% Need to update the max-width with javascript when page width is adjusted?
81
82
  html_options.merge!(style: 'min-width: 154px;field-sizing: content;') # max-width: auto;
82
- out << self.text_field(method.to_sym, html_options)
83
+ out << self.text_field(method_sym, html_options)
83
84
  end
84
85
  when :boolean
85
- out << self.check_box(method.to_sym)
86
+ out << self.check_box(method_sym)
86
87
  when :integer, :decimal, :float
87
- if model.respond_to?(:attribute_types) && (enum_type = model.attribute_types[method]).is_a?(ActiveRecord::Enum::EnumType)
88
+ if model.respond_to?(:attribute_types) && (enum_type = model.attribute_types[method.to_s]).is_a?(ActiveRecord::Enum::EnumType)
88
89
  opts = enum_type.send(:mapping)&.each_with_object([]) { |v, s| s << [v.first, v.first] } || []
89
- out << self.select(method.to_sym, [["(No #{method} chosen)", '^^^brick_NULL^^^']] + opts, { value: val || '^^^brick_NULL^^^' }, options)
90
+ out << self.select(method_sym, [["(No #{method} chosen)", '^^^brick_NULL^^^']] + opts, { value: val || '^^^brick_NULL^^^' }, options)
90
91
  else
91
92
  digit_pattern = col_type == :integer ? '(?:-|)\d*' : '(?:-|)\d*(?:\.\d*|)'
92
- # Used to do this for float / decimal: self.number_field method.to_sym
93
- out << self.text_field(method.to_sym, { pattern: digit_pattern, class: 'check-validity' })
93
+ # Used to do this for float / decimal: self.number_field method_sym
94
+ out << self.text_field(method_sym, { pattern: digit_pattern, class: 'check-validity' })
94
95
  end
95
96
  when *DT_PICKERS.keys
96
97
  template.instance_variable_set(:@_date_fields_present, true)
97
- out << self.text_field(method.to_sym, { class: DT_PICKERS[col_type] })
98
+ out << self.text_field(method_sym, { class: DT_PICKERS[col_type] })
98
99
  when :uuid
99
100
  is_revert = false
100
101
  # Postgres naturally uses the +uuid_generate_v4()+ function from the uuid-ossp extension
@@ -146,7 +147,7 @@ module Brick::Rails::FormBuilder
146
147
  end
147
148
  out << self.hidden_field("_brick_attached_#{method}", value: existing.join(',')) unless existing.blank?
148
149
  # Render a "Choose File(s)" input element
149
- args = [method.to_sym]
150
+ args = [method_sym]
150
151
  args << { multiple: true } if col&.type == :files
151
152
  out << self.file_field(*args)
152
153
  when :primary_key
@@ -163,7 +164,7 @@ module Brick::Rails::FormBuilder
163
164
  end
164
165
  # Because there are so danged many quotes in JSON, escape them specially by converting to backticks.
165
166
  # (and previous to this, escape backticks with our own goofy code of ^^br_btick__ )
166
- out << (json_field = self.hidden_field(method.to_sym, { class: 'jsonpicker', value: val_str&.gsub('`', '^^br_btick__')&.tr('\"', '`')&.html_safe }))
167
+ out << (json_field = self.hidden_field(method_sym, { class: 'jsonpicker', value: val_str&.gsub('`', '^^br_btick__')&.tr('\"', '`')&.html_safe }))
167
168
  out << "<div id=\"_br_json_#{self.field_id(method)}\"></div>"
168
169
  else
169
170
  is_revert = false
@@ -177,6 +178,7 @@ module Brick::Rails::FormBuilder
177
178
  end
178
179
  out << "</td></tr></table>
179
180
  "
181
+ @_brick&.fetch(:fields_rendered, nil)&.<< method.to_s # Track that we've rendered this one
180
182
  out.html_safe
181
183
  end # brick_field
182
184
  end