brick 1.0.97 → 1.0.99

Sign up to get free protection for your applications and to get access to all the features.
@@ -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, obj_name, pk)
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.map { |fk_part| "#{obj_name}.#{fk_part}" })
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.map { |x| "#{x.first}: #{x.last}"}.join(', ')
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
- "'#{tbl_nm}.#{associative.foreign_key}'"
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
- hm_entry = +"'#{hm_assoc.name}' => [#{assoc_name.inspect}"
164
- hm_entry << if hm_assoc.macro == :has_many
165
- if hm_fk_name # %%% Can remove this check when multiple foreign keys to same destination becomes bulletproof
166
- set_ct = if skip_klass_hms.key?(assoc_name.to_sym)
167
- 'nil'
168
- else
169
- # Postgres column names are limited to 63 characters
170
- "#{obj_name}.#{"b_r_#{assoc_name}_ct"[0..62]}&.to_i || 0"
171
- end
172
- ", #{set_ct}, #{path_keys(hm_assoc, hm_fk_name, obj_name, pk)}"
173
- end
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
- "<%= link_to '#{assoc_name}', #{hm_assoc.klass._brick_index}_path({ #{path_keys(hm_assoc, hm_fk_name, "@#{obj_name}", pk)} }) %>\n"
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}\"."
@@ -241,6 +424,14 @@ h1, h3 {
241
424
  cursor: pointer;
242
425
  }
243
426
 
427
+ #apiToggle {
428
+ border: 2px solid purple;
429
+ }
430
+
431
+ #apiToggle, .apiColName {
432
+ cursor: pointer;
433
+ }
434
+
244
435
  #dropper {
245
436
  background-color: #eee;
246
437
  }
@@ -437,7 +628,18 @@ end
437
628
  def slashify(*vals)
438
629
  vals.map { |val_part| val_part.is_a?(String) ? val_part.gsub('/', '^^sl^^') : val_part }
439
630
  end
440
- callbacks = {} %>"
631
+ callbacks = {} %>
632
+
633
+ <% avo_svg = \"#{
634
+ "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 84 90\" height=\"30\" fill=\"#3096F7\">
635
+ <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>
636
+ <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>
637
+ <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>
638
+ <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>
639
+ <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>
640
+ </svg>
641
+ ".gsub('"', '\"')
642
+ }\".html_safe %>"
441
643
 
442
644
  if ['index', 'show', 'new', 'update'].include?(args.first)
443
645
  poly_cols = []
@@ -520,31 +722,7 @@ document.querySelectorAll(\"input[type=submit][data-confirm]\").forEach(function
520
722
  });
521
723
  });
522
724
 
523
- function changeout(href, param, value, trimAfter) {
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
- }
725
+ #{JS_CHANGEOUT}
548
726
 
549
727
  // Snag first TR for sticky header
550
728
  var grid = document.getElementById(\"#{table_name}\");
@@ -730,11 +908,6 @@ erDiagram
730
908
  end
731
909
  inline = case args.first
732
910
  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
911
  if Object.const_defined?('DutyFree')
739
912
  template_link = "
740
913
  <%= link_to 'CSV', #{@_brick_model._brick_index}_path(format: :csv) %> &nbsp; <a href=\"#\" id=\"sheetsLink\">Sheets</a>
@@ -839,9 +1012,21 @@ erDiagram
839
1012
  <p style=\"color: green\"><%= notice %></p>#{"
840
1013
  #{schema_options}" if schema_options}
841
1014
  <select id=\"tbl\">#{table_options}</select>
1015
+
1016
+ <%= pick_api(#{model_name}).html_safe %>
1017
+
842
1018
  <table id=\"resourceName\"><tr>
843
1019
  <td><h1>#{model_name}</h1></td>
844
1020
  <td id=\"imgErd\" title=\"Show ERD\"></td>
1021
+ <% if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) %>
1022
+ <td><%= link_to_brick(
1023
+ avo_svg,
1024
+ { index_proc: Proc.new do |model|
1025
+ ::Avo.railtie_routes_url_helpers.send(\"resources_#\{model.base_class.model_name.route_key}_path\".to_sym)
1026
+ end,
1027
+ title: '#{model_name} in Avo' }
1028
+ ) %></td>
1029
+ <% end %>
845
1030
  </tr></table>#{template_link}<%
846
1031
  if description.present? %><%=
847
1032
  description %><br><%
@@ -875,149 +1060,41 @@ erDiagram
875
1060
  </script>
876
1061
  <% end %>
877
1062
  #{erd_markup}
878
- <table id=\"headerTop\"></table>
879
- <table id=\"#{table_name}\" class=\"shadow\">
880
- <thead><tr>#{"<th x-order=\"#{pk.join(',')}\"></th>" if pk.present?}<%=
881
- # Consider getting the name from the association -- hm.first.name -- if a more \"friendly\" alias should be used for a screwy table name
882
- cols = {#{hms_keys = []
883
- hms_headers.map do |hm|
884
- hms_keys << (assoc_name = (assoc = hm.first).name.to_s)
885
- "#{assoc_name.inspect} => [#{(assoc.options[:through] && !assoc.through_reflection).inspect}, #{assoc.klass.name}, #{hm[1].inspect}, #{hm[2].inspect}]"
886
- end.join(', ')}}
887
- # If the resource is missing, has the user simply created an inappropriately pluralised name for a table?
888
- @#{table_name} ||= if dym_list = instance_variables.reject do |entry|
889
- entry.to_s.start_with?('@_') ||
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)
1063
+
1064
+ <%= # Consider getting the name from the association -- hm.first.name -- if a more \"friendly\" alias should be used for a screwy table name
1065
+ cols = {#{hms_keys = []
1066
+ hms_headers.map do |hm|
1067
+ hms_keys << (assoc_name = (assoc = hm.first).name.to_s)
1068
+ "#{assoc_name.inspect} => [#{(assoc.options[:through] && !assoc.through_reflection).inspect}, #{assoc.klass.name}, #{hm[1].inspect}, #{hm[2].inspect}]"
1069
+ end.join(', ')}}
1070
+
1071
+ # If the resource is missing, has the user simply created an inappropriately pluralised name for a table?
1072
+ @#{table_name} ||= if dym_list = instance_variables.reject do |entry|
1073
+ entry.to_s.start_with?('@_') ||
1074
+ ['@cache_hit', '@marked_for_same_origin_verification', '@view_renderer', '@view_flow', '@output_buffer', '@virtual_path'].include?(entry.to_s)
903
1075
  end
904
- end
905
- col_keys = @#{table_name}.columns.each_with_object([]) do |col, s|
906
- col_name = col.name
907
- next if @_brick_incl&.exclude?(col_name) ||
908
- (#{(pk || []).inspect}.include?(col_name) && col.type == :integer && !bts.key?(col_name)) ||
909
- ::Brick.config.metadata_columns.include?(col_name) || poly_cols.include?(col_name)
910
-
911
- s << col_name
912
- cols[col_name] = col
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]
1076
+ msg = \"Can't find resource \\\"#{table_name}\\\".\"
1077
+ # Can't be sure otherwise of what is up, so check DidYouMean and offer a suggestion.
1078
+ if (dym = DidYouMean::SpellChecker.new(dictionary: dym_list).correct('@#{table_name}')).present?
1079
+ msg << \"\nIf you meant \\\"#\{found_dym = dym.first[1..-1]}\\\" then to avoid this message add this entry into inflections.rb:\n\"
1080
+ msg << \" inflect.singular('#\{found_dym}', '#{obj_name}')\"
1081
+ puts
1082
+ puts \"WARNING: #\{msg}\"
1083
+ puts
1084
+ @#{table_name} = instance_variable_get(dym.first.to_sym)
975
1085
  else
976
- descrips.last
1086
+ raise ActiveRecord::RecordNotFound.new(msg)
977
1087
  end
978
- bt_txt = bt_class.brick_descrip(
979
- # 0..62 because Postgres column names are limited to 63 characters
980
- #{obj_name}, descrips[0..-2].map { |id| #{obj_name}.send(id.last[0..62]) }, bt_id_col
981
- )
982
- bt_txt ||= \"<span class=\\\"orphan\\\">&lt;&lt; Orphaned ID: #\{val} >></span>\".html_safe if val
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>
1088
+ end
1089
+
1090
+ # Write out the mega-grid
1091
+ if params['_brick_api'] # API response?
1092
+ brick_grid(@#{table_name}, @_brick_bt_descrip, @_brick_sequence, [], @_brick_excl,
1093
+ cols, poly_cols, {}, [], {})
1094
+ else
1095
+ brick_grid(@#{table_name}, @_brick_bt_descrip, @_brick_sequence, @_brick_incl, @_brick_excl,
1096
+ cols, poly_cols, bts, #{hms_keys.inspect}, {#{hms_columns.join(', ')}})
1097
+ end %>
1021
1098
 
1022
1099
  #{"<hr><%= link_to \"New #{obj_name}\", new_#{path_obj_name}_path %>" unless @_brick_model.is_view?}
1023
1100
  #{script}
@@ -1125,7 +1202,18 @@ erDiagram
1125
1202
  <p style=\"color: green\"><%= notice %></p>#{"
1126
1203
  #{schema_options}" if schema_options}
1127
1204
  <select id=\"tbl\">#{table_options}</select>
1128
- <h1><%= page_title %></h1><%
1205
+ <table><td><h1><%= page_title %></h1></td>
1206
+ <% if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) %>
1207
+ <td><%= link_to_brick(
1208
+ avo_svg,
1209
+ { show_proc: Proc.new do |obj|
1210
+ ::Avo.railtie_routes_url_helpers.send(\"resources_#\{obj.class.base_class.model_name.singular_route_key}_path\".to_sym, obj)
1211
+ end,
1212
+ title: \"#\{page_title} in Avo\" }
1213
+ ) %></td>
1214
+ <% end %>
1215
+ </table>
1216
+ <%
1129
1217
  if (description = (relation = Brick.relations[#{model_name}.table_name])&.fetch(:description, nil)) %><%=
1130
1218
  description %><br><%
1131
1219
  end