eluvia-base 3.40.0 → 3.41.0
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 +4 -4
- data/CHANGELOG.md +11 -0
- data/lib/eluvia/active_record/file_array_attribute.rb +118 -45
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8d7c48fd8f44bcda78e3e702b68e2d1fc1da1f16c5c5231f9243c1a82c3132ca
|
|
4
|
+
data.tar.gz: 9eba98e2363832aea7e3015cfdc9312b7fb88fccae0d45def95cb34b8606a0ef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb92181c8003d8da9d2647734357877fc8e75aed3ae362ccd8fe91dad0da757f28648fbe1fffd9fcadb367ae97b5884670ae202847a5bf8842730b45b5355438
|
|
7
|
+
data.tar.gz: c6aa908b43d905ba1080d12b5c6c2cbeda73917c00a58ae45fbd3ef3c9ddf293ab87726036945ff27fce510dbac61d4ced691a1341d2997babeaa4c0c2e6b0d6
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.41.0] — 2026-10-27
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `Eluvia::ActiveRecord::FileArrayAttribute` registers each `file_array_attr` like `file_attr`
|
|
12
|
+
does: `file_array_attrs`, `file_array_attr_names`, `has_file_array_attr?` and
|
|
13
|
+
`find_file_array_attr` return its `underlying_attachments`, `underlying_filter`, `nature` and
|
|
14
|
+
`append_only` (HAB-991).
|
|
15
|
+
- `append_only:` option on `file_array_attr`: the setter takes a list of flat
|
|
16
|
+
`{filename, content}` files, attaches each one with content, never removes an existing
|
|
17
|
+
attachment, and ignores `nil`. Requires `underlying_attachments` (HAB-991).
|
|
18
|
+
|
|
8
19
|
## [3.40.0] — 2026-09-23
|
|
9
20
|
|
|
10
21
|
### Added
|
|
@@ -6,6 +6,30 @@ module Eluvia
|
|
|
6
6
|
|
|
7
7
|
class_methods do
|
|
8
8
|
|
|
9
|
+
# *********************************************************************
|
|
10
|
+
# Attribute store methods
|
|
11
|
+
# *********************************************************************
|
|
12
|
+
|
|
13
|
+
def _file_array_attrs
|
|
14
|
+
@file_array_attrs ||= {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def file_array_attrs
|
|
18
|
+
_file_array_attrs.values
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def file_array_attr_names
|
|
22
|
+
file_array_attrs.map { |a| a[:attr_name] }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def has_file_array_attr?(attr_name)
|
|
26
|
+
_file_array_attrs.key?(attr_name.to_sym)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def find_file_array_attr(attr_name)
|
|
30
|
+
_file_array_attrs.fetch(attr_name.to_sym, nil)
|
|
31
|
+
end
|
|
32
|
+
|
|
9
33
|
# *********************************************************************
|
|
10
34
|
# Attribute definition methods for file array
|
|
11
35
|
# *********************************************************************
|
|
@@ -16,78 +40,101 @@ module Eluvia
|
|
|
16
40
|
# @param underlying_attachments [Symbol, nil] Name of the attribute holding underlying Active Storage attachments
|
|
17
41
|
# @param underlying_filter [Proc, nil] Optional filter to be applied to scope underlying attachments
|
|
18
42
|
# @param nature [Symbol] Nature of file attribute (:file, :image, etc.)
|
|
43
|
+
# @param append_only [Boolean] Whether the setter only attaches new `{filename, content}` files and never removes existing ones
|
|
19
44
|
# @param declare [Boolean] Whether to declare the underlying attribute
|
|
20
|
-
def file_array_attr(attr_name, underlying_attachments: nil, underlying_filter: nil, nature: :file, declare: false)
|
|
45
|
+
def file_array_attr(attr_name, underlying_attachments: nil, underlying_filter: nil, nature: :file, append_only: false, declare: false)
|
|
21
46
|
attr_name = attr_name.to_sym
|
|
22
47
|
nature = nature.to_sym
|
|
23
48
|
|
|
49
|
+
raise ArgumentError, "`append_only` file array attribute `#{attr_name}` requires `underlying_attachments`." if append_only && !underlying_attachments
|
|
50
|
+
|
|
24
51
|
# Define it using "has many" attribute
|
|
25
52
|
if declare
|
|
26
53
|
has_many_attr(attr_name, nature == :image ? 'Eluvia::ImageWrapper' : 'Eluvia::FileWrapper')
|
|
27
54
|
end
|
|
28
55
|
|
|
56
|
+
# Register metadata
|
|
57
|
+
self._file_array_attrs[attr_name] = {
|
|
58
|
+
attr_name: attr_name,
|
|
59
|
+
underlying_attachments: underlying_attachments,
|
|
60
|
+
underlying_filter: underlying_filter,
|
|
61
|
+
nature: nature,
|
|
62
|
+
append_only: append_only
|
|
63
|
+
}
|
|
64
|
+
|
|
29
65
|
# *******************************************************************
|
|
30
66
|
# Dynamically defined instance methods for file array
|
|
31
67
|
# *******************************************************************
|
|
32
68
|
|
|
33
69
|
if underlying_attachments
|
|
34
70
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
71
|
+
if append_only
|
|
72
|
+
|
|
73
|
+
# Set method - attaches each new file, leaves the existing attachments untouched
|
|
74
|
+
define_method(:"#{attr_name}=") do |new_values|
|
|
75
|
+
append_file_array_uploads(attr_name, underlying_attachments, new_values)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
else
|
|
79
|
+
|
|
80
|
+
# Set method
|
|
81
|
+
define_method(:"#{attr_name}=") do |new_values|
|
|
82
|
+
|
|
83
|
+
# Check input
|
|
84
|
+
raise Eluvia::Errors::BadRequest.new("Invalid value for attribute `#{attr_name}`, expecting Array.") unless new_values.is_a?(::Array)
|
|
85
|
+
|
|
86
|
+
# Current values
|
|
87
|
+
current_values = self.send(attr_name)
|
|
88
|
+
current_ids = current_values.map(&:id)
|
|
89
|
+
new_ids = []
|
|
90
|
+
|
|
91
|
+
# Create missing attachments
|
|
92
|
+
new_values.each do |value|
|
|
93
|
+
# Normalize and check value
|
|
94
|
+
if value.is_a?(::Hash)
|
|
95
|
+
if nature == :image
|
|
96
|
+
value = Eluvia::ImageWrapper.map(value)
|
|
97
|
+
else
|
|
98
|
+
value = Eluvia::FileWrapper.map(value)
|
|
99
|
+
end
|
|
54
100
|
end
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
raise Eluvia::Errors::BadRequest.new("Invalid value for attribute `#{attr_name}[].file`, can't be blank.") if value.file.blank?
|
|
101
|
+
raise Eluvia::Errors::BadRequest.new("Invalid value for attribute `#{attr_name}[]`, expecting Eluvia::FileWrapper or Eluvia::ImageWrapper.") unless value.is_a?(Eluvia::FileWrapper) || value.is_a?(Eluvia::ImageWrapper)
|
|
102
|
+
raise Eluvia::Errors::BadRequest.new("Invalid value for attribute `#{attr_name}[].file`, can't be blank.") if value.file.blank?
|
|
58
103
|
|
|
59
|
-
|
|
60
|
-
|
|
104
|
+
# Keep track of new ids
|
|
105
|
+
new_ids << value.id if value.id
|
|
61
106
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
107
|
+
# Retrieve filename and content
|
|
108
|
+
filename = value.file&.filename
|
|
109
|
+
content = value.file&.content
|
|
65
110
|
|
|
66
|
-
|
|
67
|
-
|
|
111
|
+
# Sanitize filename
|
|
112
|
+
filename = ActiveStorage::Filename.new(filename).sanitized
|
|
68
113
|
|
|
69
|
-
|
|
70
|
-
|
|
114
|
+
# Create or update attachment only if both filename and content are passed (this means that user uploaded a new file)
|
|
115
|
+
if !filename.blank? && !content.blank?
|
|
71
116
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
117
|
+
# Delete the existing attachment
|
|
118
|
+
if value.id && current_ids.include?(value.id)
|
|
119
|
+
attachment_record = self.send("#{underlying_attachments}_attachments").find(value.id)
|
|
120
|
+
attachment_record.purge_later
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Create a new attachment
|
|
124
|
+
self.send(underlying_attachments).attach(io: StringIO.new(Base64.decode64(content)), filename: filename)
|
|
76
125
|
end
|
|
77
126
|
|
|
78
|
-
# Create a new attachment
|
|
79
|
-
self.send(underlying_attachments).attach(io: StringIO.new(Base64.decode64(content)), filename: filename)
|
|
80
127
|
end
|
|
81
128
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
attachment_record.purge_later
|
|
129
|
+
# Delete no longer wanted attachments
|
|
130
|
+
current_values.each do |value|
|
|
131
|
+
unless new_ids.include?(value.id)
|
|
132
|
+
attachment_record = self.send("#{underlying_attachments}_attachments").find(value.id)
|
|
133
|
+
attachment_record.purge_later
|
|
134
|
+
end
|
|
89
135
|
end
|
|
90
136
|
end
|
|
137
|
+
|
|
91
138
|
end
|
|
92
139
|
|
|
93
140
|
# Get method
|
|
@@ -111,6 +158,32 @@ module Eluvia
|
|
|
111
158
|
end
|
|
112
159
|
|
|
113
160
|
end
|
|
161
|
+
|
|
162
|
+
private
|
|
163
|
+
|
|
164
|
+
# Attaches each `{filename, content}` upload of an append-only file array attribute
|
|
165
|
+
def append_file_array_uploads(attr_name, underlying_attachments, new_values)
|
|
166
|
+
return if new_values.nil?
|
|
167
|
+
|
|
168
|
+
# Check input
|
|
169
|
+
raise Eluvia::Errors::BadRequest.new("Invalid value for attribute `#{attr_name}`, expecting Array.") unless new_values.is_a?(::Array)
|
|
170
|
+
|
|
171
|
+
new_values.each do |value|
|
|
172
|
+
# Normalize and check value
|
|
173
|
+
value = Eluvia::File.map(value) if value.is_a?(::Hash)
|
|
174
|
+
raise Eluvia::Errors::BadRequest.new("Invalid value for attribute `#{attr_name}[]`, expecting Eluvia::File.") unless value.is_a?(Eluvia::File)
|
|
175
|
+
raise Eluvia::Errors::BadRequest.new("Invalid value for attribute `#{attr_name}[]`, `upload_key` is not supported.") if value.upload_key.present?
|
|
176
|
+
|
|
177
|
+
# An item without content carries no new file
|
|
178
|
+
next if value.content.blank?
|
|
179
|
+
|
|
180
|
+
filename = ActiveStorage::Filename.new(value.filename.to_s).sanitized
|
|
181
|
+
raise Eluvia::Errors::BadRequest.new("Invalid value for attribute `#{attr_name}[].filename`, can't be blank.") if filename.blank?
|
|
182
|
+
|
|
183
|
+
self.send(underlying_attachments).attach(io: StringIO.new(Base64.decode64(value.content)), filename: filename)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
114
187
|
end
|
|
115
188
|
end
|
|
116
189
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: eluvia-base
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.41.0
|
|
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-
|
|
11
|
+
date: 2026-10-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rest-client
|