brick 1.0.130 → 1.0.132

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: 902948e0d502bc9c1b604f3e99b975ab8c7d42c378888280d84dac6aef2aa7d0
4
- data.tar.gz: 82988754dcae3ccc67eb5997531b3fa218ecf364f076ecf221fac201412baff0
3
+ metadata.gz: '08cc0b19ab0838b667534f4de64f3a7045bf395202a3602981a20ececdc91ab5'
4
+ data.tar.gz: 4b976489a05bb26c3702746700025c6bad3bb930fc129ae897bc9e1596a50901
5
5
  SHA512:
6
- metadata.gz: 780184ebc02c020389bc2239e115e885df056fa6895ed32bd35b5315f2e55f86deb679f2966e7290118bc54f4d50478b4240d7d77470d27a5902af22948d83f1
7
- data.tar.gz: 39c37a9db2c7c392209491e1609f8ab4c2b64d27371452b60310d326bd462b5e653196697d0faf8f4bf801793400e073bf73f2ed72a38e9c66bd9b8e98d1e778
6
+ metadata.gz: 580db611d4ab9e2bf7ab0ed62a90940b90a4818619a9a91788802f017845c0aef3c8da1b01ea8a6a87c8e1af8378dc5c0d030396ebebba05d101b46ca0bd4495
7
+ data.tar.gz: fe876de91852b7213fa4d35b378935c7f18e18ae09ff5058f0d34b32dcb3c802a15d1c2cc79f63f7b5eac4177b7bbbeae43603e55499447cfee9e1d3556f64fa
@@ -670,6 +670,7 @@ module ActiveRecord
670
670
  link_back = []
671
671
  # Track polymorphic type field if necessary
672
672
  if hm.source_reflection.options[:as]
673
+ # Might be able to simplify as: hm.source_reflection.type
673
674
  poly_ft = [hm.source_reflection.inverse_of.foreign_type, hmt_assoc.source_reflection.class_name]
674
675
  end
675
676
  # link_back << hm.source_reflection.inverse_of.name
@@ -753,7 +754,8 @@ module ActiveRecord
753
754
 
754
755
  pri_tbl = hm.active_record
755
756
  pri_key = hm.options[:primary_key] || pri_tbl.primary_key
756
- unless hm.klass.column_names.include?(pri_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
757
759
  nix << k
758
760
  next
759
761
  end
@@ -874,6 +876,12 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
874
876
  def brick_list
875
877
  pks = klass.primary_key.is_a?(String) ? [klass.primary_key] : klass.primary_key
876
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
877
885
  # ActiveStorage compatibility
878
886
  selects << 'service_name' if klass.name == 'ActiveStorage::Blob' && ActiveStorage::Blob.columns_hash.key?('service_name')
879
887
  selects << 'blob_id' if klass.name == 'ActiveStorage::Attachment' && ActiveStorage::Attachment.columns_hash.key?('blob_id')
@@ -1013,16 +1021,17 @@ Module.class_exec do
1013
1021
  is_controller = requested.end_with?('Controller')
1014
1022
  # self.name is nil when a model name is requested in an .erb file
1015
1023
  if self.name && ::Brick.config.path_prefix
1016
- camelize_prefix = ::Brick.config.path_prefix.camelize
1024
+ split_self_name.shift if (split_self_name = self.name.split('::')).first.blank?
1017
1025
  # Asking for the prefix module?
1026
+ camelize_prefix = ::Brick.config.path_prefix.camelize
1018
1027
  if self == Object && requested == camelize_prefix
1019
1028
  Object.const_set(args.first, (built_module = Module.new))
1020
1029
  puts "module #{camelize_prefix}; end\n"
1021
1030
  return built_module
1022
- end
1023
- split_self_name.shift if (split_self_name = self.name.split('::')).first.blank?
1024
- 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
1025
1033
  split_self_name.shift # Remove the identified path prefix from the split name
1034
+ is_brick_prefix = true
1026
1035
  if is_controller
1027
1036
  brick_root = split_self_name.empty? ? self : camelize_prefix.constantize
1028
1037
  end
@@ -1055,26 +1064,30 @@ Module.class_exec do
1055
1064
  self
1056
1065
  end
1057
1066
  # puts "#{self.name} - #{args.first}"
1058
- desired_classname = (self == Object || !name) ? requested : "#{name}::#{requested}"
1059
- if ((is_defined = self.const_defined?(args.first)) && (possible = self.const_get(args.first)) &&
1060
- # Reset `possible` if it's a controller request that's not a perfect match
1061
- # Was: (possible = nil) but changed to #local_variable_set in order to suppress the "= should be ==" warning
1062
- (possible&.name == desired_classname || (is_controller && binding.local_variable_set(:possible, nil)))) ||
1063
- # Try to require the respective Ruby file
1064
- ((filename = ActiveSupport::Dependencies.search_for_file(desired_classname.underscore) ||
1065
- (self != Object && ActiveSupport::Dependencies.search_for_file((desired_classname = requested).underscore))
1066
- ) && (require_dependency(filename) || true) &&
1067
- ((possible = self.const_get(args.first)) && possible.name == desired_classname)
1068
- ) ||
1069
- # If any class has turned up so far (and we're not in the middle of eager loading)
1070
- # then return what we've found.
1071
- (is_defined && !::Brick.is_eager_loading) # Used to also have: && possible != self
1072
- if ((!brick_root && (filename || possible.instance_of?(Class))) ||
1073
- (possible.instance_of?(Module) && possible&.module_parent == self) ||
1074
- (possible.instance_of?(Class) && possible == self)) && # Are we simply searching for ourselves?
1075
- # Skip when what we found as `possible` is not related to the base class of an STI model
1076
- (!sti_base || possible.is_a?(sti_base))
1077
- 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
1078
1091
  end
1079
1092
  end
1080
1093
  class_name = ::Brick.namify(requested)
@@ -1109,7 +1122,7 @@ Module.class_exec do
1109
1122
  (plural_class_name = class_name.pluralize)].find { |s| Brick.db_schemas&.include?(s) }&.camelize ||
1110
1123
  (::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}::") && class_name) ||
1111
1124
  (::Brick.config.table_name_prefixes.values.include?(class_name) && class_name))
1112
- 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)
1113
1126
 
1114
1127
  # Build out a module for the schema if it's namespaced
1115
1128
  # schema_name = schema_name.camelize
@@ -3,7 +3,7 @@
3
3
  module Brick
4
4
  module Rails
5
5
  class << self
6
- def display_value(col_type, val)
6
+ def display_value(col_type, val, lat_lng = nil)
7
7
  is_mssql_geography = nil
8
8
  # Some binary thing that really looks like a Microsoft-encoded WGS84 point? (With the first two bytes, E6 10, indicating an EPSG code of 4326)
9
9
  if col_type == :binary && val && val.length < 31 && (val.length - 6) % 8 == 0 && val[0..5].bytes == [230, 16, 0, 0, 1, 12]
@@ -61,14 +61,19 @@ module Brick
61
61
  ::Brick::Rails.display_binary(val)
62
62
  else
63
63
  if col_type
64
- ::Brick::Rails::FormBuilder.hide_bcrypt(val, col_type == :xml)
64
+ if lat_lng
65
+ # Create a link to this style of Google maps URL: https://www.google.com/maps/place/38.7071296+-121.2810649/@38.7071296,-121.2810649,12z
66
+ "<a href=\"https://www.google.com/maps/place/#{lat_lng.first}+#{lat_lng.last}/@#{lat_lng.first},#{lat_lng.last},12z\" target=\"blank\">#{val}</a>"
67
+ else
68
+ ::Brick::Rails::FormBuilder.hide_bcrypt(val, col_type == :xml)
69
+ end
65
70
  else
66
71
  '?'
67
72
  end
68
73
  end
69
74
  end
70
75
 
71
- def display_binary(val)
76
+ def display_binary(val, max_size = 100_000)
72
77
  return unless val
73
78
 
74
79
  @image_signatures ||= { (+"\xFF\xD8\xFF\xEE").force_encoding('ASCII-8BIT') => 'jpeg',
@@ -94,15 +99,12 @@ module Brick
94
99
  val = val[object_start...object_start + real_object_size]
95
100
  end
96
101
 
97
- if (signature = @image_signatures.find { |k, _v| val[0...k.length] == k }) ||
98
- (val[0..3] == 'RIFF' && val[8..11] == 'WEBP' && (signature = 'webp'))
99
- if val.length < 500_000
100
- "<img src=\"data:image/#{signature.last};base64,#{Base64.encode64(val)}\">"
101
- else
102
- "&lt;&nbsp;#{signature.last} image, #{val.length} bytes&nbsp;>"
103
- end
102
+ if ((signature = @image_signatures.find { |k, _v| val[0...k.length] == k }&.last) ||
103
+ (val[0..3] == 'RIFF' && val[8..11] == 'WEBP' && binding.local_variable_set(:signature, 'webp'))) &&
104
+ val.length < max_size
105
+ "<img src=\"data:image/#{signature.last};base64,#{Base64.encode64(val)}\">"
104
106
  else
105
- "&lt;&nbsp;Binary, #{val.length} bytes&nbsp;>"
107
+ "&lt;&nbsp;#{signature ? "#{signature} image" : 'Binary'}, #{val.length} bytes&nbsp;>"
106
108
  end
107
109
  end
108
110
  end
@@ -225,7 +227,7 @@ function linkSchemas() {
225
227
  end
226
228
 
227
229
  # 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, {})
230
+ ::Brick.config.table_name_prefixes ||= app.config.brick.fetch(:table_name_prefixes, {})
229
231
 
230
232
  # Columns to treat as being metadata for purposes of identifying associative tables for has_many :through
231
233
  ::Brick.metadata_columns = app.config.brick.fetch(:metadata_columns, ['created_at', 'updated_at', 'deleted_at'])
@@ -625,6 +627,7 @@ window.addEventListener(\"popstate\", linkSchemas);
625
627
  if (class_name = (resource_parts = resource_name.split('/')).last&.singularize)
626
628
  resource_parts[-1] = class_name # Make sure the last part, defining the class name, is singular
627
629
  begin
630
+ resource_parts.shift if resource_parts.first == ::Brick.config.path_prefix
628
631
  if (model = Object.const_get(resource_parts.map(&:camelize).join('::')))&.is_a?(Class) && (
629
632
  ['index', 'show'].include?(find_args.first) || # Everything has index and show
630
633
  # Only CUD stuff has create / update / destroy
@@ -1635,10 +1638,10 @@ end
1635
1638
  <tr><td colspan=\"2\">(No displayable fields)</td></tr>
1636
1639
  <% end %>
1637
1640
  </table>#{
1638
- "<%= binary = begin
1639
- ::Brick::Rails.display_binary(obj&.blob&.download)&.html_safe
1640
- rescue
1641
- end %>" if model_name == 'ActiveStorage::Attachment'}
1641
+ "<%= begin
1642
+ ::Brick::Rails.display_binary(obj&.blob&.download, 500_000)&.html_safe
1643
+ rescue
1644
+ end %>" if model_name == 'ActiveStorage::Attachment'}
1642
1645
  <% end %>
1643
1646
 
1644
1647
  #{unless args.first == 'new'
@@ -1669,11 +1672,11 @@ end
1669
1672
  <% if (assoc = @#{obj_name}.class.reflect_on_association(:#{hm_name})).macro == :has_one &&
1670
1673
  assoc.options&.fetch(:through, nil).nil?
1671
1674
  # In order to apply DSL properly, evaluate this HO the other way around as if it were as a BT
1672
- collection = assoc.klass.where(assoc.foreign_key => @#{obj_name}.#{pk})
1675
+ collection = assoc.klass.where(assoc.foreign_key => #{pk.is_a?(String) ? "@#{obj_name}.#{pk}" : pk.map { |pk_part| "@#{obj_name}.#{pk_part}" }.inspect})
1673
1676
  collection = collection.instance_exec(&assoc.scopes.first) if assoc.scopes.present?
1674
1677
  if assoc.klass.name == 'ActiveStorage::Attachment'
1675
1678
  br_descrip = begin
1676
- ::Brick::Rails.display_binary(obj.send(assoc.name)&.blob&.download)&.html_safe
1679
+ ::Brick::Rails.display_binary(obj.send(assoc.name)&.blob&.download, 500_000)&.html_safe
1677
1680
  rescue
1678
1681
  end
1679
1682
  end
@@ -139,8 +139,19 @@ module Brick::Rails::FormTags
139
139
  out << if @_brick_monetized_attributes&.include?(col_name)
140
140
  val ? Money.new(val.to_i).format : ''
141
141
  else
142
+ lat_lng = if [:float, :decimal].include?(col.type) &&
143
+ (
144
+ ((col_name == 'latitude' && obj.respond_to?('longitude') && (lng = obj.send('longitude')) && lng.is_a?(Numeric) && (lat = val)) ||
145
+ (col_name == 'longitude' && obj.respond_to?('latitude') && (lat = obj.send('latitude')) && lat.is_a?(Numeric) && (lng = val))
146
+ ) ||
147
+ ((col_name == 'lat' && obj.respond_to?('lng') && (lng = obj.send('lng')) && lng.is_a?(Numeric) && (lat = val)) ||
148
+ (col_name == 'lng' && obj.respond_to?('lat') && (lat = obj.send('lat')) && lat.is_a?(Numeric) && (lng = val))
149
+ )
150
+ )
151
+ [lat, lng]
152
+ end
142
153
  col_type = col&.sql_type == 'geography' ? col.sql_type : col&.type
143
- ::Brick::Rails.display_value(col_type || col&.sql_type, val).to_s
154
+ ::Brick::Rails.display_value(col_type || col&.sql_type, val, lat_lng).to_s
144
155
  end
145
156
  elsif cust_col
146
157
  data = cust_col.first.map { |cc_part| obj.send(cc_part.last) }
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 130
8
+ TINY = 132
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
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.130
4
+ version: 1.0.132
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-13 00:00:00.000000000 Z
11
+ date: 2023-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord