brick 1.0.72 → 1.0.73

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: 06c3efed47ee7086f01a4f59c177f362343f376b1d6534f86e61ee38001b0d7b
4
- data.tar.gz: 8b521c56eb5afc09b06aec0adf0085136cb9e94d2c1a93355a8a2fc79da0316b
3
+ metadata.gz: 31f69b5035fcebf947e729c3ebabdf3a686c364fb9ddc6a764a2b34740fdb7c9
4
+ data.tar.gz: 87009aad044a5a110146d26dc02f62451b18e7ad67b2b07d420edff0718a18c6
5
5
  SHA512:
6
- metadata.gz: 1566ddaa89e45496b752b64ec218456e05dca50cc81a722d5a88e4d4bbe19b28ca981eeb253688f44e55a8ef9949d195bdc5a61f5ba7980b2507c315277b4d4d
7
- data.tar.gz: 7643f78bf92721c89bdb0065f112592cba26023ea7745ba34551ce39d9378cd286f50b3ea1491c813e10f6d93521ef16c54fa70edc5dbcd4d70cb5ad6a859ad3
6
+ metadata.gz: 300fe830280b2ad9d265fa62166aac01ba3e511090f95efa412d1b9ed42fbd72b9f17f8fe51ac1bb358be42f012b46cffbb3c142f6fd3b11f2a442025824e8ec
7
+ data.tar.gz: abbd021f67adb2c03aae76340ba1cb1f38d0be5b5774d11c5a01a1315e02b80c293c9008021915315416da28937b40af59d821e41ad6ac0eed890be162865d98
data/lib/brick/config.rb CHANGED
@@ -56,6 +56,31 @@ module Brick
56
56
  @mutex.synchronize { @enable_routes = enable }
57
57
  end
58
58
 
59
+ def enable_api
60
+ @mutex.synchronize { @enable_api }
61
+ end
62
+
63
+ def enable_api=(enable)
64
+ @mutex.synchronize { @enable_api = enable }
65
+ end
66
+
67
+ def api_root
68
+ ver = api_version
69
+ @mutex.synchronize { @api_root || "/api/#{ver}/" }
70
+ end
71
+
72
+ def api_root=(path)
73
+ @mutex.synchronize { @api_root = path }
74
+ end
75
+
76
+ def api_version
77
+ @mutex.synchronize { @api_version || 'v1' }
78
+ end
79
+
80
+ def api_version=(ver)
81
+ @mutex.synchronize { @api_version = ver }
82
+ end
83
+
59
84
  # Additional table associations to use (Think of these as virtual foreign keys perhaps)
60
85
  def additional_references
61
86
  @mutex.synchronize { @additional_references }
@@ -107,7 +107,8 @@ module ActiveRecord
107
107
  bracket_name = nil
108
108
  prefix = [prefix] unless prefix.is_a?(Array)
109
109
  if (dsl = ::Brick.config.model_descrips[name] || brick_get_dsl)
110
- dsl2 = +''
110
+ dsl2 = +'' # To replace our own DSL definition in case it needs to be expanded
111
+ dsl3 = +'' # To return expanded DSL that is nested from another model
111
112
  klass = nil
112
113
  dsl.each_char do |ch|
113
114
  if bracket_name
@@ -129,11 +130,16 @@ module ActiveRecord
129
130
  end
130
131
  if klass.column_names.exclude?(parts.last) &&
131
132
  (klass = (orig_class = klass).reflect_on_association(possible_dsl = parts.pop.to_sym)&.klass)
133
+ # Expand this entry which refers to an association name
132
134
  members2, dsl2a = klass.brick_parse_dsl(build_array, prefix + [possible_dsl], translations, true)
133
135
  members += members2
134
136
  dsl2 << dsl2a
137
+ dsl3 << dsl2a
135
138
  else
136
139
  dsl2 << "[#{bracket_name}]"
140
+ if emit_dsl
141
+ dsl3 << "[#{prefix[1..-1].map { |p| "#{p.to_s}." }.join if prefix.length > 1}#{bracket_name}]"
142
+ end
137
143
  members << parts
138
144
  end
139
145
  bracket_name = nil
@@ -145,15 +151,22 @@ module ActiveRecord
145
151
  klass = self
146
152
  else
147
153
  dsl2 << ch
154
+ dsl3 << ch
148
155
  end
149
156
  end
150
157
  # Rewrite the DSL in case it's now different from having to expand it
158
+ # if ::Brick.config.model_descrips[name] != dsl2
159
+ # puts ::Brick.config.model_descrips[name]
160
+ # puts dsl2.inspect
161
+ # puts dsl3.inspect
162
+ # binding.pry
163
+ # end
151
164
  ::Brick.config.model_descrips[name] = dsl2 unless emit_dsl
152
165
  else # With no DSL available, still put this prefix into the JoinArray so we can get primary key (ID) info from this table
153
166
  x = prefix.each_with_object(build_array) { |v, s| s[v.to_sym] }
154
167
  x[prefix.last] = nil unless prefix.empty? # Using []= will "hydrate" any missing part(s) in our whole series
155
168
  end
156
- emit_dsl ? [members, dsl2] : members
169
+ emit_dsl ? [members, dsl3] : members
157
170
  end
158
171
 
159
172
  # If available, parse simple DSL attached to a model in order to provide a friendlier name.
@@ -221,13 +234,21 @@ module ActiveRecord
221
234
  end
222
235
 
223
236
  def self.bt_link(assoc_name)
224
- model_underscore = name.underscore
225
237
  assoc_name = CGI.escapeHTML(assoc_name.to_s)
226
- model_path = Rails.application.routes.url_helpers.send("#{model_underscore.tr('/', '_').pluralize}_path".to_sym)
238
+ model_path = Rails.application.routes.url_helpers.send("#{_brick_index}_path".to_sym)
227
239
  av_class = Class.new.extend(ActionView::Helpers::UrlHelper)
228
240
  av_class.extend(ActionView::Helpers::TagHelper) if ActionView.version < ::Gem::Version.new('7')
229
241
  link = av_class.link_to(name, model_path)
230
- model_underscore == assoc_name ? link : "#{assoc_name}-#{link}".html_safe
242
+ table_name == assoc_name ? link : "#{assoc_name}-#{link}".html_safe
243
+ end
244
+
245
+ def self._brick_index
246
+ tbl_parts = table_name.split('.')
247
+ tbl_parts.shift if ::Brick.apartment_multitenant && tbl_parts.first == Apartment.default_schema
248
+ if (index = tbl_parts.map(&:underscore).join('_')) == index.singularize
249
+ index << '_index' # Rails applies an _index suffix to that route when the resource name is singular
250
+ end
251
+ index
231
252
  end
232
253
 
233
254
  def self.brick_import_template
@@ -407,7 +428,7 @@ module ActiveRecord
407
428
  is_distinct = nil
408
429
  wheres = {}
409
430
  params.each do |k, v|
410
- next if ['_brick_schema', '_brick_order'].include?(k)
431
+ next if ['_brick_schema', '_brick_order', 'controller', 'action'].include?(k)
411
432
 
412
433
  case (ks = k.split('.')).length
413
434
  when 1
@@ -427,6 +448,9 @@ module ActiveRecord
427
448
  # %%% Skip the metadata columns
428
449
  if selects&.empty? # Default to all columns
429
450
  tbl_no_schema = table.name.split('.').last
451
+ # %%% Have once gotten this error with MSSQL referring to http://localhost:3000/warehouse/cold_room_temperatures__archive
452
+ # ActiveRecord::StatementInvalid (TinyTds::Error: DBPROCESS is dead or not enabled)
453
+ # Relevant info here: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/402
430
454
  columns.each do |col|
431
455
  col_alias = " AS #{col.name}_" if (col_name = col.name) == 'class'
432
456
  selects << if is_mysql
@@ -726,7 +750,8 @@ Module.class_exec do
726
750
  # Vabc instead of VABC)
727
751
  full_class_name = +''
728
752
  full_class_name << "::#{self.name}" unless self == Object
729
- full_class_name << "::#{plural_class_name.underscore.singularize.camelize}"
753
+ singular_class_name = ::Brick.namify(plural_class_name, :underscore).singularize.camelize
754
+ full_class_name << "::#{singular_class_name}"
730
755
  if plural_class_name == 'BrickSwagger' ||
731
756
  (
732
757
  (::Brick.config.add_status || ::Brick.config.add_orphans) &&
@@ -1111,64 +1136,63 @@ class Object
1111
1136
  end
1112
1137
  return [new_controller_class, code + "end # BrickGem controller\n"]
1113
1138
  when 'BrickSwagger'
1114
- is_swagger = true # if request.format == :json)
1139
+ is_swagger = true
1115
1140
  end
1116
1141
 
1117
1142
  self.protect_from_forgery unless: -> { self.request.format.js? }
1118
1143
  self.define_method :index do
1119
- # We do all of this now so that bt_descrip and hm_counts are available on the model early in case the user
1120
- # wants to do an ORDER BY based on any of that
1121
- translations = {}
1122
- join_array = ::Brick::JoinArray.new
1123
- is_add_bts = is_add_hms = true
1124
- # This builds out bt_descrip and hm_counts on the model
1125
- model._brick_calculate_bts_hms(translations, join_array) if is_add_bts || is_add_hms
1126
-
1127
1144
  if is_swagger
1128
- json = { 'openapi': '3.0.1', 'info': { 'title': 'API V1', 'version': 'v1' },
1145
+ if !params&.key?('_brick_schema') &&
1146
+ (referrer_params = request.env['HTTP_REFERER']&.split('?')&.last&.split('&')&.map { |x| x.split('=') }).present?
1147
+ if params
1148
+ referrer_params.each { |k, v| params.send(:parameters)[k] = v }
1149
+ else
1150
+ api_params = referrer_params&.to_h
1151
+ end
1152
+ end
1153
+ ::Brick.set_db_schema(params || api_params)
1154
+ json = { 'openapi': '3.0.1', 'info': { 'title': Rswag::Ui.config.config_object[:urls].last&.fetch(:name, 'API documentation'), 'version': ::Brick.config.api_version },
1129
1155
  'servers': [
1130
- { 'url': 'https://{defaultHost}', 'variables': { 'defaultHost': { 'default': 'www.example.com' } } }
1156
+ { 'url': '{scheme}://{defaultHost}', 'variables': {
1157
+ 'scheme': { 'default': request.env['rack.url_scheme'] },
1158
+ 'defaultHost': { 'default': request.env['HTTP_HOST'] }
1159
+ } }
1131
1160
  ]
1132
1161
  }
1133
- json['paths'] = relations.inject({}) do |s, v|
1134
- s["/api/v1/#{v.first}"] = {
1135
- 'get': {
1136
- 'summary': "list #{v.first}",
1137
- 'parameters': v.last[:cols].map { |k, v| { 'name' => k, 'schema': { 'type': v.first } } },
1138
- 'responses': { '200': { 'description': 'successful' } }
1139
- }
1140
- }
1141
- # next if v.last[:isView]
1142
-
1143
- s["/api/v1/#{v.first}/{id}"] = {
1144
- 'patch': {
1145
- 'summary': "update a #{v.first.singularize}",
1146
- 'parameters': v.last[:cols].reject { |k, v| Brick.config.metadata_columns.include?(k) }.map do |k, v|
1147
- { 'name' => k, 'schema': { 'type': v.first } }
1148
- end,
1149
- 'responses': { '200': { 'description': 'successful' } }
1162
+ json['paths'] = relations.inject({}) do |s, relation|
1163
+ unless ::Brick.config.enable_api == false
1164
+ table_description = relation.last[:description]
1165
+ s["#{::Brick.config.api_root}#{relation.first.tr('.', '/')}"] = {
1166
+ 'get': {
1167
+ 'summary': "list #{relation.first}",
1168
+ 'description': table_description,
1169
+ 'parameters': relation.last[:cols].map do |k, v|
1170
+ param = { 'name' => k, 'schema': { 'type': v.first } }
1171
+ if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k, nil))
1172
+ param['description'] = col_descrip
1173
+ end
1174
+ param
1175
+ end,
1176
+ 'responses': { '200': { 'description': 'successful' } }
1177
+ }
1150
1178
  }
1151
- # "/api/v1/books/{id}": {
1152
- # "parameters": [
1153
- # {
1154
- # "name": "id",
1155
- # "in": "path",
1156
- # "description": "id",
1157
- # "required": true,
1158
- # "schema": {
1159
- # "type": "string"
1160
- # }
1161
- # },
1162
- # {
1163
- # "name": "Authorization",
1164
- # "in": "header",
1165
- # "schema": {
1166
- # "type": "string"
1167
- # }
1168
- # }
1169
- # ],
1170
- }
1171
- s
1179
+
1180
+ s["#{::Brick.config.api_root}#{relation.first.tr('.', '/')}/{id}"] = {
1181
+ 'patch': {
1182
+ 'summary': "update a #{relation.first.singularize}",
1183
+ 'description': table_description,
1184
+ 'parameters': relation.last[:cols].reject { |k, v| Brick.config.metadata_columns.include?(k) }.map do |k, v|
1185
+ param = { 'name' => k, 'schema': { 'type': v.first } }
1186
+ if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k, nil))
1187
+ param['description'] = col_descrip
1188
+ end
1189
+ param
1190
+ end,
1191
+ 'responses': { '200': { 'description': 'successful' } }
1192
+ }
1193
+ } unless relation.last.fetch(:isView, nil)
1194
+ s
1195
+ end
1172
1196
  end
1173
1197
  render inline: json.to_json, content_type: request.format
1174
1198
  return
@@ -1176,6 +1200,14 @@ class Object
1176
1200
 
1177
1201
  # Normal (non-swagger) request
1178
1202
 
1203
+ # We do all of this now so that bt_descrip and hm_counts are available on the model early in case the user
1204
+ # wants to do an ORDER BY based on any of that
1205
+ translations = {}
1206
+ join_array = ::Brick::JoinArray.new
1207
+ is_add_bts = is_add_hms = true
1208
+ # This builds out bt_descrip and hm_counts on the model
1209
+ model._brick_calculate_bts_hms(translations, join_array) if is_add_bts || is_add_hms
1210
+
1179
1211
  # %%% Allow params to define which columns to use for order_by
1180
1212
  # Overriding the default by providing a querystring param?
1181
1213
  ordering = params['_brick_order']&.split(',')&.map(&:to_sym) || Object.send(:default_ordering, table_name, pk)
@@ -1189,8 +1221,9 @@ class Object
1189
1221
  end
1190
1222
  render inline: exported_csv, content_type: request.format
1191
1223
  return
1192
- elsif request.format == :js # Asking for JSON?
1193
- render inline: model.df_export(model.brick_import_template).to_json, content_type: request.format
1224
+ elsif request.format == :js || request.path.start_with?('/api/') # Asking for JSON?
1225
+ data = (model.is_view? || !Object.const_defined?('DutyFree')) ? model.limit(1000) : model.df_export(model.brick_import_template)
1226
+ render inline: data.to_json, content_type: request.format == '*/*' ? 'application/json' : request.format
1194
1227
  return
1195
1228
  end
1196
1229
 
@@ -1206,7 +1239,7 @@ class Object
1206
1239
  "b_r_#{v.first}.c_t_ AS \"b_r_#{v.first}_ct\""
1207
1240
  end
1208
1241
  end
1209
- instance_variable_set("@#{table_name}".to_sym, ar_relation.dup._select!(*selects, *counts))
1242
+ instance_variable_set("@#{table_name.pluralize}".to_sym, ar_relation.dup._select!(*selects, *counts))
1210
1243
  if namespace && (idx = lookup_context.prefixes.index(table_name))
1211
1244
  lookup_context.prefixes[idx] = "#{namespace.name.underscore}/#{lookup_context.prefixes[idx]}"
1212
1245
  end
@@ -1221,32 +1254,34 @@ class Object
1221
1254
  @_brick_erd = params['_brick_erd']&.to_i
1222
1255
  end
1223
1256
 
1224
- _, order_by_txt = model._brick_calculate_ordering(default_ordering(table_name, pk))
1225
- code << " def index\n"
1226
- code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{order_by_txt.join(', ')})" : '.all'}\n"
1227
- code << " @#{table_name}.brick_select(params)\n"
1228
- code << " end\n"
1229
-
1230
- is_pk_string = nil
1231
- if (pk_col = model&.primary_key)
1232
- code << " def show\n"
1233
- code << " #{find_by_name = "find_#{singular_table_name}"}\n"
1257
+ unless is_swagger
1258
+ ::Brick.set_db_schema
1259
+ _, order_by_txt = model._brick_calculate_ordering(default_ordering(table_name, pk)) if pk
1260
+ code << " def index\n"
1261
+ code << " @#{table_name.pluralize} = #{model.name}#{pk&.present? ? ".order(#{order_by_txt.join(', ')})" : '.all'}\n"
1262
+ code << " @#{table_name.pluralize}.brick_select(params)\n"
1234
1263
  code << " end\n"
1235
- self.define_method :show do
1236
- ::Brick.set_db_schema(params)
1237
- id = if model.columns_hash[pk_col]&.type == :string
1238
- is_pk_string = true
1239
- params[:id]
1240
- else
1241
- params[:id]&.split(/[\/,_]/)
1242
- end
1243
- id = id.first if id.is_a?(Array) && id.length == 1
1244
- instance_variable_set("@#{singular_table_name}".to_sym, find_obj)
1264
+
1265
+ is_pk_string = nil
1266
+ if pk.present?
1267
+ code << " def show\n"
1268
+ code << " #{find_by_name = "find_#{singular_table_name}"}\n"
1269
+ code << " end\n"
1270
+ self.define_method :show do
1271
+ ::Brick.set_db_schema(params)
1272
+ id = if model.columns_hash[pk.first]&.type == :string
1273
+ is_pk_string = true
1274
+ params[:id]
1275
+ else
1276
+ params[:id]&.split(/[\/,_]/)
1277
+ end
1278
+ id = id.first if id.is_a?(Array) && id.length == 1
1279
+ instance_variable_set("@#{singular_table_name}".to_sym, find_obj)
1280
+ end
1245
1281
  end
1246
- end
1247
1282
 
1248
- # By default, views get marked as read-only
1249
- # unless model.readonly # (relation = relations[model.table_name]).key?(:isView)
1283
+ # By default, views get marked as read-only
1284
+ # unless model.readonly # (relation = relations[model.table_name]).key?(:isView)
1250
1285
  code << " def new\n"
1251
1286
  code << " @#{singular_table_name} = #{model.name}.new\n"
1252
1287
  code << " end\n"
@@ -1280,7 +1315,7 @@ class Object
1280
1315
  end
1281
1316
  end
1282
1317
 
1283
- if pk_col
1318
+ if pk.present?
1284
1319
  # if (schema = ::Brick.config.schema_behavior[:multitenant]&.fetch(:schema_to_analyse, nil)) && ::Brick.db_schemas&.include?(schema)
1285
1320
  # ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
1286
1321
  # end
@@ -1327,9 +1362,9 @@ class Object
1327
1362
  end
1328
1363
  end
1329
1364
 
1330
- code << "private\n" if pk_col || is_need_params
1365
+ code << "private\n" if pk.present? || is_need_params
1331
1366
 
1332
- if pk_col
1367
+ if pk.present?
1333
1368
  code << " def find_#{singular_table_name}
1334
1369
  id = params[:id]&.split(/[\\/,_]/)
1335
1370
  @#{singular_table_name} = #{model.name}.find(id.is_a?(Array) && id.length == 1 ? id.first : id)
@@ -1357,16 +1392,16 @@ class Object
1357
1392
  private params_name
1358
1393
  # Get column names for params from relations[model.table_name][:cols].keys
1359
1394
  end
1360
- # end
1395
+ end # unless is_swagger
1361
1396
  code << "end # #{namespace_name}#{class_name}\n"
1362
1397
  end # class definition
1363
1398
  [built_controller, code]
1364
1399
  end
1365
1400
 
1366
1401
  def _brick_get_hm_assoc_name(relation, hm_assoc, source = nil)
1367
- if (relation[:hm_counts][hm_assoc[:assoc_name]]&.> 1) &&
1402
+ if (relation[:hm_counts][hm_assoc[:inverse_table]]&.> 1) &&
1368
1403
  hm_assoc[:alternate_name] != (source || name.underscore)
1369
- plural = ActiveSupport::Inflector.pluralize(hm_assoc[:alternate_name])
1404
+ plural = "#{hm_assoc[:assoc_name]}_#{ActiveSupport::Inflector.pluralize(hm_assoc[:alternate_name])}"
1370
1405
  new_alt_name = (hm_assoc[:alternate_name] == name.underscore) ? "#{hm_assoc[:assoc_name].singularize}_#{plural}" : plural
1371
1406
  # uniq = 1
1372
1407
  # while same_name = relation[:fks].find { |x| x.last[:assoc_name] == hm_assoc[:assoc_name] && x.last != hm_assoc }
@@ -1497,6 +1532,8 @@ module ActiveRecord::ConnectionHandling
1497
1532
  r['schema']
1498
1533
  end
1499
1534
  relation_name = schema_name ? "#{schema_name}.#{r['relation_name']}" : r['relation_name']
1535
+ # Both uppers and lowers as well as underscores?
1536
+ apply_double_underscore_patch if relation_name =~ /[A-Z]/ && relation_name =~ /[a-z]/ && relation_name.index('_')
1500
1537
  relation = relations[relation_name]
1501
1538
  relation[:isView] = true if r['table_type'] == 'VIEW'
1502
1539
  relation[:description] = r['table_description'] if r['table_description']
@@ -1513,6 +1550,7 @@ module ActiveRecord::ConnectionHandling
1513
1550
  cols = relation[:cols] # relation.fetch(:cols) { relation[:cols] = [] }
1514
1551
  cols[col_name] = [r['data_type'], r['max_length'], measures&.include?(col_name), r['is_nullable'] == 'NO']
1515
1552
  # puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
1553
+ relation[:col_descrips][col_name] = r['column_description'] if r['column_description']
1516
1554
  end
1517
1555
  else # MySQL2 and OracleEnhanced act a little differently, bringing back an array for each row
1518
1556
  schema_and_tables = case ActiveRecord::Base.connection.adapter_name
@@ -1542,6 +1580,9 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
1542
1580
 
1543
1581
  if (relation_name = r[1]) =~ /^[A-Z0-9_]+$/
1544
1582
  relation_name.downcase!
1583
+ # Both uppers and lowers as well as underscores?
1584
+ elsif relation_name =~ /[A-Z]/ && relation_name =~ /[a-z]/ && relation_name.index('_')
1585
+ apply_double_underscore_patch
1545
1586
  end
1546
1587
  # Expect the default schema for SQL Server to be 'dbo'.
1547
1588
  if (::Brick.is_oracle && r[0] != schema) || (is_mssql && r[0] != 'dbo')
@@ -1668,7 +1709,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
1668
1709
  name_parts = k.split('.')
1669
1710
  idx = 1
1670
1711
  name_parts = name_parts.map do |x|
1671
- (idx += 1) < name_parts.length ? x : x.singularize
1712
+ (idx += 1) <= name_parts.length ? x : x.singularize
1672
1713
  end
1673
1714
  name_parts.shift if apartment && name_parts.length > 1 && name_parts.first == Apartment.default_schema
1674
1715
  class_name = name_parts.map(&:camelize).join('::')
@@ -1696,7 +1737,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
1696
1737
  def display_classes(rels, max_length)
1697
1738
  rels.sort.each do |rel|
1698
1739
  rel_link = rel.last.dup.map(&:underscore)
1699
- rel_link[-1] = rel_link[-1].pluralize
1740
+ rel_link[-1] = rel_link[-1]
1700
1741
  puts "#{rel.first}#{' ' * (max_length - rel.first.length)} /#{rel_link.join('/')}"
1701
1742
  end
1702
1743
  puts "\n"
@@ -1706,8 +1747,11 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
1706
1747
  is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer' if is_mssql.nil?
1707
1748
  sql ||= "SELECT t.table_schema AS \"schema\", t.table_name AS relation_name, t.table_type,#{"
1708
1749
  pg_catalog.obj_description(
1709
- ('\"' || t.table_schema || '\".\"' || t.table_name || '\"')::regclass, 'pg_class'
1710
- ) AS table_description," if is_postgres}
1750
+ ('\"' || t.table_schema || '\".\"' || t.table_name || '\"')::regclass::oid, 'pg_class'
1751
+ ) AS table_description,
1752
+ pg_catalog.col_description(
1753
+ ('\"' || t.table_schema || '\".\"' || t.table_name || '\"')::regclass::oid, c.ordinal_position
1754
+ ) AS column_description," if is_postgres}
1711
1755
  c.column_name, c.data_type,
