shrine 3.5.0 → 3.7.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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -0
  3. data/doc/changing_derivatives.md +2 -1
  4. data/doc/changing_location.md +17 -5
  5. data/doc/getting_started.md +4 -2
  6. data/doc/plugins/derivation_endpoint.md +2 -1
  7. data/doc/plugins/derivatives.md +2 -1
  8. data/doc/plugins/download_endpoint.md +16 -4
  9. data/doc/plugins/refresh_metadata.md +20 -0
  10. data/doc/plugins/signature.md +8 -6
  11. data/doc/processing.md +5 -3
  12. data/doc/release_notes/3.6.0.md +23 -0
  13. data/doc/release_notes/3.7.0.md +75 -0
  14. data/doc/storage/s3.md +10 -0
  15. data/lib/shrine/attacher.rb +28 -21
  16. data/lib/shrine/attachment.rb +2 -2
  17. data/lib/shrine/plugins/_urlsafe_serialization.rb +4 -4
  18. data/lib/shrine/plugins/add_metadata.rb +2 -4
  19. data/lib/shrine/plugins/atomic_helpers.rb +7 -7
  20. data/lib/shrine/plugins/backgrounding.rb +9 -9
  21. data/lib/shrine/plugins/column.rb +6 -4
  22. data/lib/shrine/plugins/default_url.rb +4 -4
  23. data/lib/shrine/plugins/delete_raw.rb +2 -2
  24. data/lib/shrine/plugins/derivation_endpoint.rb +37 -34
  25. data/lib/shrine/plugins/derivatives.rb +5 -1
  26. data/lib/shrine/plugins/download_endpoint.rb +65 -11
  27. data/lib/shrine/plugins/entity.rb +7 -7
  28. data/lib/shrine/plugins/infer_extension.rb +1 -1
  29. data/lib/shrine/plugins/instrumentation.rb +8 -8
  30. data/lib/shrine/plugins/mirroring.rb +10 -10
  31. data/lib/shrine/plugins/model.rb +9 -9
  32. data/lib/shrine/plugins/presign_endpoint.rb +13 -10
  33. data/lib/shrine/plugins/pretty_location.rb +2 -2
  34. data/lib/shrine/plugins/processing.rb +3 -3
  35. data/lib/shrine/plugins/rack_file.rb +2 -2
  36. data/lib/shrine/plugins/rack_response.rb +10 -4
  37. data/lib/shrine/plugins/refresh_metadata.rb +6 -6
  38. data/lib/shrine/plugins/remote_url.rb +3 -3
  39. data/lib/shrine/plugins/restore_cached_data.rb +3 -3
  40. data/lib/shrine/plugins/signature.rb +2 -2
  41. data/lib/shrine/plugins/store_dimensions.rb +2 -2
  42. data/lib/shrine/plugins/upload_endpoint.rb +7 -5
  43. data/lib/shrine/plugins/upload_options.rb +1 -1
  44. data/lib/shrine/plugins/validation.rb +8 -8
  45. data/lib/shrine/plugins/versions.rb +10 -10
  46. data/lib/shrine/plugins.rb +6 -14
  47. data/lib/shrine/storage/file_system.rb +4 -17
  48. data/lib/shrine/storage/linter.rb +8 -8
  49. data/lib/shrine/storage/memory.rb +1 -3
  50. data/lib/shrine/storage/s3.rb +53 -38
  51. data/lib/shrine/uploaded_file.rb +21 -18
  52. data/lib/shrine/version.rb +1 -1
  53. data/lib/shrine.rb +18 -18
  54. data/shrine.gemspec +8 -8
  55. metadata +31 -26
@@ -67,19 +67,19 @@ class Shrine
67
67
  end
68
68
 
69
69
  # Does a background promote if promote block was registered.
70
- def promote_cached(**options)
70
+ def promote_cached(**)
71
71
  if promote? && promote_block
72
- promote_background
72
+ promote_background(**)
73
73
  else
74
74
  super
75
75
  end
76
76
  end
77
77
 
78
78
  # Calls the registered promote block.
79
- def promote_background(**options)
79
+ def promote_background(**)
80
80
  fail Error, "promote block is not registered" unless promote_block
81
81
 
82
- background_block(promote_block, **options)
82
+ background_block(promote_block, **)
83
83
  end
84
84
 
85
85
  # Does a background destroy if destroy block was registered.
@@ -92,19 +92,19 @@ class Shrine
92
92
  end
93
93
 
94
94
  # Calls the registered destroy block.
95
- def destroy_background(**options)
95
+ def destroy_background(**)
96
96
  fail Error, "destroy block is not registered" unless destroy_block
97
97
 
98
- background_block(destroy_block, **options)
98
+ background_block(destroy_block, **)
99
99
  end
100
100
 
101
101
  private
102
102
 
103
- def background_block(block, **options)
103
+ def background_block(block, **)
104
104
  if block.arity == 1
105
- block.call(self, **options)
105
+ block.call(self, **)
106
106
  else
107
- instance_exec(**options, &block)
107
+ instance_exec(**, &block)
108
108
  end
109
109
  end
110
110
  end
@@ -16,8 +16,8 @@ class Shrine
16
16
  # from a database record column.
17
17
  #
18
18
  # Attacher.from_column('{"id":"...","storage":"...","metadata":{...}}')
19
- def from_column(data, **options)
20
- attacher = new(**options)
19
+ def from_column(data, **)
20
+ attacher = new(**)
21
21
  attacher.load_column(data)
22
22
  attacher
23
23
  end
@@ -28,8 +28,8 @@ class Shrine
28
28
  attr_reader :column_serializer
29
29
 
30
30
  # Allows overriding the default column serializer.
31
- def initialize(column_serializer: shrine_class.opts[:column][:serializer], **options)
32
- super(**options)
31
+ def initialize(column_serializer: shrine_class.opts[:column][:serializer], **)
32
+ super(**)
33
33
  @column_serializer = column_serializer
34
34
  end
35
35
 
@@ -75,6 +75,8 @@ class Shrine
75
75
  # Attacher.deserialize_column(nil)
76
76
  # #=> nil
77
77
  def deserialize_column(data)
78
+ return nil if data == ""
79
+
78
80
  if column_serializer && data && !data.is_a?(Hash)
79
81
  column_serializer.load(data)
80
82
  else
@@ -16,16 +16,16 @@ class Shrine
16
16
  end
17
17
 
18
18
  module AttacherMethods
19
- def url(**options)
20
- super || default_url(**options)
19
+ def url(**)
20
+ super || default_url(**)
21
21
  end
22
22
 
23
23
  private
24
24
 
25
- def default_url(**options)
25
+ def default_url(**)
26
26
  return unless default_url_block
27
27
 
28
- url = instance_exec(**options, &default_url_block)
28
+ url = instance_exec(**, &default_url_block)
29
29
 
30
30
  [*default_url_host, url].join
31
31
  end
@@ -15,8 +15,8 @@ class Shrine
15
15
  private
16
16
 
17
17
  # Deletes the file that was uploaded, unless it's an UploadedFile.
18
- def _upload(io, delete: delete_raw?, **options)
19
- super(io, delete: delete, **options)
18
+ def _upload(io, delete: delete_raw?, **)
19
+ super(io, delete:, **)
20
20
  end
21
21
 
22
22
  def delete_raw?
@@ -48,7 +48,7 @@ class Shrine
48
48
  # It uses a trick where it removes the derivation path prefix from the
49
49
  # path info before calling the Rack app, which is what web framework
50
50
  # routers do before they're calling a mounted Rack app.
51
- def derivation_response(env, **options)
51
+ def derivation_response(env, **)
52
52
  script_name = env["SCRIPT_NAME"]
53
53
  path_info = env["PATH_INFO"]
54
54
 
@@ -61,7 +61,7 @@ class Shrine
61
61
  env["SCRIPT_NAME"] += match.to_s
62
62
  env["PATH_INFO"] = match.post_match
63
63
 
64
- derivation_endpoint(**options).call(env)
64
+ derivation_endpoint(**).call(env)
65
65
  ensure
66
66
  env["SCRIPT_NAME"] = script_name
67
67
  env["PATH_INFO"] = path_info
@@ -87,15 +87,15 @@ class Shrine
87
87
  # Generates a URL to a derivation with the receiver as the source file.
88
88
  # Any arguments provided will be included in the URL and passed to the
89
89
  # derivation block. Accepts additional URL options.
90
- def derivation_url(name, *args, **options)
91
- derivation(name, *args).url(**options)
90
+ def derivation_url(name, *, **)
91
+ derivation(name, *).url(**)
92
92
  end
93
93
 
94
94
  # Calls the specified derivation with the receiver as the source file,
95
95
  # returning a Rack response triple. The derivation endpoint ultimately
96
96
  # calls this method.
97
- def derivation_response(name, *args, env:, **options)
98
- derivation(name, *args, **options).response(env)
97
+ def derivation_response(name, *, env:, **)
98
+ derivation(name, *, **).response(env)
99
99
  end
100
100
 
101
101
  # Returns a Shrine::Derivation object created from the provided
@@ -129,14 +129,14 @@ class Shrine
129
129
  end
130
130
 
131
131
  # Returns an URL to the derivation.
132
- def url(**options)
132
+ def url(**)
133
133
  Derivation::Url.new(self).call(
134
134
  host: option(:host),
135
135
  prefix: option(:prefix),
136
136
  expires_in: option(:expires_in),
137
137
  version: option(:version),
138
138
  metadata: option(:metadata),
139
- **options,
139
+ **,
140
140
  )
141
141
  end
142
142
 
@@ -158,8 +158,8 @@ class Shrine
158
158
 
159
159
  # Uploads the derivation result to a dedicated destination on the specified
160
160
  # Shrine storage.
161
- def upload(file = nil, **options)
162
- Derivation::Upload.new(self).call(file, **options)
161
+ def upload(file = nil, **)
162
+ Derivation::Upload.new(self).call(file, **)
163
163
  end
164
164
 
165
165
  # Returns a Shrine::UploadedFile object pointing to the uploaded derivative
@@ -184,7 +184,7 @@ class Shrine
184
184
  end
185
185
 
186
186
  def self.option(name, default: nil, result: nil)
187
- options[name] = { default: default, result: result }
187
+ options[name] = { default:, result: }
188
188
  end
189
189
 
190
190
  option :cache_control, default: -> { default_cache_control }
@@ -303,15 +303,15 @@ class Shrine
303
303
  class Derivation::Url < Derivation::Command
304
304
  delegate :name, :args, :source, :secret_key, :signer
305
305
 
306
- def call(host: nil, prefix: nil, **options)
306
+ def call(host: nil, prefix: nil, metadata: [], **)
307
307
  base_url = [host, *prefix].join("/")
308
- path = path_identifier(metadata: options.delete(:metadata))
308
+ path = path_identifier(metadata:)
309
309
 
310
310
  if signer
311
311
  url = [base_url, path].join("/")
312
- signer.call(url, **options)
312
+ signer.call(url, **)
313
313
  else
314
- signed_part = signed_url("#{path}?#{query(**options)}")
314
+ signed_part = signed_url("#{path}?#{query(**)}")
315
315
  [base_url, signed_part].join("/")
316
316
  end
317
317
  end
@@ -322,7 +322,7 @@ class Shrine
322
322
  [
323
323
  name,
324
324
  *args,
325
- source.urlsafe_dump(metadata: metadata)
325
+ source.urlsafe_dump(metadata:)
326
326
  ].map{|component| Rack::Utils.escape_path(component.to_s)}.join('/')
327
327
  end
328
328
 
@@ -365,7 +365,9 @@ class Shrine
365
365
  handle_request(request)
366
366
  end
367
367
 
368
- headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s
368
+ headers = Rack::Headers[headers] if Rack.release >= "3"
369
+ headers["Content-Length"] ||= body.respond_to?(:bytesize) ? body.bytesize.to_s :
370
+ body.map(&:bytesize).inject(0, :+).to_s
369
371
 
370
372
  [status, headers, body]
371
373
  end
@@ -481,19 +483,18 @@ class Shrine
481
483
  # `Content-Type` and `Content-Disposition` response headers from derivation
482
484
  # options and file extension of the derivation result.
483
485
  def file_response(file, env)
484
- response = rack_file_response(file.path, env)
485
-
486
- status = response[0]
486
+ status, headers, body = rack_file_response(file.path, env)
487
487
 
488
+ headers = Rack::Headers[headers] if Rack.release >= "3"
488
489
  headers = {
489
- "Content-Type" => type || response[1]["Content-Type"],
490
- "Content-Length" => response[1]["Content-Length"],
490
+ "Content-Type" => type || headers["Content-Type"],
491
+ "Content-Length" => headers["Content-Length"],
491
492
  "Content-Disposition" => content_disposition(file),
492
- "Content-Range" => response[1]["Content-Range"],
493
+ "Content-Range" => headers["Content-Range"],
493
494
  "Accept-Ranges" => "bytes",
494
495
  }.compact
495
496
 
496
- body = Rack::BodyProxy.new(response[2]) { File.delete(file.path) }
497
+ body = Rack::BodyProxy.new(body) { File.delete(file.path) }
497
498
 
498
499
  file.close
499
500
 
@@ -514,8 +515,10 @@ class Shrine
514
515
 
515
516
  if upload_redirect
516
517
  redirect_url = uploaded_file.url(**upload_redirect_url_options)
518
+ headers = { "Location" => redirect_url }
519
+ headers = Rack::Headers[headers] if Rack.release >= "3"
517
520
 
518
- [302, { "Location" => redirect_url }, []]
521
+ [302, headers, []]
519
522
  else
520
523
  if derivative && File.exist?(derivative.path)
521
524
  file_response(derivative, env)
@@ -555,7 +558,7 @@ class Shrine
555
558
  filename = self.filename
556
559
  filename += File.extname(file.path) if File.extname(filename).empty?
557
560
 
558
- ContentDisposition.format(disposition: disposition, filename: filename)
561
+ ContentDisposition.format(disposition:, filename:)
559
562
  end
560
563
  end
561
564
 
@@ -594,9 +597,9 @@ class Shrine
594
597
  end
595
598
 
596
599
  # Calls the derivation block.
597
- def derive(*args)
600
+ def derive(*)
598
601
  instrument_derivation do
599
- derivation.instance_exec(*args, &derivation_block)
602
+ derivation.instance_exec(*, &derivation_block)
600
603
  end
601
604
  end
602
605
 
@@ -604,7 +607,7 @@ class Shrine
604
607
  def instrument_derivation(&block)
605
608
  return yield unless shrine_class.respond_to?(:instrument)
606
609
 
607
- shrine_class.instrument(:derivation, { derivation: derivation }, &block)
610
+ shrine_class.instrument(:derivation, { derivation: }, &block)
608
611
  end
609
612
 
610
613
  # Massages the derivation result, ensuring it's opened in binary mode,
@@ -643,22 +646,22 @@ class Shrine
643
646
  # Uploads the derivation result to the dedicated location on the storage.
644
647
  # If a file object is given, uploads that to the storage, otherwise calls
645
648
  # the derivation block and uploads the result.
646
- def call(derivative = nil, **options)
649
+ def call(derivative = nil, **)
647
650
  if derivative
648
- upload(derivative, **options)
651
+ upload(derivative, **)
649
652
  else
650
- upload(derivation.generate, delete: true, **options)
653
+ upload(derivation.generate, delete: true, **)
651
654
  end
652
655
  end
653
656
 
654
657
  private
655
658
 
656
- def upload(io, **options)
659
+ def upload(io, **)
657
660
  shrine_class.upload io, upload_storage,
658
661
  location: upload_location,
659
662
  upload_options: upload_options,
660
663
  action: :derivation,
661
- **options
664
+ **
662
665
  end
663
666
  end
664
667
 
@@ -459,9 +459,13 @@ class Shrine
459
459
  # attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }
460
460
  # attacher.change(file)
461
461
  # attacher.derivatives #=> {}
462
+ #
463
+ # # With keep_derivatives: true
464
+ # attacher.change(file)
465
+ # attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }
462
466
  def change(*)
463
467
  result = super
464
- set_derivatives({})
468
+ set_derivatives({}) unless shrine_class.derivatives_options[:keep_derivatives]
465
469
  result
466
470
  end
467
471
 
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'openssl'
4
+ require 'base64'
5
+
3
6
  class Shrine
4
7
  module Plugins
5
8
  # Documentation can be found on https://shrinerb.com/docs/plugins/download_endpoint
@@ -16,11 +19,11 @@ class Shrine
16
19
 
17
20
  module ClassMethods
18
21
  # Returns the Rack application that retrieves requested files.
19
- def download_endpoint(**options)
22
+ def download_endpoint(**)
20
23
  Shrine::DownloadEndpoint.new(
21
24
  shrine_class: self,
22
25
  **opts[:download_endpoint],
23
- **options,
26
+ **,
24
27
  )
25
28
  end
26
29
 
@@ -30,7 +33,7 @@ class Shrine
30
33
  # It uses a trick where it removes the download path prefix from the
31
34
  # path info before calling the Rack app, which is what web framework
32
35
  # routers do before they're calling a mounted Rack app.
33
- def download_response(env, **options)
36
+ def download_response(env, **)
34
37
  script_name = env["SCRIPT_NAME"]
35
38
  path_info = env["PATH_INFO"]
36
39
 
@@ -43,7 +46,7 @@ class Shrine
43
46
  env["SCRIPT_NAME"] += match.to_s
44
47
  env["PATH_INFO"] = match.post_match
45
48
 
46
- download_endpoint(**options).call(env)
49
+ download_endpoint(**).call(env)
47
50
  ensure
48
51
  env["SCRIPT_NAME"] = script_name
49
52
  env["PATH_INFO"] = path_info
@@ -53,8 +56,8 @@ class Shrine
53
56
 
54
57
  module FileMethods
55
58
  # Returns file URL on the download endpoint.
56
- def download_url(**options)
57
- FileUrl.new(self).call(**options)
59
+ def download_url(**)
60
+ FileUrl.new(self).call(**)
58
61
  end
59
62
  end
60
63
 
@@ -65,14 +68,36 @@ class Shrine
65
68
  @file = file
66
69
  end
67
70
 
68
- def call(host: self.host)
69
- [host, *prefix, path].join("/")
71
+ def call(host: self.host, expires_in: nil)
72
+ path = file.urlsafe_dump(metadata: %w[filename size mime_type])
73
+
74
+ query = signature_as_query(path: path, expires_in: expires_in)
75
+
76
+ path = [host, *prefix, path].join("/")
77
+ path += "?#{query}" if query
78
+ path
70
79
  end
71
80
 
72
81
  protected
73
82
 
74
- def path
75
- file.urlsafe_dump(metadata: %w[filename size mime_type])
83
+ def signature_as_query(path:, expires_in:)
84
+ expires_in = default_expires_in if expires_in.nil?
85
+ raise(Error, "secret_key is required for expiring URLs") if !secret_key && expires_in
86
+ raise(Error, "expires_in is required for expiring URLs") if secret_key && !expires_in
87
+
88
+ return nil unless expires_in
89
+
90
+ expires_at = (Time.now + expires_in).to_i
91
+ signature = OpenSSL::HMAC.digest(
92
+ OpenSSL::Digest::SHA256.new,
93
+ secret_key,
94
+ "#{path}--#{expires_at}"
95
+ )
96
+
97
+ Rack::Utils.build_query(
98
+ signature: Base64.urlsafe_encode64(signature),
99
+ expires_at: expires_at
100
+ )
76
101
  end
77
102
 
78
103
  def host
@@ -83,6 +108,14 @@ class Shrine
83
108
  options[:prefix]
84
109
  end
85
110
 
111
+ def default_expires_in
112
+ options[:expires_in]
113
+ end
114
+
115
+ def secret_key
116
+ options[:secret_key]
117
+ end
118
+
86
119
  def options
87
120
  file.shrine_class.opts[:download_endpoint]
88
121
  end
@@ -113,7 +146,9 @@ class Shrine
113
146
  handle_request(request)
114
147
  end
115
148
 
116
- headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s
149
+ headers = Rack::Headers[headers] if Rack.release >= "3"
150
+ headers["Content-Length"] ||= body.respond_to?(:bytesize) ? body.bytesize.to_s :
151
+ body.map(&:bytesize).inject(0, :+).to_s
117
152
 
118
153
  [status, headers, body]
119
154
  end
@@ -127,6 +162,9 @@ class Shrine
127
162
 
128
163
  def handle_request(request)
129
164
  _, serialized, * = request.path_info.split("/")
165
+ signature, expires_at = request.params.values_at("signature", "expires_at")
166
+
167
+ check_signature!(serialized, signature, expires_at) if @secret_key
130
168
 
131
169
  uploaded_file = get_uploaded_file(serialized)
132
170
 
@@ -187,6 +225,22 @@ class Shrine
187
225
  bad_request!("Invalid serialized file")
188
226
  end
189
227
 
228
+ def check_signature!(serialized, signature, expires_at)
229
+ if expires_at && expires_at.to_i < Time.now.to_i
230
+ error!(400, "URL has expired")
231
+ end
232
+
233
+ calculated_signature = OpenSSL::HMAC.digest(
234
+ OpenSSL::Digest::SHA256.new,
235
+ @secret_key,
236
+ "#{serialized}--#{expires_at}"
237
+ )
238
+
239
+ if !Rack::Utils.secure_compare(signature, Base64.urlsafe_encode64(calculated_signature))
240
+ error!(403, "Signature does not match")
241
+ end
242
+ end
243
+
190
244
  def not_found!
191
245
  error!(404, "File Not Found")
192
246
  end
@@ -10,7 +10,7 @@ class Shrine
10
10
 
11
11
  module AttachmentMethods
12
12
  # Defines instance methods on initialization.
13
- def initialize(name, **options)
13
+ def initialize(name, **)
14
14
  super
15
15
 
16
16
  define_entity_methods(name)
@@ -59,16 +59,16 @@ class Shrine
59
59
 
60
60
  # Returns the class attacher instance with loaded entity. It's not
61
61
  # memoized because the entity object could be frozen.
62
- def attacher(record, **options)
63
- attacher = class_attacher(**options)
62
+ def attacher(record, **)
63
+ attacher = class_attacher(**)
64
64
  attacher.load_entity(record, @name)
65
65
  attacher
66
66
  end
67
67
 
68
68
  # Creates an instance of the corresponding attacher class with set
69
69
  # name.
70
- def class_attacher(**options)
71
- attacher = shrine_class::Attacher.new(**@options, **options)
70
+ def class_attacher(**)
71
+ attacher = shrine_class::Attacher.new(**@options, **)
72
72
  attacher.instance_variable_set(:@name, @name)
73
73
  attacher
74
74
  end
@@ -81,8 +81,8 @@ class Shrine
81
81
  #
82
82
  # attacher = Attacher.from_entity(photo, :image)
83
83
  # attacher.file #=> #<Shrine::UploadedFile>
84
- def from_entity(record, name, **options)
85
- attacher = new(**options)
84
+ def from_entity(record, name, **)
85
+ attacher = new(**)
86
86
  attacher.load_entity(record, name)
87
87
  attacher
88
88
  end
@@ -46,7 +46,7 @@ class Shrine
46
46
  def instrument_extension(mime_type, &block)
47
47
  return yield unless respond_to?(:instrument)
48
48
 
49
- instrument(:extension, mime_type: mime_type, &block)
49
+ instrument(:extension, mime_type:, &block)
50
50
  end
51
51
  end
52
52
 
@@ -169,14 +169,14 @@ class Shrine
169
169
  end
170
170
  end
171
171
 
172
- def library_send(method_name, *args, &block)
172
+ def library_send(method_name, ...)
173
173
  case notifications.to_s
174
174
  when /Dry::Monitor::Notifications/
175
- send(:"dry_monitor_#{method_name}", *args, &block)
175
+ send(:"dry_monitor_#{method_name}", ...)
176
176
  when /ActiveSupport::Notifications/
177
- send(:"active_support_#{method_name}", *args, &block)
177
+ send(:"active_support_#{method_name}", ...)
178
178
  else
179
- notifications.send(method_name, *args, &block)
179
+ notifications.send(method_name, ...)
180
180
  end
181
181
  end
182
182
  end
@@ -224,14 +224,14 @@ class Shrine
224
224
  event.duration.to_i
225
225
  end
226
226
 
227
- def library_send(method_name, *args, &block)
227
+ def library_send(method_name, ...)
228
228
  case event.class.name
229
229
  when "ActiveSupport::Notifications::Event"
230
- send(:"active_support_#{method_name}", *args, &block)
230
+ send(:"active_support_#{method_name}", ...)
231
231
  when "Dry::Events::Event"
232
- send(:"dry_events_#{method_name}", *args, &block)
232
+ send(:"dry_events_#{method_name}", ...)
233
233
  else
234
- event.send(method_name, *args, &block)
234
+ event.send(method_name, ...)
235
235
  end
236
236
  end
237
237
  end
@@ -51,9 +51,9 @@ class Shrine
51
51
 
52
52
  module InstanceMethods
53
53
  # Mirrors upload to other mirror storages.
54
- def upload(io, mirror: true, **options)
55
- file = super(io, **options)
56
- file.trigger_mirror_upload(**options) if mirror
54
+ def upload(io, mirror: true, **)
55
+ file = super(io, **)
56
+ file.trigger_mirror_upload(**) if mirror
57
57
  file
58
58
  end
59
59
  end
@@ -61,31 +61,31 @@ class Shrine
61
61
  module FileMethods
62
62
  # Mirrors upload if mirrors are defined. Calls mirror block if
63
63
  # registered, otherwise mirrors synchronously.
64
- def trigger_mirror_upload(**options)
64
+ def trigger_mirror_upload(**)
65
65
  return unless shrine_class.mirrors[storage_key] && shrine_class.mirror_upload?
66
66
 
67
67
  if shrine_class.mirror_upload_block
68
- mirror_upload_background(**options)
68
+ mirror_upload_background(**)
69
69
  else
70
- mirror_upload(**options)
70
+ mirror_upload(**)
71
71
  end
72
72
  end
73
73
 
74
74
  # Calls mirror upload block.
75
- def mirror_upload_background(**options)
75
+ def mirror_upload_background(**)
76
76
  fail Error, "mirror upload block is not registered" unless shrine_class.mirror_upload_block
77
77
 
78
- shrine_class.mirror_upload_block.call(self, **options)
78
+ shrine_class.mirror_upload_block.call(self, **)
79
79
  end
80
80
 
81
81
  # Uploads the file to each mirror storage.
82
- def mirror_upload(**options)
82
+ def mirror_upload(**)
83
83
  previously_opened = opened?
84
84
 
85
85
  each_mirror do |mirror|
86
86
  rewind if opened?
87
87
 
88
- shrine_class.upload(self, mirror, **options, location: id, close: false, action: :mirror)
88
+ shrine_class.upload(self, mirror, **, location: id, close: false, action: :mirror)
89
89
  end
90
90
  ensure
91
91
  if opened? && !previously_opened
@@ -18,8 +18,8 @@ class Shrine
18
18
  #
19
19
  # Shrine::Attachment(:image) # model (default)
20
20
  # Shrine::Attachment(:image, model: false) # entity
21
- def initialize(name, model: true, **options)
22
- super(name, **options)
21
+ def initialize(name, model: true, **)
22
+ super(name, **)
23
23
  @model = model
24
24
  end
25
25
 
@@ -82,16 +82,16 @@ class Shrine
82
82
  #
83
83
  # attacher = Attacher.from_model(photo, :image)
84
84
  # attacher.file #=> #<Shrine::UploadedFile>
85
- def from_model(record, name, **options)
86
- attacher = new(**options)
85
+ def from_model(record, name, **)
86
+ attacher = new(**)
87
87
  attacher.load_model(record, name)
88
88
  attacher
89
89
  end
90
90
  end
91
91
 
92
92
  module AttacherMethods
93
- def initialize(model_cache: shrine_class.opts[:model][:cache], **options)
94
- super(**options)
93
+ def initialize(model_cache: shrine_class.opts[:model][:cache], **)
94
+ super(**)
95
95
  @model_cache = model_cache
96
96
  @model = nil
97
97
  end
@@ -111,11 +111,11 @@ class Shrine
111
111
  end
112
112
 
113
113
  # Called by the attachment attribute setter on the model.
114
- def model_assign(value, **options)
114
+ def model_assign(value, **)
115
115
  if model_cache?
116
- assign(value, **options)
116
+ assign(value, **)
117
117
  else
118
- attach(value, **options)
118
+ attach(value, **)
119
119
  end
120
120
  end
121
121