brick 1.0.40 → 1.0.41

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: cf29d4af64b65c01ae68df45762b816403a1ebc0035e1221552f249ff3ba2ce9
4
- data.tar.gz: 076cdb672d7c2137cc74421467d72491e9ea89751fab32015f427158fc367b45
3
+ metadata.gz: 4455ebfde80fe8a4bd51f19438171abae2614e0740adba98ab39184952843a01
4
+ data.tar.gz: d5157748c37564156fc2f823cc3c4753b536491aa2cf81175683c8241fb2a2b9
5
5
  SHA512:
6
- metadata.gz: ce17038567ddd9d6d9d9dce6eb71fd50612d167b56351898ef9b3f9441d1a7ef3ee4ec3a57f3f24e613829e6c8a2f59517fc0d4e9266f11166b92821310b0c06
7
- data.tar.gz: 6a7bb38b009853594a8bf1ab28489bbc92d6300fcd0f8497c7950e81c6a62a4c6da2bd65840f93bd7afea40153727cb93914529120ecf9b0dceca39b21fef880
6
+ metadata.gz: 2a649c78c3e597004b36d6c33f1cd812a712fcfd561dfe94994de28e489de81508f9fea30b0896b4447c4ce0ffd0877fb8c5d7e20a34011212fd797a3cbd02ca
7
+ data.tar.gz: 58ecdc4acb12abb77169a8fda8cb8146fceaedeacdf77f1cec992008f98b05412e3a9e2f5b6a9721880caa710ba94792a187d3587248eea4d9946d309c91be56
data/lib/brick/config.rb CHANGED
@@ -205,5 +205,10 @@ module Brick
205
205
  def not_nullables=(columns)
206
206
  @mutex.synchronize { @not_nullables = columns }
207
207
  end
208
+
209
+ # Add a special page to show references to non-existent records ("orphans")
210
+ def add_orphans
211
+ true
212
+ end
208
213
  end
209
214
  end
@@ -415,14 +415,16 @@ module ActiveRecord
415
415
  hm_counts.each do |k, hm|
416
416
  associative = nil
417
417
  count_column = if hm.options[:through]
418
- fk_col = (associative = associatives[hm.name]).foreign_key
419
- hm.foreign_key
418
+ fk_col = (associative = associatives[hm.name])&.foreign_key
419
+ hm.foreign_key if fk_col
420
420
  else
421
421
  fk_col = hm.foreign_key
422
422
  poly_type = hm.inverse_of.foreign_type if hm.options.key?(:as)
423
423
  pk = hm.klass.primary_key
424
424
  (pk.is_a?(Array) ? pk.first : pk) || '*'
425
425
  end
426
+ next unless count_column # %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
427
+
426
428
  tbl_alias = "_br_#{hm.name}"
427
429
  pri_tbl = hm.active_record
428
430
  on_clause = []
@@ -565,7 +567,9 @@ Module.class_exec do
565
567
  full_class_name = +''
566
568
  full_class_name << "::#{self.name}" unless self == Object
567
569
  full_class_name << "::#{plural_class_name.underscore.singularize.camelize}"
568
- if (plural_class_name == 'BrickSwagger' || model = self.const_get(full_class_name))
570
+ if (plural_class_name == 'BrickSwagger' ||
571
+ (::Brick.config.add_orphans && plural_class_name == 'BrickGem') ||
572
+ model = self.const_get(full_class_name))
569
573
  # if it's a controller and no match or a model doesn't really use the same table name, eager load all models and try to find a model class of the right name.
570
574
  Object.send(:build_controller, self, class_name, plural_class_name, model, relations)
571
575
  end
@@ -897,6 +901,16 @@ class Object
897
901
  built_controller = Class.new(ActionController::Base) do |new_controller_class|
898
902
  (namespace || Object).const_set(class_name.to_sym, new_controller_class)
899
903
 
904
+ # Brick-specific pages
905
+ if plural_class_name == 'BrickGem'
906
+ self.define_method :orphans do
907
+ instance_variable_set(:@orphans, ::Brick.find_orphans(::Brick.set_db_schema(params)))
908
+ puts "BrickGemController #{action_name} #{params.inspect}"
909
+ # render inline: 'Brick gem!'
910
+ end
911
+ return [new_controller_class, code + ' # BrickGem controller!']
912
+ end
913
+
900
914
  unless (is_swagger = plural_class_name == 'BrickSwagger') # && request.format == :json)
901
915
  code << " def index\n"
902
916
  code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{pk.inspect})" : '.all'}\n"
@@ -1475,5 +1489,72 @@ module Brick
1475
1489
  end
1476
1490
  assoc_bt[:inverse] = assoc_hm
1477
1491
  end
1492
+
1493
+ # Locate orphaned records
1494
+ def find_orphans(multi_schema)
1495
+ is_default_schema = multi_schema&.==(Apartment.default_schema)
1496
+ relations.each_with_object([]) do |v, s|
1497
+ frn_tbl = v.first
1498
+ next if (relation = v.last).key?(:isView) || config.exclude_tables.include?(frn_tbl) ||
1499
+ !(for_pk = (relation[:pkey].values.first&.first))
1500
+
1501
+ is_default_frn_schema = !is_default_schema && multi_schema &&
1502
+ ((frn_parts = frn_tbl.split('.')).length > 1 && frn_parts.first)&.==(Apartment.default_schema)
1503
+ relation[:fks].select { |_k, assoc| assoc[:is_bt] }.each do |_k, bt|
1504
+ begin
1505
+ if bt.key?(:polymorphic)
1506
+ pri_pk = for_pk
1507
+ pri_tables = Brick.config.polymorphics["#{frn_tbl}.#{bt[:fk]}"]
1508
+ .each_with_object(Hash.new { |h, k| h[k] = [] }) do |pri_class, s|
1509
+ s[Object.const_get(pri_class).table_name] << pri_class
1510
+ end
1511
+ fk_id_col = "#{bt[:fk]}_id"
1512
+ fk_type_col = "#{bt[:fk]}_type"
1513
+ selects = []
1514
+ pri_tables.each do |pri_tbl, pri_types|
1515
+ # Skip if database is multitenant, we're not focused on "public", and the foreign and primary tables
1516
+ # are both in the "public" schema
1517
+ next if is_default_frn_schema &&
1518
+ ((pri_parts = pri_tbl&.split('.'))&.length > 1 && pri_parts.first)&.==(Apartment.default_schema)
1519
+
1520
+ selects << "SELECT '#{pri_tbl}' AS pri_tbl, frn.#{fk_type_col} AS pri_type, frn.#{fk_id_col} AS pri_id, frn.#{for_pk} AS frn_id
1521
+ FROM #{frn_tbl} AS frn
1522
+ LEFT OUTER JOIN #{pri_tbl} AS pri ON pri.#{pri_pk} = frn.#{fk_id_col}
1523
+ WHERE frn.#{fk_type_col} IN (#{
1524
+ pri_types.map { |pri_type| "'#{pri_type}'" }.join(', ')
1525
+ }) AND frn.#{bt[:fk]}_id IS NOT NULL AND pri.#{pri_pk} IS NULL\n"
1526
+ end
1527
+ ActiveRecord::Base.execute_sql(selects.join("UNION ALL\n")).each do |o|
1528
+ entry = [frn_tbl, o['frn_id'], o['pri_type'], o['pri_id'], fk_id_col]
1529
+ entry << o['pri_tbl'] if (pri_class = Object.const_get(o['pri_type'])) != pri_class.base_class
1530
+ s << entry
1531
+ end
1532
+ else
1533
+ # Skip if database is multitenant, we're not focused on "public", and the foreign and primary tables
1534
+ # are both in the "public" schema
1535
+ pri_tbl = bt.key?(:inverse_table) && bt[:inverse_table]
1536
+ next if is_default_frn_schema &&
1537
+ ((pri_parts = pri_tbl&.split('.'))&.length > 1 && pri_parts.first)&.==(Apartment.default_schema)
1538
+
1539
+ pri_pk = relations[pri_tbl].fetch(:pkey, nil)&.values&.first&.first ||
1540
+ _class_pk(pri_tbl, multi_schema)
1541
+ ActiveRecord::Base.execute_sql(
1542
+ "SELECT frn.#{bt[:fk]} AS pri_id, frn.#{for_pk} AS frn_id
1543
+ FROM #{frn_tbl} AS frn
1544
+ LEFT OUTER JOIN #{pri_tbl} AS pri ON pri.#{pri_pk} = frn.#{bt[:fk]}
1545
+ WHERE frn.#{bt[:fk]} IS NOT NULL AND pri.#{pri_pk} IS NULL
1546
+ ORDER BY 1, 2"
1547
+ ).each { |o| s << [frn_tbl, o['frn_id'], pri_tbl, o['pri_id'], bt[:fk]] }
1548
+ end
1549
+ rescue StandardError => err
1550
+ puts "Strange -- #{err.inspect}"
1551
+ end
1552
+ end
1553
+ end
1554
+ end
1555
+
1556
+ def _class_pk(dotted_name, multitenant)
1557
+ Object.const_get((multitenant ? [dotted_name.split('.').last] : dotted_name.split('.')).map { |nm| "::#{nm.singularize.camelize}" }.join).primary_key
1558
+ end
1478
1559
  end
1479
1560
  end
@@ -53,7 +53,9 @@ module Brick
53
53
  # Used by Rails 5.0 and above
54
54
  alias :_brick_template_exists? :template_exists?
55
55
  def template_exists?(*args, **options)
56
- _brick_template_exists?(*args, **options) || set_brick_model(args)
56
+ (::Brick.config.add_orphans && args.first == 'orphans') ||
57
+ _brick_template_exists?(*args, **options) ||
58
+ set_brick_model(args)
57
59
  end
58
60
 
59
61
  def set_brick_model(find_args)
@@ -88,44 +90,55 @@ module Brick
88
90
  unless (model_name = (
89
91
  @_brick_model ||
90
92
  (ActionView.version < ::Gem::Version.new('5.0') && args[1].is_a?(Array) ? set_brick_model(args) : nil)
91
- )&.name)
93
+ )&.name) ||
94
+ (is_orphans = ::Brick.config.add_orphans && args[0..1] == ['orphans', ['brick_gem']])
92
95
  return _brick_find_template(*args, **options)
93
96
  end
94
97
 
95
- pk = @_brick_model._brick_primary_key(::Brick.relations.fetch(model_name, nil))
96
- obj_name = model_name.split('::').last.underscore
97
- path_obj_name = model_name.underscore.tr('/', '_')
98
- table_name = obj_name.pluralize
99
- template_link = nil
100
- bts, hms, associatives = ::Brick.get_bts_and_hms(@_brick_model) # This gets BT and HM and also has_many :through (HMT)
101
- hms_columns = [] # Used for 'index'
102
- skip_klass_hms = ::Brick.config.skip_index_hms[model_name] || {}
103
- hms_headers = hms.each_with_object([]) do |hm, s|
104
- hm_stuff = [(hm_assoc = hm.last), "H#{hm_assoc.macro == :has_one ? 'O' : 'M'}#{'T' if hm_assoc.options[:through]}", (assoc_name = hm.first)]
105
- hm_fk_name = if hm_assoc.options[:through]
106
- associative = associatives[hm_assoc.name]
107
- "'#{associative.name}.#{associative.foreign_key}'"
108
- else
109
- hm_assoc.foreign_key
110
- end
111
- if args.first == 'index'
112
- hms_columns << if hm_assoc.macro == :has_many
113
- set_ct = if skip_klass_hms.key?(assoc_name.to_sym)
114
- 'nil'
115
- else
116
- # Postgres column names are limited to 63 characters
117
- attrib_name = "_br_#{assoc_name}_ct"[0..62]
118
- "#{obj_name}.#{attrib_name} || 0"
119
- end
98
+ unless is_orphans
99
+ pk = @_brick_model._brick_primary_key(::Brick.relations.fetch(model_name, nil))
100
+ obj_name = model_name.split('::').last.underscore
101
+ path_obj_name = model_name.underscore.tr('/', '_')
102
+ table_name = obj_name.pluralize
103
+ 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)
105
+ hms_columns = [] # Used for 'index'
106
+ skip_klass_hms = ::Brick.config.skip_index_hms[model_name] || {}
107
+ hms_headers = hms.each_with_object([]) do |hm, s|
108
+ hm_stuff = [(hm_assoc = hm.last), "H#{hm_assoc.macro == :has_one ? 'O' : 'M'}#{'T' if hm_assoc.options[:through]}", (assoc_name = hm.first)]
109
+ hm_fk_name = if hm_assoc.options[:through]
110
+ associative = associatives[hm_assoc.name]
111
+ associative && "'#{associative.name}.#{associative.foreign_key}'"
112
+ else
113
+ hm_assoc.foreign_key
114
+ end
115
+ if args.first == 'index'
116
+ hms_columns << if hm_assoc.macro == :has_many
117
+ set_ct = if skip_klass_hms.key?(assoc_name.to_sym)
118
+ 'nil'
119
+ else
120
+ # Postgres column names are limited to 63 characters
121
+ attrib_name = "_br_#{assoc_name}_ct"[0..62]
122
+ "#{obj_name}.#{attrib_name} || 0"
123
+ end
124
+ if hm_fk_name
120
125
  "<%= ct = #{set_ct}
121
126
  link_to \"#\{ct || 'View'\} #{assoc_name}\", #{hm_assoc.klass.name.underscore.tr('/', '_').pluralize}_path({ #{path_keys(hm_assoc, hm_fk_name, obj_name, pk)} }) unless ct&.zero? %>\n"
122
- else # has_one
127
+ else
128
+ "#{assoc_name}\n"
129
+ end
130
+ else # has_one
123
131
  "<%= obj = #{obj_name}.#{hm.first}; link_to(obj.brick_descrip, obj) if obj %>\n"
124
- end
125
- elsif args.first == 'show'
126
- hm_stuff << "<%= link_to '#{assoc_name}', #{hm_assoc.klass.name.underscore.tr('/', '_').pluralize}_path({ #{path_keys(hm_assoc, hm_fk_name, "@#{obj_name}", pk)} }) %>\n"
132
+ end
133
+ elsif args.first == 'show'
134
+ hm_stuff << if hm_fk_name
135
+ "<%= link_to '#{assoc_name}', #{hm_assoc.klass.name.underscore.tr('/', '_').pluralize}_path({ #{path_keys(hm_assoc, hm_fk_name, "@#{obj_name}", pk)} }) %>\n"
136
+ else
137
+ assoc_name
138
+ end
139
+ end
140
+ s << hm_stuff
127
141
  end
128
- s << hm_stuff
129
142
  end
130
143
 
131
144
  schema_options = ::Brick.db_schemas.keys.each_with_object(+'') { |v, s| s << "<option value=\"#{v}\">#{v}</option>" }.html_safe
@@ -140,6 +153,7 @@ module Brick
140
153
  end.sort.each_with_object(+'') do |v, s|
141
154
  s << "<option value=\"#{v.underscore.gsub('.', '/').pluralize}\">#{v}</option>"
142
155
  end.html_safe
156
+ table_options << '<option value="brick_orphans">(Orphans)</option>'.html_safe if is_orphans
143
157
  css = +"<style>
144
158
  #dropper {
145
159
  background-color: #eee;
@@ -507,7 +521,13 @@ if (headerTop) {
507
521
  %></th><%
508
522
  end
509
523
  # Consider getting the name from the association -- h.first.name -- if a more \"friendly\" alias should be used for a screwy table name
510
- %>#{hms_headers.map { |h| "<th>#{h[1]} <%= link_to('#{h[2]}', #{h.first.klass.name.underscore.tr('/', '_').pluralize}_path) %></th>" }.join
524
+ %>#{hms_headers.map do |h|
525
+ if h.first.options[:through] && !h.first.through_reflection
526
+ "<th>#{h[1]} #{h[2]} %></th>"
527
+ else
528
+ "<th>#{h[1]} <%= link_to('#{h[2]}', #{h.first.klass.name.underscore.tr('/', '_').pluralize}_path) %></th>"
529
+ end
530
+ end.join
511
531
  }</tr></thead>
512
532
 
513
533
  <tbody>
@@ -547,6 +567,22 @@ if (headerTop) {
547
567
 
548
568
  #{"<hr><%= link_to \"New #{obj_name}\", new_#{path_obj_name}_path %>" unless @_brick_model.is_view?}
549
569
  #{script}"
570
+ when 'orphans'
571
+ if is_orphans
572
+ "#{css}
573
+ <p style=\"color: green\"><%= notice %></p>#{"
574
+ <select id=\"schema\">#{schema_options}</select>" if ::Brick.config.schema_behavior[:multitenant] && ::Brick.db_schemas.length > 1}
575
+ <select id=\"tbl\">#{table_options}</select>
576
+ <h1>Orphans<%= \" for #\{}\" if false %></h1>
577
+ <% @orphans.each do |o|
578
+ via = \" (via #\{o[4]})\" unless \"#\{o[2].split('.').last.underscore.singularize}_id\" == o[4] %>
579
+ <a href=\"/<%= o[0].split('.').last %>/<%= o[1] %>\">
580
+ <%= \"#\{o[0]} #\{o[1]} refers#\{via} to non-existent #\{o[2]} #\{o[3]}#\{\" (in table \\\"#\{o[5]}\\\")\" if o[5]}\" %>
581
+ </a><br>
582
+ <% end %>
583
+ #{script}"
584
+ end
585
+
550
586
  when 'show', 'update'
551
587
  "#{css}
552
588
  <p style=\"color: green\"><%= notice %></p>#{"
@@ -646,6 +682,8 @@ end
646
682
  <% end %>
647
683
 
648
684
  #{hms_headers.each_with_object(+'') do |hm, s|
685
+ next if hm.first.options[:through] && !hm.first.through_reflection
686
+
649
687
  if (pk = hm.first.klass.primary_key)
650
688
  hm_singular_name = (hm_name = hm.first.name.to_s).singularize.underscore
651
689
  obj_pk = (pk.is_a?(Array) ? pk : [pk]).each_with_object([]) { |pk_part, s| s << "#{hm_singular_name}.#{pk_part}" }.join(', ')
@@ -4,53 +4,25 @@ if Object.const_defined?('::Rake::TaskManager')
4
4
  namespace :brick do
5
5
  desc 'Find any seemingly-orphaned records'
6
6
  task orphans: :environment do
7
- def class_pk(dotted_name, multitenant)
8
- Object.const_get((multitenant ? [dotted_name.split('.').last] : dotted_name.split('.')).map { |nm| "::#{nm.singularize.camelize}" }.join).primary_key
9
- end
10
-
11
7
  schema_list = ((multi = ::Brick.config.schema_behavior[:multitenant]) && ::Brick.db_schemas.keys.sort) || []
12
- if schema_list.length > 1
13
- require 'fancy_gets'
14
- include FancyGets
15
- schema = gets_list(
16
- list: schema_list,
17
- chosen: multi[:schema_to_analyse]
18
- )
19
- elsif schema_list.length.positive?
20
- schema = schema_list.first
21
- end
8
+ schema = if schema_list.length == 1
9
+ schema_list.first
10
+ elsif schema_list.length.positive?
11
+ require 'fancy_gets'
12
+ include FancyGets
13
+ gets_list(list: schema_list, chosen: multi[:schema_to_analyse])
14
+ end
22
15
  ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", schema) if schema
23
- orphans = +''
24
- ::Brick.relations.each do |k, v|
25
- next if v.key?(:isView) || ::Brick.config.exclude_tables.include?(k) ||
26
- !(pri_pk = v[:pkey].values.first&.first) ||
27
- !(pri_pk = class_pk(k, multi))
28
- v[:fks].each do |k1, v1|
29
- next if v1[:is_bt] ||
30
- !(for_rel = ::Brick.relations.fetch(v1[:inverse_table], nil)) ||
31
- v1[:inverse]&.key?(:polymorphic) ||
32
- !(for_pk = for_rel.fetch(:pkey, nil)&.values&.first&.first) ||
33
- !(for_pk = class_pk(v1[:inverse_table], multi))
34
- begin
35
- ActiveRecord::Base.execute_sql(
36
- "SELECT DISTINCT frn.#{v1[:fk]} AS pri_id, frn.#{for_pk} AS fk_id
37
- FROM #{v1[:inverse_table]} AS frn
38
- LEFT OUTER JOIN #{k} AS pri ON pri.#{pri_pk} = frn.#{v1[:fk]}
39
- WHERE frn.#{v1[:fk]} IS NOT NULL AND pri.#{pri_pk} IS NULL
40
- ORDER BY 1, 2"
41
- ).each do |o|
42
- orphans << "#{v1[:inverse_table]} #{o['fk_id']} refers to non-existant #{k} #{o['pri_id']}\n"
43
- end
44
- rescue StandardError => err
45
- puts "Strange -- #{err.inspect}"
46
- end
47
- end
48
- end
49
- puts "For #{schema}:\n#{'=' * (schema.length + 5)}" if schema
50
- if orphans.blank?
16
+ orphans = ::Brick.find_orphans(schema)
17
+ puts "Orphans in #{schema}:\n#{'=' * (schema.length + 12)}" if schema
18
+ if orphans.empty?
51
19
  puts "No orphans!"
52
20
  else
53
- print orphans
21
+ orphans.each do |o|
22
+ via = " (via #{o[4]})" unless "#{o[2].split('.').last.underscore.singularize}_id" == o[4]
23
+ puts "#{o[0]} #{o[1]} refers#{via} to non-existent #{o[2]} #{o[3]}#{" (in table \"#{o[5]}\")" if o[5]}"
24
+ end
25
+ puts
54
26
  end
55
27
  end
56
28
  end
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 40
8
+ TINY = 41
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
@@ -128,7 +128,10 @@ module Brick
128
128
 
129
129
  def set_db_schema(params)
130
130
  schema = params['_brick_schema'] || 'public'
131
- ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema) if schema && ::Brick.db_schemas&.include?(schema)
131
+ if schema && ::Brick.db_schemas&.include?(schema)
132
+ ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
133
+ schema
134
+ end
132
135
  end
133
136
 
134
137
  # All tables and views (what Postgres calls "relations" including column and foreign key info)
@@ -457,6 +460,9 @@ In config/initializers/brick.rb appropriate entries would look something like:
457
460
  send(:resources, controller_name.to_sym, **options)
458
461
  end
459
462
  end
463
+ if ::Brick.config.add_orphans && instance_variable_get(:@set).named_routes.names.exclude?(:brick_orphans)
464
+ get('/brick_orphans', to: 'brick_gem#orphans', as: 'brick_orphans')
465
+ end
460
466
  end
461
467
  send(:get, '/api-docs/v1/swagger.json', { to: 'brick_swagger#index' }) if Object.const_defined?('Rswag::Ui')
462
468
  end
@@ -209,7 +209,7 @@ module Brick
209
209
  # # Specify STI subclasses either directly by name or as a general module prefix that should always relate to a specific
210
210
  # # parent STI class. The prefixed :: here for these examples is mandatory. Also having a suffixed :: means instead of
211
211
  # # a class reference, this is for a general namespace reference. So in this case requests for, say, either of the
212
- # # non-existant classes Animals::Cat or Animals::Goat (or anything else with the module prefix of \"Animals::\" would
212
+ # # non-existent classes Animals::Cat or Animals::Goat (or anything else with the module prefix of \"Animals::\" would
213
213
  # # build a model that inherits from Animal. And a request specifically for the class Snake would build a new model
214
214
  # # that inherits from Reptile, and no other request would do this -- only specifically for Snake. The ending ::
215
215
  # # indicates that it's a module prefix instead of a specific class name.
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.40
4
+ version: 1.0.41
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-02 00:00:00.000000000 Z
11
+ date: 2022-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord