brick 1.0.47 → 1.0.50

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: 4180c0164d7f086112339696df9d8fef2aeebcd32d9b81faf22d76ddc11815bc
4
- data.tar.gz: 823673bcbecc342f5df15e196ee65ad6f6ac0b75415514079c055af06d0ac7f6
3
+ metadata.gz: d5f5d009df0d4a7ce06a5dec8986d2664671e8b151f7493e5ce3f8f690ec7028
4
+ data.tar.gz: 974e86906775728e0ff0c7022cfbd87578b7c3013a8f9a5fe0db2243dcad9bf1
5
5
  SHA512:
6
- metadata.gz: 1d17b1abd64051066b71839095be1e672b897ad707a2d10f4ca4429131c894ee178711c873cef59957111704437b86b6322b8909ab5cc41cc955547146377dd8
7
- data.tar.gz: e210becff69096e73b9b7fa09bb4e1be6c36cd2982305b2c803c9c00e9d2b5b085857ea6edacc62498557cde928e2afbbb42ebbd137180385ec3a8a0c5efdf85
6
+ metadata.gz: 0f652cf44f9d18eca35304d1660ed3967d39c60dff42bcc05043607fada17d427308b7052a4b474c71d8a65462f86970838771471b6f1dd7a89accabc31ddd27
7
+ data.tar.gz: 386030a8bd3c603264ed9f534e1188eb5710fe67777ffc9634a9c318e8bac4153d2716bffbf9c34a7ff63999c3af39b2fa6db3b324abc5093467a33b144b41b8
data/lib/brick/config.rb CHANGED
@@ -190,6 +190,30 @@ module Brick
190
190
  @mutex.synchronize { @table_name_prefixes = value }
191
191
  end
192
192
 
193
+ def order
194
+ @mutex.synchronize { @order || {} }
195
+ end
196
+
197
+ # Get something like:
198
+ # Override how code sorts with:
199
+ # { 'on_call_list' => { code: "ORDER BY STRING_TO_ARRAY(code, '.')::int[]" } }
200
+ # Specify default thing to order_by with:
201
+ # { 'on_call_list' => { _brick_default: [:last_name, :first_name] } }
202
+ # { 'on_call_list' => { _brick_default: :sequence } }
203
+ def order=(orders)
204
+ @mutex.synchronize do
205
+ case (brick_default = orders.fetch(:_brick_default, nil))
206
+ when NilClass
207
+ orders[:_brick_default] = orders.keys.reject { |k| k == :_brick_default }.first
208
+ when String
209
+ orders[:_brick_default] = [brick_default.to_sym]
210
+ when Symbol
211
+ orders[:_brick_default] = [brick_default]
212
+ end
213
+ @order = orders
214
+ end
215
+ end
216
+
193
217
  def metadata_columns
194
218
  @mutex.synchronize { @metadata_columns }
195
219
  end
@@ -221,6 +221,68 @@ module ActiveRecord
221
221
  template
222
222
  end
223
223
 
224
+ class << self
225
+ # belongs_to DSL descriptions
226
+ def _br_bt_descrip
227
+ @_br_bt_descrip ||= {}
228
+ end
229
+ # has_many count definitions
230
+ def _br_hm_counts
231
+ @_br_hm_counts ||= {}
232
+ end
233
+ # has_many :through associative tables
234
+ def _br_associatives
235
+ @_br_associatives ||= {}
236
+ end
237
+ end
238
+
239
+ # Search for BT, HM, and HMT DSL stuff
240
+ def self._brick_calculate_bts_hms(translations, join_array)
241
+ bts, hms, associatives = ::Brick.get_bts_and_hms(self)
242
+ bts.each do |_k, bt|
243
+ next if bt[2] # Polymorphic?
244
+
245
+ # join_array will receive this relation name when calling #brick_parse_dsl
246
+ _br_bt_descrip[bt.first] = if bt[1].is_a?(Array)
247
+ bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations, true) }
248
+ else
249
+ { bt.last => bt[1].brick_parse_dsl(join_array, bt.first, translations) }
250
+ end
251
+ end
252
+ skip_klass_hms = ::Brick.config.skip_index_hms[self.name] || {}
253
+ hms.each do |k, hm|
254
+ next if skip_klass_hms.key?(k)
255
+
256
+ if hm.macro == :has_one
257
+ # For our purposes a :has_one is similar enough to a :belongs_to that we can just join forces
258
+ _br_bt_descrip[k] = { hm.klass => hm.klass.brick_parse_dsl(join_array, k, translations) }
259
+ else # Standard :has_many
260
+ _br_hm_counts[k] = hm
261
+ end
262
+ end
263
+ end
264
+
265
+ def self._brick_calculate_ordering(ordering, is_do_txt = true)
266
+ quoted_table_name = table_name.split('.').map { |x| "\"#{x}\"" }.join('.')
267
+ order_by_txt = [] if is_do_txt
268
+ ordering = [ordering] if ordering && !ordering.is_a?(Array)
269
+ order_by = ordering&.map do |ord_part| # %%% If a term is also used as an eqi-condition in the WHERE clause, it can be omitted from ORDER BY
270
+ case ord_part
271
+ when String
272
+ ord_expr = ord_part.gsub('^^^', quoted_table_name)
273
+ order_by_txt&.<<("Arel.sql(#{ord_expr})")
274
+ Arel.sql(ord_expr)
275
+ else # Expecting only Symbol
276
+ ord_part = "_br_#{ord_part}_ct" if _br_hm_counts.key?(ord_part)
277
+ # Retain any reference to a bt_descrip as being a symbol
278
+ # Was: "#{quoted_table_name}.\"#{ord_part}\""
279
+ order_by_txt&.<<(_br_bt_descrip.key?(ord_part) ? ord_part : ord_part.inspect)
280
+ ord_part
281
+ end
282
+ end
283
+ [order_by, order_by_txt]
284
+ end
285
+
224
286
  private
225
287
 
226
288
  def self._brick_get_fks
@@ -310,16 +372,15 @@ module ActiveRecord
310
372
  end
311
373
  end
312
374
 
313
- def brick_select(params, selects = nil, bt_descrip = {}, hm_counts = {}, join_array = ::Brick::JoinArray.new
314
- # , is_add_bts, is_add_hms
315
- )
316
- is_add_bts = is_add_hms = true
375
+ def brick_select(params, selects = nil, order_by = nil, translations = {}, join_array = ::Brick::JoinArray.new)
317
376
  is_distinct = nil
318
377
  wheres = {}
319
378
  params.each do |k, v|
379
+ next if ['_brick_schema', '_brick_order'].include?(k)
380
+
320
381
  case (ks = k.split('.')).length
321
382
  when 1
322
- next unless klass._brick_get_fks.include?(k)
383
+ next unless klass.column_names.any?(k) || klass._brick_get_fks.include?(k)
323
384
  when 2
324
385
  assoc_name = ks.first.to_sym
325
386
  # Make sure it's a good association name and that the model has that column name
@@ -345,33 +406,6 @@ module ActiveRecord
345
406
  end
346
407
  end
347
408
 
348
- # Search for BT, HM, and HMT DSL stuff
349
- translations = {}
350
- if is_add_bts || is_add_hms
351
- bts, hms, associatives = ::Brick.get_bts_and_hms(klass)
352
- bts.each do |_k, bt|
353
- next if bt[2] # Polymorphic?
354
-
355
- # join_array will receive this relation name when calling #brick_parse_dsl
356
- bt_descrip[bt.first] = if bt[1].is_a?(Array)
357
- bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations, true) }
358
- else
359
- { bt.last => bt[1].brick_parse_dsl(join_array, bt.first, translations) }
360
- end
361
- end
362
- skip_klass_hms = ::Brick.config.skip_index_hms[klass.name] || {}
363
- hms.each do |k, hm|
364
- next if skip_klass_hms.key?(k)
365
-
366
- if hm.macro == :has_one
367
- # For our purposes a :has_one is similar enough to a :belongs_to that we can just join forces
368
- bt_descrip[k] = { hm.klass => hm.klass.brick_parse_dsl(join_array, k, translations) }
369
- else # Standard :has_many
370
- hm_counts[k] = hm
371
- end
372
- end
373
- end
374
-
375
409
  if join_array.present?
376
410
  left_outer_joins!(join_array)
377
411
  # Without working from a duplicate, touching the AREL ast tree sets the @arel instance variable, which causes the relation to be immutable.
@@ -380,7 +414,8 @@ module ActiveRecord
380
414
  chains = rel_dupe._brick_chains
381
415
  id_for_tables = Hash.new { |h, k| h[k] = [] }
382
416
  field_tbl_names = Hash.new { |h, k| h[k] = {} }
383
- bt_columns = bt_descrip.each_with_object([]) do |v, s|
417
+ used_col_aliases = {} # Used to make sure there is not a name clash
418
+ bt_columns = klass._br_bt_descrip.each_with_object([]) do |v, s|
384
419
  v.last.each do |k1, v1| # k1 is class, v1 is array of columns to snag
385
420
  next if chains[k1].nil?
386
421
 
@@ -391,7 +426,12 @@ module ActiveRecord
391
426
 
392
427
  # Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
393
428
  is_xml = is_distinct && Brick.relations[sel_col.first.table_name]&.[](:cols)&.[](sel_col.last)&.first&.start_with?('xml')
394
- selects << "\"#{field_tbl_name}\".\"#{sel_col.last}\"#{'::text' if is_xml} AS \"#{(col_alias = "_brfk_#{v.first}__#{sel_col.last}")}\""
429
+ # If it's not unique then also include the belongs_to association name before the column name
430
+ if used_col_aliases.key?(col_alias = "_brfk_#{v.first}__#{sel_col.last}")
431
+ col_alias = "_brfk_#{v.first}__#{v1[idx][-2..-1].map(&:to_s).join('__')}"
432
+ end
433
+ selects << "\"#{field_tbl_name}\".\"#{sel_col.last}\"#{'::text' if is_xml} AS \"#{col_alias}\""
434
+ used_col_aliases[col_alias] = nil
395
435
  v1[idx] << col_alias
396
436
  end
397
437
 
@@ -416,11 +456,10 @@ module ActiveRecord
416
456
  end
417
457
  end
418
458
  # Add derived table JOIN for the has_many counts
419
- hm_counts.each do |k, hm|
459
+ klass._br_hm_counts.each do |k, hm|
420
460
  associative = nil
421
461
  count_column = if hm.options[:through]
422
- fk_col = (associative = associatives[hm.name])&.foreign_key
423
- hm.foreign_key if fk_col
462
+ hm.foreign_key if (fk_col = (associative = klass._br_associatives&.[](hm.name))&.foreign_key)
424
463
  else
425
464
  fk_col = hm.foreign_key
426
465
  poly_type = hm.inverse_of.foreign_type if hm.options.key?(:as)
@@ -450,6 +489,25 @@ JOIN (SELECT #{selects.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}
450
489
  joins!("#{join_clause} ON #{on_clause.join(' AND ')}")
451
490
  end
452
491
  where!(wheres) unless wheres.empty?
492
+ # Must parse the order_by and see if there are any symbols which refer to BT associations
493
+ # as they must be expanded to find the corresponding _br_model__column naming for each.
494
+ if order_by.present?
495
+ final_order_by = *order_by.each_with_object([]) do |v, s|
496
+ if v.is_a?(Symbol)
497
+ # Add the ordered series of columns derived from the BT based on its DSL
498
+ if (bt_cols = klass._br_bt_descrip[v])
499
+ bt_cols.values.each do |v1|
500
+ v1.each { |v2| s << v2.last if v2.length > 1 }
501
+ end
502
+ else
503
+ s << v
504
+ end
505
+ else # String stuff just comes straight through
506
+ s << v
507
+ end
508
+ end
509
+ order!(*final_order_by)
510
+ end
453
511
  limit!(1000) # Don't want to get too carried away just yet
454
512
  wheres unless wheres.empty? # Return the specific parameters that we did use
455
513
  end
@@ -619,7 +677,7 @@ Module.class_exec do
619
677
  else
620
678
  ActiveSupport::Inflector.pluralize(singular_table_name)
621
679
  end
622
- if ::Brick.config.schema_behavior[:multitenant] && Object.const_defined?('Apartment') &&
680
+ if ::Brick.apartment_multitenant &&
623
681
  Apartment.excluded_models.include?(table_name.singularize.camelize)
624
682
  schema_name = Apartment.default_schema
625
683
  end
@@ -654,7 +712,7 @@ class Object
654
712
  private
655
713
 
656
714
  def build_model(schema_name, inheritable_name, model_name, singular_table_name, table_name, relations, matching)
657
- if ::Brick.config.schema_behavior[:multitenant] && Object.const_defined?('Apartment') &&
715
+ if ::Brick.apartment_multitenant &&
658
716
  schema_name == Apartment.default_schema
659
717
  relation = relations["#{schema_name}.#{matching}"]
660
718
  end
@@ -665,7 +723,7 @@ class Object
665
723
  schema_name
666
724
  else
667
725
  matching = "#{schema_name}.#{matching}"
668
- (Brick.db_schemas[schema_name] ||= self.const_get(schema_name.singularize.camelize))
726
+ (Brick.db_schemas[schema_name] ||= self.const_get(schema_name.camelize))
669
727
  end
670
728
  "#{schema_module&.name}::#{inheritable_name || model_name}"
671
729
  end
@@ -846,7 +904,7 @@ class Object
846
904
  if (inverse = assoc[:inverse])
847
905
  # If it's multitenant with something like: public.____ ...
848
906
  if (it_parts = inverse_table.split('.')).length > 1 &&
849
- ::Brick.config.schema_behavior[:multitenant] && Object.const_defined?('Apartment') &&
907
+ ::Brick.apartment_multitenant &&
850
908
  it_parts.first == Apartment.default_schema
851
909
  it_parts.shift # ... then ditch the generic schema name
852
910
  end
@@ -926,21 +984,26 @@ class Object
926
984
  (namespace || Object).const_set(class_name.to_sym, new_controller_class)
927
985
 
928
986
  # Brick-specific pages
929
- if plural_class_name == 'BrickGem'
987
+ case plural_class_name
988
+ when 'BrickGem'
930
989
  self.define_method :orphans do
931
990
  instance_variable_set(:@orphans, ::Brick.find_orphans(::Brick.set_db_schema(params)))
932
991
  end
933
992
  return [new_controller_class, code + ' # BrickGem controller']
993
+ when 'BrickSwagger'
994
+ is_swagger = true # if request.format == :json)
934
995
  end
935
996
 
936
- unless (is_swagger = plural_class_name == 'BrickSwagger') # && request.format == :json)
937
- code << " def index\n"
938
- code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{pk.inspect})" : '.all'}\n"
939
- code << " @#{table_name}.brick_select(params)\n"
940
- code << " end\n"
941
- end
942
997
  self.protect_from_forgery unless: -> { self.request.format.js? }
943
998
  self.define_method :index do
999
+ # We do all of this now so that bt_descrip and hm_counts are available on the model early in case the user
1000
+ # wants to do an ORDER BY based on any of that
1001
+ translations = {}
1002
+ join_array = ::Brick::JoinArray.new
1003
+ is_add_bts = is_add_hms = true
1004
+ # This builds out bt_descrip and hm_counts on the model
1005
+ model._brick_calculate_bts_hms(translations, join_array) if is_add_bts || is_add_hms
1006
+
944
1007
  if is_swagger
945
1008
  json = { 'openapi': '3.0.1', 'info': { 'title': 'API V1', 'version': 'v1' },
946
1009
  'servers': [
@@ -990,6 +1053,32 @@ class Object
990
1053
  render inline: json.to_json, content_type: request.format
991
1054
  return
992
1055
  end
1056
+
1057
+ # Normal (non-swagger) request
1058
+
1059
+ # %%% Allow params to define which columns to use for order_by
1060
+ ordering = if (order_tbl = ::Brick.config.order[table_name])
1061
+ case (order_default = order_tbl[:_brick_default])
1062
+ when Array
1063
+ order_default.map { |od_part| order_tbl[od_part] || od_part }
1064
+ when Symbol
1065
+ order_tbl[order_default] || order_default
1066
+ else
1067
+ pk
1068
+ end
1069
+ else
1070
+ pk # If it's not a custom ORDER BY, just use the key
1071
+ end
1072
+ order_by, order_by_txt = model._brick_calculate_ordering(ordering)
1073
+ if (order_params = params['_brick_order']&.split(',')&.map(&:to_sym)) # Overriding the default by providing a querystring param?
1074
+ order_by, _ = model._brick_calculate_ordering(order_params, true) # Don't do the txt part
1075
+ end
1076
+
1077
+ code << " def index\n"
1078
+ code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{order_by_txt.join(', ')})" : '.all'}\n"
1079
+ code << " @#{table_name}.brick_select(params)\n"
1080
+ code << " end\n"
1081
+
993
1082
  ::Brick.set_db_schema(params)
994
1083
  if request.format == :csv # Asking for a template?
995
1084
  require 'csv'
@@ -1003,19 +1092,16 @@ class Object
1003
1092
  return
1004
1093
  end
1005
1094
 
1006
- quoted_table_name = model.table_name.split('.').map { |x| "\"#{x}\"" }.join('.')
1007
- order = pk.each_with_object([]) { |pk_part, s| s << "#{quoted_table_name}.\"#{pk_part}\"" }
1008
- ar_relation = order.present? ? model.order("#{order.join(', ')}") : model.all
1009
- @_brick_params = ar_relation.brick_select(params, (selects = []), (bt_descrip = {}), (hm_counts = {}), (join_array = ::Brick::JoinArray.new))
1095
+ @_brick_params = (ar_relation = model.all).brick_select(params, (selects = []), order_by, translations, join_array)
1010
1096
  # %%% Add custom HM count columns
1011
1097
  # %%% What happens when the PK is composite?
1012
- counts = hm_counts.each_with_object([]) { |v, s| s << "_br_#{v.first}._ct_ AS _br_#{v.first}_ct" }
1098
+ counts = model._br_hm_counts.each_with_object([]) { |v, s| s << "_br_#{v.first}._ct_ AS _br_#{v.first}_ct" }
1013
1099
  instance_variable_set("@#{table_name}".to_sym, ar_relation.dup._select!(*selects, *counts))
1014
1100
  if namespace && (idx = lookup_context.prefixes.index(table_name))
1015
1101
  lookup_context.prefixes[idx] = "#{namespace.name.underscore}/#{lookup_context.prefixes[idx]}"
1016
1102
  end
1017
- @_brick_bt_descrip = bt_descrip
1018
- @_brick_hm_counts = hm_counts
1103
+ @_brick_bt_descrip = model._br_bt_descrip
1104
+ @_brick_hm_counts = model._br_hm_counts
1019
1105
  @_brick_join_array = join_array
1020
1106
  end
1021
1107
 
@@ -1142,7 +1228,8 @@ module ActiveRecord::ConnectionHandling
1142
1228
  end
1143
1229
  # Load the initializer for the Apartment gem a little early so that if .excluded_models and
1144
1230
  # .default_schema are specified then we can work with non-tenanted models more appropriately
1145
- if Object.const_defined?('Apartment') && File.exist?(apartment_initializer = Rails.root.join('config/initializers/apartment.rb'))
1231
+ apartment = Object.const_defined?('Apartment')
1232
+ if apartment && File.exist?(apartment_initializer = Rails.root.join('config/initializers/apartment.rb'))
1146
1233
  load apartment_initializer
1147
1234
  apartment_excluded = Apartment.excluded_models
1148
1235
  end
@@ -1345,17 +1432,20 @@ module ActiveRecord::ConnectionHandling
1345
1432
  end
1346
1433
  end
1347
1434
 
1348
- apartment = Object.const_defined?('Apartment') && Apartment
1349
1435
  tables = []
1350
1436
  views = []
1351
1437
  relations.each do |k, v|
1352
1438
  name_parts = k.split('.')
1439
+ idx = 1
1440
+ name_parts = name_parts.map do |x|
1441
+ ((idx += 1) < name_parts.length ? x.singularize : x).camelize
1442
+ end
1353
1443
  if v.key?(:isView)
1354
1444
  views
1355
1445
  else
1356
1446
  name_parts.shift if apartment && name_parts.length > 1 && name_parts.first == Apartment.default_schema
1357
1447
  tables
1358
- end << name_parts.map { |x| x.singularize.camelize }.join('::')
1448
+ end << name_parts.join('::')
1359
1449
  end
1360
1450
  unless tables.empty?
1361
1451
  puts "\nClasses that can be built from tables:"
@@ -1437,7 +1527,7 @@ module Brick
1437
1527
  unless (cnstr_name = fk[5])
1438
1528
  # For any appended references (those that come from config), arrive upon a definitely unique constraint name
1439
1529
  pri_tbl = is_class ? fk[4][:class].underscore : pri_tbl
1440
- pri_tbl = "#{bt_assoc_name}_#{pri_tbl}" if pri_tbl.singularize != bt_assoc_name
1530
+ pri_tbl = "#{bt_assoc_name}_#{pri_tbl}" if pri_tbl&.singularize != bt_assoc_name
1441
1531
  cnstr_base = cnstr_name = "(brick) #{for_tbl}_#{pri_tbl}"
1442
1532
  cnstr_added_num = 1
1443
1533
  cnstr_name = "#{cnstr_base}_#{cnstr_added_num += 1}" while bts&.key?(cnstr_name) || hms&.key?(cnstr_name)
@@ -1463,6 +1553,8 @@ module Brick
1463
1553
  return
1464
1554
  end
1465
1555
  end
1556
+ return unless bts # Rails 5.0 and older can have bts end up being nil
1557
+
1466
1558
  if (assoc_bt = bts[cnstr_name])
1467
1559
  if is_polymorphic
1468
1560
  # Assuming same fk (don't yet support composite keys for polymorphics)
@@ -33,6 +33,9 @@ module Brick
33
33
  # Additional references (virtual foreign keys)
34
34
  ::Brick.additional_references = app.config.brick.fetch(:additional_references, nil)
35
35
 
36
+ # When table names have specific prefixes, automatically place them in their own module with a table_name_prefix.
37
+ ::Brick.order = app.config.brick.fetch(:order, {})
38
+
36
39
  # Skip creating a has_many association for these
37
40
  ::Brick.exclude_hms = app.config.brick.fetch(:exclude_hms, nil)
38
41
 
@@ -101,7 +104,7 @@ module Brick
101
104
  path_obj_name = model_name.underscore.tr('/', '_')
102
105
  table_name = obj_name.pluralize
103
106
  template_link = nil
104
- bts, hms, associatives = ::Brick.get_bts_and_hms(@_brick_model) # This gets BT and HM and also has_many :through (HMT)
107
+ bts, hms = ::Brick.get_bts_and_hms(@_brick_model) # This gets BT and HM and also has_many :through (HMT)
105
108
  hms_columns = [] # Used for 'index'
106
109
  skip_klass_hms = ::Brick.config.skip_index_hms[model_name] || {}
107
110
  hms_headers = hms.each_with_object([]) do |hm, s|
@@ -109,7 +112,7 @@ module Brick
109
112
  "H#{hm_assoc.macro == :has_one ? 'O' : 'M'}#{'T' if hm_assoc.options[:through]}",
110
113
  (assoc_name = hm.first)]
111
114
  hm_fk_name = if hm_assoc.options[:through]
112
- associative = associatives[hm.first]
115
+ associative = @_brick_model._br_associatives[hm.first]
113
116
  tbl_nm = if hm_assoc.options[:source]
114
117
  associative.klass.reflect_on_association(hm_assoc.options[:source]).inverse_of&.name
115
118
  else
@@ -162,13 +165,13 @@ module Brick
162
165
  schema_options = ::Brick.db_schemas.keys.each_with_object(+'') { |v, s| s << "<option value=\"#{v}\">#{v}</option>" }.html_safe
163
166
  # %%% If we are not auto-creating controllers (or routes) then omit by default, and if enabled anyway, such as in a development
164
167
  # environment or whatever, then get either the controllers or routes list instead
165
- apartment_default_schema = ::Brick.config.schema_behavior[:multitenant] && Object.const_defined?('Apartment') && Apartment.default_schema
166
- table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables).map do |tbl|
168
+ apartment_default_schema = ::Brick.apartment_multitenant && Apartment.default_schema
169
+ table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables).each_with_object({}) do |tbl, s|
167
170
  if (tbl_parts = tbl.split('.')).first == apartment_default_schema
168
171
  tbl = tbl_parts.last
169
172
  end
170
- tbl
171
- end.sort.each_with_object(+'') do |v, s|
173
+ s[tbl] = nil
174
+ end.keys.sort.each_with_object(+'') do |v, s|
172
175
  s << "<option value=\"#{v.underscore.gsub('.', '/').pluralize}\">#{v}</option>"
173
176
  end.html_safe
174
177
  table_options << '<option value="brick_orphans">(Orphans)</option>'.html_safe if is_orphans
@@ -199,6 +202,9 @@ table thead tr th, table tr th {
199
202
  color: #fff;
200
203
  text-align: left;
201
204
  }
205
+ #headerTop th:hover, #headerTop th:hover {
206
+ background-color: #18B090;
207
+ }
202
208
  table thead tr th a, table tr th a {
203
209
  color: #80FFB8;
204
210
  }
@@ -537,7 +543,7 @@ if (headerTop) {
537
543
  k, id = @_brick_params.first
538
544
  id = id.first if id.is_a?(Array) && id.length == 1
539
545
  origin = (key_parts = k.split('.')).length == 1 ? #{model_name} : #{model_name}.reflect_on_association(key_parts.first).klass
540
- if (destination_fk = Brick.relations[origin.table_name][:fks].values.find { |fk| puts fk.inspect; fk[:fk] == key_parts.last }) &&
546
+ if (destination_fk = Brick.relations[origin.table_name][:fks].values.find { |fk| fk[:fk] == key_parts.last }) &&
541
547
  (obj = (destination = origin.reflect_on_association(destination_fk[:assoc_name])&.klass)&.find(id)) %>
542
548
  <h3>for <%= link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination.name.underscore.tr('/', '_')\}_path\".to_sym, id) %></h3><%
543
549
  end
@@ -547,30 +553,33 @@ if (headerTop) {
547
553
  <br>
548
554
  <table id=\"headerTop\">
549
555
  <table id=\"#{table_name}\">
550
- <thead><tr>#{'<th></th>' if pk.present?}<%
556
+ <thead><tr>#{"<th x-order=\"#{pk.join(',')}\"></th>" if pk.present?}<%
551
557
  col_order = []
552
558
  @#{table_name}.columns.each do |col|
553
559
  next if (#{(pk || []).inspect}.include?(col_name = col.name) && col.type == :integer && !bts.key?(col_name)) ||
554
560
  ::Brick.config.metadata_columns.include?(col_name) || poly_cols.include?(col_name)
555
561
 
556
562
  col_order << col_name
557
- %><th<%= \" title = \\\"#\{col.comment}\\\"\".html_safe if col.respond_to?(:comment) && !col.comment.blank? %>><%
563
+ %><th<%= \" title=\\\"#\{col.comment}\\\"\".html_safe if col.respond_to?(:comment) && !col.comment.blank? %><%
558
564
  if (bt = bts[col_name]) %>
559
- BT <%
565
+ <%= \" x-order=\\\"#\{bt.first}\\\"\".html_safe unless bt[2] # Allow sorting any BT except polymorphics
566
+ %>>BT <%
560
567
  bt[1].each do |bt_pair| %><%=
561
568
  bt_pair.first.bt_link(bt.first) %> <%
562
569
  end %><%
563
- else %><%=
564
- col_name %><%
570
+ else %><%= \" x-order=\\\"#\{col_name}\\\"\".html_safe if true # Currently we always allow click to sort
571
+ %>><%= col_name %><%
565
572
  end
566
573
  %></th><%
567
574
  end
568
575
  # Consider getting the name from the association -- h.first.name -- if a more \"friendly\" alias should be used for a screwy table name
569
576
  %>#{hms_headers.map do |h|
577
+ # Currently we always allow click to sort
578
+ "<th#{" x-order=\"#{h.first.name}\"" if true}>" +
570
579
  if h.first.options[:through] && !h.first.through_reflection
571
- "<th>#{h[1]} #{h[2]} %></th>" # %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
580
+ "#{h[1]} #{h[2]} %></th>" # %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
572
581
  else
573
- "<th>#{h[1]} <%= link_to('#{h[2]}', #{h.first.klass.name.underscore.tr('/', '_').pluralize}_path) %></th>"
582
+ "#{h[1]} <%= link_to('#{h[2]}', #{h.first.klass.name.underscore.tr('/', '_').pluralize}_path) %></th>"
574
583
  end
575
584
  end.join
576
585
  }</tr></thead>
@@ -658,7 +667,7 @@ end
658
667
  <tr>
659
668
  <% next if (#{(pk || []).inspect}.include?(k) && !bts.key?(k)) ||
660
669
  ::Brick.config.metadata_columns.include?(k) %>
661
- <th class=\"show-field\"<%= \" title = \\\"#\{col.comment}\\\"\".html_safe if col.respond_to?(:comment) && !col.comment.blank? %>>
670
+ <th class=\"show-field\"<%= \" title=\\\"#\{col.comment}\\\"\".html_safe if col.respond_to?(:comment) && !col.comment.blank? %>>
662
671
  <% has_fields = true
663
672
  if (bt = bts[k])
664
673
  # Add a final member in this array with descriptive options to be used in <select> drop-downs
@@ -729,9 +738,14 @@ end
729
738
  <% when :uuid %>
730
739
  <%=
731
740
  # Postgres naturally uses the +uuid_generate_v4()+ function from the uuid-ossp extension
732
- # If it's not yet enabled then: enable_extension 'uuid-ossp'
741
+ # If it's not yet enabled then: create extension \"uuid-ossp\";
733
742
  # ActiveUUID gem created a new :uuid type
734
743
  val %>
744
+ <% when :ltree %>
745
+ <%=
746
+ # In Postgres labels of data stored in a hierarchical tree-like structure
747
+ # If it's not yet enabled then: create extension ltree;
748
+ val %>
735
749
  <% when :binary, :primary_key %>
736
750
  <% end %>
737
751
  <% end %>
@@ -789,6 +803,15 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
789
803
  </script>
790
804
  <% end %>
791
805
  <script>
806
+ <% # Make column headers sort when clicked
807
+ # %%% Create a smart javascript routine which can do this client-side %>
808
+ [... document.getElementsByTagName(\"TH\")].forEach(function (th) {
809
+ th.addEventListener(\"click\", function (e) {
810
+ var xOrder;
811
+ if (xOrder = this.getAttribute(\"x-order\"))
812
+ location.href = changeout(location.href, \"_brick_order\", xOrder);
813
+ });
814
+ });
792
815
  document.querySelectorAll(\"input, select\").forEach(function (inp) {
793
816
  var origVal = getInpVal(),
794
817
  prevVal = origVal;
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 47
8
+ TINY = 50
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
@@ -47,7 +47,6 @@ end
47
47
 
48
48
  # Add left_outer_join! to Associations::JoinDependency and Relation::QueryMethods
49
49
  if ActiveRecord.version < ::Gem::Version.new('5')
50
- is_add_left_outer_join = true
51
50
  ::Brick::Util._patch_require(
52
51
  'active_record/associations/join_dependency.rb', '/activerecord', # /associations
53
52
  ["def join_constraints(outer_joins)
@@ -140,6 +139,13 @@ module Brick
140
139
  (@relations ||= {})[ActiveRecord::Base.connection_pool.object_id] ||= Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = {} } }
141
140
  end
142
141
 
142
+ def apartment_multitenant
143
+ if @apartment_multitenant.nil?
144
+ @apartment_multitenant = ::Brick.config.schema_behavior[:multitenant] && Object.const_defined?('Apartment')
145
+ end
146
+ @apartment_multitenant
147
+ end
148
+
143
149
  # If multitenancy is enabled, a list of non-tenanted "global" models
144
150
  def non_tenanted_models
145
151
  @pending_models ||= {}
@@ -166,14 +172,14 @@ module Brick
166
172
  # Mark has_manys that go to an associative ("join") table so that they are skipped in the UI,
167
173
  # as well as any possible polymorphic associations
168
174
  skip_hms = {}
169
- associatives = hms.each_with_object({}) do |hmt, s|
175
+ hms.each do |hmt|
170
176
  if (through = hmt.last.options[:through])
171
177
  skip_hms[through] = nil # if hms[through]
172
178
  # binding.pry if !hms[through]
173
179
  # End up with a hash of HMT names pointing to join-table associations
174
180
  # Last part was: hmt.last.name
175
181
  # Changed up because looking for: hms[:issue_issue_duplicates]
176
- s[hmt.first] = hms[through] # || hms["#{(opt = hmt.last.options)[:through].to_s.singularize}_#{opt[:source].to_s.pluralize}".to_sym]
182
+ model._br_associatives[hmt.first] = hms[through] # || hms["#{(opt = hmt.last.options)[:through].to_s.singularize}_#{opt[:source].to_s.pluralize}".to_sym]
177
183
  elsif hmt.last.inverse_of.nil?
178
184
  puts "SKIPPING #{hmt.last.name.inspect}"
179
185
  # %%% If we don't do this then below associative.name will find that associative is nil
@@ -181,7 +187,7 @@ module Brick
181
187
  end
182
188
  end
183
189
  skip_hms.each { |k, _v| hms.delete(k) }
184
- [bts, hms, associatives]
190
+ [bts, hms]
185
191
  end
186
192
 
187
193
  # Switches Brick auto-models on or off, for all threads
@@ -277,6 +283,11 @@ module Brick
277
283
  end
278
284
  end
279
285
 
286
+ # @api public
287
+ def order=(value)
288
+ Brick.config.order = value
289
+ end
290
+
280
291
  # Skip creating a has_many association for these
281
292
  # (Uses the same exact three-part format as would define an additional_reference)
282
293
  # @api public
@@ -451,7 +462,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
451
462
  ::Brick.relations.each do |rel_name, v|
452
463
  rel_name = rel_name.split('.').map(&:underscore)
453
464
  schema_names = rel_name[0..-2]
454
- schema_names.shift if ::Brick.config.schema_behavior[:multitenant] && Object.const_defined?('Apartment') && schema_names.first == Apartment.default_schema
465
+ schema_names.shift if ::Brick.apartment_multitenant && schema_names.first == Apartment.default_schema
455
466
  k = rel_name.last
456
467
  unless existing_controllers.key?(controller_name = k.pluralize)
457
468
  options = {}
@@ -481,7 +492,8 @@ require 'brick/version_number'
481
492
  # Older versions of ActiveRecord would only show more serious error information from "panic" level, which is
482
493
  # a level only available in Postgres 12 and older. This patch will allow older and newer versions of Postgres
483
494
  # to work along with fairly old versions of Rails.
484
- if Object.const_defined?('PG::VERSION') && ActiveRecord.version < ::Gem::Version.new('4.2.6')
495
+ if (is_postgres = (Object.const_defined?('PG::VERSION') || Gem::Specification.find_all_by_name('pg').present?)) &&
496
+ ActiveRecord.version < ::Gem::Version.new('4.2.6')
485
497
  ::Brick::Util._patch_require(
486
498
  'active_record/connection_adapters/postgresql_adapter.rb', '/activerecord', ["'panic'", "'error'"]
487
499
  )
@@ -489,13 +501,28 @@ end
489
501
 
490
502
  require 'active_record'
491
503
  require 'active_record/relation'
492
- require 'active_record/relation/query_methods' if is_add_left_outer_join
504
+ # To support adding left_outer_join
505
+ require 'active_record/relation/query_methods' if ActiveRecord.version < ::Gem::Version.new('5')
506
+ require 'rails/railtie' if ActiveRecord.version < ::Gem::Version.new('4.2')
493
507
 
494
508
  # Rake tasks
495
509
  class Railtie < Rails::Railtie
496
510
  Dir.glob("#{File.expand_path(__dir__)}/brick/tasks/**/*.rake").each { |task| load task }
497
511
  end
498
512
 
513
+ # Rails < 4.2 does not have env
514
+ module Rails
515
+ unless respond_to?(:env)
516
+ def self.env
517
+ @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development")
518
+ end
519
+
520
+ def self.env=(environment)
521
+ @_env = ActiveSupport::StringInquirer.new(environment)
522
+ end
523
+ end
524
+ end
525
+
499
526
  # Major compatibility fixes for ActiveRecord < 4.2
500
527
  # ================================================
501
528
  ActiveSupport.on_load(:active_record) do
@@ -510,6 +537,15 @@ ActiveSupport.on_load(:active_record) do
510
537
  end
511
538
  end
512
539
  end
540
+ # ActiveRecord < 4.2 does not have default_timezone
541
+ # :singleton-method:
542
+ # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
543
+ # dates and times from the database. This is set to :utc by default.
544
+ unless respond_to?(:default_timezone)
545
+ puts "ADDING!!! 4.w"
546
+ mattr_accessor :default_timezone, instance_writer: false
547
+ self.default_timezone = :utc
548
+ end
513
549
  end
514
550
 
515
551
  # Rails < 4.0 cannot do #find_by, #find_or_create_by, or do #pluck on multiple columns, so here are the patches:
@@ -715,58 +751,60 @@ ActiveSupport.on_load(:active_record) do
715
751
  end
716
752
  end
717
753
 
718
- if is_add_left_outer_join
719
- # Final pieces for left_outer_joins support, which was derived from this commit:
720
- # https://github.com/rails/rails/commit/3f46ef1ddab87482b730a3f53987e04308783d8b
721
- module Associations
722
- class JoinDependency
723
- def make_left_outer_joins(parent, child)
724
- tables = child.tables
725
- join_type = Arel::Nodes::OuterJoin
726
- info = make_constraints parent, child, tables, join_type
727
-
728
- [info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
729
- end
754
+ # Final pieces for left_outer_joins support, which was derived from this commit:
755
+ # https://github.com/rails/rails/commit/3f46ef1ddab87482b730a3f53987e04308783d8b
756
+ module Associations
757
+ class JoinDependency
758
+ def make_left_outer_joins(parent, child)
759
+ tables = child.tables
760
+ join_type = Arel::Nodes::OuterJoin
761
+ info = make_constraints parent, child, tables, join_type
762
+
763
+ [info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
730
764
  end
731
765
  end
732
- module Querying
733
- delegate :left_outer_joins, to: :all
766
+ end
767
+ module Querying
768
+ delegate :left_outer_joins, to: :all
769
+ end
770
+ class Relation
771
+ unless MULTI_VALUE_METHODS.include?(:left_outer_joins)
772
+ _multi_value_methods = MULTI_VALUE_METHODS + [:left_outer_joins]
773
+ send(:remove_const, :MULTI_VALUE_METHODS)
774
+ MULTI_VALUE_METHODS = _multi_value_methods
734
775
  end
735
- class Relation
736
- MULTI_VALUE_METHODS = MULTI_VALUE_METHODS + [:left_outer_joins] unless MULTI_VALUE_METHODS.include?(:left_outer_joins)
776
+ end
777
+ module QueryMethods
778
+ attr_writer :left_outer_joins_values
779
+ def left_outer_joins_values
780
+ @left_outer_joins_values ||= []
737
781
  end
738
- module QueryMethods
739
- attr_writer :left_outer_joins_values
740
- def left_outer_joins_values
741
- @left_outer_joins_values ||= []
742
- end
743
782
 
744
- def left_outer_joins(*args)
745
- check_if_method_has_arguments!(:left_outer_joins, args)
783
+ def left_outer_joins(*args)
784
+ check_if_method_has_arguments!(:left_outer_joins, args)
746
785
 
747
- args.compact!
748
- args.flatten!
786
+ args.compact!
787
+ args.flatten!
749
788
 
750
- spawn.left_outer_joins!(*args)
751
- end
789
+ spawn.left_outer_joins!(*args)
790
+ end
752
791
 
753
- def left_outer_joins!(*args) # :nodoc:
754
- self.left_outer_joins_values += args
755
- self
756
- end
792
+ def left_outer_joins!(*args) # :nodoc:
793
+ self.left_outer_joins_values += args
794
+ self
795
+ end
757
796
 
758
- def build_left_outer_joins(manager, outer_joins)
759
- buckets = outer_joins.group_by do |join|
760
- case join
761
- when Hash, Symbol, Array
762
- :association_join
763
- else
764
- raise ArgumentError, 'only Hash, Symbol and Array are allowed'
765
- end
797
+ def build_left_outer_joins(manager, outer_joins)
798
+ buckets = outer_joins.group_by do |join|
799
+ case join
800
+ when Hash, Symbol, Array
801
+ :association_join
802
+ else
803
+ raise ArgumentError, 'only Hash, Symbol and Array are allowed'
766
804
  end
767
-
768
- build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
769
805
  end
806
+
807
+ build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
770
808
  end
771
809
  end
772
810
  # (End of left_outer_joins support)
@@ -788,7 +826,8 @@ ActiveSupport.on_load(:active_record) do
788
826
  end
789
827
 
790
828
  # Do this earlier because stuff here gets mixed into JoinDependency::JoinAssociation and AssociationScope
791
- if ActiveRecord.version < ::Gem::Version.new('5.0') && Object.const_defined?('PG::Connection')
829
+ if is_postgres && ActiveRecord.version < ::Gem::Version.new('5.0') # Was: && Object.const_defined?('PG::Connection')
830
+ require 'pg' # For ActiveRecord < 4.2
792
831
  # Avoid pg gem deprecation warning: "You should use PG::Connection, PG::Result, and PG::Error instead"
793
832
  PGconn = PG::Connection
794
833
  PGresult = PG::Result
@@ -159,6 +159,41 @@ module Brick
159
159
  # # When table names have specific prefixes automatically place them in their own module with a table_name_prefix.
160
160
  # Brick.table_name_prefixes = { 'nav_' => 'Navigation' }
161
161
 
162
+ # # COLUMN SEQUENCING AND INCLUSION / EXCLUSION
163
+
164
+ # # By default if there is a primary key present then rows in an index view are ordered by this primary key. To
165
+ # # use a different rule for doing ORDER BY, you can override this default ordering done by The Brick, for instance
166
+ # # to have the rows in a contact list sorted by email:
167
+ # Brick.order = { 'contacts' => { _brick_default: :email } }
168
+ # # or by last name then first name:
169
+ # Brick.order = { 'contacts' => { _brick_default: [:lastname, :firstname] } }
170
+ # # Totally legitimate to have the default order be the name of a belongs_to or has_many association instead of an
171
+ # # actual column name, in which case for has_many it just orders by the count of how many records are associated,
172
+ # # and for belongs_to it's based on the primary table's DSL if any is defined (since that is what is used to
173
+ # # calculate what is shown when a foreign table lists out related records). If contacts relates to addresses,
174
+ # # then this is perfectly fine:
175
+ # Brick.order = { 'contacts' => { _brick_default: :address } }
176
+ # # You can even have a specific custom clause used in the ORDER BY. In this case it is recommended to include a
177
+ # # special placeholder for the table name with the sequence \"^^^\". Here is an example of having the default
178
+ # # ordering happening on the \"code\" column, and also defining custom sorting to be done, in this case proper
179
+ # # ordering if that code is stored as a dotted numeric value:
180
+ # Brick.order = { 'document_trees' => { _brick_default: :code,
181
+ # code: \"ORDER BY STRING_TO_ARRAY(^^^.code, '.')::int[]\" } }
182
+
183
+ # # Sequence of columns for each model. This also allows you to add read-only calculated columns in the same
184
+ # # kind of way that they can be added in the include: portion of include/exclude columns, below.
185
+ # # Designated by { <table name> => [<column name>, <column name>] }
186
+ # Brick.column_sequence = { 'users' => ['email', 'profile.firstname', 'profile.lastname'] }
187
+
188
+ # # Specific columns to include or exclude for each model. If there are only inclusions then only those
189
+ # # columns show. If there are any exclusions then all non-excluded columns are attempted to be shown,
190
+ # # which negates the usefulness of inclusions except to add calculated column detail built from DSL.
191
+ # # Designated by <table name>.<column name>
192
+ # Brick.column_sequence = { 'users' => { include: ['email', 'profile.firstname', 'profile.lastname'] },
193
+ # 'profile' => { exclude: ['birthdate'] } }
194
+
195
+ # # EXTRA FOREIGN KEYS AND OTHER HAS_MANY SETTINGS
196
+
162
197
  # # Additional table references which are used to create has_many / belongs_to associations inside auto-created
163
198
  # # models. (You can consider these to be \"virtual foreign keys\" if you wish)... You only have to add these
164
199
  # # in cases where your database for some reason does not have foreign key constraints defined. Sometimes for
@@ -176,8 +211,9 @@ module Brick
176
211
  # Brick.exclude_hms = [['users', 'favourite_colour_id', 'colours']]
177
212
 
178
213
  # # Skip showing counts for these specific has_many associations when building auto-generated #index views.
179
- # # When there are related tables with a significant number of records, this can lessen the load on the database
180
- # # considerably, sometimes fixing what might appear to be an index page that just \"hangs\" for no apparent reason.
214
+ # # When there are related tables with a significant number of records (generally 100,000 or more), this can lessen
215
+ # # the load on the database considerably, sometimes fixing what might appear to be an index page that just \"hangs\"
216
+ # # for no apparent reason.
181
217
  # Brick.skip_index_hms = ['User.litany_of_woes']
182
218
 
183
219
  # # By default primary tables involved in a foreign key relationship will indicate a \"has_many\" relationship pointing
@@ -199,6 +235,8 @@ module Brick
199
235
  # # Designated by <table name>.<column name>
200
236
  # Brick.not_nullables = ['users.name']
201
237
 
238
+ # # FRIENDLY DSL
239
+
202
240
  # # A simple DSL is available to allow more user-friendly display of objects. Normally a user object might be shown
203
241
  # # as its first non-metadata column, or if that is not available then something like \"User #45\" where 45 is that
204
242
  # # object's ID. If there is no primary key then even that is not possible, so the object's .to_s method is called.
@@ -206,6 +244,8 @@ module Brick
206
244
  # # user, then you can use model_descrips like this, putting expressions with property references in square brackets:
207
245
  # Brick.model_descrips = { 'User' => '[profile.firstname] [profile.lastname]' }
208
246
 
247
+ # # SINGLE TABLE INHERITANCE
248
+
209
249
  # # Specify STI subclasses either directly by name or as a general module prefix that should always relate to a specific
210
250
  # # parent STI class. The prefixed :: here for these examples is mandatory. Also having a suffixed :: means instead of
211
251
  # # a class reference, this is for a general namespace reference. So in this case requests for, say, either of the
@@ -221,6 +261,8 @@ module Brick
221
261
  # Brick.sti_type_column = 'sti_type'
222
262
  # Brick.sti_type_column = { 'rails_type' => ['sales.specialoffer'] }
223
263
 
264
+ # # POLYMORPHIC ASSOCIATIONS
265
+
224
266
  # # Database schema to use when analysing existing data, such as deriving a list of polymorphic classes in the case that
225
267
  # # it wasn't originally specified.
226
268
  # Brick.schema_behavior = :namespaced
@@ -231,6 +273,8 @@ module Brick
231
273
 
232
274
  # # Polymorphic associations are set up by providing a model name and polymorphic association name#{poly}
233
275
 
276
+ # # DEFAULT ROOT ROUTE
277
+
234
278
  # # If a default route is not supplied, Brick attempts to find the most \"central\" table and wires up the default
235
279
  # # route to go to the :index action for what would be a controller for that table. You can specify any controller
236
280
  # # name and action you wish in order to override this and have that be the default route when none other has been
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.47
4
+ version: 1.0.50
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-07-17 00:00:00.000000000 Z
11
+ date: 2022-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -17,9 +17,6 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '4.2'
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '7.2'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,9 +24,6 @@ dependencies:
27
24
  - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: '4.2'
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '7.2'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: fancy_gets
35
29
  requirement: !ruby/object:Gem::Requirement