brick 1.0.107 → 1.0.109

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: 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