brick 1.0.81 → 1.0.83

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: 2627490165a63200a49afb7707882489986217bc170491e88f64eb9faf880b6b
4
- data.tar.gz: f12adff07785ffd5c4a877f555cd7899de859f99ae9b18b84eda6f3d20ac5c15
3
+ metadata.gz: fd1f37f0785af48f35bc1292e9eec44cd8ebc6a81a6ed7163e53aeaeb753ccc7
4
+ data.tar.gz: e532a10b06419ce64369e7a397a8eb4d268d87dc5a18c936847ab693cbf159bb
5
5
  SHA512:
6
- metadata.gz: f112e1bf44fc39e80bced64a2d9fb4dd16297e2c549f66d0842fb65d7f3644170fa18c3c784c59c5a93db57233a77bfa5f288dca89be1b3dec6172bb670f147b
7
- data.tar.gz: 0c2061b9b662c00aeb85a76db20fb59962302dd8b6ab17225ded332eadf4485392e3f5e44538b8f99d525666b8a036bb2fbd53a2604bbf7f69aca3ad8400fa4d
6
+ metadata.gz: 7d9dbf20d1bcdc3d3ac2beaf905392ba4990574a8165c1111bdb8f035bd8b53765f1555bd672da134cd342edadb45a2b62151e49cfa6279ebc957f16d34fc449
7
+ data.tar.gz: 276c8e4b831a866baa1b4e84a288a0137ae19afba54d08d342432f358ff4b7a45f234521c35f6d1a6ab96fd69ceb68aa30ba41bfb4b18caef7a12e25cedec8ed
@@ -59,6 +59,10 @@ end
59
59
 
60
60
  module ActiveRecord
61
61
  class Base
62
+ def self.is_brick?
63
+ instance_variables.include?(:@_brick_built) && instance_variable_get(:@_brick_built)
64
+ end
65
+
62
66
  def self._assoc_names
63
67
  @_assoc_names ||= {}
64
68
  end
@@ -521,7 +525,7 @@ module ActiveRecord
521
525
  # CUSTOM COLUMNS
522
526
  # ==============
523
527
  klass._br_cust_cols.each do |k, cc|
524
- if respond_to?(k) # Name already taken?
528
+ if rel_dupe.respond_to?(k) # Name already taken?
525
529
  # %%% Use ensure_unique here in this kind of fashion:
526
530
  # cnstr_name = ensure_unique(+"(brick) #{for_tbl}_#{pri_tbl}", bts, hms)
527
531
  # binding.pry
@@ -641,11 +645,13 @@ module ActiveRecord
641
645
  [a.table_name, a.foreign_key, a.source_reflection.macro]
642
646
  end
643
647
  next if bail_out
648
+
644
649
  # count_column is determined from the originating HMT member
645
650
  if (src_ref = hm.source_reflection).nil?
646
651
  puts "*** Warning: Could not determine destination model for this HMT association in model #{klass.name}:\n has_many :#{hm.name}, through: :#{hm.options[:through]}"
652
+ puts
647
653
  nix << k
648
- bail_out = true
654
+ next
649
655
  elsif src_ref.macro == :belongs_to # Traditional HMT using an associative table
650
656
  "br_t#{idx}.#{hm.foreign_key}"
651
657
  else # A HMT that goes HM -> HM, something like Categories -> Products -> LineItems
@@ -676,15 +682,15 @@ module ActiveRecord
676
682
  pri_tbl.table_name
677
683
  end
678
684
  on_clause = []
679
- if fk_col.is_a?(Array) # Composite key?
680
- fk_col.each_with_index { |fk_col_part, idx| on_clause << "#{tbl_alias}.#{fk_col_part} = #{pri_tbl_name}.#{pri_tbl.primary_key[idx]}" }
681
- selects = fk_col.dup
682
- else
683
- selects = [fk_col]
684
- on_clause << "#{tbl_alias}.#{fk_col} = #{pri_tbl_name}.#{pri_tbl.primary_key}"
685
- end
685
+ hm_selects = if fk_col.is_a?(Array) # Composite key?
686
+ fk_col.each_with_index { |fk_col_part, idx| on_clause << "#{tbl_alias}.#{fk_col_part} = #{pri_tbl_name}.#{pri_tbl.primary_key[idx]}" }
687
+ fk_col.dup
688
+ else
689
+ on_clause << "#{tbl_alias}.#{fk_col} = #{pri_tbl_name}.#{pri_tbl.primary_key}"
690
+ [fk_col]
691
+ end
686
692
  if poly_type
687
- selects << poly_type
693
+ hm_selects << poly_type
688
694
  on_clause << "#{tbl_alias}.#{poly_type} = '#{name}'"
689
695
  end
690
696
  unless from_clause
@@ -696,9 +702,9 @@ module ActiveRecord
696
702
  hm.table_name
697
703
  end
698
704
  end
699
- group_bys = ::Brick.is_oracle || is_mssql ? selects : (1..selects.length).to_a
705
+ group_bys = ::Brick.is_oracle || is_mssql ? hm_selects : (1..hm_selects.length).to_a
700
706
  join_clause = "LEFT OUTER
701
- JOIN (SELECT #{selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}#{count_column
707
+ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}#{count_column
702
708
  }) AS c_t_ FROM #{from_clause || hm_table_name} GROUP BY #{group_bys.join(', ')}) #{tbl_alias}"
703
709
  joins!("#{join_clause} ON #{on_clause.join(' AND ')}")
704
710
  end
@@ -735,8 +741,10 @@ JOIN (SELECT #{selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', ')},
735
741
  else
736
742
  s << v
737
743
  end
738
- else # String stuff just comes straight through
744
+ else # String stuff (which defines a custom ORDER BY) just comes straight through
739
745
  s << v
746
+ # Avoid "PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list" in Postgres
747
+ selects << v if is_distinct
740
748
  end
741
749
  end
742
750
  order!(*final_order_by)
@@ -805,6 +813,100 @@ JOIN (SELECT #{selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', ')},
805
813
  end
806
814
  end
807
815
 
816
+ if Object.const_defined?('ActionView')
817
+ module ActionView::Helpers::FormTagHelper
818
+ def link_to_brick(*args, **kwargs)
819
+ text = (args.first.is_a?(String) && args.first) || args[1]
820
+ klass_or_obj = ((args.first.is_a?(ActiveRecord::Relation) ||
821
+ args.first.is_a?(ActiveRecord::Base) ||
822
+ args.first.is_a?(Class)) &&
823
+ args.first) ||
824
+ @_brick_model
825
+ # If not provided, do a best-effort to automatically determine the resource class or object
826
+ sti_type = nil
827
+ filter_parts = []
828
+ klass_or_obj ||= begin
829
+ res_names = ::Brick.relations.each_with_object({}) do |v, s|
830
+ v_parts = v.first.split('.')
831
+ v_parts.shift if v_parts.first == 'public'
832
+ s[v_parts.join('.')] = v.first
833
+ end
834
+ c_path_parts = controller_path.split('/')
835
+ klass = nil
836
+ while c_path_parts.present?
837
+ possible_c_path = c_path_parts.join('.')
838
+ possible_c_path_singular = c_path_parts[0..-2] + [c_path_parts.last.singularize]
839
+ possible_sti = possible_c_path_singular.join('/').camelize
840
+ break if (
841
+ res_name = res_names[possible_c_path] ||
842
+ ((klass = Brick.config.sti_namespace_prefixes.key?("::#{possible_sti}") && possible_sti.constantize) &&
843
+ (sti_type = possible_sti)) ||
844
+ # %%% Used to have the more flexible: (DidYouMean::SpellChecker.new(dictionary: res_names.keys).correct(possible_c_path)).first
845
+ res_names[possible_c_path] || res_names[possible_c_path_singular.join('.')]
846
+ ) &&
847
+ (
848
+ klass ||
849
+ ((rel = ::Brick.relations.fetch(res_name, nil)) &&
850
+ (klass ||= rel[:class_name]&.constantize))
851
+ )
852
+ c_path_parts.shift
853
+ end
854
+ if klass
855
+ type_col = klass.inheritance_column # Usually 'type'
856
+ filter_parts << "#{type_col}=#{sti_type}" if sti_type && klass.column_names.include?(type_col)
857
+ path_params = request.path_parameters.dup
858
+ path_params.delete(:controller)
859
+ path_params.delete(:action)
860
+ pk = (klass.primary_key || ActiveRecord::Base.primary_key).to_sym
861
+ # Used to also have this but it's a bit too permissive to identify a primary key: (path_params.length == 1 && path_params.values.first) ||
862
+ if ((id = (path_params[pk] || path_params[:id] || path_params["#{klass.name.underscore}_id".to_sym])) && (obj = klass.find_by(pk => id))) ||
863
+ (['show', 'edit'].include?(action_name) && (obj = klass.first))
864
+ obj
865
+ else
866
+ # %%% If there is a HMT that refers to some ___id then try to identify an appropriate filter
867
+ # %%% If there is a polymorphic association that might relate to stuff in the path_params,
868
+ # try to identify an appropriate ___able_id and ___able_type filter
869
+ ((klass.column_names - [pk.to_s]) & path_params.keys.map(&:to_s)).each do |path_param|
870
+ filter_parts << "#{path_param}=#{path_params[path_param.to_sym]}"
871
+ end
872
+ klass
873
+ end
874
+ end
875
+ rescue
876
+ end
877
+ if klass_or_obj
878
+ if klass_or_obj.is_a?(ActiveRecord::Relation)
879
+ klass_or_obj.where_values_hash.each do |whr|
880
+ filter_parts << "#{whr.first}=#{whr.last}" unless whr.last.is_a?(Array)
881
+ end
882
+ klass_or_obj = klass_or_obj.klass
883
+ end
884
+ filter = "?#{filter_parts.join('&')}" if filter_parts.present?
885
+ if klass_or_obj&.is_a?(Class) && klass_or_obj < ActiveRecord::Base
886
+ lt_args = [text || "Index for #{klass_or_obj.name.pluralize}",
887
+ "#{send("#{klass_or_obj._brick_index}_path")}#{filter}"]
888
+ else
889
+ # If there are multiple incoming parameters then last one is probably the actual ID, and first few might be some nested tree of stuff leading up to it
890
+ lt_args = [text || "Show this #{klass_or_obj.class.name}",
891
+ "#{send("#{klass_or_obj.class._brick_index(:singular)}_path", klass_or_obj)}#{filter}"]
892
+ end
893
+ link_to(*lt_args, **kwargs)
894
+ else
895
+ # puts "Warning: link_to_brick could not find a class for \"#{controller_path}\" -- consider setting @_brick_model within that controller."
896
+ # if (hits = res_names.keys & instance_variables.map { |v| v.to_s[1..-1] }).present?
897
+ links = instance_variables.each_with_object([]) do |name, s|
898
+ iv_name = name.to_s[1..-1]
899
+ case (val = instance_variable_get(name))
900
+ when ActiveRecord::Relation, ActiveRecord::Base
901
+ s << link_to_brick(val, iv_name) if val
902
+ end
903
+ end
904
+ links.join(' &nbsp; ').html_safe
905
+ end
906
+ end
907
+ end
908
+ end
909
+
808
910
  if ActiveSupport::Dependencies.respond_to?(:autoload_module!) # %%% Only works with previous non-zeitwerk auto-loading
809
911
  module ActiveSupport::Dependencies
810
912
  class << self
@@ -1138,6 +1240,7 @@ class Object
1138
1240
  end # class definition
1139
1241
  # Having this separate -- will this now work out better?
1140
1242
  built_model.class_exec do
1243
+ @_brick_built = true
1141
1244
  hmts&.each do |hmt_fk, hms|
1142
1245
  hmt_fk = hmt_fk.tr('.', '_')
1143
1246
  hms.each do |hm|
@@ -130,15 +130,15 @@ module Brick
130
130
  next unless @_brick_model.instance_methods.include?(through) &&
131
131
  (associative = @_brick_model._br_associatives.fetch(hm.first, nil))
132
132
 
133
- tbl_nm = if hm_assoc.options[:source]
134
- associative.klass.reflect_on_association(hm_assoc.options[:source]).inverse_of&.name
133
+ tbl_nm = if (source = hm_assoc.source_reflection).macro == :belongs_to
134
+ hm_assoc.through_reflection&.name # for standard HMT, which is HM -> BT
135
135
  else
136
- associative.name
136
+ source.inverse_of&.name # For HM -> HM style HMT
137
137
  end
138
138
  # If there is no inverse available for the source belongs_to association, make one based on the class name
139
139
  unless tbl_nm
140
140
  tbl_nm = associative.class_name.underscore
141
- tbl_nm.slice!(0) if tbl_nm[0] == ('/')
141
+ tbl_nm.slice!(0) if tbl_nm[0] == '/'
142
142
  tbl_nm = tbl_nm.tr('/', '_').pluralize
143
143
  end
144
144
  "'#{tbl_nm}.#{associative.foreign_key}'"
@@ -166,7 +166,13 @@ module Brick
166
166
  hms_columns << hm_entry
167
167
  when 'show', 'new', 'update'
168
168
  hm_stuff << if hm_fk_name
169
- "<%= link_to '#{assoc_name}', #{hm_assoc.klass._brick_index}_path({ #{path_keys(hm_assoc, hm_fk_name, "@#{obj_name}", pk)} }) %>\n"
169
+ if hm_assoc.klass.column_names.include?(hm_fk_name)
170
+ "<%= link_to '#{assoc_name}', #{hm_assoc.klass._brick_index}_path({ #{path_keys(hm_assoc, hm_fk_name, "@#{obj_name}", pk)} }) %>\n"
171
+ else
172
+ puts "Warning: has_many :#{hm_assoc.name} in model #{hm_assoc.active_record.name} currently looks for a foreign key called \"#{hm_assoc.foreign_key}\". "\
173
+ "Instead it should use the clause \"foreign_key: :#{hm_assoc.inverse_of&.foreign_key}\"."
174
+ assoc_name
175
+ end
170
176
  else # %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
171
177
  assoc_name
172
178
  end
@@ -1331,10 +1337,18 @@ document.querySelectorAll(\"input, select\").forEach(function (inp) {
1331
1337
  prepend ::Brick::RouteSet
1332
1338
  end
1333
1339
  # Do the root route before the Rails Welcome one would otherwise take precedence
1334
- unless (route = ::Brick.config.default_route_fallback).blank? ||
1335
- ::Rails.application.routes.named_routes.send(:routes)[:root]
1336
- ::Rails.application.routes.append do
1337
- send(:root, "#{route}#{'#index' unless route.index('#')}")
1340
+ if (route = ::Brick.config.default_route_fallback).present?
1341
+ action = "#{route}#{'#index' unless route.index('#')}"
1342
+ if ::Brick.config.path_prefix
1343
+ ::Rails.application.routes.append do
1344
+ send(:namespace, ::Brick.config.path_prefix) do
1345
+ send(:root, action)
1346
+ end
1347
+ end
1348
+ elsif ::Rails.application.routes.named_routes.send(:routes)[:root].nil?
1349
+ ::Rails.application.routes.append do
1350
+ send(:root, action)
1351
+ end
1338
1352
  end
1339
1353
  end
1340
1354
  end
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 81
8
+ TINY = 83
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
@@ -113,15 +113,15 @@ if Gem::Specification.all_names.any? { |g| g.start_with?('rails-') }
113
113
  require 'brick/frameworks/rails'
114
114
  end
115
115
  module Brick
116
- def self.sti_models
117
- @sti_models ||= {}
118
- end
116
+ class << self
117
+ def sti_models
118
+ @sti_models ||= {}
119
+ end
119
120
 
120
- def self.existing_stis
121
- @existing_stis ||= Brick.config.sti_namespace_prefixes.each_with_object({}) { |snp, s| s[snp.first[2..-1]] = snp.last unless snp.first.end_with?('::') }
122
- end
121
+ def existing_stis
122
+ @existing_stis ||= Brick.config.sti_namespace_prefixes.each_with_object({}) { |snp, s| s[snp.first[2..-1]] = snp.last unless snp.first.end_with?('::') }
123
+ end
123
124
 
124
- class << self
125
125
  attr_accessor :default_schema, :db_schemas, :routes_done, :is_oracle, :is_eager_loading, :auto_models
126
126
 
127
127
  def set_db_schema(params = nil)
@@ -203,11 +203,9 @@ module Brick
203
203
  skip_hms = {}
204
204
  hms.each do |hmt|
205
205
  if (through = hmt.last.options[:through])
206
- skip_hms[through] = nil # if hms[through]
207
- # binding.pry if !hms[through]
206
+ # ::Brick.relations[hmt.last.through_reflection.table_name]
207
+ skip_hms[through] = nil if hms[through] && model.is_brick?
208
208
  # End up with a hash of HMT names pointing to join-table associations
209
- # Last part was: hmt.last.name
210
- # Changed up because looking for: hms[:issue_issue_duplicates]
211
209
  model._br_associatives[hmt.first] = hms[through] # || hms["#{(opt = hmt.last.options)[:through].to_s.singularize}_#{opt[:source].to_s.pluralize}".to_sym]
212
210
  elsif hmt.last.inverse_of.nil?
213
211
  puts "SKIPPING #{hmt.last.name.inspect}"
@@ -549,7 +547,11 @@ In config/initializers/brick.rb appropriate entries would look something like:
549
547
  module RouteSet
550
548
  def finalize!
551
549
  unless ::Rails.application.routes.named_routes.route_defined?(:brick_status_path)
552
- existing_controllers = routes.each_with_object({}) { |r, s| c = r.defaults[:controller]; s[c] = nil if c }
550
+ path_prefix = ::Brick.config.path_prefix
551
+ existing_controllers = routes.each_with_object({}) do |r, s|
552
+ c = r.defaults[:controller]
553
+ s[c] = nil if c
554
+ end
553
555
  ::Rails.application.routes.append do
554
556
  tables = []
555
557
  views = []
@@ -568,7 +570,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
568
570
 
569
571
  # %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
570
572
  # If auto-controllers and auto-models are both enabled then this makes sense:
571
- controller_prefix = (::Brick.config.path_prefix ? "#{::Brick.config.path_prefix}/" : '')
573
+ controller_prefix = (path_prefix ? "#{path_prefix}/" : '')
572
574
  ::Brick.relations.each do |k, v|
573
575
  unless !(controller_name = v.fetch(:resource, nil)&.pluralize) || existing_controllers.key?(controller_name)
574
576
  options = {}
@@ -583,9 +585,9 @@ In config/initializers/brick.rb appropriate entries would look something like:
583
585
  send(:get, "#{::Brick.api_root}#{v[:resource]}", { to: "#{controller_prefix}#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
584
586
  end
585
587
  # Now the normal routes
586
- if ::Brick.config.path_prefix
587
- # Was: send(:scope, path: ::Brick.config.path_prefix) do
588
- send(:namespace, ::Brick.config.path_prefix) do
588
+ if path_prefix
589
+ # Was: send(:scope, path: path_prefix) do
590
+ send(:namespace, path_prefix) do
589
591
  brick_routes_create.call(schema_name, controller_name, v, options)
590
592
  end
591
593
  else
@@ -298,7 +298,9 @@ if Object.const_defined?('Brick')
298
298
  # # route to go to the :index action for what would be a controller for that table. You can specify any controller
299
299
  # # name and action you wish in order to override this and have that be the default route when none other has been
300
300
  # # specified in routes.rb or elsewhere. (Or just use an empty string in order to disable this behaviour.)
301
- # Brick.default_route_fallback = 'customers' # This defaults to \"customers#index\"
301
+ # This defaults to \"customers#index\", and if there was also a prefix set called \"admin\" then it would instead
302
+ # go to \"admin/customers#index\".
303
+ # Brick.default_route_fallback = 'customers'
302
304
  # Brick.default_route_fallback = 'orders#outstanding' # Example of a non-RESTful route
303
305
  # Brick.default_route_fallback = '' # Omits setting a default route in the absence of any other
304
306
  end
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.81
4
+ version: 1.0.83
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-10-18 00:00:00.000000000 Z
11
+ date: 2022-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord