brick 1.0.127 → 1.0.128

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: 7a424ad95046c33fae8463661b1fb53d770e44e37b503cb68b30fce5725c5ebd
4
- data.tar.gz: 2fc4b004e49497b1b15c864a646717c162e7d7d55edf6913e916fab86ebbcbb7
3
+ metadata.gz: 4465179ddef5435627439c7123bbb63e951abcda5bd8bd8e20952cca2fc9e274
4
+ data.tar.gz: b567eb687c8eea355ece80562a67e424629df4a8b64aae48596e6c6b01c730b4
5
5
  SHA512:
6
- metadata.gz: 5ebf6939ba66e746baf8c84b347630ec4e68951b23dc1efcd72bff47b3b6550c3c8d3444194489aa227a3be153b387552951fdd0fec03dc66a73306df6d6f0d4
7
- data.tar.gz: e3a90e24d98d3bcb47d09e628df9958b101f8dfb9998c97307b791664e8a8b429d12febe73a1e3e322ef3aab054a9e0a103614f2053f58f1b0d2d3e580c9c083
6
+ metadata.gz: 162cf46af2e43b2652e59f9fe8f4d2826e490def50fb868869f614aacb256cfeecee3833dfb41807bef5ea64c83e921b7e993e728e6e42730a214ec49601de67
7
+ data.tar.gz: 15fdcc9b67bc82b55a195168e020575ca04d9d1ae5830e0d1bcd2c7e04944aba90238342cabc1c93b7b7d01619a369b1bb0a0383dbbe91666e796ffa4051aa55
@@ -108,16 +108,16 @@ module ActiveRecord
108
108
  dsl
109
109
  end
110
110
 
111
- def self.brick_parse_dsl(build_array = nil, prefix = [], translations = {}, is_polymorphic = false, dsl = nil, emit_dsl = false)
112
- unless build_array.is_a?(::Brick::JoinArray)
113
- build_array = ::Brick::JoinArray.new.tap { |ary| ary.replace([build_array]) } if build_array.is_a?(::Brick::JoinHash)
114
- build_array = ::Brick::JoinArray.new unless build_array.nil? || build_array.is_a?(Array)
111
+ def self.brick_parse_dsl(join_array = nil, prefix = [], translations = {}, is_polymorphic = false, dsl = nil, emit_dsl = false)
112
+ unless join_array.is_a?(::Brick::JoinArray)
113
+ join_array = ::Brick::JoinArray.new.tap { |ary| ary.replace([join_array]) } if join_array.is_a?(::Brick::JoinHash)
114
+ join_array = ::Brick::JoinArray.new unless join_array.nil? || join_array.is_a?(Array)
115
115
  end
116
116
  prefix = [prefix] unless prefix.is_a?(Array)
117
117
  members = []
118
118
  unless dsl || (dsl = ::Brick.config.model_descrips[name] || brick_get_dsl)
119
119
  # With no DSL available, still put this prefix into the JoinArray so we can get primary key (ID) info from this table
120
- x = prefix.each_with_object(build_array) { |v, s| s[v.to_sym] }
120
+ x = prefix.each_with_object(join_array) { |v, s| s[v.to_sym] }
121
121
  x[prefix.last] = nil unless prefix.empty? # Using []= will "hydrate" any missing part(s) in our whole series
122
122
  return members
123
123
  end
@@ -141,7 +141,7 @@ module ActiveRecord
141
141
  if first_parts
142
142
  if (parts = prefix + first_parts + [parts[-1]]).length > 1 && klass
143
143
  unless is_polymorphic
144
- s = build_array
144
+ s = join_array
145
145
  parts[0..-3].each { |v| s = s[v.to_sym] }
146
146
  s[parts[-2]] = nil # unless parts[-2].empty? # Using []= will "hydrate" any missing part(s) in our whole series
147
147
  end
@@ -153,7 +153,7 @@ module ActiveRecord
153
153
  prefix << parts.shift until parts.empty?
154
154
  end
155
155
  # Expand this entry which refers to an association name
156
- members2, dsl2a = klass.brick_parse_dsl(build_array, prefix + [possible_dsl], translations, is_polymorphic, nil, true)
156
+ members2, dsl2a = klass.brick_parse_dsl(join_array, prefix + [possible_dsl], translations, is_polymorphic, nil, true)
157
157
  members += members2
158
158
  dsl2 << dsl2a
159
159
  dsl3 << dsl2a
@@ -434,7 +434,9 @@ module ActiveRecord
434
434
  @brick_links ||= { '' => table_name }
435
435
  end
436
436
 
437
- def brick_select(params, selects = [], order_by = nil, translations = {}, join_array = ::Brick::JoinArray.new)
437
+ def brick_select(params, selects = [], order_by = nil, translations = {},
438
+ join_array = ::Brick::JoinArray.new,
439
+ cust_col_override = nil)
438
440
  is_add_bts = is_add_hms = true
439
441
 
440
442
  # Build out cust_cols, bt_descrip and hm_counts now so that they are available on the
@@ -467,7 +469,7 @@ module ActiveRecord
467
469
  is_distinct = true
468
470
  distinct!
469
471
  end
470
- wheres[k] = v.split(',')
472
+ wheres[k] = v.is_a?(String) ? v.split(',') : v
471
473
  end
472
474
 
473
475
  # %%% Skip the metadata columns
@@ -506,84 +508,90 @@ module ActiveRecord
506
508
  end
507
509
  end
508
510
 
509
- if join_array.present?
510
- left_outer_joins!(join_array)
511
- # Touching AREL AST walks the JoinDependency tree, and in that process uses our
512
- # "brick_links" patch to find how every AR chain of association names relates to exact
513
- # table correlation names chosen by AREL. We use a duplicate relation object for this
514
- # because an important side-effect of referencing the AST is that the @arel instance
515
- # variable gets set, and this is a signal to ActiveRecord that a relation has now
516
- # become immutable. (We aren't quite ready for our "real deal" relation object to be
517
- # set in stone ... still need to add .select(), and possibly .where() and .order()
518
- # things ... also if there are any HM counts then an OUTER JOIN for each of them out
519
- # to a derived table to do that counting. All of these things need to know proper
520
- # table correlation names, which will now become available in brick_links on the
521
- # rel_dupe object.)
522
- (rel_dupe = dup).arel.ast
523
-
524
- core_selects = selects.dup
525
- id_for_tables = Hash.new { |h, k| h[k] = [] }
526
- field_tbl_names = Hash.new { |h, k| h[k] = {} }
527
- used_col_aliases = {} # Used to make sure there is not a name clash
528
-
529
- # CUSTOM COLUMNS
530
- # ==============
531
- klass._br_cust_cols.each do |k, cc|
532
- if rel_dupe.respond_to?(k) # Name already taken?
533
- # %%% Use ensure_unique here in this kind of fashion:
534
- # cnstr_name = ensure_unique(+"(brick) #{for_tbl}_#{pri_tbl}", bts, hms)
535
- # binding.pry
536
- next
537
- end
511
+ left_outer_joins!(join_array) if join_array.present?
512
+
513
+ # If it's a CollectionProxy (which inherits from Relation) then need to dig out the
514
+ # core Relation object which is found in the association scope.
515
+ rel_dupe = (is_a?(ActiveRecord::Associations::CollectionProxy) ? scope : self).dup
516
+
517
+ # Touching AREL AST walks the JoinDependency tree, and in that process uses our
518
+ # "brick_links" patch to find how every AR chain of association names relates to exact
519
+ # table correlation names chosen by AREL. We use a duplicate relation object for this
520
+ # because an important side-effect of referencing the AST is that the @arel instance
521
+ # variable gets set, and this is a signal to ActiveRecord that a relation has now
522
+ # become immutable. (We aren't quite ready for our "real deal" relation object to be
523
+ # set in stone ... still need to add .select(), and possibly .where() and .order()
524
+ # things ... also if there are any HM counts then an OUTER JOIN for each of them out
525
+ # to a derived table to do that counting. All of these things need to know proper
526
+ # table correlation names, which will now become available in brick_links on the
527
+ # rel_dupe object.)
528
+ rel_dupe.arel.ast
529
+
530
+ core_selects = selects.dup
531
+ id_for_tables = Hash.new { |h, k| h[k] = [] }
532
+ field_tbl_names = Hash.new { |h, k| h[k] = {} }
533
+ used_col_aliases = {} # Used to make sure there is not a name clash
534
+
535
+ # CUSTOM COLUMNS
536
+ # ==============
537
+ (cust_col_override || klass._br_cust_cols).each do |k, cc|
538
+ if rel_dupe.respond_to?(k) # Name already taken?
539
+ # %%% Use ensure_unique here in this kind of fashion:
540
+ # cnstr_name = ensure_unique(+"(brick) #{for_tbl}_#{pri_tbl}", bts, hms)
541
+ # binding.pry
542
+ next
543
+ end
538
544
 
539
- key_klass = nil
540
- key_tbl_name = nil
541
- dest_pk = nil
542
- key_alias = nil
543
- cc.first.each do |cc_part|
544
- dest_klass = cc_part[0..-2].inject(klass) do |kl, cc_part_term|
545
- # %%% Clear column info properly so we can do multiple subsequent requests
546
- # binding.pry unless kl.reflect_on_association(cc_part_term)
547
- kl.reflect_on_association(cc_part_term)&.klass || klass
548
- end
549
- tbl_name = rel_dupe.brick_links[cc_part[0..-2].map(&:to_s).join('.')]
550
- # Deal with the conflict if there are two parts in the custom column named the same,
551
- # "category.name" and "product.name" for instance will end up with aliases of "name"
552
- # and "product__name".
553
- if (cc_part_idx = cc_part.length - 1).zero?
554
- col_alias = "br_cc_#{k}__#{table_name.tr('.', '_')}"
555
- else
556
- while cc_part_idx > 0 &&
557
- (col_alias = "br_cc_#{k}__#{cc_part[cc_part_idx..-1].map(&:to_s).join('__').tr('.', '_')}") &&
558
- used_col_aliases.key?(col_alias)
559
- cc_part_idx -= 1
560
- end
561
- end
562
- used_col_aliases[col_alias] = nil
563
- # Set up custom column links by preparing key_klass and key_alias
564
- # (If there are multiple different tables referenced in the DSL, we end up creating a link to the last one)
565
- if cc[2] && (dest_pk = dest_klass.primary_key)
566
- key_klass = dest_klass
567
- key_tbl_name = tbl_name
568
- cc_part_idx = cc_part.length - 1
569
- while cc_part_idx > 0 &&
570
- (key_alias = "br_cc_#{k}__#{(cc_part[cc_part_idx..-2] + [dest_pk]).map(&:to_s).join('__')}") &&
571
- key_alias != col_alias && # We break out if this key alias does exactly match the col_alias
572
- used_col_aliases.key?(key_alias)
573
- cc_part_idx -= 1
574
- end
545
+ key_klass = nil
546
+ key_tbl_name = nil
547
+ dest_pk = nil
548
+ key_alias = nil
549
+ cc.first.each do |cc_part|
550
+ dest_klass = cc_part[0..-2].inject(klass) do |kl, cc_part_term|
551
+ # %%% Clear column info properly so we can do multiple subsequent requests
552
+ # binding.pry unless kl.reflect_on_association(cc_part_term)
553
+ kl.reflect_on_association(cc_part_term)&.klass || klass
554
+ end
555
+ tbl_name = rel_dupe.brick_links[cc_part[0..-2].map(&:to_s).join('.')]
556
+ # Deal with the conflict if there are two parts in the custom column named the same,
557
+ # "category.name" and "product.name" for instance will end up with aliases of "name"
558
+ # and "product__name".
559
+ if (cc_part_idx = cc_part.length - 1).zero?
560
+ col_alias = "br_cc_#{k}__#{table_name.tr('.', '_')}_#{cc_part.first}"
561
+ else
562
+ while cc_part_idx > 0 &&
563
+ (col_alias = "br_cc_#{k}__#{cc_part[cc_part_idx..-1].map(&:to_s).join('__').tr('.', '_')}") &&
564
+ used_col_aliases.key?(col_alias)
565
+ cc_part_idx -= 1
575
566
  end
576
- selects << "#{tbl_name}.#{cc_part.last} AS #{col_alias}"
577
- cc_part << col_alias
578
567
  end
579
- # Add a key column unless we've already got it
580
- if key_alias && !used_col_aliases.key?(key_alias)
581
- selects << "#{key_tbl_name}.#{dest_pk} AS #{key_alias}"
582
- used_col_aliases[key_alias] = nil
568
+ used_col_aliases[col_alias] = nil
569
+ # Set up custom column links by preparing key_klass and key_alias
570
+ # (If there are multiple different tables referenced in the DSL, we end up creating a link to the last one)
571
+ if cc[2] && (dest_pk = dest_klass.primary_key)
572
+ key_klass = dest_klass
573
+ key_tbl_name = tbl_name
574
+ cc_part_idx = cc_part.length - 1
575
+ while cc_part_idx > 0 &&
576
+ (key_alias = "br_cc_#{k}__#{(cc_part[cc_part_idx..-2] + [dest_pk]).map(&:to_s).join('__')}") &&
577
+ key_alias != col_alias && # We break out if this key alias does exactly match the col_alias
578
+ used_col_aliases.key?(key_alias)
579
+ cc_part_idx -= 1
580
+ end
583
581
  end
584
- cc[2] = key_alias ? [key_klass, key_alias] : nil
582
+ selects << "#{tbl_name}.#{cc_part.last} AS #{col_alias}"
583
+ cc_part << col_alias
585
584
  end
585
+ # Add a key column unless we've already got it
586
+ if key_alias && !used_col_aliases.key?(key_alias)
587
+ selects << "#{key_tbl_name}.#{dest_pk} AS #{key_alias}"
588
+ used_col_aliases[key_alias] = nil
589
+ end
590
+ cc[2] = key_alias ? [key_klass, key_alias] : nil
591
+ end
586
592
 
593
+ # LEFT OUTER JOINs
594
+ unless cust_col_override
587
595
  klass._br_bt_descrip.each do |v|
588
596
  v.last.each do |k1, v1| # k1 is class, v1 is array of columns to snag
589
597
  next unless (tbl_name = rel_dupe.brick_links[v.first.to_s]&.split('.')&.last)
@@ -644,6 +652,7 @@ module ActiveRecord
644
652
  _assoc_names[assoc_name] = [table_alias, klass]
645
653
  end
646
654
  end
655
+
647
656
  # Add derived table JOIN for the has_many counts
648
657
  nix = []
649
658
  klass._br_hm_counts.each do |k, hm|
@@ -781,7 +790,7 @@ module ActiveRecord
781
790
  JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}#{count_column
782
791
  }) AS c_t_ FROM #{from_clause || hm_table_name} GROUP BY #{group_bys.join(', ')}) #{tbl_alias}"
783
792
  self.joins_values |= ["#{join_clause} ON #{on_clause.join(' AND ')}"] # Same as: joins!(...)
784
- end
793
+ end unless cust_col_override
785
794
  while (n = nix.pop)
786
795
  klass._br_hm_counts.delete(n)
787
796
  end
@@ -851,6 +860,19 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
851
860
  wheres unless wheres.empty? # Return the specific parameters that we did use
852
861
  end
853
862
 
863
+ # Build out an AR relation that queries for a list of objects, and include all the appropriate JOINs to later apply DSL using #brick_descrip
864
+ def brick_list
865
+ pks = klass.primary_key.is_a?(String) ? [klass.primary_key] : klass.primary_key
866
+ selects = pks.each_with_object([]) { |pk, s| s << pk unless s.include?(pk) }
867
+ pieces, my_dsl = klass.brick_parse_dsl(join_array = ::Brick::JoinArray.new, [], translations = {}, false, nil, true)
868
+ brick_select(
869
+ where_values_hash, selects, nil, translations, join_array,
870
+ { '_br' => (descrip_cols = [pieces, my_dsl]) }
871
+ )
872
+ order_values = klass.primary_key
873
+ [self.select(selects), descrip_cols]
874
+ end
875
+
854
876
  private
855
877
 
856
878
  def shift_or_first(ary)
@@ -1254,6 +1276,11 @@ class Object
1254
1276
  end
1255
1277
  self.abstract_class = true
1256
1278
  code << " self.abstract_class = true\n"
1279
+ elsif Object.const_defined?('BCrypt') && relation[:cols].include?('password_digest') &&
1280
+ !instance_methods.include?(:password) && respond_to?(:has_secure_password)
1281
+ puts "Appears that the #{full_name} model is intended to hold user account information. Applying #has_secure_password."
1282
+ has_secure_password
1283
+ code << " has_secure_password\n"
1257
1284
  end
1258
1285
  # Accommodate singular or camel-cased table names such as "order_detail" or "OrderDetails"
1259
1286
  code << " self.table_name = '#{self.table_name = matching}'\n" if inheritable_name || table_name != matching
@@ -2718,12 +2745,17 @@ module Brick
2718
2745
  end
2719
2746
  end
2720
2747
  ::Brick.relations.map do |k, v|
2748
+ # next if Brick.config.exclude_tables.include?(k)
2749
+
2721
2750
  tbl_parts = k.split('.')
2722
2751
  tbl_parts.shift if ::Brick.apartment_multitenant && tbl_parts.length > 1 && tbl_parts.first == ::Brick.apartment_default_tenant
2723
2752
  res = tbl_parts.join('.')
2724
- [k, (model = models[res])&.last&.table_name || v[:class_name].constantize.table_name,
2725
- migrations&.fetch(res, nil),
2726
- model&.first]
2753
+ table_name = (model = models[res])&.last&.table_name
2754
+ table_name ||= begin
2755
+ v[:class_name].constantize.table_name
2756
+ rescue
2757
+ end
2758
+ [k, table_name || k, migrations&.fetch(res, nil), model&.first]
2727
2759
  end
2728
2760
  end
2729
2761
 
@@ -1602,7 +1602,15 @@ end
1602
1602
  if bt.length < 4
1603
1603
  bt << (option_detail = [[\"(No #\{bt_name\} chosen)\", '^^^brick_NULL^^^']])
1604
1604
  # %%% Accommodate composite keys for obj.pk at the end here
1605
- bt_class&.order(obj_pk = bt_class.primary_key)&.each { |obj| option_detail << [obj.brick_descrip(nil, obj_pk), obj.send(obj_pk)] }
1605
+ collection, descrip_cols = bt_class&.order(obj_pk = bt_class.primary_key).brick_list
1606
+ collection&.each do |obj|
1607
+ option_detail << [
1608
+ obj.brick_descrip(
1609
+ descrip_cols&.first&.map { |col| obj.send(col.last) },
1610
+ obj_pk
1611
+ ), obj.send(obj_pk)
1612
+ ]
1613
+ end
1606
1614
  end %>
1607
1615
  BT <%= bt_class&.bt_link(bt.first) || orig_poly_name %>
1608
1616
  <% else %>
@@ -1634,34 +1642,45 @@ end
1634
1642
  obj_pk = (pk.is_a?(Array) ? pk : [pk]).each_with_object([]) { |pk_part, s| s << "#{hm_singular_name}.#{pk_part}" }.join(', ')
1635
1643
  poly_fix = if (poly_type = (hm.first.options[:as] && hm.first.type))
1636
1644
  "
1637
- # Let's fix an unexpected \"feature\" of AR -- when going through a polymorphic has_many
1638
- # association that points to an STI model then filtering for the __able_type column is done
1639
- # with a .where(). And the polymorphic class name it points to is the base class name of
1640
- # the STI model instead of its subclass.
1641
- poly_type = #{poly_type.inspect}
1642
- #{ (inh_col = @_brick_model.inheritance_column).present? &&
1643
- " if poly_type && @#{obj_name}.respond_to?(:#{inh_col}) &&
1644
- (base_type = collection.where_values_hash[poly_type])
1645
- collection = collection.rewhere(poly_type => [base_type, @#{obj_name}.#{inh_col}])
1646
- end"}"
1645
+ # Let's fix an unexpected \"feature\" of AR -- when going through a polymorphic has_many
1646
+ # association that points to an STI model then filtering for the __able_type column is done
1647
+ # with a .where(). And the polymorphic class name it points to is the base class name of
1648
+ # the STI model instead of its subclass.
1649
+ poly_type = #{poly_type.inspect}
1650
+ #{ (inh_col = @_brick_model.inheritance_column).present? &&
1651
+ " if poly_type && @#{obj_name}.respond_to?(:#{inh_col}) &&
1652
+ (base_type = collection.where_values_hash[poly_type])
1653
+ collection = collection.rewhere(poly_type => [base_type, @#{obj_name}.#{inh_col}])
1654
+ end"}"
1647
1655
  end
1648
1656
  s << "<table id=\"#{hm_name}\" class=\"shadow\">
1649
1657
  <tr><th>#{hm[1]}#{' poly' if hm[0].options[:as]} #{hm[3]}</th></tr>
1650
- <% collection = @#{obj_name}.#{hm_name}
1651
- collection = case collection
1652
- when ActiveRecord::Associations::CollectionProxy#{
1653
- poly_fix}
1654
- collection.order(#{pk.inspect})
1655
- when ActiveRecord::Base # Object from a has_one
1656
- [collection]
1657
- else # We get an array back when AR < 4.2
1658
- collection.to_a.compact
1659
- end
1660
- if collection.empty? %>
1658
+ <% if (assoc = @#{obj_name}.class.reflect_on_association(:#{hm_name})).macro == :has_one &&
1659
+ assoc.options&.fetch(:through, nil).nil?
1660
+ # In order to apply DSL properly, evaluate this HO the other way around as if it were as a BT
1661
+ collection = assoc.klass.where(assoc.foreign_key => @#{obj_name}.#{pk})
1662
+ collection = collection.instance_exec(&assoc.scopes.first) if assoc.scopes.present?
1663
+ else
1664
+ collection = @#{obj_name}.#{hm_name}
1665
+ end
1666
+ case collection
1667
+ when ActiveRecord::Relation # has_many (which comes in as a CollectionProxy) or a has_one#{
1668
+ poly_fix}
1669
+ collection2, descrip_cols = collection.brick_list
1670
+ when ActiveRecord::Base # Object from a has_one :through
1671
+ collection2 = [collection]
1672
+ else # We get an array back when AR < 4.2
1673
+ collection2 = collection.to_a.compact
1674
+ end
1675
+ collection2 = collection2.uniq
1676
+ if collection2.empty? %>
1661
1677
  <tr><td>(none)</td></tr>
1662
- <% else %>
1663
- <% collection.uniq.each do |#{hm_singular_name}| %>
1664
- <tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm.first.klass._brick_index(:singular)}_path(slashify(#{obj_pk}))) %></td></tr>
1678
+ <% else
1679
+ collection2.each do |#{hm_singular_name}| %>
1680
+ <tr><td><%= br_descrip = #{hm_singular_name}.brick_descrip(
1681
+ descrip_cols&.first&.map { |col| #{hm_singular_name}.send(col.last) }
1682
+ )
1683
+ link_to(br_descrip, #{hm.first.klass._brick_index(:singular)}_path(slashify(#{obj_pk}))) %></td></tr>
1665
1684
  <% end %>
1666
1685
  <% end %>
1667
1686
  </table>"
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 127
8
+ TINY = 128
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
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.127
4
+ version: 1.0.128
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-05 00:00:00.000000000 Z
11
+ date: 2023-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord