shrine 2.18.1 → 2.19.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.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +53 -1
- data/README.md +96 -137
- data/doc/advantages.md +4 -4
- data/doc/attacher.md +1 -2
- data/doc/carrierwave.md +3 -2
- data/doc/creating_storages.md +0 -20
- data/doc/design.md +1 -1
- data/doc/metadata.md +62 -36
- data/doc/paperclip.md +7 -6
- data/doc/plugins/data_uri.md +50 -4
- data/doc/plugins/derivation_endpoint.md +24 -0
- data/doc/plugins/determine_mime_type.md +47 -5
- data/doc/plugins/infer_extension.md +45 -9
- data/doc/plugins/instrumentation.md +170 -0
- data/doc/plugins/presign_endpoint.md +1 -1
- data/doc/plugins/pretty_location.md +23 -0
- data/doc/plugins/remote_url.md +59 -8
- data/doc/plugins/signature.md +54 -7
- data/doc/plugins/store_dimensions.md +69 -4
- data/doc/plugins/upload_endpoint.md +2 -2
- data/doc/plugins/validation_helpers.md +71 -29
- data/doc/refile.md +1 -1
- data/doc/release_notes/2.18.0.md +2 -2
- data/doc/release_notes/2.19.0.md +263 -0
- data/doc/storage/file_system.md +26 -8
- data/doc/testing.md +10 -10
- data/lib/shrine.rb +32 -16
- data/lib/shrine/attacher.rb +3 -0
- data/lib/shrine/attachment.rb +3 -0
- data/lib/shrine/plugins/add_metadata.rb +12 -16
- data/lib/shrine/plugins/backup.rb +2 -0
- data/lib/shrine/plugins/copy.rb +2 -0
- data/lib/shrine/plugins/data_uri.rb +56 -28
- data/lib/shrine/plugins/derivation_endpoint.rb +61 -27
- data/lib/shrine/plugins/determine_mime_type.rb +27 -5
- data/lib/shrine/plugins/infer_extension.rb +26 -5
- data/lib/shrine/plugins/instrumentation.rb +300 -0
- data/lib/shrine/plugins/logging.rb +2 -0
- data/lib/shrine/plugins/moving.rb +2 -0
- data/lib/shrine/plugins/pretty_location.rb +21 -12
- data/lib/shrine/plugins/rack_file.rb +23 -18
- data/lib/shrine/plugins/refresh_metadata.rb +4 -4
- data/lib/shrine/plugins/remote_url.rb +42 -23
- data/lib/shrine/plugins/signature.rb +32 -1
- data/lib/shrine/plugins/store_dimensions.rb +54 -9
- data/lib/shrine/plugins/validation_helpers.rb +148 -47
- data/lib/shrine/storage/file_system.rb +32 -15
- data/lib/shrine/storage/linter.rb +0 -13
- data/lib/shrine/storage/s3.rb +2 -5
- data/lib/shrine/uploaded_file.rb +8 -0
- data/lib/shrine/version.rb +2 -2
- data/shrine.gemspec +18 -3
- metadata +58 -27
@@ -12,13 +12,21 @@ class Shrine
|
|
12
12
|
#
|
13
13
|
# [doc/plugins/derivation_endpoint.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/derivation_endpoint.md
|
14
14
|
module DerivationEndpoint
|
15
|
+
LOG_SUBSCRIBER = -> (event) do
|
16
|
+
Shrine.logger.info "Derivation (#{event.duration}ms) – #{{
|
17
|
+
name: event[:name],
|
18
|
+
args: event[:args],
|
19
|
+
uploader: event[:uploader],
|
20
|
+
}.inspect}"
|
21
|
+
end
|
22
|
+
|
15
23
|
def self.load_dependencies(uploader, opts = {})
|
16
24
|
uploader.plugin :rack_response
|
17
25
|
uploader.plugin :_urlsafe_serialization
|
18
26
|
end
|
19
27
|
|
20
28
|
def self.configure(uploader, opts = {})
|
21
|
-
uploader.opts[:derivation_endpoint_options] ||= {}
|
29
|
+
uploader.opts[:derivation_endpoint_options] ||= { log_subscriber: LOG_SUBSCRIBER }
|
22
30
|
uploader.opts[:derivation_endpoint_options].merge!(opts)
|
23
31
|
|
24
32
|
uploader.opts[:derivation_endpoint_derivations] ||= {}
|
@@ -26,6 +34,11 @@ class Shrine
|
|
26
34
|
unless uploader.opts[:derivation_endpoint_options][:secret_key]
|
27
35
|
fail Error, "must provide :secret_key option to derivation_endpoint plugin"
|
28
36
|
end
|
37
|
+
|
38
|
+
# instrumentation plugin integration
|
39
|
+
if uploader.respond_to?(:subscribe)
|
40
|
+
uploader.subscribe(:derivation, &uploader.opts[:derivation_endpoint_options][:log_subscriber])
|
41
|
+
end
|
29
42
|
end
|
30
43
|
|
31
44
|
module ClassMethods
|
@@ -192,7 +205,7 @@ class Shrine
|
|
192
205
|
option :upload_options, default: -> { {} }
|
193
206
|
option :upload_redirect, default: -> { false }
|
194
207
|
option :upload_redirect_url_options, default: -> { {} }
|
195
|
-
option :upload_storage, default: -> {
|
208
|
+
option :upload_storage, default: -> { default_upload_storage }
|
196
209
|
option :version
|
197
210
|
|
198
211
|
# Retrieves the value of a derivation option.
|
@@ -245,6 +258,11 @@ class Shrine
|
|
245
258
|
[directory, filename].join("/")
|
246
259
|
end
|
247
260
|
|
261
|
+
# The source uploaded file storage is the default derivative storage.
|
262
|
+
def default_upload_storage
|
263
|
+
source.storage_key.to_sym
|
264
|
+
end
|
265
|
+
|
248
266
|
# Allows caching for 1 year or until the URL expires.
|
249
267
|
def default_cache_control
|
250
268
|
if option(:expires_in)
|
@@ -504,7 +522,7 @@ class Shrine
|
|
504
522
|
|
505
523
|
[302, { "Location" => redirect_url }, []]
|
506
524
|
else
|
507
|
-
if derivative
|
525
|
+
if derivative && File.exist?(derivative.path)
|
508
526
|
file_response(derivative, env)
|
509
527
|
else
|
510
528
|
uploaded_file.open(**upload_open_options)
|
@@ -526,7 +544,6 @@ class Shrine
|
|
526
544
|
if Rack.release > "2"
|
527
545
|
server.serving(Rack::Request.new(env), path)
|
528
546
|
else
|
529
|
-
server = server.dup
|
530
547
|
server.path = path
|
531
548
|
server.serving(env)
|
532
549
|
end
|
@@ -567,23 +584,43 @@ class Shrine
|
|
567
584
|
|
568
585
|
private
|
569
586
|
|
570
|
-
#
|
571
|
-
#
|
572
|
-
#
|
587
|
+
# Determines how to call the derivation block. If a file object is given,
|
588
|
+
# passes that as the source file, otherwise downloads the source uploaded
|
589
|
+
# file.
|
573
590
|
def generate(file)
|
574
591
|
if download
|
575
592
|
with_downloaded(file) do |file|
|
576
593
|
if include_uploaded_file
|
577
|
-
|
594
|
+
derive(file, source, *args)
|
578
595
|
else
|
579
|
-
|
596
|
+
derive(file, *args)
|
580
597
|
end
|
581
598
|
end
|
582
599
|
else
|
583
|
-
|
600
|
+
derive(source, *args)
|
584
601
|
end
|
585
602
|
end
|
586
603
|
|
604
|
+
# Calls the derivation block.
|
605
|
+
def derive(*args)
|
606
|
+
instrument_derivation do
|
607
|
+
uploader.instance_exec(*args, &derivation_block)
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
# Sends a `derivation.shrine` event for instrumentation plugin.
|
612
|
+
def instrument_derivation(&block)
|
613
|
+
return yield unless shrine_class.respond_to?(:instrument)
|
614
|
+
|
615
|
+
shrine_class.instrument(
|
616
|
+
:derivation,
|
617
|
+
name: derivation.name,
|
618
|
+
args: derivation.args,
|
619
|
+
derivation: derivation,
|
620
|
+
&block
|
621
|
+
)
|
622
|
+
end
|
623
|
+
|
587
624
|
# Massages the derivation result, ensuring it's opened in binary mode,
|
588
625
|
# rewinded and flushed to disk.
|
589
626
|
def normalize(derivative)
|
@@ -644,9 +681,7 @@ class Shrine
|
|
644
681
|
with_derivative(derivative) do |uploadable|
|
645
682
|
uploader.upload uploadable,
|
646
683
|
location: upload_location,
|
647
|
-
upload_options: upload_options
|
648
|
-
delete: false, # disable delete_raw plugin
|
649
|
-
move: false # disable moving plugin
|
684
|
+
upload_options: upload_options
|
650
685
|
end
|
651
686
|
end
|
652
687
|
|
@@ -654,9 +689,14 @@ class Shrine
|
|
654
689
|
|
655
690
|
def with_derivative(derivative)
|
656
691
|
if derivative
|
657
|
-
|
658
|
-
|
659
|
-
|
692
|
+
begin
|
693
|
+
# we want to keep the provided file open and rewinded
|
694
|
+
File.open(derivative.path, binmode: true) do |file|
|
695
|
+
yield file
|
696
|
+
end
|
697
|
+
ensure
|
698
|
+
# close the file handler if the file was deleted during upload
|
699
|
+
derivative.close if !File.exist?(derivative.path)
|
660
700
|
end
|
661
701
|
else
|
662
702
|
# generate the derivative and delete it afterwards
|
@@ -681,18 +721,12 @@ class Shrine
|
|
681
721
|
# Returns a Shrine::UploadedFile object pointing to the uploaded derivation
|
682
722
|
# result it exists on the storage.
|
683
723
|
def call
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
)
|
689
|
-
end
|
690
|
-
end
|
691
|
-
|
692
|
-
private
|
724
|
+
uploaded_file = shrine_class::UploadedFile.new(
|
725
|
+
"storage" => upload_storage.to_s,
|
726
|
+
"id" => upload_location,
|
727
|
+
)
|
693
728
|
|
694
|
-
|
695
|
-
shrine_class.find_storage(upload_storage)
|
729
|
+
uploaded_file if uploaded_file.exists?
|
696
730
|
end
|
697
731
|
end
|
698
732
|
|
@@ -6,34 +6,47 @@ class Shrine
|
|
6
6
|
#
|
7
7
|
# [doc/plugins/determine_mime_type.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/determine_mime_type.md
|
8
8
|
module DetermineMimeType
|
9
|
+
LOG_SUBSCRIBER = -> (event) do
|
10
|
+
Shrine.logger.info "MIME Type (#{event.duration}ms) – #{{
|
11
|
+
io: event[:io].class,
|
12
|
+
uploader: event[:uploader],
|
13
|
+
}.inspect}"
|
14
|
+
end
|
15
|
+
|
9
16
|
def self.configure(uploader, opts = {})
|
10
17
|
if opts[:analyzer] == :default
|
11
18
|
Shrine.deprecation("The :default analyzer of the determine_mime_type plugin has been renamed to :content_type. The :default alias will not be supported in Shrine 3.")
|
12
19
|
opts = opts.merge(analyzer: :content_type)
|
13
20
|
end
|
14
21
|
|
15
|
-
uploader.opts[:
|
16
|
-
uploader.opts[:
|
22
|
+
uploader.opts[:determine_mime_type] ||= { analyzer: :file, analyzer_options: {}, log_subscriber: LOG_SUBSCRIBER }
|
23
|
+
uploader.opts[:determine_mime_type].merge!(opts)
|
24
|
+
|
25
|
+
# instrumentation plugin integration
|
26
|
+
if uploader.respond_to?(:subscribe)
|
27
|
+
uploader.subscribe(:mime_type, &uploader.opts[:determine_mime_type][:log_subscriber])
|
28
|
+
end
|
17
29
|
end
|
18
30
|
|
19
31
|
module ClassMethods
|
20
32
|
# Determines the MIME type of the IO object by calling the specified
|
21
33
|
# analyzer.
|
22
34
|
def determine_mime_type(io)
|
23
|
-
analyzer = opts[:
|
35
|
+
analyzer = opts[:determine_mime_type][:analyzer]
|
24
36
|
|
25
37
|
analyzer = mime_type_analyzer(analyzer) if analyzer.is_a?(Symbol)
|
26
38
|
args = if analyzer.is_a?(Proc)
|
27
39
|
[io, mime_type_analyzers].take(analyzer.arity.abs)
|
28
40
|
else
|
29
|
-
[io, opts[:
|
41
|
+
[io, opts[:determine_mime_type][:analyzer_options]]
|
30
42
|
end
|
31
43
|
|
32
|
-
mime_type = analyzer.call(*args)
|
44
|
+
mime_type = instrument_mime_type(io) { analyzer.call(*args) }
|
33
45
|
io.rewind
|
34
46
|
|
35
47
|
mime_type
|
36
48
|
end
|
49
|
+
alias mime_type determine_mime_type
|
37
50
|
|
38
51
|
# Returns a hash of built-in MIME type analyzers, where keys are
|
39
52
|
# analyzer names and values are `#call`-able objects which accepts the
|
@@ -48,6 +61,15 @@ class Shrine
|
|
48
61
|
def mime_type_analyzer(name)
|
49
62
|
MimeTypeAnalyzer.new(name)
|
50
63
|
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Sends a `mime_type.shrine` event for instrumentation plugin.
|
68
|
+
def instrument_mime_type(io, &block)
|
69
|
+
return yield unless respond_to?(:instrument)
|
70
|
+
|
71
|
+
instrument(:mime_type, io: io, &block)
|
72
|
+
end
|
51
73
|
end
|
52
74
|
|
53
75
|
module InstanceMethods
|
@@ -6,18 +6,30 @@ class Shrine
|
|
6
6
|
#
|
7
7
|
# [doc/plugins/infer_extension.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/infer_extension.md
|
8
8
|
module InferExtension
|
9
|
+
LOG_SUBSCRIBER = -> (event) do
|
10
|
+
Shrine.logger.info "Extension (#{event.duration}ms) – #{{
|
11
|
+
mime_type: event[:mime_type],
|
12
|
+
uploader: event[:uploader],
|
13
|
+
}.inspect}"
|
14
|
+
end
|
15
|
+
|
9
16
|
def self.configure(uploader, opts = {})
|
10
|
-
uploader.opts[:
|
11
|
-
uploader.opts[:
|
17
|
+
uploader.opts[:infer_extension] ||= { inferrer: :mime_types, force: false, log_subscriber: LOG_SUBSCRIBER }
|
18
|
+
uploader.opts[:infer_extension].merge!(opts)
|
19
|
+
|
20
|
+
# instrumentation plugin integration
|
21
|
+
if uploader.respond_to?(:subscribe)
|
22
|
+
uploader.subscribe(:extension, &uploader.opts[:infer_extension][:log_subscriber])
|
23
|
+
end
|
12
24
|
end
|
13
25
|
|
14
26
|
module ClassMethods
|
15
27
|
def infer_extension(mime_type)
|
16
|
-
inferrer = opts[:
|
28
|
+
inferrer = opts[:infer_extension][:inferrer]
|
17
29
|
inferrer = extension_inferrer(inferrer) if inferrer.is_a?(Symbol)
|
18
30
|
args = [mime_type, extension_inferrers].take(inferrer.arity.abs)
|
19
31
|
|
20
|
-
inferrer.call(*args)
|
32
|
+
instrument_extension(mime_type) { inferrer.call(*args) }
|
21
33
|
end
|
22
34
|
|
23
35
|
def extension_inferrers
|
@@ -29,6 +41,15 @@ class Shrine
|
|
29
41
|
def extension_inferrer(name)
|
30
42
|
ExtensionInferrer.new(name).method(:call)
|
31
43
|
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Sends a `extension.shrine` event for instrumentation plugin.
|
48
|
+
def instrument_extension(mime_type, &block)
|
49
|
+
return yield unless respond_to?(:instrument)
|
50
|
+
|
51
|
+
instrument(:extension, mime_type: mime_type, &block)
|
52
|
+
end
|
32
53
|
end
|
33
54
|
|
34
55
|
module InstanceMethods
|
@@ -38,7 +59,7 @@ class Shrine
|
|
38
59
|
location = super
|
39
60
|
current_extension = File.extname(location)
|
40
61
|
|
41
|
-
if current_extension.empty? || opts[:
|
62
|
+
if current_extension.empty? || opts[:infer_extension][:force]
|
42
63
|
inferred_extension = infer_extension(mime_type)
|
43
64
|
location = location.chomp(current_extension) << inferred_extension unless inferred_extension.empty?
|
44
65
|
end
|
@@ -0,0 +1,300 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Shrine
|
4
|
+
module Plugins
|
5
|
+
# Documentation lives in [doc/plugins/instrumentation.md] on GitHub.
|
6
|
+
#
|
7
|
+
# [doc/plugins/instrumentation.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/instrumentation.md
|
8
|
+
module Instrumentation
|
9
|
+
EVENTS = %i[upload download exists delete metadata].freeze
|
10
|
+
|
11
|
+
# We use a proc in order to be able identify listeners.
|
12
|
+
LOG_SUBSCRIBER = -> (event) { LogSubscriber.call(event) }
|
13
|
+
|
14
|
+
def self.configure(uploader, opts = {})
|
15
|
+
uploader.opts[:instrumentation] ||= { log_subscriber: LOG_SUBSCRIBER, log_events: EVENTS }
|
16
|
+
uploader.opts[:instrumentation].merge!(opts)
|
17
|
+
uploader.opts[:instrumentation][:notifications] ||= ::ActiveSupport::Notifications
|
18
|
+
|
19
|
+
# we assign it to the top-level so that it's duplicated on subclassing
|
20
|
+
uploader.opts[:instrumentation_subscribers] ||= Hash.new { |h, k| h[k] = [] }
|
21
|
+
|
22
|
+
uploader.opts[:instrumentation][:log_events].each do |event_name|
|
23
|
+
uploader.subscribe(event_name, &uploader.opts[:instrumentation][:log_subscriber])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
# Sends a `*.shrine` event.
|
29
|
+
#
|
30
|
+
# # sends a `my_event.shrine` event
|
31
|
+
# Shrine.instrument(:my_event) do
|
32
|
+
# # work
|
33
|
+
# end
|
34
|
+
def instrument(event_name, payload = {}, &block)
|
35
|
+
payload[:uploader] = self
|
36
|
+
|
37
|
+
notifications.instrument("#{event_name}.shrine", payload, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Subscribes to a `*.shrine` event. It rejects duplicate subscribers.
|
41
|
+
#
|
42
|
+
# # subscribes to the `storage_upload.shrine` event
|
43
|
+
# Shrine.subscribe(:storage_upload) do |event|
|
44
|
+
# event.name #=> :storage_upload
|
45
|
+
# event.payload #=> { location: "...", ... }
|
46
|
+
# event[:location] #=> "..."
|
47
|
+
# event.duration #=> 50 (in milliseconds)
|
48
|
+
# end
|
49
|
+
def subscribe(event_name, &subscriber)
|
50
|
+
return if subscriber.nil?
|
51
|
+
return if subscribers[event_name].include?(subscriber)
|
52
|
+
|
53
|
+
notifications.subscribe("#{event_name}.shrine") do |event|
|
54
|
+
subscriber.call(event) if event[:uploader] <= self
|
55
|
+
end
|
56
|
+
|
57
|
+
subscribers[event_name] << subscriber
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def notifications
|
63
|
+
Notifications.new(opts[:instrumentation][:notifications])
|
64
|
+
end
|
65
|
+
|
66
|
+
def log_subscriber
|
67
|
+
opts[:instrumentation][:log_subscriber]
|
68
|
+
end
|
69
|
+
|
70
|
+
def subscribers
|
71
|
+
opts[:instrumentation_subscribers]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module InstanceMethods
|
76
|
+
private
|
77
|
+
|
78
|
+
# Sends a `upload.shrine` event.
|
79
|
+
def copy(io, context)
|
80
|
+
self.class.instrument(
|
81
|
+
:upload,
|
82
|
+
storage: storage_key,
|
83
|
+
location: context[:location],
|
84
|
+
io: io,
|
85
|
+
upload_options: context[:upload_options] || {},
|
86
|
+
options: context,
|
87
|
+
) { super }
|
88
|
+
end
|
89
|
+
|
90
|
+
# Sends a `metadata.shrine` event.
|
91
|
+
def get_metadata(io, context)
|
92
|
+
return super if io.is_a?(UploadedFile) && context[:metadata] != true || context[:metadata] == false
|
93
|
+
|
94
|
+
self.class.instrument(
|
95
|
+
:metadata,
|
96
|
+
storage: storage_key,
|
97
|
+
io: io,
|
98
|
+
options: context,
|
99
|
+
) { super }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
module FileMethods
|
104
|
+
# Sends a `download.shrine` event.
|
105
|
+
def open(**options)
|
106
|
+
shrine_class.instrument(
|
107
|
+
:download,
|
108
|
+
storage: storage_key.to_sym,
|
109
|
+
location: id,
|
110
|
+
download_options: options,
|
111
|
+
) { super }
|
112
|
+
end
|
113
|
+
|
114
|
+
# Sends a `exists.shrine` event.
|
115
|
+
def exists?
|
116
|
+
shrine_class.instrument(
|
117
|
+
:exists,
|
118
|
+
storage: storage_key.to_sym,
|
119
|
+
location: id,
|
120
|
+
) { super }
|
121
|
+
end
|
122
|
+
|
123
|
+
# Sends a `delete.shrine` event.
|
124
|
+
def delete
|
125
|
+
shrine_class.instrument(
|
126
|
+
:delete,
|
127
|
+
storage: storage_key.to_sym,
|
128
|
+
location: id,
|
129
|
+
) { super }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Abstracts away different types of notifications objects
|
134
|
+
# (`ActiveSupport::Notifications` and `Dry::Monitor::Notifications`).
|
135
|
+
class Notifications
|
136
|
+
attr_reader :notifications
|
137
|
+
|
138
|
+
def initialize(notifications)
|
139
|
+
@notifications = notifications
|
140
|
+
end
|
141
|
+
|
142
|
+
def subscribe(event_name, &block)
|
143
|
+
library_send(:subscribe, event_name) do |event|
|
144
|
+
yield Event.new(event)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def instrument(event_name, payload, &block)
|
149
|
+
notifications.instrument(event_name, payload, &block)
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def dry_monitor_subscribe(event_name, &block)
|
155
|
+
notifications.register_event(event_name)
|
156
|
+
notifications.subscribe(event_name, &block)
|
157
|
+
end
|
158
|
+
|
159
|
+
def active_support_subscribe(event_name, &block)
|
160
|
+
notifications.subscribe(event_name) do |*args|
|
161
|
+
yield ActiveSupport::Notifications::Event.new(*args)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def library_send(method_name, *args, &block)
|
166
|
+
case notifications.to_s
|
167
|
+
when /Dry::Monitor::Notifications/
|
168
|
+
send(:"dry_monitor_#{method_name}", *args, &block)
|
169
|
+
when /ActiveSupport::Notifications/
|
170
|
+
send(:"active_support_#{method_name}", *args, &block)
|
171
|
+
else
|
172
|
+
notifications.send(method_name, *args, &block)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Abstracts away different kind of event objects
|
178
|
+
# (`ActiveSupport::Notifications::Event` and `Dry::Events::Event`).
|
179
|
+
class Event
|
180
|
+
attr_reader :event
|
181
|
+
|
182
|
+
def initialize(event)
|
183
|
+
@event = event
|
184
|
+
end
|
185
|
+
|
186
|
+
def name
|
187
|
+
library_send(:name).chomp(".shrine").to_sym
|
188
|
+
end
|
189
|
+
|
190
|
+
def payload
|
191
|
+
event.payload
|
192
|
+
end
|
193
|
+
|
194
|
+
def [](name)
|
195
|
+
event.payload.fetch(name)
|
196
|
+
end
|
197
|
+
|
198
|
+
def duration
|
199
|
+
library_send(:duration)
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
|
204
|
+
def dry_events_name
|
205
|
+
event.id
|
206
|
+
end
|
207
|
+
|
208
|
+
def active_support_name
|
209
|
+
event.name
|
210
|
+
end
|
211
|
+
|
212
|
+
def dry_events_duration
|
213
|
+
event[:time]
|
214
|
+
end
|
215
|
+
|
216
|
+
def active_support_duration
|
217
|
+
event.duration.to_i
|
218
|
+
end
|
219
|
+
|
220
|
+
def library_send(method_name, *args, &block)
|
221
|
+
case event.class.name
|
222
|
+
when "ActiveSupport::Notifications::Event"
|
223
|
+
send(:"active_support_#{method_name}", *args, &block)
|
224
|
+
when "Dry::Events::Event"
|
225
|
+
send(:"dry_events_#{method_name}", *args, &block)
|
226
|
+
else
|
227
|
+
event.send(method_name, *args, &block)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# Logs received events.
|
233
|
+
class LogSubscriber
|
234
|
+
# Entry point for logging.
|
235
|
+
def self.call(event)
|
236
|
+
new.public_send(:"on_#{event.name}", event)
|
237
|
+
end
|
238
|
+
|
239
|
+
def on_upload(event)
|
240
|
+
log "Upload (#{event.duration}ms) – #{format(
|
241
|
+
storage: event[:storage],
|
242
|
+
location: event[:location],
|
243
|
+
io: event[:io].class,
|
244
|
+
upload_options: event[:upload_options],
|
245
|
+
uploader: event[:uploader],
|
246
|
+
)}"
|
247
|
+
end
|
248
|
+
|
249
|
+
def on_download(event)
|
250
|
+
log "Download (#{event.duration}ms) – #{format(
|
251
|
+
storage: event[:storage],
|
252
|
+
location: event[:location],
|
253
|
+
download_options: event[:download_options],
|
254
|
+
uploader: event[:uploader],
|
255
|
+
)}"
|
256
|
+
end
|
257
|
+
|
258
|
+
def on_exists(event)
|
259
|
+
log "Exists (#{event.duration}ms) – #{format(
|
260
|
+
storage: event[:storage],
|
261
|
+
location: event[:location],
|
262
|
+
uploader: event[:uploader],
|
263
|
+
)}"
|
264
|
+
end
|
265
|
+
|
266
|
+
def on_delete(event)
|
267
|
+
log "Delete (#{event.duration}ms) – #{format(
|
268
|
+
storage: event[:storage],
|
269
|
+
location: event[:location],
|
270
|
+
uploader: event[:uploader],
|
271
|
+
)}"
|
272
|
+
end
|
273
|
+
|
274
|
+
def on_metadata(event)
|
275
|
+
log "Metadata (#{event.duration}ms) – #{format(
|
276
|
+
storage: event[:storage],
|
277
|
+
io: event[:io].class,
|
278
|
+
uploader: event[:uploader],
|
279
|
+
)}"
|
280
|
+
end
|
281
|
+
|
282
|
+
private
|
283
|
+
|
284
|
+
def format(properties = {})
|
285
|
+
properties.inspect
|
286
|
+
end
|
287
|
+
|
288
|
+
def log(message)
|
289
|
+
logger.info(message)
|
290
|
+
end
|
291
|
+
|
292
|
+
def logger
|
293
|
+
Shrine.logger
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
register_plugin(:instrumentation, Instrumentation)
|
299
|
+
end
|
300
|
+
end
|