brick 1.0.210 → 1.0.211

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: 803d4790408a0b789ff8086b5b81346581071b260cbc528039fd8cfa8894b7ac
4
- data.tar.gz: 4503e1b9feba62d33e83f3fadc25a34b70bf9c661dc750187a3c391e3b24d5e8
3
+ metadata.gz: 7950119f788fe1cac3fde68806f2b4a1145da0cf17ff0d47363b102be9c69933
4
+ data.tar.gz: 33bb7d89a0744890d4450afde332dbd0f0635a8d3acd0beb4107cf4e44ff9718
5
5
  SHA512:
6
- metadata.gz: 1fe28c2a9ca30ef663f28fe79e65e1b2787686c6fce6d5738d9b6addd6070437411d5210e1ba641b28eae5215fb37756f3faa80f246d823d5d553c9db990a427
7
- data.tar.gz: daa9bb88c21467075717062c1ede0ab38af4207bf96d9a2495ce84f99850a4d0cb09f4c1c95017cb3862ef915fab85036397375d43518f7bee2d7bd6f1255aed
6
+ metadata.gz: 959c6ada7e03de34f566ad6059a4431ea66d2be9f12764de3edb3760074a70f3e91354e9052bc3c1a74eed4d5d7a274a78cf490195815b736b02f282ffe03d47
7
+ data.tar.gz: a87abaf8c878fecda93fc6751bae171e5194b6b63d933f83ba331191aee701859d3022754f68af3d730c25add0cc0a8174246fd18913040bed75ec700ba2b543
@@ -92,11 +92,49 @@ module ActiveRecord
92
92
  reflect_on_association(assoc).foreign_type || "#{assoc}_type"
93
93
  end
94
94
 
95
- def _brick_all_fields
96
- rtans = if respond_to?(:rich_text_association_names)
97
- rich_text_association_names&.map { |rtan| rtan.to_s.start_with?('rich_text_') ? rtan[10..-1] : rtan }
98
- end
99
- columns_hash.keys.map(&:to_sym) + (rtans || [])
95
+ def _brick_all_fields(skip_id = nil)
96
+ col_names = columns_hash.keys
97
+ # If it's a composite primary key then allow all the values through
98
+ # TODO: Should disallow any autoincrement / SERIAL columns
99
+ if skip_id && (pk_as_array = _pk_as_array).length == 1
100
+ col_names -= _pk_as_array
101
+ end
102
+ hoa, hma, rtans = _activestorage_actiontext_fields
103
+ col_names.map(&:to_sym) + hoa + hma.map { |as| { as => [] } } + rtans.values
104
+ end
105
+
106
+ # Return three lists of fields for this model --
107
+ # has_one_attached, has_many_attached, and has_rich_text
108
+ def _activestorage_actiontext_fields
109
+ fields = [[], [], {}]
110
+ if !(self <= ActiveStorage::Blob) && respond_to?(:generated_association_methods) # ActiveStorage
111
+ generated_association_methods.instance_methods.each do |method_sym|
112
+ method_str = method_sym.to_s
113
+ fields[0] << method_str[0..-13].to_sym if method_str.end_with?('_attachment=') # has_one_attached
114
+ fields[1] << method_str[0..-14].to_sym if method_str.end_with?('_attachments=') # has_many_attached
115
+ end
116
+ end
117
+ if respond_to?(:rich_text_association_names) # ActionText
118
+ rich_text_association_names&.each do |rtan| # has_rich_text
119
+ rtan_str = rtan.to_s
120
+ fields[2][rtan] = rtan_str.start_with?('rich_text_') ? rtan_str[10..-1].to_sym : rtan
121
+ end
122
+ end
123
+ fields
124
+ end
125
+
126
+ def _active_storage_name(col_name)
127
+ if Object.const_defined?('ActiveStorage') && (self <= ActiveStorage::Attachment || self <= ActiveStorage::Blob)
128
+ if (col_str = col_name.to_s).end_with?('_attachments')
129
+ col_str[0..-13]
130
+ elsif col_str.end_with?('_blobs')
131
+ col_str[0..-7]
132
+ end
133
+ end
134
+ end
135
+
136
+ def _pk_as_array
137
+ self.primary_key.is_a?(Array) ? self.primary_key : [self.primary_key]
100
138
  end
101
139
 
102
140
  def _br_quoted_name(name)
@@ -147,8 +185,8 @@ module ActiveRecord
147
185
  skip_columns = _brick_get_fks + (::Brick.config.metadata_columns || []) + [primary_key]
148
186
  dsl = if (descrip_col = columns.find { |c| [:boolean, :binary, :xml].exclude?(c.type) && skip_columns.exclude?(c.name) })
149
187
  "[#{descrip_col.name}]"
150
- elsif (pk_parts = self.primary_key.is_a?(Array) ? self.primary_key : [self.primary_key])
151
- "#{name} ##{pk_parts.map { |pk_part| "[#{pk_part}]" }.join(', ')}"
188
+ else
189
+ "#{name} ##{_pk_as_array.map { |pk_part| "[#{pk_part}]" }.join(', ')}"
152
190
  end
153
191
  ::Brick.config.model_descrips[name] = dsl
154
192
  end
@@ -841,6 +879,8 @@ module ActiveRecord
841
879
  end
842
880
  through_sources.push(src_ref) unless src_ref.belongs_to?
843
881
  from_clause = +"#{_br_quoted_name(through_sources.first.table_name)} br_t0"
882
+ # ActiveStorage will not get the correct count unless we do some extra filtering later
883
+ tbl_nm = 'br_t0' if Object.const_defined?('ActiveStorage') && through_sources.first.klass <= ActiveStorage::Attachment
844
884
  fk_col = through_sources.shift.foreign_key
845
885
 
846
886
  idx = 0
@@ -934,10 +974,14 @@ module ActiveRecord
934
974
  tbl_nm = hm.macro == :has_and_belongs_to_many ? hm.join_table : hm.table_name
935
975
  hm_table_name = _br_quoted_name(tbl_nm)
936
976
  end
977
+ # ActiveStorage has_many_attached needs a bit more filtering
978
+ if (k_str = hm.klass._active_storage_name(k))
979
+ where_ct_clause = "WHERE #{_br_quoted_name("#{tbl_nm}.name")} = '#{k_str}' "
980
+ end
937
981
  group_bys = ::Brick.is_oracle || is_mssql ? hm_selects : (1..hm_selects.length).to_a
938
982
  join_clause = "LEFT OUTER
939
983
  JOIN (SELECT #{hm_selects.map { |s| _br_quoted_name("#{'br_t0.' if from_clause}#{s}") }.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}#{_br_quoted_name(count_column)
940
- }) AS c_t_ FROM #{from_clause || hm_table_name} GROUP BY #{group_bys.join(', ')}) #{_br_quoted_name(tbl_alias)}"
984
+ }) AS c_t_ FROM #{from_clause || hm_table_name} #{where_ct_clause}GROUP BY #{group_bys.join(', ')}) #{_br_quoted_name(tbl_alias)}"
941
985
  self.joins_values |= ["#{join_clause} ON #{on_clause.join(' AND ')}"] # Same as: joins!(...)
942
986
  end unless cust_col_override
943
987
  while (n = nix.pop)
@@ -2369,6 +2413,8 @@ class Object
2369
2413
  end
2370
2414
  end
2371
2415
 
2416
+ params_name_sym = (params_name = "#{singular_table_name}_params").to_sym
2417
+
2372
2418
  # By default, views get marked as read-only
2373
2419
  # unless model.readonly # (relation = relations[model.table_name]).key?(:isView)
2374
2420
  code << " def new\n"
@@ -2376,7 +2422,11 @@ class Object
2376
2422
  code << " end\n"
2377
2423
  self.define_method :new do
2378
2424
  _schema, @_is_show_schema_list = ::Brick.set_db_schema(params)
2379
- new_params = model.attribute_names.each_with_object({}) do |a, s|
2425
+ new_params = begin
2426
+ send(params_name_sym)
2427
+ rescue
2428
+ end
2429
+ new_params ||= model.attribute_names.each_with_object({}) do |a, s|
2380
2430
  if (val = params["__#{a}"])
2381
2431
  # val = case new_obj.class.column_for_attribute(a).type
2382
2432
  # when :datetime, :date, :time, :timestamp
@@ -2397,8 +2447,6 @@ class Object
2397
2447
  add_csp_hash
2398
2448
  end
2399
2449
 
2400
- params_name_sym = (params_name = "#{singular_table_name}_params").to_sym
2401
-
2402
2450
  code << " def create\n"
2403
2451
  code << " @#{singular_table_name} = #{model.name}.create(#{params_name})\n"
2404
2452
  code << " end\n"
@@ -2415,8 +2463,7 @@ class Object
2415
2463
  end
2416
2464
  render json: { result: ::Brick.unexclude_column(table_name, col) }
2417
2465
  else
2418
- @_lookup_context.instance_variable_set("@#{singular_table_name}".to_sym,
2419
- (created_obj = model.send(:create, send(params_name_sym))))
2466
+ created_obj = model.send(:create, send(params_name_sym))
2420
2467
  @_lookup_context.instance_variable_set(:@_brick_model, model)
2421
2468
  if created_obj.errors.empty?
2422
2469
  index
@@ -2493,7 +2540,16 @@ class Object
2493
2540
  if (upd_hash ||= upd_params).fetch(model.inheritance_column, nil)&.strip == ''
2494
2541
  upd_hash[model.inheritance_column] = nil
2495
2542
  end
2496
- obj.send(:update, upd_hash || upd_params)
2543
+ # Do not clear out a has_many_attached field if it already has an entry and nothing is supplied
2544
+ hoa, hma, rtans = model._activestorage_actiontext_fields
2545
+ all_params = params[singular_table_name]
2546
+ hma.each do |hma_field|
2547
+ if upd_hash.fetch(hma_field) == [''] && # No new attachments...
2548
+ all_params&.fetch("_brick_attached_#{hma_field}", nil) # ...and there is something existing
2549
+ upd_hash.delete(hma_field)
2550
+ end
2551
+ end
2552
+ obj.send(:update, upd_hash)
2497
2553
  if obj.errors.any? # Surface errors to the user in a flash message
2498
2554
  flash.now.alert = (obj.errors.errors.map { |err| "<b>#{err.attribute}</b> #{err.message}" }.join(', '))
2499
2555
  end
@@ -2543,7 +2599,7 @@ class Object
2543
2599
 
2544
2600
  if is_need_params
2545
2601
  code << " def #{params_name}\n"
2546
- permits_txt = model._brick_find_permits(model, permits = model._brick_all_fields)
2602
+ permits_txt = model._brick_find_permits(model, permits = model._brick_all_fields(true))
2547
2603
  code << " params.require(:#{require_name = model.name.underscore.tr('/', '_')
2548
2604
  }).permit(#{permits_txt.map(&:inspect).join(', ')})\n"
2549
2605
  code << " end\n"
@@ -641,6 +641,11 @@ window.addEventListener(\"popstate\", linkSchemas);
641
641
  type_col = hm_assoc.inverse_of&.foreign_type || hm_assoc.type
642
642
  keys << [type_col, poly_type]
643
643
  end
644
+ # ActiveStorage has_one_attached and has_many_attached needs additional filtering on the name
645
+ if (as_name = hm_assoc.klass&._active_storage_name(hm_assoc.name)) # ActiveStorage HMT
646
+ prefix = 'attachments.' if hm_assoc.through_reflection&.klass&.<= ActiveStorage::Attachment
647
+ keys << ["#{prefix}name", as_name]
648
+ end
644
649
  keys.to_h
645
650
  end
646
651
 
@@ -1506,7 +1511,7 @@ end
1506
1511
 
1507
1512
  if (pk = hm.first.klass.primary_key)
1508
1513
  hm_singular_name = (hm_name = hm.first.name.to_s).singularize.underscore
1509
- obj_br_pk = (pk.is_a?(Array) ? pk : [pk]).each_with_object([]) { |pk_part, s| s << "br_#{hm_singular_name}.#{pk_part}" }.join(', ')
1514
+ obj_br_pk = hm.first.klass._pk_as_array.map { |pk_part| "br_#{hm_singular_name}.#{pk_part}" }.join(', ')
1510
1515
  poly_fix = if (poly_type = (hm.first.options[:as] && hm.first.type))
1511
1516
  "
1512
1517
  # Let's fix an unexpected \"feature\" of AR -- when going through a polymorphic has_many
@@ -110,6 +110,41 @@ module Brick::Rails::FormBuilder
110
110
  ::Brick::Rails.display_binary(val)
111
111
  end
112
112
  end
113
+ when :file, :files
114
+ if attached = begin
115
+ self.object.send(method)
116
+ rescue
117
+ end
118
+ # Show any existing image(s)
119
+ existing = []
120
+ got_one = nil
121
+ (attached.respond_to?(:attachments) ? attached.attachments : [attached]).each do |attachment|
122
+ next unless (blob = attachment.blob)
123
+
124
+ existing << blob.key
125
+ out << "#{blob.filename}<br>"
126
+ url = begin
127
+ self.object.send(method)&.url
128
+ rescue StandardError => e
129
+ # Another possible option:
130
+ # Rails.application.routes.url_helpers.rails_blob_path(attachment, only_path: true)
131
+ Rails.application.routes.url_helpers.rails_storage_proxy_path(attachment, only_path: true)
132
+ end
133
+ out << if url
134
+ "<img src=\"#{url}\" title=\"#{val}\">"
135
+ else # Convert the raw binary to a Base64 image
136
+ ::Brick::Rails.display_binary(attachment.download, 500_000)
137
+ end
138
+ got_one = true
139
+ out << '<br>'
140
+ end
141
+ out << 'Update: ' if got_one
142
+ end
143
+ out << self.hidden_field("_brick_attached_#{method}", value: existing.join(',')) unless existing.blank?
144
+ # Render a "Choose File(s)" input element
145
+ args = [method.to_sym]
146
+ args << { multiple: true } if col&.type == :files
147
+ out << self.file_field(*args)
113
148
  when :primary_key
114
149
  is_revert = false
115
150
  when :json, :jsonb
@@ -445,19 +445,25 @@ function onImagesLoaded(event) {
445
445
  obj.send("#{model.brick_foreign_type(v.first)}=", v[1].first&.first&.name)
446
446
  end
447
447
  end if obj.new_record?
448
- rtans = model.rich_text_association_names if model.respond_to?(:rich_text_association_names)
449
- (model.column_names + (rtans || [])).each do |k|
448
+ hoa, hma, rtans = model._activestorage_actiontext_fields
449
+ (model.column_names + hoa + hma + rtans.keys).each do |k|
450
450
  pk_pos = (pk.index(k)&.+ 1)
451
451
  next if (pk_pos && pk.length == 1 && !bts.key?(k)) ||
452
452
  ::Brick.config.metadata_columns.include?(k)
453
453
 
454
454
  col = model.columns_hash[k]
455
- if !col && rtans&.include?(k)
456
- k = k[10..-1] if k.start_with?('rich_text_')
457
- col = (rt_col ||= ActiveRecord::ConnectionAdapters::Column.new(
458
- '', nil, ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(sql_type: 'varchar', type: :text)
459
- )
460
- )
455
+ if !col
456
+ kwargs = if hoa.include?(k) # has_one_attached
457
+ { sql_type: 'binary', type: :file }
458
+ elsif hma.include?(k) # has_many_attached
459
+ { sql_type: 'binary', type: :files }
460
+ elsif rtans&.key?(k) # has_rich_text
461
+ k = rtans[k]
462
+ { sql_type: 'varchar', type: :text }
463
+ end
464
+ col = (ActiveRecord::ConnectionAdapters::Column.new(
465
+ '', nil, ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(**kwargs)
466
+ )) if kwargs
461
467
  end
462
468
  val = obj.attributes[k]
463
469
  out << "
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 210
8
+ TINY = 211
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.210
4
+ version: 1.0.211
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-22 00:00:00.000000000 Z
11
+ date: 2024-03-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord