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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08688c20b1b8190ce44c95079e5d71a302b42f18f635876f1f8265381650e086'
4
- data.tar.gz: 3c5fa541d7cfe9620b405e33ce7457ede9df3f37503583492a0e0089e7ee0f35
3
+ metadata.gz: 77f0c1a42fe262acd41c72ef110fd74cadf993c0b180624ddd1d47d4a76dd1e7
4
+ data.tar.gz: 60996e76aa76030fabf52f525be6e6048009c2d8dc063495935d23c2b730ce4e
5
5
  SHA512:
6
- metadata.gz: d5571ca6c940dc80e911a826bc4231aa6fbb712841d5038a355a4fa8ed0664caf0fa6e506e27d90e73784e77ee690f60d44e5e0904560c7a3ff1d8d8a8fa0252
7
- data.tar.gz: 3228249ed0d1246cc04976fd2a7fb7b600bdfe3b20ca5ea343c8c60bda85a540ea1b42808445c2cc4b7a663e8e4908aa91ef3d579128f827f13314888bd0c1e0
6
+ metadata.gz: 217ee96460de7fea169f1ce225812866a54608f98e4e4af5935a328bcc092b19450561fb9c3b314d298b3b682c31d28749eb963414594656073be3c2fe87d7cc
7
+ data.tar.gz: 16a011fbd591b0224420807290a1dadf694f79706107b0cc029c108d13fc46ab2c9ba23b929cbed664055fae0e85f9879e36b5ba9b3e04cf9dbef049620fa986
data/CHANGELOG.md CHANGED
@@ -1,6 +1,25 @@
1
1
  # Changelog
2
2
 
3
- ## [Unreleased]
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: ["**/*.xml"]
33
+ plugin :zip_guard, required_files: ["*.xml"]
34
34
  end
35
35
  ```
36
36
 
37
- This will automatically check that the ZIP is not password-protected and that it
38
- contains at least one file matching each glob pattern.
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, checking
43
- specific paths, or adding custom error rules), use the `with_zip` helper inside
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: ["**/*.xml"]) do |zip_file|
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. Add your own translations under the `shrine.zip_guard` key:
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, unexpected `ZipGuard::Error` subclasses are reported to the Rails error
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`. You can override this with your own handler:
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: ["**/*.xml"],
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("shrine.zip_guard.password_protected")
51
+ errors << I18n.t(error_message_key("password_protected"))
52
52
  {}
53
53
  rescue ZipGuard::MissingFileError => e
54
- errors << I18n.t("shrine.zip_guard.missing_file", pattern: e.pattern)
54
+ errors << I18n.t(error_message_key("missing_file"), pattern: e.pattern)
55
55
  {}
56
- rescue ZipGuard::Error => e
57
- handle_zip_guard_error(e)
56
+ rescue StandardError => e
57
+ handle_error(e)
58
58
  {}
59
59
  end
60
60
 
61
61
  private
62
62
 
63
- def handle_zip_guard_error(error)
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
- report_fallback(error)
70
- errors << I18n.t("shrine.zip_guard.processing_failed")
86
+ report_error(error)
87
+ errors << I18n.t(error_message_key("processing_failed"))
71
88
  end
72
89
  end
73
90
 
74
- def report_fallback(error)
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShrineZipGuard
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
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.1.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-06 00:00:00.000000000 Z
11
+ date: 2026-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n