brick 1.0.108 → 1.0.110

Sign up to get free protection for your applications and to get access to all the features.
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