1712
1756
  COALESCE(c.character_maximum_length, c.numeric_precision) AS max_length,
1713
1757
  kcu.constraint_type AS const, kcu.constraint_name AS \"key\",
@@ -1751,6 +1795,43 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
1751
1795
  ar_imtn = ActiveRecord.version >= ::Gem::Version.new('5.0') ? ActiveRecord::Base.internal_metadata_table_name : ''
1752
1796
  [ar_smtn, ar_imtn]
1753
1797
  end
1798
+
1799
+ def apply_double_underscore_patch
1800
+ unless @double_underscore_applied
1801
+ # Same as normal #camelize and #underscore, just that double-underscores turn into a single underscore
1802
+ ActiveSupport::Inflector.class_eval do
1803
+ def camelize(term, uppercase_first_letter = true)
1804
+ strings = term.to_s.split('__').map do |string|
1805
+ # String#camelize takes a symbol (:upper or :lower), so here we also support :lower to keep the methods consistent.
1806
+ if !uppercase_first_letter || uppercase_first_letter == :lower
1807
+ string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase! || match }
1808
+ else
1809
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize! || match }
1810
+ end
1811
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) do
1812
+ word = $2
1813
+ substituted = inflections.acronyms[word] || word.capitalize! || word
1814
+ $1 ? "::#{substituted}" : substituted
1815
+ end
1816
+ string
1817
+ end
1818
+ strings.join('_')
1819
+ end
1820
+
1821
+ def underscore(camel_cased_word)
1822
+ return camel_cased_word.to_s unless /[A-Z-]|::/.match?(camel_cased_word)
1823
+ camel_cased_word.to_s.gsub("::", "/").split('_').map do |word|
1824
+ word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
1825
+ word.gsub!(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { ($1 || $2) << "_" }
1826
+ word.tr!("-", "_")
1827
+ word.downcase!
1828
+ word
1829
+ end.join('__')
1830
+ end
1831
+ end
1832
+ @double_underscore_applied = true
1833
+ end
1834
+ end
1754
1835
  end
1755
1836
 
1756
1837
  # ==========================================
@@ -1777,7 +1858,7 @@ module Brick
1777
1858
 
1778
1859
  class << self
1779
1860
  def _add_bt_and_hm(fk, relations, is_polymorphic = false, is_optional = false)
1780
- bt_assoc_name = ::Brick.namify(fk[2], true)
1861
+ bt_assoc_name = ::Brick.namify(fk[2], :downcase)
1781
1862
  unless is_polymorphic
1782
1863
  bt_assoc_name = if bt_assoc_name.underscore.end_with?('_id')
1783
1864
  bt_assoc_name[-3] == '_' ? bt_assoc_name[0..-4] : bt_assoc_name[0..-3]
@@ -1878,6 +1959,11 @@ module Brick
1878
1959
 
1879
1960
  return if is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[1] == exclusion[0] && fk[2] == exclusion[1] && primary_table == exclusion[2] } || hms.nil?
1880
1961
 
1962
+ # if fk[1].end_with?('Suppliers') && fk[4] == 'People'
1963
+ # puts fk.inspect
1964
+ # binding.pry
1965
+ # end
1966
+
1881
1967
  if (assoc_hm = hms.fetch((hm_cnstr_name = "hm_#{cnstr_name}"), nil))
1882
1968
  if assoc_hm[:fk].is_a?(String)
1883
1969
  assoc_hm[:fk] = [assoc_hm[:fk], fk[2]] unless fk[2] == assoc_hm[:fk]
@@ -1885,7 +1971,6 @@ module Brick
1885
1971
  assoc_hm[:fk] << fk[2]
1886
1972
  end
1887
1973
  assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
1888
- assoc_hm[:inverse] = assoc_bt
1889
1974
  else
1890
1975
  inv_tbl = if ::Brick.config.schema_behavior[:multitenant] && apartment && fk[0] == Apartment.default_schema
1891
1976
  for_tbl
@@ -1896,7 +1981,7 @@ module Brick
1896
1981
  inverse_table: inv_tbl, inverse: assoc_bt }
1897
1982
  assoc_hm[:polymorphic] = true if is_polymorphic
1898
1983
  hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
1899
- hm_counts[fk[1]] = hm_counts.fetch(fk[1]) { 0 } + 1
1984
+ this_hm_count = hm_counts[fk[1]] = hm_counts.fetch(fk[1]) { 0 } + 1
1900
1985
  end
1901
1986
  assoc_bt[:inverse] = assoc_hm
1902
1987
  end
@@ -159,7 +159,7 @@ module Brick
159
159
  hms_columns << hm_entry
160
160
  when 'show', 'new', 'update'
161
161
  hm_stuff << if hm_fk_name
162
- "<%= link_to '#{assoc_name}', #{hm_assoc.klass.name.underscore.tr('/', '_').pluralize}_path({ #{path_keys(hm_assoc, hm_fk_name, "@#{obj_name}", pk)} }) %>\n"
162
+ "<%= link_to '#{assoc_name}', #{hm_assoc.klass._brick_index}_path({ #{path_keys(hm_assoc, hm_fk_name, "@#{obj_name}", pk)} }) %>\n"
163
163
  else # %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
164
164
  assoc_name
165
165
  end
@@ -173,12 +173,13 @@ module Brick
173
173
  # environment or whatever, then get either the controllers or routes list instead
174
174
  apartment_default_schema = ::Brick.apartment_multitenant && Apartment.default_schema
175
175
  table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables).each_with_object({}) do |tbl, s|
176
+ binding.pry if tbl.is_a?(Symbol)
176
177
  if (tbl_parts = tbl.split('.')).first == apartment_default_schema
