brick 1.0.129 → 1.0.131

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: 3b46bf80d393c0d6e4ffea3e8bfc1fbc03dc667902077efc9a887c8ab046a817
4
- data.tar.gz: 927828e46dfd9ae6b7c14aced0f814bbc1d876e5af14aa872a13d2c13af85083
3
+ metadata.gz: 6e9356edd43a6af6120f30bfbcd520383f3ae700e6ff04be11ebdd3493852ef4
4
+ data.tar.gz: a099075c53acc9bf29c718043cc619b5ab4f895366f9c7ddd14d53c8f7f432b8
5
5
  SHA512:
6
- metadata.gz: 49073fb48479c8df6805a35632ef321efa3144556feb1e2da701044ce110aa6cfcb8ced12186408929b8c3d6280ca6e393e886ba54eea87f0a622a58f9216e8e
7
- data.tar.gz: eaccf857c54a108c764e786b5e8abd0f54ff5abbcadec558138dc947a5d7e3015fc1a5510a311301947cee815d086018e535a15958357abd3463ab59ee375908
6
+ metadata.gz: 9f9df598bad9ce128a88f4fe364d568be8c38eee57e595024866d39a255b42f513a26adc68d64d14cdecd4b5d13fb7424935f17df7dbb41c3bda2ae31057a4c7
7
+ data.tar.gz: 845e5747a916a338ee494426e4ef0ac6fd5c65c3579df0210c8a80468887fac12103407acfbaee3659b3b45763f98ef0bd109935d70920b932b4fd41cc15fdc5
@@ -148,7 +148,8 @@ module ActiveRecord
148
148
  translations[parts[0..-2].join('.')] = klass
149
149
  end
150
150
  if klass&.column_names.exclude?(parts.last) &&
151
- (klass = (orig_class = klass).reflect_on_association(possible_dsl = parts.pop.to_sym)&.klass)
151
+ (klass = (orig_class = klass).reflect_on_association(possible_dsl = parts.last&.to_sym)&.klass)
152
+ parts.pop
152
153
  if prefix.empty? # Custom columns start with an empty prefix
153
154
  prefix << parts.shift until parts.empty?
154
155
  end
@@ -162,6 +163,7 @@ module ActiveRecord
162
163
  if emit_dsl
163
164
  dsl3 << "[#{prefix[1..-1].map { |p| "#{p.to_s}." }.join if prefix.length > 1}#{bracket_name}]"
164
165
  end
166
+ parts[-1] = column_names.first if parts[-1].nil? # No primary key to be found? Grab something to display!
165
167
  members << parts
166
168
  end
167
169
  end
@@ -231,7 +233,8 @@ module ActiveRecord
231
233
  if this_obj.is_a?(ActiveRecord::Base) && (obj_descrip = this_obj.class.brick_descrip(this_obj))
232
234
  this_obj = obj_descrip
233
235
  end
234
- if this_obj.is_a?(ActiveStorage::Filename) && this_obj.instance_variable_get(:@filename).nil?
236
+ if Object.const_defined?('ActiveStorage') && this_obj.is_a?(::ActiveStorage::Filename) &&
237
+ this_obj.instance_variable_get(:@filename).nil?
235
238
  this_obj.instance_variable_set(:@filename, '')
236
239
  end
237
240
  this_obj&.to_s || ''
@@ -667,6 +670,7 @@ module ActiveRecord
667
670
  link_back = []
668
671
  # Track polymorphic type field if necessary
669
672
  if hm.source_reflection.options[:as]
673
+ # Might be able to simplify as: hm.source_reflection.type
670
674
  poly_ft = [hm.source_reflection.inverse_of.foreign_type, hmt_assoc.source_reflection.class_name]
671
675
  end
672
676
  # link_back << hm.source_reflection.inverse_of.name
@@ -741,12 +745,21 @@ module ActiveRecord
741
745
  end
742
746
  else
743
747
  fk_col = (inv = hm.inverse_of)&.foreign_key || hm.foreign_key
744
- poly_type = inv.foreign_type if hm.options.key?(:as)
748
+ # %%% Might only need hm.type and not the first part :)
749
+ poly_type = inv&.foreign_type || hm.type if hm.options.key?(:as)
745
750
  pk = hm.klass.primary_key
746
751
  (pk.is_a?(Array) ? pk.first : pk) || '*'
747
752
  end
748
753
  next unless count_column # %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
749
754
 
755
+ pri_tbl = hm.active_record
756
+ pri_key = hm.options[:primary_key] || pri_tbl.primary_key
757
+ unless hm.active_record.column_names.include?(pri_key)
758
+ # %%% When this gets hit then if an attempt is made to display the ERD, it might end up being blank
759
+ nix << k
760
+ next
761
+ end
762
+
750
763
  tbl_alias = if is_mysql
751
764
  "`b_r_#{hm.name}`"
752
765
  elsif is_postgres
@@ -754,7 +767,6 @@ module ActiveRecord
754
767
  else
755
768
  "b_r_#{hm.name}"
756
769
  end
757
- pri_tbl = hm.active_record
758
770
  pri_tbl_name = is_mysql ? "`#{pri_tbl.table_name}`" : "\"#{pri_tbl.table_name.gsub('.', '"."')}\""
759
771
  pri_tbl_name = if is_mysql
760
772
  "`#{pri_tbl.table_name}`"
@@ -765,10 +777,10 @@ module ActiveRecord
765
777
  end
766
778
  on_clause = []
767
779
  hm_selects = if fk_col.is_a?(Array) # Composite key?
768
- fk_col.each_with_index { |fk_col_part, idx| on_clause << "#{tbl_alias}.#{fk_col_part} = #{pri_tbl_name}.#{pri_tbl.primary_key[idx]}" }
780
+ fk_col.each_with_index { |fk_col_part, idx| on_clause << "#{tbl_alias}.#{fk_col_part} = #{pri_tbl_name}.#{pri_key[idx]}" }
769
781
  fk_col.dup
770
782
  else
771
- on_clause << "#{tbl_alias}.#{fk_col} = #{pri_tbl_name}.#{pri_tbl.primary_key}"
783
+ on_clause << "#{tbl_alias}.#{fk_col} = #{pri_tbl_name}.#{pri_key}"
772
784
  [fk_col]
773
785
  end
774
786
  if poly_type
@@ -864,6 +876,12 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
864
876
  def brick_list
865
877
  pks = klass.primary_key.is_a?(String) ? [klass.primary_key] : klass.primary_key
866
878
  selects = pks.each_with_object([]) { |pk, s| s << pk unless s.include?(pk) }
879
+ # Get foreign keys for anything marked to be auto-preloaded, or a self-referencing JOIN
880
+ klass_cols = klass.column_names
881
+ reflect_on_all_associations.each do |a|
882
+ selects << a.foreign_key if a.belongs_to? && (preload_values.include?(a.name) ||
883
+ (!a.options[:polymorphic] && a.klass == klass && klass_cols.include?(a.foreign_key)))
884
+ end
867
885
  # ActiveStorage compatibility
868
886
  selects << 'service_name' if klass.name == 'ActiveStorage::Blob' && ActiveStorage::Blob.columns_hash.key?('service_name')
869
887
  selects << 'blob_id' if klass.name == 'ActiveStorage::Attachment' && ActiveStorage::Attachment.columns_hash.key?('blob_id')
@@ -1003,16 +1021,17 @@ Module.class_exec do
1003
1021
  is_controller = requested.end_with?('Controller')
1004
1022
  # self.name is nil when a model name is requested in an .erb file
1005
1023
  if self.name && ::Brick.config.path_prefix
1006
- camelize_prefix = ::Brick.config.path_prefix.camelize
1024
+ split_self_name.shift if (split_self_name = self.name.split('::')).first.blank?
1007
1025
  # Asking for the prefix module?
1026
+ camelize_prefix = ::Brick.config.path_prefix.camelize
1008
1027
  if self == Object && requested == camelize_prefix
1009
1028
  Object.const_set(args.first, (built_module = Module.new))
1010
1029
  puts "module #{camelize_prefix}; end\n"
1011
1030
  return built_module
1012
- end
1013
- split_self_name.shift if (split_self_name = self.name.split('::')).first.blank?
1014
- if split_self_name.first == camelize_prefix
1031
+ elsif module_parent == Object && self.name == camelize_prefix ||
1032
+ module_parent.name == camelize_prefix && module_parent.module_parent == Object
1015
1033
  split_self_name.shift # Remove the identified path prefix from the split name
1034
+ is_brick_prefix = true
1016
1035
  if is_controller
1017
1036
  brick_root = split_self_name.empty? ? self : camelize_prefix.constantize
1018
1037
  end
@@ -1045,26 +1064,30 @@ Module.class_exec do
1045
1064
  self
1046
1065
  end
1047
1066
  # puts "#{self.name} - #{args.first}"
1048
- desired_classname = (self == Object || !name) ? requested : "#{name}::#{requested}"
1049
- if ((is_defined = self.const_defined?(args.first)) && (possible = self.const_get(args.first)) &&
1050
- # Reset `possible` if it's a controller request that's not a perfect match
1051
- # Was: (possible = nil) but changed to #local_variable_set in order to suppress the "= should be ==" warning
1052
- (possible&.name == desired_classname || (is_controller && binding.local_variable_set(:possible, nil)))) ||
1053
- # Try to require the respective Ruby file
1054
- ((filename = ActiveSupport::Dependencies.search_for_file(desired_classname.underscore) ||
1055
- (self != Object && ActiveSupport::Dependencies.search_for_file((desired_classname = requested).underscore))
1056
- ) && (require_dependency(filename) || true) &&
1057
- ((possible = self.const_get(args.first)) && possible.name == desired_classname)
1058
- ) ||
1059
- # If any class has turned up so far (and we're not in the middle of eager loading)
1060
- # then return what we've found.
1061
- (is_defined && !::Brick.is_eager_loading) # Used to also have: && possible != self
1062
- if ((!brick_root && (filename || possible.instance_of?(Class))) ||
1063
- (possible.instance_of?(Module) && possible&.module_parent == self) ||
1064
- (possible.instance_of?(Class) && possible == self)) && # Are we simply searching for ourselves?
1065
- # Skip when what we found as `possible` is not related to the base class of an STI model
1066
- (!sti_base || possible.is_a?(sti_base))
1067
- return possible
1067
+ # Unless it's a Brick prefix looking for a TNP that should create a module ...
1068
+ unless (is_tnp_module = (is_brick_prefix && !is_controller && ::Brick.config.table_name_prefixes.values.include?(requested)))
1069
+ # ... first look around for an existing module or class.
1070
+ desired_classname = (self == Object || !name) ? requested : "#{name}::#{requested}"
1071
+ if ((is_defined = self.const_defined?(args.first)) && (possible = self.const_get(args.first)) &&
1072
+ # Reset `possible` if it's a controller request that's not a perfect match
1073
+ # Was: (possible = nil) but changed to #local_variable_set in order to suppress the "= should be ==" warning
1074
+ (possible&.name == desired_classname || (is_controller && binding.local_variable_set(:possible, nil)))) ||
1075
+ # Try to require the respective Ruby file
1076
+ ((filename = ActiveSupport::Dependencies.search_for_file(desired_classname.underscore) ||
1077
+ (self != Object && ActiveSupport::Dependencies.search_for_file((desired_classname = requested).underscore))
1078
+ ) && (require_dependency(filename) || true) &&
1079
+ ((possible = self.const_get(args.first)) && possible.name == desired_classname)
1080
+ ) ||
1081
+ # If any class has turned up so far (and we're not in the middle of eager loading)
1082
+ # then return what we've found.
1083
+ (is_defined && !::Brick.is_eager_loading) # Used to also have: && possible != self
1084
+ if ((!brick_root && (filename || possible.instance_of?(Class))) ||
1085
+ (possible.instance_of?(Module) && possible&.module_parent == self) ||
1086
+ (possible.instance_of?(Class) && possible == self)) && # Are we simply searching for ourselves?
1087
+ # Skip when what we found as `possible` is not related to the base class of an STI model
1088
+ (!sti_base || possible.is_a?(sti_base))
1089
+ return possible
1090
+ end
1068
1091
  end
1069
1092
  end
1070
1093
  class_name = ::Brick.namify(requested)
@@ -1099,7 +1122,7 @@ Module.class_exec do
1099
1122
  (plural_class_name = class_name.pluralize)].find { |s| Brick.db_schemas&.include?(s) }&.camelize ||
1100
1123
  (::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}::") && class_name) ||
1101
1124
  (::Brick.config.table_name_prefixes.values.include?(class_name) && class_name))
1102
- return self.const_get(schema_name) if self.const_defined?(schema_name)
1125
+ return self.const_get(schema_name) if !is_tnp_module && self.const_defined?(schema_name)
1103
1126
 
1104
1127
  # Build out a module for the schema if it's namespaced
1105
1128
  # schema_name = schema_name.camelize
@@ -1869,7 +1892,7 @@ class Object
1869
1892
  # Convert any Filename objects with nil into an empty string so that #encode can be called on them
1870
1893
  new_obj.serializable_hash.each do |k, v|
1871
1894
  new_obj.send("#{k}=", ActiveStorage::Filename.new('')) if v.is_a?(ActiveStorage::Filename) && !v.instance_variable_get(:@filename)
1872
- end
1895
+ end if Object.const_defined?('ActiveStorage')
1873
1896
  end
1874
1897
  instance_variable_set("@#{singular_table_name}".to_sym, new_obj)
1875
1898
  end
@@ -2441,7 +2464,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
2441
2464
  singular = rel_name.last
2442
2465
  end
2443
2466
  name_parts = if (tnp = ::Brick.config.table_name_prefixes
2444
- .find { |k1, _v1| singular.start_with?(k1) && singular.length > k1.length }
2467
+ &.find { |k1, _v1| singular.start_with?(k1) && singular.length > k1.length }
2445
2468
  ).present?
2446
2469
  v[:auto_prefixed_schema] = tnp.first
2447
2470
  v[:resource] = rel_name.last[(tnp_length = tnp.first.length)..-1]
@@ -225,7 +225,7 @@ function linkSchemas() {
225
225
  end
226
226
 
227
227
  # When table names have specific prefixes, automatically place them in their own module with a table_name_prefix.
228
- ::Brick.table_name_prefixes = app.config.brick.fetch(:table_name_prefixes, {})
228
+ ::Brick.config.table_name_prefixes ||= app.config.brick.fetch(:table_name_prefixes, {})
229
229
 
230
230
  # Columns to treat as being metadata for purposes of identifying associative tables for has_many :through
231
231
  ::Brick.metadata_columns = app.config.brick.fetch(:metadata_columns, ['created_at', 'updated_at', 'deleted_at'])
@@ -625,6 +625,7 @@ window.addEventListener(\"popstate\", linkSchemas);
625
625
  if (class_name = (resource_parts = resource_name.split('/')).last&.singularize)
626
626
  resource_parts[-1] = class_name # Make sure the last part, defining the class name, is singular
627
627
  begin
628
+ resource_parts.shift if resource_parts.first == ::Brick.config.path_prefix
628
629
  if (model = Object.const_get(resource_parts.map(&:camelize).join('::')))&.is_a?(Class) && (
629
630
  ['index', 'show'].include?(find_args.first) || # Everything has index and show
630
631
  # Only CUD stuff has create / update / destroy
@@ -652,7 +653,9 @@ window.addEventListener(\"popstate\", linkSchemas);
652
653
  else
653
654
  hm_assoc.active_record.name
654
655
  end
655
- keys << [hm_assoc.inverse_of.foreign_type, poly_type]
656
+ # %%% Might only need hm_assoc.type and not the first part :)
657
+ type_col = hm_assoc.inverse_of&.foreign_type || hm_assoc.type
658
+ keys << [type_col, poly_type]
656
659
  end
657
660
  keys.to_h
658
661
  end
@@ -1667,7 +1670,7 @@ end
1667
1670
  <% if (assoc = @#{obj_name}.class.reflect_on_association(:#{hm_name})).macro == :has_one &&
1668
1671
  assoc.options&.fetch(:through, nil).nil?
1669
1672
  # In order to apply DSL properly, evaluate this HO the other way around as if it were as a BT
1670
- collection = assoc.klass.where(assoc.foreign_key => @#{obj_name}.#{pk})
1673
+ collection = assoc.klass.where(assoc.foreign_key => #{pk.is_a?(String) ? "@#{obj_name}.#{pk}" : pk.map { |pk_part| "@#{obj_name}.#{pk_part}" }.inspect})
1671
1674
  collection = collection.instance_exec(&assoc.scopes.first) if assoc.scopes.present?
1672
1675
  if assoc.klass.name == 'ActiveStorage::Attachment'
1673
1676
  br_descrip = begin
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 129
8
+ TINY = 131
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
data/lib/brick.rb CHANGED
@@ -125,6 +125,7 @@ if Gem::Specification.all_names.any? { |g| g.start_with?('rails-') }
125
125
  end
126
126
  module Brick
127
127
  ALL_API_ACTIONS = [:index, :show, :create, :update, :destroy]
128
+ CURRENCY_SYMBOLS = '$£¢₵€₠ƒ¥₿₩₪₹₫₴₱₲₳₸₺₼₽៛₡₢₣₤₥₦₧₨₭₮₯₰₶₷₻₾'
128
129
 
129
130
  class << self
130
131
  def sti_models
@@ -200,9 +201,20 @@ module Brick
200
201
  end
201
202
 
202
203
  def get_bts_and_hms(model)
204
+ model_cols = model.columns_hash
205
+ pk_type = if (mpk = model.primary_key).is_a?(Array)
206
+ # Composite keys should really use: model.primary_key.map { |pk_part| model_cols[pk_part].type }
207
+ model_cols[mpk.first].type
208
+ else
209
+ mpk && model_cols[mpk].type
210
+ end
203
211
  bts, hms = model.reflect_on_all_associations.each_with_object([{}, {}]) do |a, s|
212
+ # %%% The time will come when we will support type checking of composite foreign keys!
213
+ # binding.pry if a.foreign_key.is_a?(Array)
204
214
  next unless a.polymorphic? || (!a.belongs_to? && (through = a.options[:through])) ||
205
- (a.klass && ::Brick.config.exclude_tables.exclude?(a.klass.table_name))
215
+ (a.klass && ::Brick.config.exclude_tables.exclude?(a.klass.table_name) &&
216
+ (!a.belongs_to? || model_cols[a.foreign_key]&.type == pk_type)
217
+ )
206
218
 
207
219
  if a.belongs_to?
208
220
  if a.polymorphic?
@@ -239,6 +251,15 @@ module Brick
239
251
  puts "WARNING: HMT relationship :#{a.name} in model #{model.name} has invalid source :#{a.source_reflection_name}."
240
252
  next
241
253
  end
254
+ else
255
+ if !a.options.key?(:as) && a.klass.column_names.exclude?(a.foreign_key)
256
+ options = ", #{a.options.map { |k, v| "#{k.inspect} => #{v.inspect}" }.join(', ')}" if a.options.present?
257
+ puts "WARNING: Model #{model.name} has this association:
258
+ has_many :#{a.name}#{options}
259
+ which expects column #{a.foreign_key} to exist in table #{a.klass.table_name}. This column is missing."
260
+ next
261
+
262
+ end
242
263
  end
243
264
  s.last[a.name] = a
244
265
  end
@@ -1661,7 +1682,8 @@ module ActiveRecord
1661
1682
  relation.brick_links[link_path] = if child.table.is_a?(Arel::Nodes::TableAlias)
1662
1683
  child.table.right
1663
1684
  else
1664
- result.first&.left&.table_alias || child.table_name
1685
+ # Was: result.first&.left&.table_alias || child.table_name
1686
+ child.table.table_alias || child.table_name
1665
1687
  end
1666
1688
  end
1667
1689
  result
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.129
4
+ version: 1.0.131
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-11 00:00:00.000000000 Z
11
+ date: 2023-04-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord