shrine-zip-guard 0.1.0 → 0.3.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 +20 -1
- data/README.md +53 -14
- data/lib/shrine/plugins/zip_guard.rb +25 -8
- data/lib/shrine-zip-guard/version.rb +1 -1
- 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: 77f0c1a42fe262acd41c72ef110fd74cadf993c0b180624ddd1d47d4a76dd1e7
|
|
4
|
+
data.tar.gz: 60996e76aa76030fabf52f525be6e6048009c2d8dc063495935d23c2b730ce4e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 217ee96460de7fea169f1ce225812866a54608f98e4e4af5935a328bcc092b19450561fb9c3b314d298b3b682c31d28749eb963414594656073be3c2fe87d7cc
|
|
7
|
+
data.tar.gz: 16a011fbd591b0224420807290a1dadf694f79706107b0cc029c108d13fc46ab2c9ba23b929cbed664055fae0e85f9879e36b5ba9b3e04cf9dbef049620fa986
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [0.3.0] - 2026-08-11
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- All rescue branches in `create_derivatives` now explicitly return `{}` instead of the error messages array.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- `rescue ZipGuard::Error` broadened to `rescue StandardError` — catches corrupt ZIP archives, other unexpected errors, and custom error subclasses like `NestedFileError`.
|
|
10
|
+
- Fallback callback (`shrine_class.opts[:zip_guard][:fallback]`) is now invoked for all rescued errors, not just `ZipGuard::Error` subclasses.
|
|
11
|
+
- Renamed internal method `handle_zip_guard_error` to `handle_error`.
|
|
12
|
+
- Renamed internal method `report_fallback` to `report_error`.
|
|
13
|
+
|
|
14
|
+
## [0.2.0] - 2026-08-06
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- Per-uploader custom I18n messages via `shrine.zip_guard.errors.<uploader_name>`.
|
|
18
|
+
- Custom messages are supported for `password_protected`, `missing_file`, and `processing_failed` keys.
|
|
19
|
+
- `error_message_key` helper that resolves a custom key based on the uploader class name.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Custom message lookup no longer depends on ActiveSupport `String#underscore`.
|
|
4
23
|
|
|
5
24
|
## [0.1.0] - 2026-08-06
|
|
6
25
|
|
data/README.md
CHANGED
|
@@ -30,18 +30,28 @@ the plugin with `required_files`:
|
|
|
30
30
|
```ruby
|
|
31
31
|
class XmlUploader < Shrine
|
|
32
32
|
plugin :derivatives
|
|
33
|
-
plugin :zip_guard, required_files: ["
|
|
33
|
+
plugin :zip_guard, required_files: ["*.xml"]
|
|
34
34
|
end
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
This
|
|
38
|
-
|
|
37
|
+
This checks that the ZIP is not password-protected and that it contains at least
|
|
38
|
+
one file matching each glob pattern. Patterns are matched against the root of the
|
|
39
|
+
archive only. If you want to allow nested files, use a recursive pattern:
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
plugin :zip_guard, required_files: ["**/*.xml"]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
For a specific subdirectory, include the path in the pattern:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
plugin :zip_guard, required_files: ["data/*.xml"]
|
|
49
|
+
```
|
|
39
50
|
|
|
40
51
|
### Custom processing with `with_zip`
|
|
41
52
|
|
|
42
|
-
If you need to run your own logic (for example extracting files
|
|
43
|
-
specific paths
|
|
44
|
-
your `Attacher.derivatives` block:
|
|
53
|
+
If you need to run your own logic (for example extracting files or checking
|
|
54
|
+
specific paths), use the `with_zip` helper inside your `Attacher.derivatives` block:
|
|
45
55
|
|
|
46
56
|
```ruby
|
|
47
57
|
class XmlUploader < Shrine
|
|
@@ -49,12 +59,9 @@ class XmlUploader < Shrine
|
|
|
49
59
|
plugin :zip_guard
|
|
50
60
|
|
|
51
61
|
Attacher.derivatives do |original|
|
|
52
|
-
Shrine::Plugins::ZipGuard.with_zip(original, required_files: ["
|
|
62
|
+
Shrine::Plugins::ZipGuard.with_zip(original, required_files: ["*.xml"]) do |zip_file|
|
|
53
63
|
xmls = zip_file.glob("*.xml")
|
|
54
64
|
|
|
55
|
-
# Your custom rule — for example requiring the file to be at the root level
|
|
56
|
-
raise ZipUploadErrors::NestedFileError if xmls.empty? && zip_file.glob("**/*.xml").any?
|
|
57
|
-
|
|
58
65
|
if xmls.size == 1
|
|
59
66
|
extracted = Tempfile.new(["extracted", ".xml"], binmode: true)
|
|
60
67
|
extracted.write(xmls.first.get_input_stream.read)
|
|
@@ -68,10 +75,14 @@ class XmlUploader < Shrine
|
|
|
68
75
|
end
|
|
69
76
|
```
|
|
70
77
|
|
|
78
|
+
`with_zip` checks for encryption and optional required file patterns before
|
|
79
|
+
yielding the opened ZIP file. You do not need to pass `required_files` to both
|
|
80
|
+
the plugin and `with_zip`.
|
|
81
|
+
|
|
71
82
|
## Error messages
|
|
72
83
|
|
|
73
84
|
All user-facing errors are translated via I18n. The gem ships with English
|
|
74
|
-
defaults
|
|
85
|
+
defaults under the `shrine.zip_guard` key:
|
|
75
86
|
|
|
76
87
|
```yaml
|
|
77
88
|
en:
|
|
@@ -82,21 +93,49 @@ en:
|
|
|
82
93
|
processing_failed: "File could not be processed. Please try again or contact support."
|
|
83
94
|
```
|
|
84
95
|
|
|
96
|
+
### Per-uploader custom messages
|
|
97
|
+
|
|
98
|
+
You can override any message for a specific uploader by adding a key under
|
|
99
|
+
`shrine.zip_guard.errors.<uploader_name>`:
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
en:
|
|
103
|
+
shrine:
|
|
104
|
+
zip_guard:
|
|
105
|
+
missing_file: "Archive does not contain required file matching '%{pattern}'."
|
|
106
|
+
errors:
|
|
107
|
+
digital_box_rd_uploader:
|
|
108
|
+
missing_file: "XML file must be at the root level of the ZIP archive."
|
|
109
|
+
password_protected: "This ZIP is password protected."
|
|
110
|
+
processing_failed: "Could not process this XML ZIP."
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
If the per-uploader key exists, it is used; otherwise the default key is used.
|
|
114
|
+
The class name is converted with `underscore`, so `MyUploaders::XmlUploader`
|
|
115
|
+
would become `my_uploaders/xml_uploader`.
|
|
116
|
+
|
|
85
117
|
## Custom fallback for unexpected errors
|
|
86
118
|
|
|
87
|
-
By default,
|
|
119
|
+
By default, any unhandled error (including corrupt ZIP archives, `StandardError`
|
|
120
|
+
subclasses not explicitly caught by the plugin) is reported to the Rails error
|
|
88
121
|
reporter (if available) and a generic `processing_failed` message is added to
|
|
89
|
-
`attacher.errors`.
|
|
122
|
+
`attacher.errors`.
|
|
123
|
+
|
|
124
|
+
You can override this with your own handler:
|
|
90
125
|
|
|
91
126
|
```ruby
|
|
92
127
|
plugin :zip_guard,
|
|
93
|
-
required_files: ["
|
|
128
|
+
required_files: ["*.xml"],
|
|
94
129
|
fallback: lambda { |error, attacher|
|
|
95
130
|
Sentry.capture_exception(error)
|
|
96
131
|
attacher.errors << "Custom processing error: #{error.message}"
|
|
97
132
|
}
|
|
98
133
|
```
|
|
99
134
|
|
|
135
|
+
The fallback is called for any rescued error that is not a specific
|
|
136
|
+
`EncryptedZipError` or `MissingFileError`. This includes corrupt ZIPs, custom
|
|
137
|
+
application error classes, and other unexpected errors.
|
|
138
|
+
|
|
100
139
|
## Supported versions
|
|
101
140
|
|
|
102
141
|
- Ruby >= 3.1
|
|
@@ -48,30 +48,47 @@ class Shrine
|
|
|
48
48
|
def create_derivatives(*args, **options, &)
|
|
49
49
|
super
|
|
50
50
|
rescue ZipGuard::EncryptedZipError
|
|
51
|
-
errors << I18n.t("
|
|
51
|
+
errors << I18n.t(error_message_key("password_protected"))
|
|
52
52
|
{}
|
|
53
53
|
rescue ZipGuard::MissingFileError => e
|
|
54
|
-
errors << I18n.t("
|
|
54
|
+
errors << I18n.t(error_message_key("missing_file"), pattern: e.pattern)
|
|
55
55
|
{}
|
|
56
|
-
rescue
|
|
57
|
-
|
|
56
|
+
rescue StandardError => e
|
|
57
|
+
handle_error(e)
|
|
58
58
|
{}
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
private
|
|
62
62
|
|
|
63
|
-
def
|
|
63
|
+
def error_message_key(base_key)
|
|
64
|
+
uploader_name = shrine_class.name
|
|
65
|
+
return "shrine.zip_guard.#{base_key}" unless uploader_name
|
|
66
|
+
|
|
67
|
+
custom_key = "shrine.zip_guard.errors.#{underscore(uploader_name)}.#{base_key}"
|
|
68
|
+
I18n.t(custom_key, default: nil) ? custom_key : "shrine.zip_guard.#{base_key}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def underscore(class_name)
|
|
72
|
+
class_name
|
|
73
|
+
.gsub("::", "/")
|
|
74
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
75
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
76
|
+
.tr("-", "_")
|
|
77
|
+
.downcase
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def handle_error(error)
|
|
64
81
|
fallback = shrine_class.opts[:zip_guard][:fallback]
|
|
65
82
|
|
|
66
83
|
if fallback
|
|
67
84
|
fallback.call(error, self)
|
|
68
85
|
else
|
|
69
|
-
|
|
70
|
-
errors << I18n.t("
|
|
86
|
+
report_error(error)
|
|
87
|
+
errors << I18n.t(error_message_key("processing_failed"))
|
|
71
88
|
end
|
|
72
89
|
end
|
|
73
90
|
|
|
74
|
-
def
|
|
91
|
+
def report_error(error)
|
|
75
92
|
return unless defined?(Rails) && Rails.respond_to?(:error) && Rails.error.respond_to?(:report)
|
|
76
93
|
|
|
77
94
|
Rails.error.report(error)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: shrine-zip-guard
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Martin Dobrovodský
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: i18n
|