brick 1.0.107 → 1.0.109

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: 74bd7cd6539a5f048aecc3e02be8912e1bc77851ac8b9f65402892e964a024de
4
- data.tar.gz: c32c53d0012f2a9292463d803d0c679d4f32800c8931c3361469aa22c09a3fc5
3
+ metadata.gz: 1621b6af75759b90c788ce080db85608731c2bfc026c82d7bcea011a89c2162e
4
+ data.tar.gz: a4242388d2fa29a12229c620d91b5d572e9e2f7688783beb6963817cf3fac340
5
5
  SHA512:
6
- metadata.gz: f84ee2aae03622a4ca67a6921daf7e975f85601e66fc608c2357613da976414d2ceeec87276887232a832413116ec1495638c883ecc712db3706f51c34efbd44
7
- data.tar.gz: e97fbe41eca4d839795f7567bfd3e8a3d6494d4887032fa5f0f87d88ba3c9fdc810bde8a259c3cbf0b8f944c8b345c33290aa5d1e8f7aa67737e7fe7be248a6d
6
+ metadata.gz: fa089c1bc9663e764b783c639ca9ca24c18a5cbbc788281e11620b33277e87282b6e1a0195d12686b72f53a5ae4a5bcb9e8343e9ef9bb4d97f53fb69ad4619fe
7
+ data.tar.gz: 784eed99a879191dff95b5dfd061f8025d52d3e30eb16b7a754b425c2f4ad58e03e8631949d807f130dc86e55cb3a5e08d215a3f1729b4465c58dc3cecffb96d
@@ -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
data/lib/brick/config.rb CHANGED
@@ -110,6 +110,50 @@ module Brick
110
110
  @mutex.synchronize { @api_filter = proc }
111
111
  end
112
112
 
113
+ # # Proc gets called with up to 4 arguments: object_name, api_version, columns, data
114
+ # # Expected to return an array, either just of symbols defining column names, or an array with two sub-arrays, first of column detail and second of data
115
+ # def api_column_filter
116
+ # @mutex.synchronize { @api_column_filter }
117
+ # end
118
+
119
+ # def api_column_filter=(proc)
120
+ # @mutex.synchronize { @api_column_filter = proc }
121
+ # end
122
+
123
+ # Allows you to rename and exclude columns either specific to a given API version, or generally for a database object name
124
+ def api_column_renaming
125
+ @mutex.synchronize { @api_column_renaming }
126
+ end
127
+
128
+ def api_column_renaming=(renames)
129
+ @mutex.synchronize { @api_column_renaming = renames }
130
+ end
131
+
132
+ # All the view prefix things
133
+ def api_view_prefix
134
+ @mutex.synchronize { @api_view_prefix }
135
+ end
136
+
137
+ def api_view_prefix=(view_prefix)
138
+ @mutex.synchronize { @api_view_prefix = view_prefix }
139
+ end
140
+
141
+ def api_remove_view_prefix
142
+ @mutex.synchronize { @api_remove_view_prefix || @api_view_prefix }
143
+ end
144
+
145
+ def api_remove_view_prefix=(view_prefix)
146
+ @mutex.synchronize { @api_remove_view_prefix = view_prefix }
147
+ end
148
+
149
+ def api_add_view_prefix
150
+ @mutex.synchronize { @api_add_view_prefix || @api_view_prefix }
151
+ end
152
+
153
+ def api_add_view_prefix=(view_prefix)
154
+ @mutex.synchronize { @api_add_view_prefix = view_prefix }
155
+ end
156
+
113
157
  # Additional table associations to use (Think of these as virtual foreign keys perhaps)
114
158
  def additional_references
115
159
  @mutex.synchronize { @additional_references }
@@ -44,16 +44,20 @@
44
44
 
45
45
  module ActiveRecord
46
46
  class Base
47
- def self.is_brick?
48
- instance_variables.include?(:@_brick_built) && instance_variable_get(:@_brick_built)
49
- end
47
+ class << self
48
+ attr_reader :_brick_relation
50
49
 
51
- def self._assoc_names
52
- @_assoc_names ||= {}
53
- end
50
+ def is_brick?
51
+ instance_variables.include?(:@_brick_built) && instance_variable_get(:@_brick_built)
52
+ end
53
+
54
+ def _assoc_names
55
+ @_assoc_names ||= {}
56
+ end
54
57
 
55
- def self.is_view?
56
- false
58
+ def is_view?
59
+ false
60
+ end
57
61
  end
58
62
 
59
63
  def self._brick_primary_key(relation = nil)
@@ -417,7 +421,10 @@ module ActiveRecord
417
421
  is_distinct = nil
418
422
  wheres = {}
419
423
  params.each do |k, v|
420
- next if ['_brick_schema', '_brick_order', 'controller', 'action'].include?(k)
424
+ next if ['_brick_schema', '_brick_order',
425
+ '_brick_erd', '_brick_exclude', '_brick_unexclude',
426
+ '_brick_page', '_brick_page_size', '_brick_offset', '_brick_limit',
427
+ '_brick_is_api', 'controller', 'action'].include?(k)
421
428
 
422
429
  if (where_col = (ks = k.split('.')).last)[-1] == '!'
423
430
  where_col = where_col[0..-2]
@@ -444,7 +451,10 @@ module ActiveRecord
444
451
  # %%% Have once gotten this error with MSSQL referring to http://localhost:3000/warehouse/cold_room_temperatures__archive
445
452
  # ActiveRecord::StatementInvalid (TinyTds::Error: DBPROCESS is dead or not enabled)
446
453
  # Relevant info here: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/402
454
+ is_api = params['_brick_is_api']
447
455
  columns.each do |col|
456
+ next if (col.type.nil? || col.type == :binary) && is_api
457
+
448
458
  col_alias = " AS #{col.name}_" if (col_name = col.name) == 'class'
449
459
  selects << if is_mysql
450
460
  "`#{tbl_no_schema}`.`#{col_name}`#{col_alias}"
@@ -787,21 +797,21 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
787
797
  end
788
798
  self.order_values |= final_order_by # Same as: order!(*final_order_by)
789
799
  end
790
- if (page = params['_brick_page']&.to_i)
791
- page = 1 if page < 1
792
- limit = params['_brick_page_size'] || 1000
793
- offset = (page - 1) * limit.to_i
794
- else
795
- offset = params['_brick_offset']
796
- limit = params['_brick_limit']
797
- end
800
+ # By default just 1000 rows
801
+ row_limit = params['_brick_limit'] || params['_brick_page_size'] || 1000
802
+ offset = if (page = params['_brick_page']&.to_i)
803
+ page = 1 if page < 1
804
+ (page - 1) * row_limit.to_i
805
+ else
806
+ params['_brick_offset']
807
+ end
798
808
  if offset.is_a?(Numeric) || offset&.present?
799
809
  offset = offset.to_i
800
810
  self.offset_value = offset unless offset == 0
801
- @_brick_page_num = (offset / limit.to_i) + 1 if limit&.!= 0 && (offset % limit.to_i) == 0
811
+ @_brick_page_num = (offset / row_limit.to_i) + 1 if row_limit&.!= 0 && (offset % row_limit.to_i) == 0
802
812
  end
803
- # By default just 1000 rows (Like doing: limit!(1000) but this way is compatible with AR <= 4.2)
804
- self.limit_value = limit&.to_i || 1000 unless limit.is_a?(String) && limit.empty?
813
+ # Setting limit_value= is the same as doing: limit!(1000) but this way is compatible with AR <= 4.2
814
+ self.limit_value = row_limit.to_i unless row_limit.is_a?(String) && row_limit.empty?
805
815
  wheres unless wheres.empty? # Return the specific parameters that we did use
806
816
  end
807
817
 
@@ -1261,6 +1271,7 @@ class Object
1261
1271
  # Having this separate -- will this now work out better?
1262
1272
  built_model.class_exec do
1263
1273
  @_brick_built = true
1274
+ @_brick_relation = relation
1264
1275
  hmts&.each do |hmt_fk, hms|
1265
1276
  hmt_fk = hmt_fk.tr('.', '_')
1266
1277
  hms.each do |hm|
@@ -1533,9 +1544,13 @@ class Object
1533
1544
  end
1534
1545
  if (current_api_root || is_openapi) &&
1535
1546
  !params&.key?('_brick_schema') &&
1536
- (referrer_params = request.env['HTTP_REFERER']&.split('?')&.last&.split('&')&.map { |x| x.split('=') }).present?
1547
+ (referrer_params = request.env['HTTP_REFERER']&.split('?')&.last&.split('&')&.each_with_object({}) do |x, s|
1548
+ if (kv = x.split('=')).length > 1
1549
+ s[kv.first] = kv[1..-1].join('=')
1550
+ end
1551
+ end).present?
1537
1552
  if params
1538
- referrer_params.each { |k, v| params.send(:parameters)[k] = v }
1553
+ referrer_params.each { |k, v| (params.respond_to?(:parameters) ? send(:parameters) : params)[k] = v }
1539
1554
  else
1540
1555
  api_params = referrer_params&.to_h
1541
1556
  end
@@ -1570,13 +1585,22 @@ class Object
1570
1585
  (api_ver_paths || { relation.first => all_actions }).each do |api_ver_path, actions|
1571
1586
  relation_name = (api_ver_path || relation.first).tr('.', '/')
1572
1587
  table_description = relation.last[:description]
1588
+
1589
+ # Column renaming / exclusions
1590
+ renamed_columns = if (column_renaming = ::Brick.find_col_renaming(api_ver_path, relation.first))
1591
+ column_renaming.each_with_object({}) do |rename, s|
1592
+ s[rename.last] = relation.last[:cols][rename.first] if rename.last
1593
+ end
1594
+ else
1595
+ relation.last[:cols]
1596
+ end
1573
1597
  { :index => [:get, 'list'], :create => [:post, 'create a'] }.each do |k, v|
1574
1598
  unless actions&.exclude?(k)
1575
1599
  this_resource = (s["#{current_api_root}#{relation_name}"] ||= {})
1576
1600
  this_resource[v.first] = {
1577
1601
  'summary': "#{v[1]} #{relation.first}",
1578
1602
  'description': table_description,
1579
- 'parameters': relation.last[:cols].map do |k2, v2|
1603
+ 'parameters': renamed_columns.map do |k2, v2|
1580
1604
  param = { in: 'query', 'name': k2, 'schema': { 'type': v2.first } }
1581
1605
  if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k2, nil))
1582
1606
  param['description'] = col_descrip
@@ -1596,7 +1620,7 @@ class Object
1596
1620
  this_resource[v.first] = {
1597
1621
  'summary': "#{v[1]} a #{relation.first.singularize}",
1598
1622
  'description': table_description,
1599
- 'parameters': relation.last[:cols].reject { |k1, _v1| Brick.config.metadata_columns.include?(k1) }.map do |k2, v2|
1623
+ 'parameters': renamed_columns.reject { |k1, _v1| Brick.config.metadata_columns.include?(k1) }.map do |k2, v2|
1600
1624
  param = { 'name': k2, 'schema': { 'type': v2.first } }
1601
1625
  if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k2, nil))
1602
1626
  param['description'] = col_descrip
@@ -1637,13 +1661,46 @@ class Object
1637
1661
  order_by, _ = model._brick_calculate_ordering(ordering, true) # Don't do the txt part
1638
1662
 
1639
1663
  ar_relation = ActiveRecord.version < Gem::Version.new('4') ? model.preload : model.all
1664
+ params['_brick_is_api'] = true if (is_api = request.format == :js || current_api_root)
1640
1665
  @_brick_params = ar_relation.brick_select(params, (selects ||= []), order_by,
1641
1666
  translations = {},
1642
1667
  join_array = ::Brick::JoinArray.new)
1643
1668
 
1644
- if request.format == :js || current_api_root # Asking for JSON?
1669
+ if is_api # Asking for JSON?
1670
+ # Apply column renaming
1645
1671
  data = ar_relation.respond_to?(:_select!) ? ar_relation.dup._select!(*selects) : ar_relation.select(selects)
1646
- render inline: { data: data }.to_json, content_type: request.format == '*/*' ? 'application/json' : request.format
1672
+ if data.present? &&
1673
+ (column_renaming = ::Brick.find_col_renaming(current_api_root, model&._brick_relation)&.select { |cr| cr.last })
1674
+ data.map!({}) do |row, s|
1675
+ column_renaming.each_with_object({}) do |rename, s|
1676
+ s[rename.last] = row[rename.first] if rename.last
1677
+ end
1678
+ end
1679
+ end
1680
+
1681
+ # # %%% This currently only gives a window to check security and raise an exception if someone isn't
1682
+ # # authenticated / authorised. Still need to figure out column filtering and transformations.
1683
+ # proc_result = if (column_filter = ::Brick.config.api_column_filter).is_a?(Proc)
1684
+ # object_columns = (relation = model&._brick_relation)[:cols]
1685
+ # begin
1686
+ # num_args = column_filter.arity.negative? ? 5 : column_filter.arity
1687
+ # # object_name, api_version, columns, data
1688
+ # api_ver_path = request.path[0..-relation[:resource].length]
1689
+ # # Call the api_column_filter in the context of this auto-built controller
1690
+ # instance_exec(*[relation[:resource], relation, api_ver_path, object_columns, data][0...num_args], &column_filter)
1691
+ # rescue StandardError => e
1692
+ # puts "::Brick.api_column_filter Proc error: #{e.message}"
1693
+ # end
1694
+ # end
1695
+ # columns = if (proc_result) # Proc returns up to 2 things: columns, data
1696
+ # # If it's all valid column name strings then we're just rearranging the column sequence
1697
+ # col_names = proc_result.all? { |pr| object_columns.key?(pr) } ? proc_result : proc_result.first.keys
1698
+ # col_names.each_with_object({}) { |cn, s| s[cn] = relation.last[:cols][cn] }
1699
+ # else
1700
+ # relation.last[:cols]
1701
+ # end
1702
+
1703
+ render inline: { data: data }.to_json, content_type: ['*/*', 'text/html'].include?(request.format) ? 'application/json' : request.format
1647
1704
  return
1648
1705
  end
1649
1706
 
@@ -1940,7 +1997,6 @@ end.class_exec do
1940
1997
 
1941
1998
  # return if ActiveRecord::Base.connection.current_database == 'postgres'
1942
1999
 
1943
- initializer_loaded = false
1944
2000
  orig_schema = nil
1945
2001
  if (relations = ::Brick.relations).empty?
1946
2002
  # Very first thing, load inflections since we'll be using .pluralize and .singularize on table and model names
@@ -1949,7 +2005,7 @@ end.class_exec do
1949
2005
  end
1950
2006
  # Now the Brick initializer since there may be important schema things configured
1951
2007
  if File.exist?(brick_initializer = ::Rails.root.join('config/initializers/brick.rb'))
1952
- initializer_loaded = load brick_initializer
2008
+ ::Brick.initializer_loaded = load brick_initializer
1953
2009
  end
1954
2010
  # Load the initializer for the Apartment gem a little early so that if .excluded_models and
1955
2011
  # .default_schema are specified then we can work with non-tenanted models more appropriately
@@ -2222,7 +2278,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
2222
2278
  end
2223
2279
  v[:class_name] = (schema_names + [singular]).map(&:camelize).join('::')
2224
2280
  end
2225
- ::Brick.load_additional_references if initializer_loaded
2281
+ ::Brick.load_additional_references if ::Brick.initializer_loaded
2226
2282
 
2227
2283
  if orig_schema && (orig_schema = (orig_schema - ['pg_catalog', 'pg_toast', 'heroku_ext']).first)
2228
2284
  puts "Now switching back to \"#{orig_schema}\" schema."
@@ -2603,6 +2659,13 @@ module Brick
2603
2659
  end
2604
2660
  end
2605
2661
 
2662
+ def find_col_renaming(api_ver_path, relation_name)
2663
+ ::Brick.config.api_column_renaming&.fetch(
2664
+ api_ver_path,
2665
+ ::Brick.config.api_column_renaming&.fetch(relation_name, nil)
2666
+ )
2667
+ end
2668
+
2606
2669
  def _class_pk(dotted_name, multitenant)
2607
2670
  Object.const_get((multitenant ? [dotted_name.split('.').last] : dotted_name.split('.')).map { |nm| "::#{nm.singularize.camelize}" }.join).primary_key
2608
2671
  end
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 107
8
+ TINY = 109
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
@@ -682,45 +682,86 @@ In config/initializers/brick.rb appropriate entries would look something like:
682
682
  if (schema_name = v.fetch(:schema, nil))
683
683
  schema_prefix = "#{schema_name}."
684
684
  end
685
+
686
+ # Track routes being built
687
+ if (class_name = v.fetch(:class_name, nil))
688
+ if v.key?(:isView)
689
+ view_class_length = class_name.length if class_name.length > view_class_length
690
+ views
691
+ else
692
+ table_class_length = class_name.length if class_name.length > table_class_length
693
+ tables
694
+ end << [class_name, "#{schema_prefix&.tr('.', '/')}#{v[:resource]}"]
695
+ end
696
+
685
697
  options = {}
686
698
  options[:only] = [:index, :show] if v.key?(:isView)
687
- # First do the API routes if necessary
699
+
700
+ # First do the normal routes
701
+ if path_prefix
702
+ # Was: send(:scope, path: path_prefix) do
703
+ send(:namespace, path_prefix) do
704
+ brick_routes_create.call(schema_name, v[:resource], options)
705
+ sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
706
+ brick_routes_create.call(schema_name, sc.underscore.tr('/', '_').pluralize, options)
707
+ end
708
+ end
709
+ else
710
+ brick_routes_create.call(schema_name, v[:resource], options)
711
+ sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
712
+ brick_routes_create.call(schema_name, sc.underscore.tr('/', '_').pluralize, options)
713
+ end
714
+ end
715
+
716
+ # Now the API routes if necessary
688
717
  full_resource = nil
689
718
  ::Brick.api_roots&.each do |api_root|
690
719
  api_done_views = (versioned_views[api_root] ||= {})
691
720
  found = nil
721
+ test_ver_num = nil
692
722
  view_relation = nil
693
723
  # If it's a view then see if there's a versioned one available by searching for resource names
694
724
  # versioned with the closest number (equal to or less than) compared with our API version number.
695
- if v.key?(:isView) && (ver = object_name.match(/^v([\d_]*)/)&.captures&.first) && ver[-1] == '_'
696
- core_object_name = object_name[ver.length + 1..-1]
697
- next if api_done_views.key?(unversioned = "#{schema_prefix}v_#{core_object_name}")
698
-
699
- # Expect that the last item in the path generally holds versioning information
700
- api_ver = api_root.split('/')[-1]&.gsub('_', '.')
701
- vn_idx = api_ver.rindex(/[^\d._]/) # Position of the first numeric digit at the end of the version number
702
- # Was: .to_d
703
- test_ver_num = api_ver_num = api_ver[vn_idx + 1..-1].gsub('_', '.').to_i # Attempt to turn something like "v3" into the decimal value 3
704
- # puts [api_ver, vn_idx, api_ver_num, unversioned].inspect
705
-
706
- next if ver.to_i > api_ver_num # Don't surface any newer views in an older API
707
-
708
- test_ver_num -= 1 until test_ver_num.zero? ||
709
- (view_relation = ::Brick.relations.fetch(
710
- found = "#{schema_prefix}v#{test_ver_num}_#{core_object_name}", nil
711
- ))
712
- api_done_views[unversioned] = nil # Mark that for this API version this view is done
713
-
714
- # puts "Found #{found}" if view_relation
715
- # If we haven't found "v3_view_name" or "v2_view_name" or so forth, at the last
716
- # fall back to simply looking for "v_view_name", and then finally "view_name".
717
- no_v_prefix_name = "#{schema_prefix}#{core_object_name}"
725
+ if v.key?(:isView)
726
+ if (ver = object_name.match(/^v([\d_]*)/)&.captures&.first) && ver[-1] == '_'
727
+ core_object_name = object_name[ver.length + 1..-1]
728
+ next if api_done_views.key?(unversioned = "#{schema_prefix}v_#{core_object_name}")
729
+
730
+ # Expect that the last item in the path generally holds versioning information
731
+ api_ver = api_root.split('/')[-1]&.gsub('_', '.')
732
+ vn_idx = api_ver.rindex(/[^\d._]/) # Position of the first numeric digit at the end of the version number
733
+ # Was: .to_d
734
+ test_ver_num = api_ver_num = api_ver[vn_idx + 1..-1].gsub('_', '.').to_i # Attempt to turn something like "v3" into the decimal value 3
735
+ # puts [api_ver, vn_idx, api_ver_num, unversioned].inspect
736
+
737
+ next if ver.to_i > api_ver_num # Don't surface any newer views in an older API
738
+
739
+ test_ver_num -= 1 until test_ver_num.zero? ||
740
+ (view_relation = ::Brick.relations.fetch(
741
+ found = "#{schema_prefix}v#{test_ver_num}_#{core_object_name}", nil
742
+ ))
743
+ api_done_views[unversioned] = nil # Mark that for this API version this view is done
744
+
745
+ # puts "Found #{found}" if view_relation
746
+ # If we haven't found "v3_view_name" or "v2_view_name" or so forth, at the last
747
+ # fall back to simply looking for "v_view_name", and then finally "view_name".
748
+ no_v_prefix_name = "#{schema_prefix}#{core_object_name}"
749
+ standard_prefix = 'v_'
750
+ else
751
+ core_object_name = object_name
752
+ end
753
+ if (rvp = ::Brick.config.api_remove_view_prefix) && core_object_name.start_with?(rvp)
754
+ core_object_name.slice!(0, rvp.length)
755
+ end
756
+ no_prefix_name = "#{schema_prefix}#{core_object_name}"
757
+ unversioned = "#{schema_prefix}#{standard_prefix}#{::Brick.config.api_add_view_prefix}#{core_object_name}"
718
758
  else
719
759
  unversioned = k
720
760
  end
721
761
 
722
762
  view_relation ||= ::Brick.relations.fetch(found = unversioned, nil) ||
723
- (no_v_prefix_name && ::Brick.relations.fetch(found = no_v_prefix_name, nil))
763
+ (no_v_prefix_name && ::Brick.relations.fetch(found = no_v_prefix_name, nil)) ||
764
+ (no_prefix_name && ::Brick.relations.fetch(found = no_prefix_name, nil))
724
765
  if view_relation
725
766
  actions = view_relation.key?(:isView) ? [:index, :show] : ::Brick::ALL_API_ACTIONS # By default all actions are allowed
726
767
  # Call proc that limits which endpoints get surfaced based on version, table or view name, method (get list / get one / post / patch / delete)
@@ -730,7 +771,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
730
771
  proc_result = if (filter = ::Brick.config.api_filter).is_a?(Proc)
731
772
  begin
732
773
  num_args = filter.arity.negative? ? 6 : filter.arity
733
- filter.call(*[unversioned, k, actions, api_ver_num, found, test_ver_num][0...num_args])
774
+ filter.call(*[unversioned, k, view_relation, actions, api_ver_num, found, test_ver_num][0...num_args])
734
775
  rescue StandardError => e
735
776
  puts "::Brick.api_filter Proc error: #{e.message}"
736
777
  end
@@ -799,33 +840,6 @@ In config/initializers/brick.rb appropriate entries would look something like:
799
840
  end
800
841
  end
801
842
  end
802
-
803
- # Track routes being built
804
- if (class_name = v.fetch(:class_name, nil))
805
- if v.key?(:isView)
806
- view_class_length = class_name.length if class_name.length > view_class_length
807
- views
808
- else
809
- table_class_length = class_name.length if class_name.length > table_class_length
810
- tables
811
- end << [class_name, "#{schema_prefix&.tr('.', '/')}#{v[:resource]}"]
812
- end
813
-
814
- # Now the normal routes
815
- if path_prefix
816
- # Was: send(:scope, path: path_prefix) do
817
- send(:namespace, path_prefix) do
818
- brick_routes_create.call(schema_name, v[:resource], options)
819
- sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
820
- brick_routes_create.call(schema_name, sc.underscore.tr('/', '_').pluralize, options)
821
- end
822
- end
823
- else
824
- brick_routes_create.call(schema_name, v[:resource], options)
825
- sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
826
- brick_routes_create.call(schema_name, sc.underscore.tr('/', '_').pluralize, options)
827
- end
828
- end
829
843
  end
830
844
 
831
845
  if ::Brick.config.add_status && instance_variable_get(:@set).named_routes.names.exclude?(:brick_status)
@@ -845,7 +859,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
845
859
  if Object.const_defined?('Rswag::Ui')
846
860
  rswag_path = ::Rails.application.routes.routes.find { |r| r.app.app == Rswag::Ui::Engine }&.instance_variable_get(:@path_formatter)&.instance_variable_get(:@parts)&.join
847
861
  first_endpoint_parts = nil
848
- (doc_endpoints = Rswag::Ui.config.config_object[:urls]&.uniq!)&.each do |doc_endpoint|
862
+ (doc_endpoints = Rswag::Ui.config.config_object[:urls])&.each do |doc_endpoint|
849
863
  puts "Mounting OpenApi 3.0 documentation endpoint for \"#{doc_endpoint[:name]}\" on #{doc_endpoint[:url]}"
850
864
  send(:get, doc_endpoint[:url], { to: 'brick_openapi#index' })
851
865
  endpoint_parts = doc_endpoint[:url]&.split('/')
@@ -1180,8 +1194,13 @@ ActiveSupport.on_load(:active_record) do
1180
1194
  arsc.class_exec do
1181
1195
  def self.create(connection, callable = nil, &block)
1182
1196
  relation = (callable || block).call ::ActiveRecord::StatementCache::Params.new
1183
- bind_map = ::ActiveRecord::StatementCache::BindMap.new relation.bound_attributes
1184
- query_builder = connection.cacheable_query(self, relation.arel)
1197
+ bind_map = ::ActiveRecord::StatementCache::BindMap.new(
1198
+ # AR <= 4.2 uses relation.bind_values
1199
+ relation.respond_to?(:bound_attributes) ? relation.bound_attributes : relation.bind_values
1200
+ )
1201
+ options = [self, relation.arel]
1202
+ options.shift if connection.method(:cacheable_query).arity == 1 # Rails <= 5.0
1203
+ query_builder = connection.cacheable_query(*options)
1185
1204
  new query_builder, bind_map
1186
1205
  end
1187
1206
  end
@@ -1225,7 +1244,8 @@ ActiveSupport.on_load(:active_record) do
1225
1244
  end
1226
1245
  end
1227
1246
 
1228
- if Psych.respond_to?(:unsafe_load) && ActiveRecord.version < ::Gem::Version.new('6.1')
1247
+ if ActiveRecord.version < ::Gem::Version.new('6.1') &&
1248
+ Psych.method(:load).parameters.any? { |param| param.first == :key && param.last == :aliases }
1229
1249
  Psych.class_exec do
1230
1250
  class << self
1231
1251
  alias _original_load load
@@ -1267,7 +1287,9 @@ ActiveSupport.on_load(:active_record) do
1267
1287
  def initialize(base, associations, joins, eager_loading: true)
1268
1288
  araat = ::ActiveRecord::Associations::AliasTracker
1269
1289
  if araat.respond_to?(:create_with_joins) # Rails 5.0 and 5.1
1270
- @alias_tracker = araat.create_with_joins(base.connection, base.table_name, joins)
1290
+ cwj_options = [base.connection, base.table_name, joins]
1291
+ cwj_options << base.type_caster if araat.method(:create_with_joins).arity > 3 # Rails <= 5.1
1292
+ @alias_tracker = araat.create_with_joins(*cwj_options)
1271
1293
  @eager_loading = eager_loading # (Unused in Rails 5.0)
1272
1294
  else # Rails 4.2
1273
1295
  @alias_tracker = araat.create(base.connection, joins)
@@ -1636,21 +1658,24 @@ if ActiveRecord.version < ::Gem::Version.new('6.0') && ruby_version >= ::Gem::Ve
1636
1658
  end
1637
1659
 
1638
1660
  require 'active_model'
1639
- require 'active_model/type'
1640
- require 'active_model/type/value'
1641
- class ActiveModel::Type::Value
1642
- def initialize(*args, precision: nil, limit: nil, scale: nil)
1643
- @precision = precision
1644
- @scale = scale
1645
- @limit = limit
1661
+ begin
1662
+ require 'active_model/type'
1663
+ require 'active_model/type/value'
1664
+ class ActiveModel::Type::Value
1665
+ def initialize(*args, precision: nil, limit: nil, scale: nil)
1666
+ @precision = precision
1667
+ @scale = scale
1668
+ @limit = limit
1669
+ end
1646
1670
  end
1671
+ rescue LoadError => e # AR <= 4.2 doesn't have ActiveModel::Type
1647
1672
  end
1648
1673
 
1649
1674
  if Object.const_defined?('I18n')
1650
1675
  module I18n::Base
1651
1676
  alias _brick_translate translate
1652
1677
  def translate(key = nil, *args, throw: false, raise: false, locale: nil, **options)
1653
- options.merge!(args.pop) if args.length > 0 && args.last.is_a?(Hash)
1678
+ options.merge!(args.pop) if args.last.is_a?(Hash)
1654
1679
  _brick_translate(key = nil, throw: false, raise: false, locale: nil, **options)
1655
1680
  end
1656
1681
  end
@@ -1661,8 +1686,12 @@ if ActiveRecord.version < ::Gem::Version.new('6.0') && ruby_version >= ::Gem::Ve
1661
1686
 
1662
1687
  # Creates the authenticity token for the current request.
1663
1688
  def form_authenticity_token(*args, form_options: {}) # :doc:
1664
- form_options.merge!(args.pop) if args.length > 0 && args.last.is_a?(Hash)
1665
- masked_authenticity_token(session, form_options: form_options)
1689
+ if method(:masked_authenticity_token).arity == 1
1690
+ masked_authenticity_token(session) # AR <= 4.2 doesn't use form_options
1691
+ else
1692
+ form_options.merge!(args.pop) if args.last.is_a?(Hash)
1693
+ masked_authenticity_token(session, form_options: form_options)
1694
+ end
1666
1695
  end
1667
1696
  end
1668
1697
 
@@ -1672,7 +1701,7 @@ if ActiveRecord.version < ::Gem::Version.new('6.0') && ruby_version >= ::Gem::Ve
1672
1701
  encrypted = if method(:_encrypt).arity == 1
1673
1702
  _encrypt(value) # Rails <= 5.1
1674
1703
  else
1675
- if args.length > 0 && args.last.is_a?(Hash)
1704
+ if args.last.is_a?(Hash)
1676
1705
  expires_at ||= args.last[:expires_at]
1677
1706
  expires_in ||= args.last[:expires_in]
1678
1707
  purpose ||= args.last[:purpose]
@@ -1685,7 +1714,7 @@ if ActiveRecord.version < ::Gem::Version.new('6.0') && ruby_version >= ::Gem::Ve
1685
1714
  if const_defined?('Messages')
1686
1715
  class Messages::Metadata
1687
1716
  def self.wrap(message, *args, expires_at: nil, expires_in: nil, purpose: nil)
1688
- if args.length > 0 && args.last.is_a?(Hash)
1717
+ if args.last.is_a?(Hash)
1689
1718
  expires_at ||= args.last[:expires_at]
1690
1719
  expires_in ||= args.last[:expires_in]
1691
1720
  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.107
4
+ version: 1.0.109
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-19 00:00:00.000000000 Z
11
+ date: 2023-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord