shrine-zip-guard 0.1.0 → 0.2.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: 9359699bdaf1bf53ba7b23a7c271880166360df60bf26d78ef5b874cd8e605e2
4
+ data.tar.gz: d353357a3022c8f981501e735ca40f1a3264cdb8bc0d42c5093be2a369609d81
5
5
  SHA512:
6
- metadata.gz: d5571ca6c940dc80e911a826bc4231aa6fbb712841d5038a355a4fa8ed0664caf0fa6e506e27d90e73784e77ee690f60d44e5e0904560c7a3ff1d8d8a8fa0252
7
- data.tar.gz: 3228249ed0d1246cc04976fd2a7fb7b600bdfe3b20ca5ea343c8c60bda85a540ea1b42808445c2cc4b7a663e8e4908aa91ef3d579128f827f13314888bd0c1e0
6
+ metadata.gz: 0c170879388c9bb5c29a50d25d74a3ce50718f30ae315b318151333b4c5e9424213ab6172254bf63302ff94ea32c456006ba4cd4334e3be3659998ed0578595c
7
+ data.tar.gz: 82c23d6223bfe1c618471cf62c06b44d86a93f17779bf149bc1272b120cfc9bed557927416ca47a1b9adc3313f99bbcb5e837d0ed9665aa581da01680adbe47d
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.2.0] - 2026-08-06
6
+
7
+ ### Added
8
+ - Per-uploader custom I18n messages via `shrine.zip_guard.errors.<uploader_name>`.
9
+ - Custom messages are supported for `password_protected`, `missing_file`, and `processing_failed` keys.
10
+ - `error_message_key` helper that resolves a custom key based on the uploader class name.
11
+
12
+ ### Changed
13
+ - Custom message lookup no longer depends on ActiveSupport `String#underscore`.
14
+
5
15
  ## [0.1.0] - 2026-08-06
6
16
 
7
17
  ### Added
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,13 @@ class XmlUploader < Shrine
68
75
  end
69
76
  ```
70
77
 
78
+ Note that `with_zip` performs its own `required_files` check, so you should not also
79
+ pass `required_files` to the plugin when you provide a custom `derivatives` block.
80
+
71
81
  ## Error messages
72
82
 
73
83
  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:
84
+ defaults under the `shrine.zip_guard` key:
75
85
 
76
86
  ```yaml
77
87
  en:
@@ -82,6 +92,27 @@ en:
82
92
  processing_failed: "File could not be processed. Please try again or contact support."
83
93
  ```
84
94
 
95
+ ### Per-uploader custom messages
96
+
97
+ You can override any message for a specific uploader by adding a key under
98
+ `shrine.zip_guard.errors.<uploader_name>`:
99
+
100
+ ```yaml
101
+ en:
102
+ shrine:
103
+ zip_guard:
104
+ missing_file: "Archive does not contain required file matching '%{pattern}'."
105
+ errors:
106
+ digital_box_rd_uploader:
107
+ missing_file: "XML file must be at the root level of the ZIP archive."
108
+ password_protected: "This ZIP is password protected."
109
+ processing_failed: "Could not process this XML ZIP."
110
+ ```
111
+
112
+ If the per-uploader key exists, it is used; otherwise the default key is used.
113
+ The class name is converted with `underscore`, so `MyUploaders::XmlUploader`
114
+ would become `my_uploaders/xml_uploader`.
115
+
85
116
  ## Custom fallback for unexpected errors
86
117
 
87
118
  By default, unexpected `ZipGuard::Error` subclasses are reported to the Rails error
@@ -90,7 +121,7 @@ reporter (if available) and a generic `processing_failed` message is added to
90
121
 
91
122
  ```ruby
92
123
  plugin :zip_guard,
93
- required_files: ["**/*.xml"],
124
+ required_files: ["*.xml"],
94
125
  fallback: lambda { |error, attacher|
95
126
  Sentry.capture_exception(error)
96
127
  attacher.errors << "Custom processing error: #{error.message}"
@@ -48,18 +48,32 @@ 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")
52
- {}
51
+ errors << I18n.t(error_message_key("password_protected"))
53
52
  rescue ZipGuard::MissingFileError => e
54
- errors << I18n.t("shrine.zip_guard.missing_file", pattern: e.pattern)
55
- {}
53
+ errors << I18n.t(error_message_key("missing_file"), pattern: e.pattern)
56
54
  rescue ZipGuard::Error => e
57
55
  handle_zip_guard_error(e)
58
- {}
59
56
  end
60
57
 
61
58
  private
62
59
 
60
+ def error_message_key(base_key)
61
+ uploader_name = shrine_class.name
62
+ return "shrine.zip_guard.#{base_key}" unless uploader_name
63
+
64
+ custom_key = "shrine.zip_guard.errors.#{underscore(uploader_name)}.#{base_key}"
65
+ I18n.t(custom_key, default: nil) ? custom_key : "shrine.zip_guard.#{base_key}"
66
+ end
67
+
68
+ def underscore(class_name)
69
+ class_name
70
+ .gsub("::", "/")
71
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
72
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
73
+ .tr("-", "_")
74
+ .downcase
75
+ end
76
+
63
77
  def handle_zip_guard_error(error)
64
78
  fallback = shrine_class.opts[:zip_guard][:fallback]
65
79
 
@@ -67,7 +81,7 @@ class Shrine
67
81
  fallback.call(error, self)
68
82
  else
69
83
  report_fallback(error)
70
- errors << I18n.t("shrine.zip_guard.processing_failed")
84
+ errors << I18n.t(error_message_key("processing_failed"))
71
85
  end
72
86
  end
73
87
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShrineZipGuard
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
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.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Dobrovodský