habarico 7.3.0.pre.beta.11 → 7.3.0.pre.beta.13

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: 6c0c12a78eabc5ec2b734b49fe2631728bebfaaf0dccab0af628c195dc333880
4
- data.tar.gz: 5bd53485346b0d43bef3791d74c69603a5b6ae8f005fed3eebacabeb4479affa
3
+ metadata.gz: d23f057b4303faf11bdd33bd6bcc7d0584afd1da5b4b732c3783480062445eb9
4
+ data.tar.gz: 18715b9bc472670e094686c12fff65200a4dfdf9ff0a1bbadb24a43c8a92de71
5
5
  SHA512:
6
- metadata.gz: 554f357c1b5c47e163d4cbbb9fc441a921a116a05d360e3a01351f8cd120df1135603d512b31c92dd745ecb704347f478b4093b1d657568559407c50d8e66d5e
7
- data.tar.gz: f1276d2569f91a2365dcdbf07d0c66d1eee8d24cdf39e9d34212b685f788baba7c70686aec9eca61ce0f508edc1ff870819ab5ff63ae2b6483dd729a421071d0
6
+ metadata.gz: 42ceff926802b0d9fc94beb97ddf28a72dceea6c55ec8cefd60c0a538f126ea7c21dc426758bfba02002314644c05347d851bb7e2c27bd7702dc7f9d7707140f
7
+ data.tar.gz: 66fcb4c6b8ac4446a7d376656de02b1ba2ecc99c568ffbdb27db96ccdeed9b297498f738f5ecf1bcd9488db0036c1da2a315324c34279697c4091127b5335da5
data/VERSION CHANGED
@@ -1 +1 @@
1
- 7.3.0-beta.11
1
+ 7.3.0-beta.13
@@ -94,7 +94,7 @@ module Habarico
94
94
  nil # not possible to generate dynamic render without FieldReference
95
95
  end
96
96
 
97
- # Largest file (bytes) this `file_attr` accepts, derived from its own
97
+ # Largest file (bytes) this file field accepts, derived from its own
98
98
  # `active_storage_validations` size declaration (HAB-966). `habarico_enterprise.uploads`, when
99
99
  # installed, additionally folds in the project-wide upload size ceiling for a field a
100
100
  # chunked-upload provider serves - see `Habarico::FileRulesResolver`.
@@ -105,7 +105,7 @@ module Habarico
105
105
  file_rules(model, attr_name).max_size
106
106
  end
107
107
 
108
- # File types this `file_attr` accepts, derived from its own `active_storage_validations`
108
+ # File types this file field accepts, derived from its own `active_storage_validations`
109
109
  # content-type declaration (HAB-966).
110
110
  def generate_accept_at(model, attr_name)
111
111
  file_rules(model, attr_name).accept
@@ -46,7 +46,7 @@ module Habarico
46
46
  sort_by: generate_sort_by(model, field_name, attr_name, variant),
47
47
  reference: Habarico::ResourceReference.build(generate_reference(model, field_name, attr_name, variant)),
48
48
  enum: generate_enum(model, field_name, attr_name, variant),
49
- items: generate_items(model, field_name, attr_name, variant, subfields),
49
+ items: generate_items(model, field_name, attr_name, variant, subfields, field_reference: field_reference),
50
50
  properties: generate_properties(model, field_name, attr_name, variant, subfields),
51
51
  options: generate_options(model, field_name, attr_name, variant),
52
52
  )
@@ -301,10 +301,22 @@ module Habarico
301
301
  nil
302
302
  end
303
303
 
304
- def generate_items(model, field_name, attr_name, _variant, subfields)
304
+ def generate_items(model, field_name, attr_name, _variant, subfields, field_reference: nil)
305
305
  name = ''
306
306
  label = generate_label(model, field_name, attr_name, nil)
307
307
 
308
+ # "Append only" file array attribute is a list of uploads, each item a file the frontend uploads
309
+ if Habarico::ReflectionHelper.is_append_only_file_array_attr?(model, attr_name)
310
+ return Habarico::FieldSchema.new(
311
+ name: name,
312
+ label: label,
313
+ type: 'object',
314
+ format: model.find_file_array_attr(attr_name).fetch(:nature) == :image ? 'base64-image' : 'base64-file',
315
+ properties: fields_schema_generator.generate(Eluvia::File, %i[type url filename size content]),
316
+ options: field_options_generator.generate(model, attr_name, field_reference),
317
+ )
318
+ end
319
+
308
320
  # Manually declared "has many" reference is interpreted as array of relations, so we need to define items schema
309
321
  if Habarico::ReflectionHelper.is_has_many_reference?(model, field_name)
310
322
  reference = model.find_reference(field_name)
@@ -374,6 +386,9 @@ module Habarico
374
386
  end
375
387
 
376
388
  def generate_options(model, _field_name, attr_name, _variant)
389
+ # The upload options of an append-only file array are on its items, where the frontend reads them
390
+ return nil if Habarico::ReflectionHelper.is_append_only_file_array_attr?(model, attr_name)
391
+
377
392
  field_options_generator.generate(model, attr_name, nil)
378
393
  end
379
394
 
@@ -21,6 +21,11 @@ module Habarico
21
21
  model_class.respond_to?(:has_reference?) && model_class.has_reference?(field_name, type: :has_many)
22
22
  end
23
23
 
24
+ def self.is_append_only_file_array_attr?(model_class, field_name)
25
+ model_class.respond_to?(:has_file_array_attr?) && model_class.has_file_array_attr?(field_name) &&
26
+ model_class.find_file_array_attr(field_name).fetch(:append_only)
27
+ end
28
+
24
29
  # *************************************************************************
25
30
  # Active model reflection methods
26
31
  # *************************************************************************
@@ -1,7 +1,7 @@
1
1
  require 'marcel'
2
2
 
3
3
  module Habarico
4
- # The file rules of one `file_attr`: the allowed content types and the largest size its
4
+ # The file rules of one `file_attr` or `file_array_attr`: the allowed content types and the largest size its
5
5
  # `active_storage_validations` declarations admit - the rules a value is measured against, never
6
6
  # a value's own claimed properties. `allowed_content_types` is nil when the field declares no
7
7
  # content-type restriction; `max_size` is nil when it declares no upper bound.
@@ -22,12 +22,13 @@ module Habarico
22
22
  end
23
23
  end
24
24
 
25
- # No declarations at all - what an unknown model, a non-`file_attr` attribute, or a host
26
- # without `active_storage_validations` resolves to.
25
+ # No declarations at all - what an unknown model, an attribute that is not a file field, or a
26
+ # host without `active_storage_validations` resolves to.
27
27
  FileRules::NONE = FileRules.new(allowed_content_types: nil, max_size: nil).freeze
28
28
 
29
- # Resolves the `FileRules` of a model's `file_attr` from the host's `active_storage_validations`
30
- # declarations on its `underlying_attachment`. Mirrors the Python package's
29
+ # Resolves the `FileRules` of a model's `file_attr` or `file_array_attr` from the host's
30
+ # `active_storage_validations` declarations on its `underlying_attachment` /
31
+ # `underlying_attachments`. Mirrors the Python package's
31
32
  # `habarico.validators.content_types` / `habarico.validators.files`.
32
33
  #
33
34
  # `active_storage_validations` is deliberately NOT a dependency of this gem: a host that does not
@@ -38,22 +39,41 @@ module Habarico
38
39
  end
39
40
 
40
41
  # The `FileRules` of `attr_name`; `FileRules::NONE` when there is nothing to derive: no model,
41
- # the attribute is not a `file_attr` or has no `underlying_attachment`, or
42
- # `active_storage_validations` is not installed.
42
+ # the attribute is neither file field or has no underlying attachment, or
43
+ # `active_storage_validations` is not installed. An image `file_array_attr` keeps only the
44
+ # image types of its attachment's declaration, which may be shared with a file array filtered
45
+ # to the other types.
43
46
  def resolve(attr_name)
44
47
  return FileRules::NONE if @model.nil? || attr_name.blank?
45
- return FileRules::NONE unless @model.respond_to?(:has_file_attr?) && @model.has_file_attr?(attr_name)
46
48
  return FileRules::NONE unless @model.respond_to?(:validators_on)
47
49
 
48
- attachment = @model.find_file_attr(attr_name)&.fetch(:underlying_attachment, nil)
50
+ attachment, images_only = underlying_attachment(attr_name)
49
51
  return FileRules::NONE if attachment.blank?
50
52
 
51
53
  validators = @model.validators_on(attachment)
52
- FileRules.new(allowed_content_types: content_types_from(validators), max_size: max_size_from(validators))
54
+ content_types = content_types_from(validators)
55
+ content_types = image_content_types(content_types) if images_only
56
+ FileRules.new(allowed_content_types: content_types, max_size: max_size_from(validators))
53
57
  end
54
58
 
55
59
  private
56
60
 
61
+ # The attachment name behind a file attribute, and whether only its image types apply.
62
+ def underlying_attachment(attr_name)
63
+ if @model.respond_to?(:has_file_attr?) && @model.has_file_attr?(attr_name)
64
+ [@model.find_file_attr(attr_name)&.fetch(:underlying_attachment, nil), false]
65
+ elsif @model.respond_to?(:has_file_array_attr?) && @model.has_file_array_attr?(attr_name)
66
+ spec = @model.find_file_array_attr(attr_name)
67
+ [spec[:underlying_attachments], spec[:nature] == :image]
68
+ end
69
+ end
70
+
71
+ def image_content_types(content_types)
72
+ return nil if content_types.nil?
73
+
74
+ content_types.select { |type| type.is_a?(::Regexp) || type.to_s.start_with?('image/') }.presence
75
+ end
76
+
57
77
  # The allowed content types of an `ActiveStorageValidations::ContentTypeValidator`, normalized
58
78
  # through Marcel exactly as the validator itself does at save time (`:png` and `'png'` both
59
79
  # become `'image/png'`), or nil when the field declares none.
@@ -9,7 +9,7 @@ module Habarico
9
9
  # `params` narrowed to the resource's `fields` and `write_only_fields`.
10
10
  def resolve(params)
11
11
  write_fields = (@definition.fields + @definition.write_only_fields).uniq
12
- params.permit(*write_fields.map { |field| permit_spec(field) })
12
+ params.permit(*write_fields.map { |field| permit_spec(field) }.uniq)
13
13
  end
14
14
 
15
15
  # A 422 with each error of `record` under every name its attribute is written by (an amount as
@@ -26,16 +26,22 @@ module Habarico
26
26
  private
27
27
 
28
28
  def permit_spec(field)
29
+ # Normalize Symbol/String/Hash/FieldReference entries so `permit` never receives a FieldReference.
30
+ name = Habarico::AttributesHelper.field_name(field)
31
+ model = @definition.model
32
+
33
+ # An append-only `file_array_attr` takes a list of base64 `{filename:, content:}` uploads,
34
+ # whatever subfields its declaration lists for reading.
35
+ return { name => %i[filename content] } if Habarico::ReflectionHelper.is_append_only_file_array_attr?(model, name)
36
+
29
37
  # Hash entries (`{ name => subfields }`) are already a nested permit spec - keep intact.
30
38
  return field if field.is_a?(::Hash)
31
39
 
32
- # Normalize Symbol/String/FieldReference entries so `permit` never receives a FieldReference.
33
- name = Habarico::AttributesHelper.field_name(field)
34
40
  # `ActionController::Parameters#permit` only allows scalar values for a bare field name -
35
41
  # a Hash value (e.g. a `file_attr` field's base64 `{filename:, content:}` or chunked-upload
36
42
  # `{upload_key:}` payload) is silently dropped unless its nested keys are explicitly
37
43
  # permitted. Expand file_attr fields into a nested permit spec; leave every other field as-is.
38
- if @definition.model.respond_to?(:has_file_attr?) && @definition.model.has_file_attr?(name)
44
+ if model.respond_to?(:has_file_attr?) && model.has_file_attr?(name)
39
45
  { name => %i[filename content upload_key] }
40
46
  else
41
47
  name
@@ -42,13 +42,13 @@ module Habarico
42
42
  has_one_attr :ai, 'Habarico::AiFieldOptions'
43
43
 
44
44
  # Largest file (bytes) the frontend accepts for this field, checked before the upload starts.
45
- # Derived from a `file_attr`'s own `active_storage_validations` size declaration (HAB-966); see
45
+ # Derived from a file field's own `active_storage_validations` size declaration (HAB-966); see
46
46
  # `Habarico::FileRulesResolver`. `habarico_enterprise.uploads` additionally folds in its
47
47
  # project-wide `Config.max_upload_size` ceiling for a field a chunked-upload provider serves.
48
48
  integer_attr :max_size
49
49
 
50
50
  # File types the frontend accepts for this field, checked before the upload starts. Derived
51
- # from a `file_attr`'s own `active_storage_validations` content-type declaration (HAB-966); see
51
+ # from a file field's own `active_storage_validations` content-type declaration (HAB-966); see
52
52
  # `Habarico::FileRulesResolver`.
53
53
  has_many_attr :accept, 'String'
54
54
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: habarico
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.3.0.pre.beta.11
4
+ version: 7.3.0.pre.beta.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matěj Outlý
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-30 00:00:00.000000000 Z
11
+ date: 2026-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails