brick 1.0.108 → 1.0.110

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: 1e9a085e3f9ba811623d8ebc5d840018348d207b6b1ecab97c066894c44c6ab0
4
- data.tar.gz: 83f32dc71791730d7370f3375b3c19e6cc00e8062efe5b01bdb4cf5aa40b92b9
3
+ metadata.gz: 3d11240139b576ead983fbe9811f4476b8819878068c46db852b515fbc0849c6
4
+ data.tar.gz: 2db932779fcd8517f7e5be524ce387061d4e8cab193715be76893dc1e675d1c9
5
5
  SHA512:
6
- metadata.gz: dda58c12995437cb95ce60224543d64ef2ed16b0792a083e0def39e7be4569ceaaad86098f6470daf45c358db1d10a1ff350195c0c61ccd3363cbcfae0c344c2
7
- data.tar.gz: 673a82fbbd268c4c8ba590c8eb980fe6e71a62caff710d32ff3b21b3c67927a22ec02380d5d8f8366a1dcadaece238aab3e737f92e879de9da5893dcde0251fa
6
+ metadata.gz: dd22a1ab8d2b13f2d289fe02262ae8aecb3be921094bbb5dd0392fe5cd46ecadda259e72a18c3ecb7daea0fd5f5a50d697383a4bb949b068976e3b6007769d04
7
+ data.tar.gz: 39d26f6c99511d3651003c660fed495223ebd4fb34865b1eaa3b8554e294ed0fb3672b17e7dc43ca43fecbd1b7dda2ec6b9a4371025880e12c83694f45557173
@@ -42,9 +42,36 @@ end
42
42
  # file by having the line "require 'brick/compatibility'" to be the last line in that
43
43
  # file.
44
44
  require 'bigdecimal'
45
- if ::Gem::Version.new(RUBY_VERSION) >= ::Gem::Version.new('2.6') &&
46
- ActiveRecord.version < ::Gem::Version.new('5.0')
47
- def BigDecimal.new(*args, **kwargs)
48
- BigDecimal(*args, **kwargs)
45
+ if ActiveRecord.version < ::Gem::Version.new('5.0')
46
+ if ::Gem::Version.new(RUBY_VERSION) >= ::Gem::Version.new('2.6')
47
+ def BigDecimal.new(*args, **kwargs)
48
+ BigDecimal(*args, **kwargs)
49
+ end
50
+
51
+ if ::Gem::Version.new(RUBY_VERSION) >= ::Gem::Version.new('3.1')
52
+ # @@schemes fix for global_id gem < 1.0
53
+ URI.class_variable_set(:@@schemes, {}) unless URI.class_variables.include?(:@@schemes)
54
+ if Gem::Specification.all_names.find { |g| g.start_with?('puma-') }
55
+ require 'rack/handler/puma'
56
+ module Rack::Handler::Puma
57
+ class << self
58
+ alias _brick_run run
59
+ def run(app, *args, **options)
60
+ options.merge!(args.pop) if args.last.is_a?(Hash)
61
+ _brick_run(app, **options)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ require 'json'
68
+ if JSON::Parser.method(:initialize).parameters.length < 2 && JSON.method(:parse).arity == -2
69
+ JSON.class_exec do
70
+ def self.parse(source, opts = {})
71
+ ::JSON::Parser.new(source, **opts).parse
72
+ end
73
+ end
74
+ end
75
+ end
49
76
  end
50
77
  end
@@ -261,9 +261,16 @@ module ActiveRecord
261
261
  assoc_html_name ? "#{assoc_name}-#{link}".html_safe : link
262
262
  end
263
263
 
264
- def self._brick_index(mode = nil, separator = '_')
264
+ # Providing a relation object allows auto-modules built from table name prefixes to work
265
+ def self._brick_index(mode = nil, separator = '_', relation = nil)
265
266
  tbl_parts = ((mode == :singular) ? table_name.singularize : table_name).split('.')
266
267
  tbl_parts.shift if ::Brick.apartment_multitenant && tbl_parts.length > 1 && tbl_parts.first == ::Brick.apartment_default_tenant
268
+ if (aps = relation&.fetch(:auto_prefixed_schema, nil)) && tbl_parts.last.start_with?(aps)
269
+ last_part = tbl_parts.last[aps.length..-1]
270
+ aps = aps[0..-2] if aps[-1] == '_'
271
+ tbl_parts[-1] = aps
272
+ tbl_parts << last_part
273
+ end
267
274
  tbl_parts.unshift(::Brick.config.path_prefix) if ::Brick.config.path_prefix
268
275
  index = tbl_parts.map(&:underscore).join(separator)
269
276
  # Rails applies an _index suffix to that route when the resource name is singular
@@ -421,7 +428,10 @@ module ActiveRecord
421
428
  is_distinct = nil
422
429
  wheres = {}
423
430
  params.each do |k, v|
424
- next if ['_brick_schema', '_brick_order', 'controller', 'action'].include?(k)
431
+ next if ['_brick_schema', '_brick_order',
432
+ '_brick_erd', '_brick_exclude', '_brick_unexclude',
433
+ '_brick_page', '_brick_page_size', '_brick_offset', '_brick_limit',
434
+ '_brick_is_api', 'controller', 'action'].include?(k)
425
435
 
426
436
  if (where_col = (ks = k.split('.')).last)[-1] == '!'
427
437
  where_col = where_col[0..-2]
@@ -448,7 +458,10 @@ module ActiveRecord
448
458
  # %%% Have once gotten this error with MSSQL referring to http://localhost:3000/warehouse/cold_room_temperatures__archive
449
459
  # ActiveRecord::StatementInvalid (TinyTds::Error: DBPROCESS is dead or not enabled)
450
460
  # Relevant info here: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/402
461
+ is_api = params['_brick_is_api']
451
462
  columns.each do |col|
463
+ next if (col.type.nil? || col.type == :binary) && is_api
464
+
452
465
  col_alias = " AS #{col.name}_" if (col_name = col.name) == 'class'
453
466
  selects << if is_mysql
454
467
  "`#{tbl_no_schema}`.`#{col_name}`#{col_alias}"
@@ -999,7 +1012,8 @@ Module.class_exec do
999
1012
 
1000
1013
  # MODULE
1001
1014
  elsif (::Brick.enable_models? || ::Brick.enable_controllers?) && # Schema match?
1002
- base_module == Object && # %%% This works for Person::Person -- but also limits us to not being able to allow more than one level of namespacing
1015
+ # %%% This works for Person::Person -- but also limits us to not being able to allow more than one level of namespacing
1016
+ (base_module == Object || (camelize_prefix && base_module == Object.const_get(camelize_prefix))) &&
1003
1017
  (schema_name = [(singular_table_name = class_name.underscore),
1004
1018
  (table_name = singular_table_name.pluralize),
1005
1019
  ::Brick.is_oracle ? class_name.upcase : class_name,
@@ -1431,7 +1445,7 @@ class Object
1431
1445
  is_postgres = ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
1432
1446
  is_mysql = ['Mysql2', 'Trilogy'].include?(ActiveRecord::Base.connection.adapter_name)
1433
1447
 
1434
- code = +"class #{class_name} < #{controller_base&.name || 'ApplicationController'}\n"
1448
+ code = +"class #{namespace}::#{class_name} < #{controller_base&.name || 'ApplicationController'}\n"
1435
1449
  built_controller = Class.new(controller_base || ActionController::Base) do |new_controller_class|
1436
1450
  (namespace || Object).const_set(class_name.to_sym, new_controller_class)
1437
1451
 
@@ -1538,9 +1552,13 @@ class Object
1538
1552
  end
1539
1553
  if (current_api_root || is_openapi) &&
1540
1554
  !params&.key?('_brick_schema') &&
1541
- (referrer_params = request.env['HTTP_REFERER']&.split('?')&.last&.split('&')&.map { |x| x.split('=') }).present?
1555
+ (referrer_params = request.env['HTTP_REFERER']&.split('?')&.last&.split('&')&.each_with_object({}) do |x, s|
1556
+ if (kv = x.split('=')).length > 1
1557
+ s[kv.first] = kv[1..-1].join('=')
1558
+ end
1559
+ end).present?
1542
1560
  if params
1543
- referrer_params.each { |k, v| params.send(:parameters)[k] = v }
1561
+ referrer_params.each { |k, v| (params.respond_to?(:parameters) ? send(:parameters) : params)[k] = v }
1544
1562
  else
1545
1563
  api_params = referrer_params&.to_h
1546
1564
  end
@@ -1651,11 +1669,12 @@ class Object
1651
1669
  order_by, _ = model._brick_calculate_ordering(ordering, true) # Don't do the txt part
1652
1670
 
1653
1671
  ar_relation = ActiveRecord.version < Gem::Version.new('4') ? model.preload : model.all
1672
+ params['_brick_is_api'] = true if (is_api = request.format == :js || current_api_root)
1654
1673
  @_brick_params = ar_relation.brick_select(params, (selects ||= []), order_by,
1655
1674
  translations = {},
1656
1675
  join_array = ::Brick::JoinArray.new)
1657
1676
 
1658
- if request.format == :js || current_api_root # Asking for JSON?
1677
+ if is_api # Asking for JSON?
1659
1678
  # Apply column renaming
1660
1679
  data = ar_relation.respond_to?(:_select!) ? ar_relation.dup._select!(*selects) : ar_relation.select(selects)
1661
1680
  if data.present? &&
@@ -1667,13 +1686,16 @@ class Object
1667
1686
  end
1668
1687
  end
1669
1688
 
1670
- # %%% Still need to figure out column filtering and transformations
1689
+ # # %%% This currently only gives a window to check security and raise an exception if someone isn't
1690
+ # # authenticated / authorised. Still need to figure out column filtering and transformations.
1671
1691
  # proc_result = if (column_filter = ::Brick.config.api_column_filter).is_a?(Proc)
1672
- # object_columns = relation.last[:cols]
1692
+ # object_columns = (relation = model&._brick_relation)[:cols]
1673
1693
  # begin
1674
1694
  # num_args = column_filter.arity.negative? ? 5 : column_filter.arity
1675
1695
  # # object_name, api_version, columns, data
1676
- # column_filter.call(*[relation.first, api_ver_path, object_columns, nil][0...num_args])
1696
+ # api_ver_path = request.path[0..-relation[:resource].length]
1697
+ # # Call the api_column_filter in the context of this auto-built controller
1698
+ # instance_exec(*[relation[:resource], relation, api_ver_path, object_columns, data][0...num_args], &column_filter)
1677
1699
  # rescue StandardError => e
1678
1700
  # puts "::Brick.api_column_filter Proc error: #{e.message}"
1679
1701
  # end
@@ -1685,7 +1707,6 @@ class Object
1685
1707
  # else
1686
1708
  # relation.last[:cols]
1687
1709
  # end
1688
- # binding.pry
1689
1710
 
1690
1711
  render inline: { data: data }.to_json, content_type: ['*/*', 'text/html'].include?(request.format) ? 'application/json' : request.format
1691
1712
  return
@@ -1984,7 +2005,6 @@ end.class_exec do
1984
2005
 
1985
2006
  # return if ActiveRecord::Base.connection.current_database == 'postgres'
1986
2007
 
1987
- initializer_loaded = false
1988
2008
  orig_schema = nil
1989
2009
  if (relations = ::Brick.relations).empty?
1990
2010
  # Very first thing, load inflections since we'll be using .pluralize and .singularize on table and model names
@@ -1993,7 +2013,7 @@ end.class_exec do
1993
2013
  end
1994
2014
  # Now the Brick initializer since there may be important schema things configured
1995
2015
  if File.exist?(brick_initializer = ::Rails.root.join('config/initializers/brick.rb'))
1996
- initializer_loaded = load brick_initializer
2016
+ ::Brick.initializer_loaded = load brick_initializer
1997
2017
  end
1998
2018
  # Load the initializer for the Apartment gem a little early so that if .excluded_models and
1999
2019
  # .default_schema are specified then we can work with non-tenanted models more appropriately
@@ -2260,13 +2280,22 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
2260
2280
  schema_names.shift if ::Brick.apartment_multitenant && schema_names.first == ::Brick.apartment_default_tenant
2261
2281
  v[:schema] = schema_names.join('.') unless schema_names.empty?
2262
2282
  # %%% If more than one schema has the same table name, will need to add a schema name prefix to have uniqueness
2263
- v[:resource] = rel_name.last
2264
2283
  if (singular = rel_name.last.singularize).blank?
2265
2284
  singular = rel_name.last
2266
2285
  end
2267
- v[:class_name] = (schema_names + [singular]).map(&:camelize).join('::')
2286
+ name_parts = if (tnp = ::Brick.config.table_name_prefixes
2287
+ .find { |k1, _v1| singular.start_with?(k1) && singular.length > k1.length }
2288
+ ).present?
2289
+ v[:auto_prefixed_schema] = tnp.first
2290
+ v[:resource] = rel_name.last[(tnp_length = tnp.first.length)..-1]
2291
+ [tnp.last, singular[tnp_length..-1]]
2292
+ else
2293
+ v[:resource] = rel_name.last
2294
+ [singular]
2295
+ end
2296
+ v[:class_name] = (schema_names + name_parts).map(&:camelize).join('::')
2268
2297
  end
2269
- ::Brick.load_additional_references if initializer_loaded
2298
+ ::Brick.load_additional_references if ::Brick.initializer_loaded
2270
2299
 
2271
2300
  if orig_schema && (orig_schema = (orig_schema - ['pg_catalog', 'pg_toast', 'heroku_ext']).first)
2272
2301
  puts "Now switching back to \"#{orig_schema}\" schema."
@@ -2648,8 +2677,10 @@ module Brick
2648
2677
  end
2649
2678
 
2650
2679
  def find_col_renaming(api_ver_path, relation_name)
2651
- (column_renames = ::Brick.config.api_column_renaming&.fetch(api_ver_path, nil) ||
2652
- ::Brick.config.api_column_renaming)&.fetch(relation_name, nil)
2680
+ ::Brick.config.api_column_renaming&.fetch(
2681
+ api_ver_path,
2682
+ ::Brick.config.api_column_renaming&.fetch(relation_name, nil)
2683
+ )
2653
2684
  end
2654
2685
 
2655
2686
  def _class_pk(dotted_name, multitenant)
@@ -10,10 +10,13 @@ module Brick
10
10
  if (param === undefined || param === null || param === -1) {
11
11
  hrefParts = hrefParts[0].split(\"://\");
12
12
  var pathParts = hrefParts[hrefParts.length - 1].split(\"/\").filter(function (pp) {return pp !== \"\";});
13
- if (value === undefined)
13
+ if (value === undefined) {
14
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 {
15
+ if (pathParts.length > 3)
16
+ return [pathParts.slice(1, 4).join('/'), pathParts.slice(1, 3).join('/')];
17
+ else
18
+ return [pathParts.slice(1, 3).join('/'), pathParts[1]];
19
+ } else {
17
20
  var queryString = param ? \"?\" + params.join(\"&\") : \"\";
18
21
  return hrefParts[0] + \"://\" + pathParts[0] + \"/\" + value + queryString;
19
22
  }
@@ -187,7 +190,8 @@ function linkSchemas() {
187
190
  s[r.name[0..-9]] = nil if r.name.end_with?('Resource')
188
191
  end
189
192
  ::Brick.relations.each do |k, v|
190
- unless existing.key?(class_name = v[:class_name]) || Brick.config.exclude_tables.include?(k) || class_name.blank?
193
+ unless existing.key?(class_name = v[:class_name]) || Brick.config.exclude_tables.include?(k) ||
194
+ class_name.blank? || class_name.include?('::')
191
195
  Object.const_get("#{class_name}Resource")
192
196
  end
193
197
  end
@@ -452,11 +456,22 @@ window.addEventListener(\"popstate\", linkSchemas);
452
456
  # %%% If we are not auto-creating controllers (or routes) then omit by default, and if enabled anyway, such as in a development
453
457
  # environment or whatever, then get either the controllers or routes list instead
454
458
  prefix = "#{::Brick.config.path_prefix}/" if ::Brick.config.path_prefix
455
- table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables).each_with_object({}) do |tbl, s|
456
- if (tbl_parts = tbl.split('.')).first == apartment_default_schema
457
- tbl = tbl_parts.last
459
+ table_options = ::Brick.relations.each_with_object({}) do |rel, s|
460
+ next if ::Brick.config.exclude_tables.include?(rel.first)
461
+
462
+ tbl_parts = rel.first.split('.')
463
+ if (aps = rel.last.fetch(:auto_prefixed_schema, nil))
464
+ tbl_parts << tbl_parts.last[aps.length..-1]
465
+ aps = aps[0..-2] if aps[-1] == '_'
466
+ tbl_parts[-2] = aps
467
+ end
468
+ if tbl_parts.first == apartment_default_schema
469
+ tbl_parts.shift
458
470
  end
459
- s[tbl] = nil
471
+ # %%% When table_name_prefixes are use then during rendering empty non-TNP
472
+ # entries get added at some point when an attempt is made to find the table.
473
+ # Will have to hunt that down at some point.
474
+ s[tbl_parts.join('.')] = nil unless rel.last[:cols].empty?
460
475
  end.keys.sort.each_with_object(+'') do |v, s|
461
476
  s << "<option value=\"#{prefix}#{v.underscore.gsub('.', '/')}\">#{v}</option>"
462
477
  end.html_safe
@@ -881,7 +896,8 @@ if (grid) {
881
896
  // });
882
897
  }
883
898
  function setHeaderSizes() {
884
- document.getElementById(\"titleBox\").style.width = grid.clientWidth;
899
+ if (grid.clientWidth > window.outerWidth)
900
+ document.getElementById(\"titleBox\").style.width = grid.clientWidth;
885
901
  // console.log(\"start\");
886
902
  // See if the headerTop is already populated
887
903
  // %%% Grab the TRs from headerTop, clear it out, do this stuff, add them back
@@ -1136,12 +1152,13 @@ erDiagram
1136
1152
  <td><h1><%= td_count = 2
1137
1153
  model.name %></h1></td>
1138
1154
  <td id=\"imgErd\" title=\"Show ERD\"></td>
1139
- <% if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace)
1155
+ <% if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) && model.name.exclude?('::')
1140
1156
  td_count += 1 %>
1141
1157
  <td><%= link_to_brick(
1142
1158
  avo_svg,
1143
- { index_proc: Proc.new do |avo_model|
1144
- ::Avo.railtie_routes_url_helpers.send(\"resources_#\{model.model_name.route_key}_path\".to_sym)
1159
+ { index_proc: Proc.new do |avo_model, relation|
1160
+ path_helper = \"resources_#\{relation.fetch(:auto_prefixed_schema, nil)}#\{model.model_name.route_key}_path\".to_sym
1161
+ ::Avo.railtie_routes_url_helpers.send(path_helper) if ::Avo.railtie_routes_url_helpers.respond_to?(path_helper)
1145
1162
  end,
1146
1163
  title: \"#\{model.name} in Avo\" }
1147
1164
  ) %></td>
@@ -1328,8 +1345,9 @@ erDiagram
1328
1345
  <% if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) %>
1329
1346
  <td><%= link_to_brick(
1330
1347
  avo_svg,
1331
- { show_proc: Proc.new do |obj|
1332
- ::Avo.railtie_routes_url_helpers.send(\"resources_#\{obj.class.base_class.model_name.singular_route_key}_path\".to_sym, obj)
1348
+ { show_proc: Proc.new do |obj, relation|
1349
+ path_helper = \"resources_#\{relation.fetch(:auto_prefixed_schema, nil)}#\{obj.class.base_class.model_name.singular_route_key}_path\".to_sym
1350
+ ::Avo.railtie_routes_url_helpers.send(path_helper, obj) if ::Avo.railtie_routes_url_helpers.respond_to?(path_helper)
1333
1351
  end,
1334
1352
  title: \"#\{page_title} in Avo\" }
1335
1353
  ) %></td>
@@ -1408,8 +1426,8 @@ end
1408
1426
  \"<span class=\\\"orphan\\\">Orphaned ID: #\{val}</span>\".html_safe
1409
1427
  end %>
1410
1428
  <% else
1411
- col_type = col.sql_type == 'geography' ? col.sql_type : col.type
1412
- case (col_type ||= col.sql_type)
1429
+ col_type = col&.sql_type == 'geography' ? col.sql_type : col&.type
1430
+ case (col_type ||= col&.sql_type)
1413
1431
  when :string, :text %>
1414
1432
  <% if is_bcrypt?(val) # || .readonly?
1415
1433
  is_revert = false %>
@@ -1489,9 +1507,9 @@ end
1489
1507
  # association that points to an STI model then filtering for the __able_type column is done
1490
1508
  # with a .where(). And the polymorphic class name it points to is the base class name of
1491
1509
  # the STI model instead of its subclass.
1492
- if (poly_type = #{poly_type.inspect}) &&
1493
- @#{obj_name}.respond_to?(:#{@_brick_model.inheritance_column}) &&
1494
- (base_type = collection.where_values_hash[poly_type])
1510
+ poly_type = #{poly_type.inspect}
1511
+ if poly_type && @#{obj_name}.respond_to?(:#{@_brick_model.inheritance_column}) &&
1512
+ (base_type = collection.where_values_hash[poly_type])
1495
1513
  collection = collection.rewhere(poly_type => [base_type, @#{obj_name}.#{@_brick_model.inheritance_column}])
1496
1514
  end"
1497
1515
  end
@@ -162,8 +162,9 @@ module Brick::Rails::FormTags
162
162
  @_brick_model
163
163
  # If not provided, do a best-effort to automatically determine the resource class or object
164
164
  filter_parts = []
165
+ rel_name = nil
165
166
  klass_or_obj ||= begin
166
- klass, sti_type = ::Brick.ctrl_to_klass(controller_path)
167
+ klass, sti_type, rel_name = ::Brick.ctrl_to_klass(controller_path)
167
168
  if klass
168
169
  type_col = klass.inheritance_column # Usually 'type'
169
170
  filter_parts << "#{type_col}=#{sti_type}" if sti_type && klass.column_names.include?(type_col)
@@ -202,11 +203,11 @@ module Brick::Rails::FormTags
202
203
  app_routes = Rails.application.routes # In case we're operating in another engine, reference the application since Brick routes are placed there.
203
204
  if (klass_or_obj&.is_a?(Class) && klass_or_obj < ActiveRecord::Base) ||
204
205
  (klass_or_obj&.is_a?(ActiveRecord::Base) && klass_or_obj.new_record? && (klass_or_obj = klass_or_obj.class))
205
- path = (proc = kwargs[:index_proc]) ? proc.call(klass_or_obj) : "#{app_routes.path_for(controller: klass_or_obj.base_class._brick_index(nil, '/'), action: :index)}#{filter}"
206
+ path = (proc = kwargs[:index_proc]) ? proc.call(klass_or_obj, relation) : "#{app_routes.path_for(controller: klass_or_obj.base_class._brick_index(nil, '/', relation), action: :index)}#{filter}"
206
207
  lt_args = [text || "Index for #{klass_or_obj.name.pluralize}", path]
207
208
  else
208
209
  # 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
209
- path = (proc = kwargs[:show_proc]) ? proc.call(klass_or_obj) : "#{app_routes.path_for(controller: klass_or_obj.class.base_class._brick_index(nil, '/'), action: :show, id: klass_or_obj)}#{filter}"
210
+ path = (proc = kwargs[:show_proc]) ? proc.call(klass_or_obj, relation) : "#{app_routes.path_for(controller: klass_or_obj.class.base_class._brick_index(nil, '/', relation), action: :show, id: klass_or_obj)}#{filter}"
210
211
  lt_args = [text || "Show this #{klass_or_obj.class.name}", path]
211
212
  end
212
213
  kwargs.delete(:visited)
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 108
8
+ TINY = 110
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
data/lib/brick.rb CHANGED
@@ -135,7 +135,7 @@ module Brick
135
135
  @existing_stis ||= Brick.config.sti_namespace_prefixes.each_with_object({}) { |snp, s| s[snp.first[2..-1]] = snp.last unless snp.first.end_with?('::') }
136
136
  end
137
137
 
138
- attr_accessor :default_schema, :db_schemas, :routes_done, :is_oracle, :is_eager_loading, :auto_models
138
+ attr_accessor :default_schema, :db_schemas, :routes_done, :is_oracle, :is_eager_loading, :auto_models, :initializer_loaded
139
139
 
140
140
  def set_db_schema(params = nil)
141
141
  # If Apartment::Tenant.current is not still the default (usually 'public') then an elevator has brought us into
@@ -601,7 +601,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
601
601
  def display_classes(prefix, rels, max_length)
602
602
  rels.sort.each do |rel|
603
603
  (::Brick.auto_models ||= []) << rel.first
604
- puts "#{rel.first}#{' ' * (max_length - rel.first.length)} /#{prefix}#{rel.last}"
604
+ puts "#{rel.first}#{' ' * (max_length - rel.first.length)} /#{prefix}#{"#{rel[1]}/" if rel[1]}#{rel.last}"
605
605
  end
606
606
  puts "\n"
607
607
  end
@@ -620,16 +620,21 @@ In config/initializers/brick.rb appropriate entries would look something like:
620
620
  end
621
621
 
622
622
  c_path_parts = ctrl_path.split('/')
623
+ found = nil
623
624
  while c_path_parts.present?
624
625
  possible_c_path = c_path_parts.join('.')
625
626
  possible_c_path_singular = c_path_parts[0..-2] + [c_path_parts.last.singularize]
626
627
  possible_sti = possible_c_path_singular.join('/').camelize
627
628
  break if (
628
- res_name = res_names[possible_c_path] ||
629
+ res_name = res_names[found = possible_c_path] ||
629
630
  ((klass = Brick.config.sti_namespace_prefixes.key?("::#{possible_sti}") && possible_sti.constantize) &&
630
631
  (sti_type = possible_sti)) ||
631
632
  # %%% Used to have the more flexible: (DidYouMean::SpellChecker.new(dictionary: res_names.keys).correct(possible_c_path)).first
632
- res_names[possible_c_path] || res_names[possible_c_path_singular.join('.')]
633
+ res_names[found = possible_c_path] || res_names[found = possible_c_path_singular.join('.')] ||
634
+ ((::Brick.config.table_name_prefixes.key?(tn_prefix = c_path_parts.first) ||
635
+ ::Brick.config.table_name_prefixes.key?(tn_prefix = "#{c_path_parts.first}_")) &&
636
+ res_names[found = tn_prefix + c_path_parts.last]
637
+ )
633
638
  ) &&
634
639
  (
635
640
  klass ||
@@ -638,7 +643,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
638
643
  )
639
644
  c_path_parts.shift
640
645
  end
641
- [klass, sti_type]
646
+ [klass, sti_type, found]
642
647
  end
643
648
  end
644
649
 
@@ -657,10 +662,14 @@ In config/initializers/brick.rb appropriate entries would look something like:
657
662
  table_class_length = 38 # Length of "Classes that can be built from tables:"
658
663
  view_class_length = 37 # Length of "Classes that can be built from views:"
659
664
 
660
- brick_routes_create = lambda do |schema_name, res_name, options|
661
- if schema_name # && !Object.const_defined('Apartment')
662
- send(:namespace, schema_name) do
663
- send(:resources, res_name.to_sym, **options)
665
+ brick_namespace_create = lambda do |path_names, res_name, options|
666
+ if path_names&.present?
667
+ if (path_name = path_names.pop).is_a?(Array)
668
+ module_name = path_name[1]
669
+ path_name = path_name.first
670
+ end
671
+ send(:scope, { module: module_name || path_name, path: path_name, as: path_name }) do
672
+ brick_namespace_create.call(path_names, res_name, options)
664
673
  end
665
674
  else
666
675
  send(:resources, res_name.to_sym, **options)
@@ -676,15 +685,49 @@ In config/initializers/brick.rb appropriate entries would look something like:
676
685
  end
677
686
  versioned_views = {} # Track which views have already been done for each api_root
678
687
  ::Brick.relations.each do |k, v|
679
- next if !(controller_name = v.fetch(:resource, nil)&.pluralize) || existing_controllers.key?(controller_name)
680
-
681
- object_name = k.split('.').last # Take off any first schema part
682
688
  if (schema_name = v.fetch(:schema, nil))
683
689
  schema_prefix = "#{schema_name}."
684
690
  end
691
+
692
+ next if !(resource_name = v.fetch(:resource, nil)) ||
693
+ existing_controllers.key?(controller_name = (
694
+ resource_name = "#{schema_prefix&.tr('.', '/')}#{resource_name}"
695
+ ).pluralize)
696
+
697
+ object_name = k.split('.').last # Take off any first schema part
698
+
699
+ full_schema_prefix = if (aps = v.fetch(:auto_prefixed_schema, nil))
700
+ aps = aps[0..-2] if aps[-1] == '_'
701
+ (schema_prefix&.dup || +'') << "#{aps}."
702
+ else
703
+ schema_prefix
704
+ end
705
+
706
+ # Track routes being built
707
+ if (class_name = v.fetch(:class_name, nil))
708
+ if v.key?(:isView)
709
+ view_class_length = class_name.length if class_name.length > view_class_length
710
+ views
711
+ else
712
+ table_class_length = class_name.length if class_name.length > table_class_length
713
+ tables
714
+ end << [class_name, aps, resource_name]
715
+ end
716
+
685
717
  options = {}
686
718
  options[:only] = [:index, :show] if v.key?(:isView)
687
- # First do the API routes if necessary
719
+
720
+ # First do the normal routes
721
+ prefixes = []
722
+ prefixes << [aps, v[:class_name]&.split('::')[-2]&.underscore] if aps
723
+ prefixes << schema_name if schema_name
724
+ prefixes << path_prefix if path_prefix
725
+ brick_namespace_create.call(prefixes, v[:resource], options)
726
+ sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
727
+ brick_namespace_create.call(prefixes, sc.underscore.tr('/', '_').pluralize, options)
728
+ end
729
+
730
+ # Now the API routes if necessary
688
731
  full_resource = nil
689
732
  ::Brick.api_roots&.each do |api_root|
690
733
  api_done_views = (versioned_views[api_root] ||= {})
@@ -742,7 +785,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
742
785
  proc_result = if (filter = ::Brick.config.api_filter).is_a?(Proc)
743
786
  begin
744
787
  num_args = filter.arity.negative? ? 6 : filter.arity
745
- filter.call(*[unversioned, k, actions, api_ver_num, found, test_ver_num][0...num_args])
788
+ filter.call(*[unversioned, k, view_relation, actions, api_ver_num, found, test_ver_num][0...num_args])
746
789
  rescue StandardError => e
747
790
  puts "::Brick.api_filter Proc error: #{e.message}"
748
791
  end
@@ -786,9 +829,8 @@ In config/initializers/brick.rb appropriate entries would look something like:
786
829
  # view_ver_num = if (first_part = k.split('_').first) =~ /^v[\d_]+/
787
830
  # first_part[1..-1].gsub('_', '.').to_i
788
831
  # end
789
-
790
832
  controller_name = if (last = view_relation.fetch(:resource, nil)&.pluralize)
791
- "#{schema_prefix}#{last}"
833
+ "#{full_schema_prefix}#{last}"
792
834
  else
793
835
  found
794
836
  end.tr('.', '/')
@@ -811,33 +853,6 @@ In config/initializers/brick.rb appropriate entries would look something like:
811
853
  end
812
854
  end
813
855
  end
814
-
815
- # Track routes being built
816
- if (class_name = v.fetch(:class_name, nil))
817
- if v.key?(:isView)
818
- view_class_length = class_name.length if class_name.length > view_class_length
819
- views
820
- else
821
- table_class_length = class_name.length if class_name.length > table_class_length
822
- tables
823
- end << [class_name, "#{schema_prefix&.tr('.', '/')}#{v[:resource]}"]
824
- end
825
-
826
- # Now the normal routes
827
- if path_prefix
828
- # Was: send(:scope, path: path_prefix) do
829
- send(:namespace, path_prefix) do
830
- brick_routes_create.call(schema_name, v[:resource], options)
831
- sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
832
- brick_routes_create.call(schema_name, sc.underscore.tr('/', '_').pluralize, options)
833
- end
834
- end
835
- else
836
- brick_routes_create.call(schema_name, v[:resource], options)
837
- sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
838
- brick_routes_create.call(schema_name, sc.underscore.tr('/', '_').pluralize, options)
839
- end
840
- end
841
856
  end
842
857
 
843
858
  if ::Brick.config.add_status && instance_variable_get(:@set).named_routes.names.exclude?(:brick_status)
@@ -857,7 +872,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
857
872
  if Object.const_defined?('Rswag::Ui')
858
873
  rswag_path = ::Rails.application.routes.routes.find { |r| r.app.app == Rswag::Ui::Engine }&.instance_variable_get(:@path_formatter)&.instance_variable_get(:@parts)&.join
859
874
  first_endpoint_parts = nil
860
- (doc_endpoints = Rswag::Ui.config.config_object[:urls]&.uniq!)&.each do |doc_endpoint|
875
+ (doc_endpoints = Rswag::Ui.config.config_object[:urls])&.each do |doc_endpoint|
861
876
  puts "Mounting OpenApi 3.0 documentation endpoint for \"#{doc_endpoint[:name]}\" on #{doc_endpoint[:url]}"
862
877
  send(:get, doc_endpoint[:url], { to: 'brick_openapi#index' })
863
878
  endpoint_parts = doc_endpoint[:url]&.split('/')
@@ -1192,8 +1207,13 @@ ActiveSupport.on_load(:active_record) do
1192
1207
  arsc.class_exec do
1193
1208
  def self.create(connection, callable = nil, &block)
1194
1209
  relation = (callable || block).call ::ActiveRecord::StatementCache::Params.new
1195
- bind_map = ::ActiveRecord::StatementCache::BindMap.new relation.bound_attributes
1196
- query_builder = connection.cacheable_query(self, relation.arel)
1210
+ bind_map = ::ActiveRecord::StatementCache::BindMap.new(
1211
+ # AR <= 4.2 uses relation.bind_values
1212
+ relation.respond_to?(:bound_attributes) ? relation.bound_attributes : relation.bind_values
1213
+ )
1214
+ options = [self, relation.arel]
1215
+ options.shift if connection.method(:cacheable_query).arity == 1 # Rails <= 5.0
1216
+ query_builder = connection.cacheable_query(*options)
1197
1217
  new query_builder, bind_map
1198
1218
  end
1199
1219
  end
@@ -1237,7 +1257,8 @@ ActiveSupport.on_load(:active_record) do
1237
1257
  end
1238
1258
  end
1239
1259
 
1240
- if Psych.respond_to?(:unsafe_load) && ActiveRecord.version < ::Gem::Version.new('6.1')
1260
+ if ActiveRecord.version < ::Gem::Version.new('6.1') &&
1261
+ Psych.method(:load).parameters.any? { |param| param.first == :key && param.last == :aliases }
1241
1262
  Psych.class_exec do
1242
1263
  class << self
1243
1264
  alias _original_load load
@@ -1279,7 +1300,9 @@ ActiveSupport.on_load(:active_record) do
1279
1300
  def initialize(base, associations, joins, eager_loading: true)
1280
1301
  araat = ::ActiveRecord::Associations::AliasTracker
1281
1302
  if araat.respond_to?(:create_with_joins) # Rails 5.0 and 5.1
1282
- @alias_tracker = araat.create_with_joins(base.connection, base.table_name, joins)
1303
+ cwj_options = [base.connection, base.table_name, joins]
1304
+ cwj_options << base.type_caster if araat.method(:create_with_joins).arity > 3 # Rails <= 5.1
1305
+ @alias_tracker = araat.create_with_joins(*cwj_options)
1283
1306
  @eager_loading = eager_loading # (Unused in Rails 5.0)
1284
1307
  else # Rails 4.2
1285
1308
  @alias_tracker = araat.create(base.connection, joins)
@@ -1648,21 +1671,24 @@ if ActiveRecord.version < ::Gem::Version.new('6.0') && ruby_version >= ::Gem::Ve
1648
1671
  end
1649
1672
 
1650
1673
  require 'active_model'
1651
- require 'active_model/type'
1652
- require 'active_model/type/value'
1653
- class ActiveModel::Type::Value
1654
- def initialize(*args, precision: nil, limit: nil, scale: nil)
1655
- @precision = precision
1656
- @scale = scale
1657
- @limit = limit
1674
+ begin
1675
+ require 'active_model/type'
1676
+ require 'active_model/type/value'
1677
+ class ActiveModel::Type::Value
1678
+ def initialize(*args, precision: nil, limit: nil, scale: nil)
1679
+ @precision = precision
1680
+ @scale = scale
1681
+ @limit = limit
1682
+ end
1658
1683
  end
1684
+ rescue LoadError => e # AR <= 4.2 doesn't have ActiveModel::Type
1659
1685
  end
1660
1686
 
1661
1687
  if Object.const_defined?('I18n')
1662
1688
  module I18n::Base
1663
1689
  alias _brick_translate translate
1664
1690
  def translate(key = nil, *args, throw: false, raise: false, locale: nil, **options)
1665
- options.merge!(args.pop) if args.length > 0 && args.last.is_a?(Hash)
1691
+ options.merge!(args.pop) if args.last.is_a?(Hash)
1666
1692
  _brick_translate(key = nil, throw: false, raise: false, locale: nil, **options)
1667
1693
  end
1668
1694
  end
@@ -1673,8 +1699,12 @@ if ActiveRecord.version < ::Gem::Version.new('6.0') && ruby_version >= ::Gem::Ve
1673
1699
 
1674
1700
  # Creates the authenticity token for the current request.
1675
1701
  def form_authenticity_token(*args, form_options: {}) # :doc:
1676
- form_options.merge!(args.pop) if args.length > 0 && args.last.is_a?(Hash)
1677
- masked_authenticity_token(session, form_options: form_options)
1702
+ if method(:masked_authenticity_token).arity == 1
1703
+ masked_authenticity_token(session) # AR <= 4.2 doesn't use form_options
1704
+ else
1705
+ form_options.merge!(args.pop) if args.last.is_a?(Hash)
1706
+ masked_authenticity_token(session, form_options: form_options)
1707
+ end
1678
1708
  end
1679
1709
  end
1680
1710
 
@@ -1684,7 +1714,7 @@ if ActiveRecord.version < ::Gem::Version.new('6.0') && ruby_version >= ::Gem::Ve
1684
1714
  encrypted = if method(:_encrypt).arity == 1
1685
1715
  _encrypt(value) # Rails <= 5.1
1686
1716
  else
1687
- if args.length > 0 && args.last.is_a?(Hash)
1717
+ if args.last.is_a?(Hash)
1688
1718
  expires_at ||= args.last[:expires_at]
1689
1719
  expires_in ||= args.last[:expires_in]
1690
1720
  purpose ||= args.last[:purpose]
@@ -1697,7 +1727,7 @@ if ActiveRecord.version < ::Gem::Version.new('6.0') && ruby_version >= ::Gem::Ve
1697
1727
  if const_defined?('Messages')
1698
1728
  class Messages::Metadata
1699
1729
  def self.wrap(message, *args, expires_at: nil, expires_in: nil, purpose: nil)
1700
- if args.length > 0 && args.last.is_a?(Hash)
1730
+ if args.last.is_a?(Hash)
1701
1731
  expires_at ||= args.last[:expires_at]
1702
1732
  expires_in ||= args.last[:expires_in]
1703
1733
  purpose ||= args.last[:purpose]
@@ -139,7 +139,7 @@ module Brick
139
139
  # Settings for the Brick gem
140
140
  # (By default this auto-creates models, controllers, views, and routes on-the-fly.)
141
141
 
142
- if ActiveRecord::Base.respond_to?(:brick_select)
142
+ if ActiveRecord::Base.respond_to?(:brick_select) && !::Brick.initializer_loaded
143
143
  # Mode -- generally :on or :off, or only in :development. Also available is :diag_env which enables only
144
144
  # when the environment variable BRICK is set.
145
145
  Brick.mode = :development
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.108
4
+ version: 1.0.110
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-21 00:00:00.000000000 Z
11
+ date: 2023-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord