brick 1.0.210 → 1.0.211

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