brick 1.0.129 → 1.0.131

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: 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