shrine 2.19.4 → 3.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +299 -11
- data/README.md +9 -3
- data/doc/advantages.md +1 -1
- data/doc/carrierwave.md +4 -4
- data/doc/creating_persistence_plugins.md +172 -0
- data/doc/creating_plugins.md +1 -1
- data/doc/creating_storages.md +3 -1
- data/doc/design.md +2 -2
- data/doc/direct_s3.md +0 -22
- data/doc/paperclip.md +3 -3
- data/doc/plugins/activerecord.md +211 -42
- data/doc/plugins/atomic_helpers.md +153 -0
- data/doc/plugins/column.md +90 -0
- data/doc/plugins/derivation_endpoint.md +54 -62
- data/doc/plugins/derivatives.md +752 -0
- data/doc/plugins/entity.md +204 -0
- data/doc/plugins/infer_extension.md +8 -8
- data/doc/plugins/instrumentation.md +33 -13
- data/doc/plugins/keep_files.md +5 -15
- data/doc/plugins/model.md +157 -0
- data/doc/plugins/presign_endpoint.md +2 -1
- data/doc/plugins/refresh_metadata.md +44 -7
- data/doc/plugins/sequel.md +190 -33
- data/doc/plugins/{default_url_options.md → url_options.md} +5 -5
- data/doc/processing.md +1 -1
- data/doc/release_notes/1.1.0.md +2 -2
- data/doc/release_notes/2.15.0.md +1 -1
- data/doc/storage/s3.md +2 -2
- data/doc/testing.md +1 -1
- data/lib/shrine.rb +72 -138
- data/lib/shrine/attacher.rb +272 -176
- data/lib/shrine/attachment.rb +2 -42
- data/lib/shrine/plugins/activerecord.rb +103 -26
- data/lib/shrine/plugins/add_metadata.rb +9 -10
- data/lib/shrine/plugins/atomic_helpers.rb +111 -0
- data/lib/shrine/plugins/attacher_options.rb +55 -0
- data/lib/shrine/plugins/backgrounding.rb +147 -115
- data/lib/shrine/plugins/cached_attachment_data.rb +6 -9
- data/lib/shrine/plugins/column.rb +104 -0
- data/lib/shrine/plugins/data_uri.rb +35 -38
- data/lib/shrine/plugins/default_storage.rb +18 -12
- data/lib/shrine/plugins/default_url.rb +11 -21
- data/lib/shrine/plugins/default_url_options.rb +3 -30
- data/lib/shrine/plugins/delete_raw.rb +9 -13
- data/lib/shrine/plugins/derivation_endpoint.rb +75 -114
- data/lib/shrine/plugins/derivatives.rb +576 -0
- data/lib/shrine/plugins/determine_mime_type.rb +3 -15
- data/lib/shrine/plugins/download_endpoint.rb +83 -131
- data/lib/shrine/plugins/dynamic_storage.rb +4 -8
- data/lib/shrine/plugins/entity.rb +128 -0
- data/lib/shrine/plugins/form_assign.rb +107 -0
- data/lib/shrine/plugins/included.rb +4 -3
- data/lib/shrine/plugins/infer_extension.rb +10 -17
- data/lib/shrine/plugins/instrumentation.rb +45 -25
- data/lib/shrine/plugins/keep_files.rb +2 -12
- data/lib/shrine/plugins/metadata_attributes.rb +15 -14
- data/lib/shrine/plugins/model.rb +137 -0
- data/lib/shrine/plugins/module_include.rb +2 -0
- data/lib/shrine/plugins/presign_endpoint.rb +1 -15
- data/lib/shrine/plugins/pretty_location.rb +5 -5
- data/lib/shrine/plugins/processing.rb +21 -6
- data/lib/shrine/plugins/rack_file.rb +1 -39
- data/lib/shrine/plugins/rack_response.rb +14 -7
- data/lib/shrine/plugins/recache.rb +5 -2
- data/lib/shrine/plugins/refresh_metadata.rb +12 -8
- data/lib/shrine/plugins/remote_url.rb +44 -53
- data/lib/shrine/plugins/remove_attachment.rb +7 -2
- data/lib/shrine/plugins/remove_invalid.rb +8 -4
- data/lib/shrine/plugins/restore_cached_data.rb +12 -4
- data/lib/shrine/plugins/sequel.rb +115 -27
- data/lib/shrine/plugins/signature.rb +2 -7
- data/lib/shrine/plugins/store_dimensions.rb +13 -27
- data/lib/shrine/plugins/upload_endpoint.rb +14 -15
- data/lib/shrine/plugins/upload_options.rb +9 -8
- data/lib/shrine/plugins/url_options.rb +33 -0
- data/lib/shrine/plugins/validation.rb +87 -0
- data/lib/shrine/plugins/validation_helpers.rb +33 -54
- data/lib/shrine/plugins/versions.rb +106 -84
- data/lib/shrine/storage/file_system.rb +32 -57
- data/lib/shrine/storage/linter.rb +9 -1
- data/lib/shrine/storage/memory.rb +42 -0
- data/lib/shrine/storage/s3.rb +38 -146
- data/lib/shrine/uploaded_file.rb +22 -29
- data/lib/shrine/version.rb +4 -4
- data/shrine.gemspec +2 -3
- metadata +27 -54
- data/doc/plugins/backup.md +0 -31
- data/doc/plugins/copy.md +0 -24
- data/doc/plugins/delete_promoted.md +0 -12
- data/doc/plugins/direct_upload.md +0 -172
- data/doc/plugins/hooks.md +0 -58
- data/doc/plugins/logging.md +0 -42
- data/doc/plugins/migration_helpers.md +0 -60
- data/doc/plugins/moving.md +0 -19
- data/doc/plugins/multi_delete.md +0 -20
- data/doc/plugins/parallelize.md +0 -16
- data/doc/plugins/parsed_json.md +0 -23
- data/lib/shrine/plugins/background_helpers.rb +0 -5
- data/lib/shrine/plugins/backup.rb +0 -90
- data/lib/shrine/plugins/copy.rb +0 -50
- data/lib/shrine/plugins/delete_promoted.rb +0 -20
- data/lib/shrine/plugins/direct_upload.rb +0 -217
- data/lib/shrine/plugins/hooks.rb +0 -90
- data/lib/shrine/plugins/logging.rb +0 -142
- data/lib/shrine/plugins/migration_helpers.rb +0 -70
- data/lib/shrine/plugins/moving.rb +0 -57
- data/lib/shrine/plugins/multi_delete.rb +0 -32
- data/lib/shrine/plugins/parallelize.rb +0 -78
- data/lib/shrine/plugins/parsed_json.rb +0 -29
@@ -8,9 +8,14 @@ class Shrine
|
|
8
8
|
#
|
9
9
|
# [doc/plugins/sequel.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/sequel.md
|
10
10
|
module Sequel
|
11
|
-
def self.
|
12
|
-
uploader.
|
13
|
-
uploader.
|
11
|
+
def self.load_dependencies(uploader, **)
|
12
|
+
uploader.plugin :model
|
13
|
+
uploader.plugin :atomic_helpers
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configure(uploader, **opts)
|
17
|
+
uploader.opts[:sequel] ||= { callbacks: true, validations: true }
|
18
|
+
uploader.opts[:sequel].merge!(opts)
|
14
19
|
end
|
15
20
|
|
16
21
|
module AttachmentMethods
|
@@ -21,64 +26,147 @@ class Shrine
|
|
21
26
|
|
22
27
|
name = attachment_name
|
23
28
|
|
24
|
-
if shrine_class.opts[:
|
29
|
+
if shrine_class.opts[:sequel][:validations]
|
25
30
|
define_method :validate do
|
26
31
|
super()
|
27
|
-
|
28
|
-
|
32
|
+
# validation plugin integration
|
33
|
+
if send(:"#{name}_attacher").respond_to?(:errors)
|
34
|
+
send(:"#{name}_attacher").errors.each do |message|
|
35
|
+
errors.add(name, *message)
|
36
|
+
end
|
29
37
|
end
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
33
|
-
if shrine_class.opts[:
|
41
|
+
if shrine_class.opts[:sequel][:callbacks]
|
34
42
|
define_method :before_save do
|
35
43
|
super()
|
36
|
-
|
37
|
-
|
44
|
+
if send(:"#{name}_attacher").changed?
|
45
|
+
send(:"#{name}_attacher").save
|
46
|
+
end
|
38
47
|
end
|
39
48
|
|
40
49
|
define_method :after_save do
|
41
50
|
super()
|
42
|
-
|
43
|
-
|
51
|
+
if send(:"#{name}_attacher").changed?
|
52
|
+
db.after_commit do
|
53
|
+
send(:"#{name}_attacher").finalize
|
54
|
+
send(:"#{name}_attacher").sequel_persist
|
55
|
+
end
|
56
|
+
end
|
44
57
|
end
|
45
58
|
|
46
59
|
define_method :after_destroy do
|
47
60
|
super()
|
48
|
-
|
49
|
-
|
61
|
+
if send(:"#{name}_attacher").attached?
|
62
|
+
db.after_commit do
|
63
|
+
send(:"#{name}_attacher").destroy_attached
|
64
|
+
end
|
65
|
+
end
|
50
66
|
end
|
51
67
|
end
|
52
|
-
end
|
53
|
-
end
|
54
68
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
69
|
+
# reload the attacher on record reload
|
70
|
+
define_method :_refresh do |*args|
|
71
|
+
result = super(*args)
|
72
|
+
instance_variable_set(:"@#{name}_attacher", nil)
|
73
|
+
result
|
74
|
+
end
|
75
|
+
private :_refresh
|
59
76
|
end
|
60
77
|
end
|
61
78
|
|
62
79
|
module AttacherMethods
|
80
|
+
# Promotes cached file to permanent storage in an atomic way. It's
|
81
|
+
# intended to be called from a background job.
|
82
|
+
#
|
83
|
+
# attacher.assign(file)
|
84
|
+
# attacher.cached? #=> true
|
85
|
+
#
|
86
|
+
# # ... in background job ...
|
87
|
+
#
|
88
|
+
# attacher.atomic_promote
|
89
|
+
# attacher.stored? #=> true
|
90
|
+
#
|
91
|
+
# It accepts `:reload` and `:persist` strategies:
|
92
|
+
#
|
93
|
+
# attacher.atomic_promote(reload: :lock) # uses database locking (default)
|
94
|
+
# attacher.atomic_promote(reload: :fetch) # reloads with no locking
|
95
|
+
# attacher.atomic_promote(reload: ->(&b){}) # custom reloader
|
96
|
+
# attacher.atomic_promote(reload: false) # skips reloading
|
97
|
+
#
|
98
|
+
# attacher.atomic_promote(persist: :save) # persists stored file (default)
|
99
|
+
# attacher.atomic_promote(persist: ->{}) # custom persister
|
100
|
+
# attacher.atomic_promote(persist: false) # skips persistence
|
101
|
+
def sequel_atomic_promote(**options, &block)
|
102
|
+
abstract_atomic_promote(sequel_strategies(**options), &block)
|
103
|
+
end
|
104
|
+
alias atomic_promote sequel_atomic_promote
|
105
|
+
|
106
|
+
# Persist the the record only if the attachment hasn't changed.
|
107
|
+
# Optionally yields reloaded attacher to the block before persisting.
|
108
|
+
# It's intended to be called from a background job.
|
109
|
+
#
|
110
|
+
# # ... in background job ...
|
111
|
+
#
|
112
|
+
# attacher.file.metadata["foo"] = "bar"
|
113
|
+
# attacher.write
|
114
|
+
#
|
115
|
+
# attacher.atomic_persist
|
116
|
+
def sequel_atomic_persist(*args, **options, &block)
|
117
|
+
abstract_atomic_persist(*args, sequel_strategies(**options), &block)
|
118
|
+
end
|
119
|
+
alias atomic_persist sequel_atomic_persist
|
120
|
+
|
121
|
+
# Called in the `after_commit` callback after finalization.
|
122
|
+
def sequel_persist
|
123
|
+
sequel_save
|
124
|
+
end
|
125
|
+
alias persist sequel_persist
|
126
|
+
|
63
127
|
private
|
64
128
|
|
65
|
-
#
|
66
|
-
def
|
67
|
-
|
129
|
+
# Resolves strategies for atomic promotion and persistence.
|
130
|
+
def sequel_strategies(reload: :lock, persist: :save, **options)
|
131
|
+
reload = method(:"sequel_#{reload}") if reload.is_a?(Symbol)
|
132
|
+
persist = method(:"sequel_#{persist}") if persist.is_a?(Symbol)
|
133
|
+
|
134
|
+
{ reload: reload, persist: persist, **options }
|
135
|
+
end
|
136
|
+
|
137
|
+
# Implements the "fetch" reload strategy for #sequel_promote.
|
138
|
+
def sequel_fetch
|
139
|
+
yield record.dup.refresh
|
140
|
+
end
|
141
|
+
|
142
|
+
# Implements the "lock" reload strategy for #sequel_promote.
|
143
|
+
def sequel_lock
|
144
|
+
record.db.transaction { yield record.dup.lock! }
|
145
|
+
end
|
146
|
+
|
147
|
+
# Implements the "save" persist strategy for #sequel_promote.
|
148
|
+
def sequel_save
|
68
149
|
record.save_changes(validate: false)
|
69
150
|
end
|
70
151
|
|
71
|
-
#
|
72
|
-
#
|
152
|
+
# Sequel JSON column attribute with `pg_json` Sequel extension loaded
|
153
|
+
# returns a `Sequel::Postgres::JSONHashBase` object will be returned,
|
73
154
|
# which we convert into a Hash.
|
74
|
-
def
|
75
|
-
sequel_json_column? ?
|
155
|
+
def deserialize_column(data)
|
156
|
+
sequel_json_column? ? data&.to_hash : super
|
157
|
+
end
|
158
|
+
|
159
|
+
# Sequel JSON column attribute with `pg_json` Sequel extension loaded
|
160
|
+
# can receive a Hash object, so there is no need to generate a JSON
|
161
|
+
# string.
|
162
|
+
def serialize_column(data)
|
163
|
+
sequel_json_column? ? data : super
|
76
164
|
end
|
77
165
|
|
78
166
|
# Returns true if the data attribute represents a JSON or JSONB column.
|
79
167
|
def sequel_json_column?
|
80
168
|
return false unless record.is_a?(::Sequel::Model)
|
81
|
-
return false unless column = record.class.db_schema[
|
169
|
+
return false unless column = record.class.db_schema[attribute]
|
82
170
|
|
83
171
|
[:json, :jsonb].include?(column[:type])
|
84
172
|
end
|
@@ -15,14 +15,9 @@ class Shrine
|
|
15
15
|
}.inspect}"
|
16
16
|
end
|
17
17
|
|
18
|
-
def self.configure(uploader,
|
19
|
-
uploader.opts[:signature] ||= { log_subscriber: LOG_SUBSCRIBER }
|
20
|
-
uploader.opts[:signature].merge!(opts)
|
21
|
-
|
18
|
+
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER)
|
22
19
|
# instrumentation plugin integration
|
23
|
-
if uploader.respond_to?(:subscribe)
|
24
|
-
uploader.subscribe(:signature, &uploader.opts[:signature][:log_subscriber])
|
25
|
-
end
|
20
|
+
uploader.subscribe(:signature, &log_subscriber) if uploader.respond_to?(:subscribe)
|
26
21
|
end
|
27
22
|
|
28
23
|
module ClassMethods
|
@@ -13,24 +13,22 @@ class Shrine
|
|
13
13
|
}.inspect}"
|
14
14
|
end
|
15
15
|
|
16
|
-
def self.configure(uploader,
|
17
|
-
uploader.opts[:store_dimensions] ||= { analyzer: :fastimage, on_error: :
|
16
|
+
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
|
17
|
+
uploader.opts[:store_dimensions] ||= { analyzer: :fastimage, on_error: :warn }
|
18
18
|
uploader.opts[:store_dimensions].merge!(opts)
|
19
19
|
|
20
20
|
# resolve error strategy
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
uploader.opts[:store_dimensions][:on_error] =
|
22
|
+
case uploader.opts[:store_dimensions][:on_error]
|
23
|
+
when :fail then -> (error) { fail error }
|
24
|
+
when :warn then -> (error) { Shrine.warn "Error occurred when attempting to extract image dimensions: #{error.inspect}" }
|
25
|
+
when :ignore then -> (error) { }
|
26
|
+
else
|
27
|
+
uploader.opts[:store_dimensions][:on_error]
|
28
|
+
end
|
29
29
|
|
30
30
|
# instrumentation plugin integration
|
31
|
-
if uploader.respond_to?(:subscribe)
|
32
|
-
uploader.subscribe(:image_dimensions, &uploader.opts[:store_dimensions][:log_subscriber])
|
33
|
-
end
|
31
|
+
uploader.subscribe(:image_dimensions, &log_subscriber) if uploader.respond_to?(:subscribe)
|
34
32
|
end
|
35
33
|
|
36
34
|
module ClassMethods
|
@@ -76,23 +74,11 @@ class Shrine
|
|
76
74
|
|
77
75
|
module InstanceMethods
|
78
76
|
# We update the metadata with "width" and "height".
|
79
|
-
def extract_metadata(io,
|
80
|
-
width, height = extract_dimensions(io)
|
77
|
+
def extract_metadata(io, **options)
|
78
|
+
width, height = self.class.extract_dimensions(io)
|
81
79
|
|
82
80
|
super.merge!("width" => width, "height" => height)
|
83
81
|
end
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
# Extracts dimensions using the specified analyzer.
|
88
|
-
def extract_dimensions(io)
|
89
|
-
self.class.extract_dimensions(io)
|
90
|
-
end
|
91
|
-
|
92
|
-
# Returns a hash of built-in dimensions analyzers.
|
93
|
-
def dimensions_analyzers
|
94
|
-
self.class.dimensions_analyzers
|
95
|
-
end
|
96
82
|
end
|
97
83
|
|
98
84
|
module FileMethods
|
@@ -146,7 +146,7 @@ class Shrine
|
|
146
146
|
# keys, which is to be passed to `Shrine#upload`. Calls
|
147
147
|
# `:upload_context` option if given.
|
148
148
|
def get_context(request)
|
149
|
-
context = { action: :upload
|
149
|
+
context = { action: :upload }
|
150
150
|
context.merge! @upload_context.call(request) if @upload_context
|
151
151
|
context
|
152
152
|
end
|
@@ -166,26 +166,29 @@ class Shrine
|
|
166
166
|
# a Rack response triple - an array consisting of a status number, hash
|
167
167
|
# of headers, and a body enumerable. If a `:rack_response` option is
|
168
168
|
# given, calls that instead.
|
169
|
-
def make_response(
|
169
|
+
def make_response(uploaded_file, request)
|
170
170
|
if @rack_response
|
171
|
-
@rack_response.call(
|
171
|
+
@rack_response.call(uploaded_file, request)
|
172
172
|
else
|
173
173
|
if @url
|
174
|
-
url
|
175
|
-
|
176
|
-
when Hash then object.url(**@url)
|
177
|
-
else @url.call(object, request)
|
178
|
-
end
|
179
|
-
|
180
|
-
body = { data: object, url: url }.to_json
|
174
|
+
url = resolve_url(uploaded_file, request)
|
175
|
+
body = { data: uploaded_file, url: url }.to_json
|
181
176
|
else
|
182
|
-
body =
|
177
|
+
body = uploaded_file.to_json
|
183
178
|
end
|
184
179
|
|
185
180
|
[200, { "Content-Type" => CONTENT_TYPE_JSON }, [body]]
|
186
181
|
end
|
187
182
|
end
|
188
183
|
|
184
|
+
def resolve_url(uploaded_file, request)
|
185
|
+
case @url
|
186
|
+
when true then uploaded_file.url
|
187
|
+
when Hash then uploaded_file.url(**@url)
|
188
|
+
else @url.call(uploaded_file, request)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
189
192
|
def verify_size!(file, request)
|
190
193
|
error!(413, "Upload Too Large") if @max_size && file.size > @max_size
|
191
194
|
end
|
@@ -212,8 +215,4 @@ class Shrine
|
|
212
215
|
@shrine_class.new(@storage_key)
|
213
216
|
end
|
214
217
|
end
|
215
|
-
|
216
|
-
# backwards compatibility
|
217
|
-
Plugins::UploadEndpoint.const_set(:App, UploadEndpoint)
|
218
|
-
Plugins::UploadEndpoint.deprecate_constant(:App)
|
219
218
|
end
|
@@ -12,18 +12,19 @@ class Shrine
|
|
12
12
|
end
|
13
13
|
|
14
14
|
module InstanceMethods
|
15
|
-
def
|
16
|
-
upload_options = get_upload_options(io,
|
17
|
-
|
18
|
-
super
|
15
|
+
def _upload(io, **options)
|
16
|
+
upload_options = get_upload_options(io, options)
|
17
|
+
|
18
|
+
super(io, **options, upload_options: upload_options)
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
|
-
def get_upload_options(io,
|
24
|
-
|
25
|
-
|
26
|
-
options
|
23
|
+
def get_upload_options(io, options)
|
24
|
+
upload_options = opts[:upload_options][storage_key] || {}
|
25
|
+
upload_options = upload_options.call(io, options) if upload_options.respond_to?(:call)
|
26
|
+
upload_options = upload_options.merge(options[:upload_options]) if options[:upload_options]
|
27
|
+
upload_options
|
27
28
|
end
|
28
29
|
end
|
29
30
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Shrine
|
4
|
+
module Plugins
|
5
|
+
# Documentation lives in [doc/plugins/url_options.md] on GitHub.
|
6
|
+
#
|
7
|
+
# [doc/plugins/url_options.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/url_options.md
|
8
|
+
module UrlOptions
|
9
|
+
def self.configure(uploader, **options)
|
10
|
+
uploader.opts[:url_options] ||= {}
|
11
|
+
uploader.opts[:url_options].merge!(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
module FileMethods
|
15
|
+
def url(**options)
|
16
|
+
default_options = url_options(options)
|
17
|
+
|
18
|
+
super(**default_options, **options)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def url_options(options)
|
24
|
+
default_options = shrine_class.opts[:url_options][storage_key]
|
25
|
+
default_options = default_options.call(self, options) if default_options.respond_to?(:call)
|
26
|
+
default_options || {}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
register_plugin(:url_options, UrlOptions)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Shrine
|
4
|
+
module Plugins
|
5
|
+
module Validation
|
6
|
+
module AttacherClassMethods
|
7
|
+
# Block that is executed in context of Shrine::Attacher during
|
8
|
+
# validation. Example:
|
9
|
+
#
|
10
|
+
# Shrine::Attacher.validate do
|
11
|
+
# if file.size > 5*1024*1024
|
12
|
+
# errors << "is too big (max is 5 MB)"
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
def validate(&block)
|
16
|
+
private define_method(:validate_block, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module AttacherMethods
|
21
|
+
# Returns an array of validation errors created on file assignment in
|
22
|
+
# the `Attacher.validate` block.
|
23
|
+
attr_reader :errors
|
24
|
+
|
25
|
+
# Initializes validation errors to an empty array.
|
26
|
+
def initialize(**options)
|
27
|
+
super
|
28
|
+
@errors = []
|
29
|
+
end
|
30
|
+
|
31
|
+
# Registers options that will be passed to validation.
|
32
|
+
def validate_options(options = nil)
|
33
|
+
if options
|
34
|
+
@validate_options ||= {}
|
35
|
+
@validate_options.merge!(options)
|
36
|
+
else
|
37
|
+
defined?(@validate_options) ? @validate_options : {}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Leaves out :validate option when calling `Shrine.upload`.
|
42
|
+
def upload(*args, validate: nil, **options)
|
43
|
+
super(*args, **options)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Performs validations after changing the file.
|
47
|
+
def change(file, validate: nil, **)
|
48
|
+
result = super
|
49
|
+
validation(validate)
|
50
|
+
result
|
51
|
+
end
|
52
|
+
|
53
|
+
# Runs the validation defined by `Attacher.validate`.
|
54
|
+
def validate(**options)
|
55
|
+
errors.clear
|
56
|
+
_validate(**options) if attached?
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# Calls validation appropriately based on the :validate value.
|
62
|
+
def validation(argument)
|
63
|
+
case argument
|
64
|
+
when Hash then validate(argument)
|
65
|
+
when false then errors.clear # skip validation
|
66
|
+
else validate
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Calls #validate_block, passing it accepted parameters.
|
71
|
+
def _validate(**options)
|
72
|
+
if method(:validate_block).arity.zero?
|
73
|
+
validate_block
|
74
|
+
else
|
75
|
+
validate_block(**validate_options, **options)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Overridden by the `Attacher.validate` block.
|
80
|
+
def validate_block(**options)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
register_plugin(:validation, Validation)
|
86
|
+
end
|
87
|
+
end
|