177
178
  tbl = tbl_parts.last
178
179
  end
179
180
  s[tbl] = nil
180
181
  end.keys.sort.each_with_object(+'') do |v, s|
181
- s << "<option value=\"#{v.underscore.gsub('.', '/').pluralize}\">#{v}</option>"
182
+ s << "<option value=\"#{v.underscore.gsub('.', '/')}\">#{v}</option>"
182
183
  end.html_safe
183
184
  table_options << '<option value="brick_status">(Status)</option>'.html_safe if ::Brick.config.add_status
184
185
  table_options << '<option value="brick_orphans">(Orphans)</option>'.html_safe if is_orphans
@@ -351,15 +352,39 @@ def hide_bcrypt(val, max_len = 200)
351
352
  end
352
353
  def display_value(col_type, val)
353
354
  case col_type
354
- when 'geometry'
355
+ when 'geometry', 'geography'
355
356
  if Object.const_defined?('RGeo')
356
357
  @is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2' if @is_mysql.nil?
357
- if @is_mysql
358
- # MySQL's \"Internal Geometry Format\" is like WKB, but with an initial 4 bytes that indicates the SRID.
359
- srid = val[0..3].unpack('I')
360
- val = val[4..-1]
358
+ @is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer' if @is_mssql.nil?
359
+ val_err = nil
360
+ if @is_mysql || @is_mssql
361
+ # MySQL's \"Internal Geometry Format\" and MSSQL's Geography are like WKB, but with an initial 4 bytes that indicates the SRID.
362
+ if (srid = val&.[](0..3)&.unpack('I'))
363
+ val = val.force_encoding('BINARY')[4..-1].bytes
364
+
365
+ # MSSQL spatial bitwise flags, often 0C for a point:
366
+ # xxxx xxx1 = HasZValues
367
+ # xxxx xx1x = HasMValues
368
+ # xxxx x1xx = IsValid
369
+ # xxxx 1xxx = IsSinglePoint
370
+ # xxx1 xxxx = IsSingleLineSegment
371
+ # xx1x xxxx = IsWholeGlobe
372
+ # Convert Microsoft's unique geography binary to standard WKB
373
+ # (MSSQL point usually has two doubles, lng / lat, and can also have Z)
374
+ if @is_mssql
375
+ if val[0] == 1 && (val[1] & 8 > 0) && # Single point?
376
+ (val.length - 2) % 8 == 0 && val.length < 27 # And containing up to three 8-byte values?
377
+ idx = 2
378
+ new_val = [0, 0, 0, 0, 1]
379
+ new_val.concat(val[idx - 8...idx].reverse) while (idx += 8) <= val.length
380
+ val = new_val
381
+ else
382
+ val_err = '(Microsoft internal SQL geography type)'
383
+ end
384
+ end
385
+ end
361
386
  end
362
- RGeo::WKRep::WKBParser.new.parse(val)
387
+ val_err || (val ? RGeo::WKRep::WKBParser.new.parse(val.pack('c*')) : nil)
363
388
  else
364
389
  '(Add RGeo gem to parse geometry detail)'
365
390
  end
@@ -448,7 +473,7 @@ function changeout(href, param, value, trimAfter) {
448
473
  var params = hrefParts.length > 1 ? hrefParts[1].split(\"&\") : [];
449
474
  if (param === undefined || param === null || param === -1) {
450
475
  hrefParts = hrefParts[0].split(\"://\");
451
- var pathParts = hrefParts[hrefParts.length - 1].split(\"/\");
476
+ var pathParts = hrefParts[hrefParts.length - 1].split(\"/\").filter(function (pp) {return pp !== \"\";});
452
477
  if (value === undefined)
453
478
  // A couple possibilities if it's namespaced, starting with two parts in the path -- and then try just one
454
479
  return [pathParts.slice(1, 3).join('/'), pathParts.slice(1, 2)[0]];
@@ -562,7 +587,7 @@ erDiagram
562
587
  <%= \"#\{model_short_name} }o..o{ #\{hm_name} : \\\"#\{hm.first}\\\"\".html_safe %><%
563
588
  else # has_many
564
589
  %> <%= \"#\{model_short_name} ||--o{ #\{hm_name} : \\\"#\{
565
- hm_name unless hm.first.to_s == hm_class.name.underscore.pluralize.tr('/', '_')
590
+ hm.first.to_s unless hm.first.to_s.downcase == hm_class.name.underscore.pluralize.tr('/', '_')
566
591
  }\\\"\".html_safe %><%
567
592
  end %>
568
593
  <% end
@@ -604,7 +629,7 @@ erDiagram
604
629
  end
605
630
  if Object.const_defined?('DutyFree')
606
631
  template_link = "
607
- <%= link_to 'CSV', #{table_name}_path(format: :csv) %> &nbsp; <a href=\"#\" id=\"sheetsLink\">Sheets</a>
632
+ <%= link_to 'CSV', #{@_brick_model._brick_index}_path(format: :csv) %> &nbsp; <a href=\"#\" id=\"sheetsLink\">Sheets</a>
608
633
  <div id=\"dropper\" contenteditable=\"true\"></div>
609
634
  <input type=\"button\" id=\"btnImport\" value=\"Import\">
610
635
 
@@ -667,7 +692,7 @@ erDiagram
667
692
  console.log(\"x1\", sheetUrl);
668
693
 
669
694
  // Get JSON data
670
- fetch(changeout(<%= #{table_name}_path(format: :js).inspect.html_safe %>, \"_brick_schema\", brickSchema)).then(function (response) {
695
+ fetch(changeout(<%= #{@_brick_model._brick_index}_path(format: :js).inspect.html_safe %>, \"_brick_schema\", brickSchema)).then(function (response) {
671
696
  response.json().then(function (data) {
672
697
  gapi.client.sheets.spreadsheets.values.append({
673
698
  spreadsheetId: spreadsheetId,
@@ -698,7 +723,7 @@ erDiagram
698
723
  <select id=\"schema\">#{schema_options}</select>" if ::Brick.config.schema_behavior[:multitenant] && ::Brick.db_schemas.length > 1}
699
724
  <select id=\"tbl\">#{table_options}</select>
700
725
  <table id=\"resourceName\"><tr>
701
- <td><h1>#{model_plural = model_name.pluralize}</h1></td>
726
+ <td><h1>#{model_name}</h1></td>
702
727
  <td id=\"imgErd\" title=\"Show ERD\"></td>
703
728
  </tr></table>#{template_link}<%
704
729
  if (description = (relation = Brick.relations[#{model_name}.table_name])&.fetch(:description, nil)) %><%=
@@ -712,10 +737,10 @@ erDiagram
712
737
  origin = (key_parts = k.split('.')).length == 1 ? #{model_name} : #{model_name}.reflect_on_association(key_parts.first).klass
713
738
  if (destination_fk = Brick.relations[origin.table_name][:fks].values.find { |fk| fk[:fk] == key_parts.last }) &&
714
739
  (obj = (destination = origin.reflect_on_association(destination_fk[:assoc_name])&.klass)&.find(id)) %>
715
- <h3>for <%= link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination.name.underscore.tr('/', '_')\}_path\".to_sym, id) %></h3><%
740
+ <h3>for <%= link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination._brick_index\}_path\".to_sym, id) %></h3><%
716
741
  end
717
742
  end %>
718
- (<%= link_to 'See all #{model_plural.split('::').last}', #{path_obj_name.pluralize}_path %>)
743
+ (<%= link_to 'See all #{model_name.split('::').last.pluralize}', #{@_brick_model._brick_index}_path %>)
719
744
  <% end
720
745
  # COLUMN EXCLUSIONS
721
746
  if @_brick_excl&.present? %>
@@ -768,7 +793,7 @@ erDiagram
768
793
  end
769
794
  elsif col # HM column
770
795
  s << \"<th#\{' x-order=\"' + col_name + '\"' if true}>#\{col[2]} \"
771
- s << (col.first ? \"#\{col[3]}\" : \"#\{link_to(col[3], send(\"#\{col[1].name.underscore.tr('/', '_').pluralize}_path\"))}\")
796
+ s << (col.first ? \"#\{col[3]}\" : \"#\{link_to(col[3], send(\"#\{col[1]._brick_index}_path\"))}\")
772
797
  else # Bad column name!
773
798
  s << \"<th title=\\\"<< Unknown column >>\\\">#\{col_name}\"
774
799
  end
@@ -776,7 +801,11 @@ erDiagram
776
801
  end.html_safe
777
802
  %></tr></thead>
778
803
  <tbody>
779
- <% @#{table_name}.each do |#{obj_name}|
804
+ <% # %%% Have once gotten this error with MSSQL referring to http://localhost:3000/warehouse/cold_room_temperatures__archive
805
+ # ActiveRecord::StatementTimeout in Warehouse::ColdRoomTemperatures_Archive#index
806
+ # TinyTds::Error: Adaptive Server connection timed out
807
+ # (After restarting the server it worked fine again.)
808
+ @#{table_name}.each do |#{obj_name}|
780
809
  hms_cols = {#{hms_columns.join(', ')}} %>
781
810
  <tr>#{"
782
811
  <td><%= link_to '⇛', #{path_obj_name}_path(#{obj_pk}), { class: 'big-arrow' } %></td>" if obj_pk}
@@ -811,10 +840,11 @@ erDiagram
811
840
  else
812
841
  \"#\{hms_col[1] || 'View'\} #\{hms_col.first}\"
813
842
  end %>
814
- <%= link_to txt, send(\"#\{klass.name.underscore.tr('/', '_').pluralize}_path\".to_sym, hms_col[2]) unless hms_col[1]&.zero? %>
843
+ <%= link_to txt, send(\"#\{klass._brick_index}_path\".to_sym, hms_col[2]) unless hms_col[1]&.zero? %>
815
844
  <% end
816
845
  elsif (col = cols[col_name])
817
- %><%= display_value(col&.type || col&.sql_type, val) %><%
846
+ col_type = col&.sql_type == 'geography' ? col.sql_type : col&.type
847
+ %><%= display_value(col_type || col&.sql_type, val) %><%
818
848
  else # Bad column name!
819
849
  %>?<%
820
850
  end
@@ -908,7 +938,7 @@ erDiagram
908
938
  if (description = (relation = Brick.relations[#{model_name}.table_name])&.fetch(:description, nil)) %><%=
909
939
  description %><br><%
910
940
  end
911
- %><%= link_to '(See all #{obj_name.pluralize})', #{path_obj_name.pluralize}_path %>
941
+ %><%= link_to '(See all #{obj_name.pluralize})', #{@_brick_model._brick_index}_path %>
912
942
  #{erd_markup}
913
943
  <% if obj %>
914
944
  <br><br>
@@ -974,7 +1004,8 @@ end
974
1004
  \"<span class=\\\"orphan\\\">Orphaned ID: #\{val}</span>\".html_safe
975
1005
  end %>
976
1006
  <% else
977
- case (col_type = col.type || col.sql_type)
1007
+ col_type = col.sql_type == 'geography' ? col.sql_type : col.type
1008
+ case (col_type ||= col.sql_type)
978
1009
  when :string, :text %>
979
1010
  <% if is_bcrypt?(val) # || .readonly?
980
1011
  is_revert = false %>
@@ -1008,8 +1039,8 @@ end
1008
1039
  <% when :binary, :primary_key
1009
1040
  is_revert = false %>
1010
1041
  <% else %>
1011
- <%= display_value(col_type, val)
1012
- is_revert = false %>
1042
+ <%= is_revert = false
1043
+ display_value(col_type, val) %>
1013
1044
  <% end
1014
1045
  end
1015
1046
  if is_revert
@@ -1077,7 +1108,7 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
1077
1108
  var imgErd = document.getElementById(\"imgErd\");
1078
1109
  var mermaidErd = document.getElementById(\"mermaidErd\");
1079
1110
  var mermaidCode;
1080
- var cbs = {<%= callbacks.map { |k, v| \"#\{k}: \\\"#\{v.name.underscore.pluralize}\\\"\" }.join(', ').html_safe %>};
1111
+ var cbs = {<%= callbacks.map { |k, v| \"#\{k}: \\\"#\{send(\"#\{v._brick_index}_path\".to_sym)}\\\"\" }.join(', ').html_safe %>};
1081
1112
  if (imgErd) imgErd.addEventListener(\"click\", showErd);
1082
1113
  function showErd() {
1083
1114
  imgErd.style.display = \"none\";
@@ -1101,7 +1132,7 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
1101
1132
  function (evt) {
1102
1133
  location.href = changeout(changeout(
1103
1134
  changeout(location.href, '_brick_order', null), // Remove any ordering
1104
- -1, cbs[this.id]), \"_brick_erd\", \"1\");
1135
+ -1, cbs[this.id].replace(/^[\/]+/, \"\")), \"_brick_erd\", \"1\");
1105
1136
  }
1106
1137
  );
1107
1138
  }
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 72
8
+ TINY = 73
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
@@ -125,8 +125,8 @@ module Brick
125
125
  class << self
126
126
  attr_accessor :default_schema, :db_schemas, :routes_done, :is_oracle
127
127
 
128
- def set_db_schema(params)
129
- schema = params['_brick_schema'] || 'public'
128
+ def set_db_schema(params = nil)
129
+ schema = (params ? params['_brick_schema'] : ::Brick.default_schema) || 'public'
130
130
  if schema && ::Brick.db_schemas&.include?(schema)
131
131
  ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
132
132
  schema
@@ -152,17 +152,19 @@ module Brick
152
152
  end
153
153
 
154
154
  # Convert spaces to underscores if the second character and onwards is mixed case
155
- def namify(name, attempt_downcase = false)
156
- name.downcase! if attempt_downcase && name =~ /^[A-Z0-9_]+$/
155
+ def namify(name, action = nil)
156
+ has_uppers = name =~ /[A-Z]+/
157
+ has_lowers = name =~ /[a-z]+/
158
+ name.downcase! if has_uppers && action == :downcase
157
159
  if name.include?(' ')
158
160
  # All uppers or all lowers?
159
- if name[1..-1] =~ /^[A-Z0-9_]+$/ || name[1..-1] =~ /^[a-z0-9_]+$/
161
+ if !has_uppers || !has_lowers
160
162
  name.titleize.tr(' ', '_')
161
163
  else # Mixed uppers and lowers -- just remove existing spaces
162
164
  name.tr(' ', '')
163
165
  end
164
166
  else
165
- name
167
+ action == :underscore ? name.underscore : name
166
168
  end
167
169
  end
168
170
 
@@ -266,6 +268,26 @@ module Brick
266
268
  !!Brick.config.enable_routes
267
269
  end
268
270
 
271
+ # @api public
272
+ def enable_api=(path)
273
+ Brick.config.enable_api = path
274
+ end
275
+
276
+ # @api public
277
+ def enable_api
278
+ Brick.config.enable_api
279
+ end
280
+
281
+ # @api public
282
+ def api_root=(path)
283
+ Brick.config.api_root = path
284
+ end
285
+
286
+ # @api public
287
+ def api_root
288
+ Brick.config.api_root
289
+ end
290
+
269
291
  # @api public
270
292
  def skip_database_views=(value)
271
293
  Brick.config.skip_database_views = value
@@ -484,7 +506,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
484
506
  # %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
485
507
  # If auto-controllers and auto-models are both enabled then this makes sense:
486
508
  ::Brick.relations.each do |rel_name, v|
487
- rel_name = rel_name.split('.').map { |x| ::Brick.namify(x).underscore }
509
+ rel_name = rel_name.split('.').map { |rel_part| ::Brick.namify(rel_part, :underscore) }
488
510
  schema_names = rel_name[0..-2]
489
511
  schema_names.shift if ::Brick.apartment_multitenant && schema_names.first == Apartment.default_schema
490
512
  # %%% If more than one schema has the same table name, will need to add a schema name prefix to have uniqueness
@@ -494,10 +516,12 @@ In config/initializers/brick.rb appropriate entries would look something like:
494
516
  options[:only] = [:index, :show] if v.key?(:isView)
495
517
  if schema_names.present? # && !Object.const_defined('Apartment')
496
518
  send(:namespace, schema_names.first) do
497
- send(:resources, controller_name.to_sym, **options)
519
+ send(:resources, k.to_sym, **options)
498
520
  end
521
+ send(:get, "/api/v1/#{schema_names.first}/#{k}", { to: "#{schema_names.first}/#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
499
522
  else
500
- send(:resources, controller_name.to_sym, **options)
523
+ send(:resources, k.to_sym, **options)
524
+ send(:get, "/api/v1/#{k}", { to: "#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
501
525
  end
502
526
  end
503
527
  if ::Brick.config.add_status && instance_variable_get(:@set).named_routes.names.exclude?(:brick_status)
@@ -507,7 +531,11 @@ In config/initializers/brick.rb appropriate entries would look something like:
507
531
  get('/brick_orphans', to: 'brick_gem#orphans', as: 'brick_orphans')
508
532
  end
509
533
  end
510
- send(:get, '/api-docs/v1/swagger.json', { to: 'brick_swagger#index' }) if Object.const_defined?('Rswag::Ui')
534
+ if Object.const_defined?('Rswag::Ui') && doc_endpoint = Rswag::Ui.config.config_object[:urls].last
535
+ # Serves JSON swagger info from a path such as '/api-docs/v1/swagger.json'
536
+ puts "Mounting swagger info endpoint for \"#{doc_endpoint[:name]}\" on #{doc_endpoint[:url]}"
537
+ send(:get, doc_endpoint[:url], { to: 'brick_swagger#index' })
538
+ end
511
539
  end
512
540
  super
513
541
  end
@@ -147,6 +147,8 @@ module Brick
147
147
  # Brick.enable_controllers = true # Setting this to \"false\" will disable controllers in development
148
148
  # Brick.enable_views = true # Setting this to \"false\" will disable views in development
149
149
 
150
+ # Brick.api_root = '/api/v1/' # Path from which to serve out API resources when the RSwag gem is present
151
+
150
152
  # # By default models are auto-created for database views, and set to be read-only. This can be skipped.
151
153
  # Brick.skip_database_views = true
152
154
 
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.72
4
+ version: 1.0.73
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-20 00:00:00.000000000 Z
11
+ date: 2022-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord