shrine 2.19.4 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (209) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +485 -43
  3. data/LICENSE.txt +1 -1
  4. data/README.md +81 -977
  5. data/doc/advantages.md +231 -204
  6. data/doc/attacher.md +304 -153
  7. data/doc/carrierwave.md +297 -226
  8. data/doc/changing_derivatives.md +308 -0
  9. data/doc/changing_location.md +102 -21
  10. data/doc/changing_storage.md +110 -0
  11. data/doc/creating_persistence_plugins.md +132 -0
  12. data/doc/creating_plugins.md +43 -23
  13. data/doc/creating_storages.md +19 -5
  14. data/doc/design.md +147 -97
  15. data/doc/direct_s3.md +38 -28
  16. data/doc/external/articles.md +63 -0
  17. data/doc/external/extensions.md +53 -0
  18. data/doc/external/misc.md +32 -0
  19. data/doc/getting_started.md +1115 -0
  20. data/doc/metadata.md +190 -109
  21. data/doc/multiple_files.md +62 -34
  22. data/doc/paperclip.md +384 -262
  23. data/doc/plugins/activerecord.md +177 -46
  24. data/doc/plugins/add_metadata.md +139 -38
  25. data/doc/plugins/atomic_helpers.md +217 -0
  26. data/doc/plugins/backgrounding.md +156 -98
  27. data/doc/plugins/cached_attachment_data.md +7 -5
  28. data/doc/plugins/column.md +121 -0
  29. data/doc/plugins/data_uri.md +23 -22
  30. data/doc/plugins/default_storage.md +36 -10
  31. data/doc/plugins/default_url.md +30 -13
  32. data/doc/plugins/delete_raw.md +4 -2
  33. data/doc/plugins/derivation_endpoint.md +162 -101
  34. data/doc/plugins/derivatives.md +829 -0
  35. data/doc/plugins/determine_mime_type.md +4 -2
  36. data/doc/plugins/download_endpoint.md +64 -8
  37. data/doc/plugins/dynamic_storage.md +5 -3
  38. data/doc/plugins/entity.md +263 -0
  39. data/doc/plugins/form_assign.md +55 -0
  40. data/doc/plugins/included.md +31 -8
  41. data/doc/plugins/infer_extension.md +21 -10
  42. data/doc/plugins/instrumentation.md +38 -16
  43. data/doc/plugins/keep_files.md +14 -17
  44. data/doc/plugins/metadata_attributes.md +42 -13
  45. data/doc/plugins/mirroring.md +118 -0
  46. data/doc/plugins/model.md +210 -0
  47. data/doc/plugins/module_include.md +4 -2
  48. data/doc/plugins/multi_cache.md +24 -0
  49. data/doc/plugins/persistence.md +101 -0
  50. data/doc/plugins/presign_endpoint.md +9 -4
  51. data/doc/plugins/pretty_location.md +16 -3
  52. data/doc/plugins/processing.md +4 -2
  53. data/doc/plugins/rack_file.md +8 -2
  54. data/doc/plugins/rack_response.md +6 -2
  55. data/doc/plugins/recache.md +4 -2
  56. data/doc/plugins/refresh_metadata.md +49 -9
  57. data/doc/plugins/remote_url.md +84 -47
  58. data/doc/plugins/remove_attachment.md +27 -6
  59. data/doc/plugins/remove_invalid.md +21 -6
  60. data/doc/plugins/restore_cached_data.md +11 -3
  61. data/doc/plugins/sequel.md +159 -35
  62. data/doc/plugins/signature.md +16 -5
  63. data/doc/plugins/store_dimensions.md +14 -2
  64. data/doc/plugins/tempfile.md +4 -2
  65. data/doc/plugins/type_predicates.md +96 -0
  66. data/doc/plugins/upload_endpoint.md +13 -13
  67. data/doc/plugins/upload_options.md +6 -4
  68. data/doc/plugins/{default_url_options.md → url_options.md} +9 -7
  69. data/doc/plugins/validation.md +97 -0
  70. data/doc/plugins/validation_helpers.md +16 -13
  71. data/doc/plugins/versions.md +15 -19
  72. data/doc/processing.md +438 -221
  73. data/doc/refile.md +185 -167
  74. data/doc/release_notes/1.0.0.md +4 -0
  75. data/doc/release_notes/1.1.0.md +6 -2
  76. data/doc/release_notes/1.2.0.md +4 -0
  77. data/doc/release_notes/1.3.0.md +4 -0
  78. data/doc/release_notes/1.4.0.md +4 -0
  79. data/doc/release_notes/1.4.1.md +4 -0
  80. data/doc/release_notes/1.4.2.md +4 -0
  81. data/doc/release_notes/2.0.0.md +4 -0
  82. data/doc/release_notes/2.0.1.md +4 -0
  83. data/doc/release_notes/2.1.0.md +4 -0
  84. data/doc/release_notes/2.1.1.md +4 -0
  85. data/doc/release_notes/2.10.0.md +4 -0
  86. data/doc/release_notes/2.10.1.md +4 -0
  87. data/doc/release_notes/2.11.0.md +4 -0
  88. data/doc/release_notes/2.12.0.md +4 -0
  89. data/doc/release_notes/2.13.0.md +4 -0
  90. data/doc/release_notes/2.14.0.md +5 -1
  91. data/doc/release_notes/2.15.0.md +11 -7
  92. data/doc/release_notes/2.16.0.md +4 -0
  93. data/doc/release_notes/2.17.0.md +4 -0
  94. data/doc/release_notes/2.18.0.md +4 -0
  95. data/doc/release_notes/2.19.0.md +6 -3
  96. data/doc/release_notes/2.2.0.md +4 -0
  97. data/doc/release_notes/2.3.0.md +4 -0
  98. data/doc/release_notes/2.3.1.md +4 -0
  99. data/doc/release_notes/2.4.0.md +4 -0
  100. data/doc/release_notes/2.4.1.md +4 -0
  101. data/doc/release_notes/2.5.0.md +4 -0
  102. data/doc/release_notes/2.6.0.md +4 -0
  103. data/doc/release_notes/2.6.1.md +4 -0
  104. data/doc/release_notes/2.7.0.md +4 -0
  105. data/doc/release_notes/2.8.0.md +4 -0
  106. data/doc/release_notes/2.9.0.md +4 -0
  107. data/doc/release_notes/3.0.0.md +981 -0
  108. data/doc/release_notes/3.0.1.md +22 -0
  109. data/doc/release_notes/3.1.0.md +73 -0
  110. data/doc/release_notes/3.2.0.md +96 -0
  111. data/doc/release_notes/3.2.1.md +31 -0
  112. data/doc/release_notes/3.2.2.md +14 -0
  113. data/doc/release_notes/3.3.0.md +105 -0
  114. data/doc/release_notes/3.4.0.md +35 -0
  115. data/doc/retrieving_uploads.md +4 -1
  116. data/doc/securing_uploads.md +60 -37
  117. data/doc/storage/file_system.md +20 -3
  118. data/doc/storage/memory.md +19 -0
  119. data/doc/storage/s3.md +117 -83
  120. data/doc/testing.md +124 -144
  121. data/doc/upgrading_to_3.md +710 -0
  122. data/doc/validation.md +54 -90
  123. data/lib/shrine/attacher.rb +287 -171
  124. data/lib/shrine/attachment.rb +13 -46
  125. data/lib/shrine/plugins/_persistence.rb +93 -0
  126. data/lib/shrine/plugins/activerecord.rb +77 -34
  127. data/lib/shrine/plugins/add_metadata.rb +25 -17
  128. data/lib/shrine/plugins/atomic_helpers.rb +119 -0
  129. data/lib/shrine/plugins/backgrounding.rb +77 -113
  130. data/lib/shrine/plugins/cached_attachment_data.rb +6 -15
  131. data/lib/shrine/plugins/column.rb +102 -0
  132. data/lib/shrine/plugins/data_uri.rb +38 -36
  133. data/lib/shrine/plugins/default_storage.rb +45 -15
  134. data/lib/shrine/plugins/default_url.rb +12 -24
  135. data/lib/shrine/plugins/default_url_options.rb +3 -30
  136. data/lib/shrine/plugins/delete_raw.rb +10 -16
  137. data/lib/shrine/plugins/derivation_endpoint.rb +89 -134
  138. data/lib/shrine/plugins/derivatives.rb +637 -0
  139. data/lib/shrine/plugins/determine_mime_type.rb +9 -21
  140. data/lib/shrine/plugins/download_endpoint.rb +109 -133
  141. data/lib/shrine/plugins/dynamic_storage.rb +5 -11
  142. data/lib/shrine/plugins/entity.rb +152 -0
  143. data/lib/shrine/plugins/form_assign.rb +108 -0
  144. data/lib/shrine/plugins/included.rb +6 -6
  145. data/lib/shrine/plugins/infer_extension.rb +13 -20
  146. data/lib/shrine/plugins/instrumentation.rb +54 -42
  147. data/lib/shrine/plugins/keep_files.rb +3 -15
  148. data/lib/shrine/plugins/metadata_attributes.rb +28 -19
  149. data/lib/shrine/plugins/mirroring.rb +142 -0
  150. data/lib/shrine/plugins/model.rb +158 -0
  151. data/lib/shrine/plugins/module_include.rb +3 -3
  152. data/lib/shrine/plugins/multi_cache.rb +27 -0
  153. data/lib/shrine/plugins/presign_endpoint.rb +18 -22
  154. data/lib/shrine/plugins/pretty_location.rb +15 -9
  155. data/lib/shrine/plugins/processing.rb +22 -9
  156. data/lib/shrine/plugins/rack_file.rb +2 -42
  157. data/lib/shrine/plugins/rack_response.rb +15 -10
  158. data/lib/shrine/plugins/recache.rb +6 -5
  159. data/lib/shrine/plugins/refresh_metadata.rb +13 -11
  160. data/lib/shrine/plugins/remote_url.rb +49 -49
  161. data/lib/shrine/plugins/remove_attachment.rb +10 -6
  162. data/lib/shrine/plugins/remove_invalid.rb +19 -8
  163. data/lib/shrine/plugins/restore_cached_data.rb +13 -7
  164. data/lib/shrine/plugins/sequel.rb +86 -36
  165. data/lib/shrine/plugins/signature.rb +10 -16
  166. data/lib/shrine/plugins/store_dimensions.rb +35 -40
  167. data/lib/shrine/plugins/tempfile.rb +1 -3
  168. data/lib/shrine/plugins/type_predicates.rb +113 -0
  169. data/lib/shrine/plugins/upload_endpoint.rb +25 -23
  170. data/lib/shrine/plugins/upload_options.rb +14 -15
  171. data/lib/shrine/plugins/url_options.rb +31 -0
  172. data/lib/shrine/plugins/validation.rb +80 -0
  173. data/lib/shrine/plugins/validation_helpers.rb +34 -57
  174. data/lib/shrine/plugins/versions.rb +107 -87
  175. data/lib/shrine/plugins.rb +22 -0
  176. data/lib/shrine/storage/file_system.rb +46 -64
  177. data/lib/shrine/storage/linter.rb +42 -7
  178. data/lib/shrine/storage/memory.rb +49 -0
  179. data/lib/shrine/storage/s3.rb +154 -158
  180. data/lib/shrine/uploaded_file.rb +28 -30
  181. data/lib/shrine/version.rb +3 -3
  182. data/lib/shrine.rb +86 -149
  183. data/shrine.gemspec +9 -10
  184. metadata +79 -83
  185. data/doc/migrating_storage.md +0 -76
  186. data/doc/plugins/backup.md +0 -31
  187. data/doc/plugins/copy.md +0 -24
  188. data/doc/plugins/delete_promoted.md +0 -12
  189. data/doc/plugins/direct_upload.md +0 -172
  190. data/doc/plugins/hooks.md +0 -58
  191. data/doc/plugins/logging.md +0 -42
  192. data/doc/plugins/migration_helpers.md +0 -60
  193. data/doc/plugins/moving.md +0 -19
  194. data/doc/plugins/multi_delete.md +0 -20
  195. data/doc/plugins/parallelize.md +0 -16
  196. data/doc/plugins/parsed_json.md +0 -23
  197. data/doc/regenerating_versions.md +0 -143
  198. data/lib/shrine/plugins/background_helpers.rb +0 -5
  199. data/lib/shrine/plugins/backup.rb +0 -90
  200. data/lib/shrine/plugins/copy.rb +0 -50
  201. data/lib/shrine/plugins/delete_promoted.rb +0 -20
  202. data/lib/shrine/plugins/direct_upload.rb +0 -217
  203. data/lib/shrine/plugins/hooks.rb +0 -90
  204. data/lib/shrine/plugins/logging.rb +0 -142
  205. data/lib/shrine/plugins/migration_helpers.rb +0 -70
  206. data/lib/shrine/plugins/moving.rb +0 -57
  207. data/lib/shrine/plugins/multi_delete.rb +0 -32
  208. data/lib/shrine/plugins/parallelize.rb +0 -78
  209. data/lib/shrine/plugins/parsed_json.rb +0 -29
data/lib/shrine.rb CHANGED
@@ -1,40 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shrine/version"
4
3
  require "shrine/uploaded_file"
5
4
  require "shrine/attacher"
6
5
  require "shrine/attachment"
7
6
  require "shrine/plugins"
7
+ require "shrine/version"
8
8
 
9
9
  require "securerandom"
10
10
  require "json"
11
11
  require "tempfile"
12
12
  require "logger"
13
13
 
14
- # Core class that represents uploader.
15
- # Base implementation is defined in InstanceMethods and ClassMethods.
14
+ # Core class that handles uploading files to specified storage.
16
15
  class Shrine
17
16
  # A generic exception used by Shrine.
18
- class Error < StandardError; end
17
+ class Error < StandardError
18
+ end
19
19
 
20
20
  # Raised when a file is not a valid IO.
21
21
  class InvalidFile < Error
22
22
  def initialize(io, missing_methods)
23
- super "#{io.inspect} is not a valid IO object (it doesn't respond to \
24
- #{missing_methods.map{|m, _|"##{m}"}.join(", ")})"
23
+ super "#{io.inspect} is not a valid IO object (it doesn't respond to #{missing_methods.map{|m, _|"##{m}"}.join(", ")})"
25
24
  end
26
25
  end
27
26
 
28
- # Methods which an object has to respond to in order to be considered
29
- # an IO object, along with their arguments.
30
- IO_METHODS = {
31
- read: [:length, :outbuf],
32
- eof?: [],
33
- rewind: [],
34
- size: [],
35
- close: [],
36
- }
37
- deprecate_constant(:IO_METHODS)
27
+ # Raised by the storage in the #open method.
28
+ class FileNotFound < Error
29
+ end
38
30
 
39
31
  @opts = {}
40
32
  @storages = {}
@@ -54,12 +46,7 @@ class Shrine
54
46
  # When inheriting Shrine, copy the instance variables into the subclass,
55
47
  # and create subclasses of core classes.
56
48
  def inherited(subclass)
57
- subclass.instance_variable_set(:@opts, opts.dup)
58
- subclass.opts.each do |key, value|
59
- if value.is_a?(Enumerable) && !value.frozen?
60
- subclass.opts[key] = value.dup
61
- end
62
- end
49
+ subclass.instance_variable_set(:@opts, deep_dup(opts))
63
50
  subclass.instance_variable_set(:@storages, storages.dup)
64
51
 
65
52
  file_class = Class.new(self::UploadedFile)
@@ -81,9 +68,9 @@ class Shrine
81
68
  #
82
69
  # Shrine.plugin MyPlugin
83
70
  # Shrine.plugin :my_plugin
84
- def plugin(plugin, *args, &block)
71
+ def plugin(plugin, *args, **kwargs, &block)
85
72
  plugin = Plugins.load_plugin(plugin) if plugin.is_a?(Symbol)
86
- plugin.load_dependencies(self, *args, &block) if plugin.respond_to?(:load_dependencies)
73
+ Plugins.load_dependencies(plugin, self, *args, **kwargs, &block)
87
74
  self.include(plugin::InstanceMethods) if defined?(plugin::InstanceMethods)
88
75
  self.extend(plugin::ClassMethods) if defined?(plugin::ClassMethods)
89
76
  self::UploadedFile.include(plugin::FileMethods) if defined?(plugin::FileMethods)
@@ -92,7 +79,7 @@ class Shrine
92
79
  self::Attachment.extend(plugin::AttachmentClassMethods) if defined?(plugin::AttachmentClassMethods)
93
80
  self::Attacher.include(plugin::AttacherMethods) if defined?(plugin::AttacherMethods)
94
81
  self::Attacher.extend(plugin::AttacherClassMethods) if defined?(plugin::AttacherClassMethods)
95
- plugin.configure(self, *args, &block) if plugin.respond_to?(:configure)
82
+ Plugins.configure(plugin, self, *args, **kwargs, &block)
96
83
  plugin
97
84
  end
98
85
 
@@ -108,8 +95,8 @@ class Shrine
108
95
  # class Photo
109
96
  # include Shrine::Attachment(:image) # creates a Shrine::Attachment object
110
97
  # end
111
- def Attachment(name, *args)
112
- self::Attachment.new(name, *args)
98
+ def Attachment(name, **args)
99
+ self::Attachment.new(name, **args)
113
100
  end
114
101
  alias attachment Attachment
115
102
  alias [] Attachment
@@ -117,25 +104,26 @@ class Shrine
117
104
  # Uploads the file to the specified storage. It delegates to `Shrine#upload`.
118
105
  #
119
106
  # Shrine.upload(io, :store) #=> #<Shrine::UploadedFile>
120
- def upload(io, storage, context = {})
121
- new(storage).upload(io, context)
107
+ def upload(io, storage, **options)
108
+ new(storage).upload(io, **options)
122
109
  end
123
110
 
124
111
  # Instantiates a Shrine::UploadedFile from a hash, and optionally
125
112
  # yields the returned object.
126
113
  #
127
- # data = {"storage" => "cache", "id" => "abc123.jpg", "metadata" => {}}
114
+ # data = { "storage" => "cache", "id" => "abc123.jpg", "metadata" => {} }
128
115
  # Shrine.uploaded_file(data) #=> #<Shrine::UploadedFile>
129
- def uploaded_file(object, &block)
116
+ def uploaded_file(object)
130
117
  case object
131
118
  when String
132
- uploaded_file(JSON.parse(object), &block)
119
+ uploaded_file(JSON.parse(object))
133
120
  when Hash
134
- uploaded_file(self::UploadedFile.new(object), &block)
121
+ object = JSON.parse(object.to_json) if object.keys.grep(Symbol).any? # deep stringify keys
122
+ self::UploadedFile.new(object)
135
123
  when self::UploadedFile
136
- object.tap { |f| yield(f) if block_given? }
124
+ object
137
125
  else
138
- raise Error, "cannot convert #{object.inspect} to a #{self}::UploadedFile"
126
+ fail ArgumentError, "cannot convert #{object.inspect} to a #{self}::UploadedFile"
139
127
  end
140
128
  end
141
129
 
@@ -169,27 +157,39 @@ class Shrine
169
157
  def deprecation(message)
170
158
  Shrine.logger.warn "SHRINE DEPRECATION WARNING: #{message}"
171
159
  end
160
+
161
+ private
162
+
163
+ # Deep duplicates a nested hash of options.
164
+ def deep_dup(collection)
165
+ duplicate_collection = collection.dup
166
+
167
+ if duplicate_collection.is_a?(Hash)
168
+ duplicate_collection.each do |key, value|
169
+ duplicate_collection[key] = deep_dup(value) if value.is_a?(Enumerable)
170
+ end
171
+ end
172
+
173
+ duplicate_collection
174
+ end
172
175
  end
173
176
 
174
177
  module InstanceMethods
175
178
  # The symbol identifier for the storage used by the uploader.
176
179
  attr_reader :storage_key
177
180
 
178
- # The storage object used by the uploader.
179
- attr_reader :storage
180
-
181
181
  # Accepts a storage symbol registered in `Shrine.storages`.
182
182
  #
183
183
  # Shrine.new(:store)
184
184
  def initialize(storage_key)
185
- @storage = self.class.find_storage(storage_key)
186
185
  @storage_key = storage_key.to_sym
186
+
187
+ storage # ensure storage is registered
187
188
  end
188
189
 
189
- # The class-level options hash. This should probably not be modified at
190
- # the instance level.
191
- def opts
192
- self.class.opts
190
+ # Returns the storage object referenced by the identifier.
191
+ def storage
192
+ self.class.find_storage(storage_key)
193
193
  end
194
194
 
195
195
  # The main method for uploading files. Takes an IO-like object and an
@@ -201,55 +201,31 @@ class Shrine
201
201
  # uploader.upload(io, metadata: { "foo" => "bar" }) # add metadata
202
202
  # uploader.upload(io, location: "path/to/file") # specify location
203
203
  # uploader.upload(io, upload_options: { acl: "public-read" }) # add upload options
204
- def upload(io, context = {})
205
- io = processed(io, context) || io
206
- store(io, context)
207
- end
204
+ def upload(io, **options)
205
+ _enforce_io(io)
208
206
 
209
- # User is expected to perform processing inside this method, and
210
- # return the processed files. Returning nil signals that no proccessing
211
- # has been done and that the original file should be used.
212
- #
213
- # class ImageUploader < Shrine
214
- # def process(io, context)
215
- # # do processing and return processed files
216
- # end
217
- # end
218
- def process(io, context = {})
219
- end
207
+ metadata = get_metadata(io, **options)
208
+ location = get_location(io, **options, metadata: metadata)
220
209
 
221
- # Uploads the file and returns an instance of Shrine::UploadedFile. By
222
- # default the location of the file is automatically generated by
223
- # \#generate_location, but you can pass in `:location` to upload to
224
- # a specific location.
225
- #
226
- # uploader.store(io)
227
- def store(io, context = {})
228
- _store(io, context)
229
- end
210
+ _upload(io, **options, location: location, metadata: metadata)
230
211
 
231
- # Returns true if the storage of the given uploaded file matches the
232
- # storage of this uploader.
233
- def uploaded?(uploaded_file)
234
- uploaded_file.storage_key == storage_key.to_s
235
- end
236
-
237
- # Deletes the given uploaded file and returns it.
238
- def delete(uploaded_file, context = {})
239
- _delete(uploaded_file, context)
240
- uploaded_file
212
+ self.class::UploadedFile.new(
213
+ id: location,
214
+ storage: storage_key,
215
+ metadata: metadata,
216
+ )
241
217
  end
242
218
 
243
219
  # Generates a unique location for the uploaded file, preserving the
244
220
  # file extension. Can be overriden in uploaders for generating custom
245
221
  # location.
246
- def generate_location(io, context = {})
247
- basic_location(io, metadata: context[:metadata] || {})
222
+ def generate_location(io, metadata: {}, **options)
223
+ basic_location(io, metadata: metadata)
248
224
  end
249
225
 
250
226
  # Extracts filename, size and MIME type from the file, which is later
251
227
  # accessible through UploadedFile#metadata.
252
- def extract_metadata(io, context = {})
228
+ def extract_metadata(io, **options)
253
229
  {
254
230
  "filename" => extract_filename(io),
255
231
  "size" => extract_size(io),
@@ -257,8 +233,21 @@ class Shrine
257
233
  }
258
234
  end
259
235
 
236
+ # The class-level options hash. This should probably not be modified at
237
+ # the instance level.
238
+ def opts
239
+ self.class.opts
240
+ end
241
+
260
242
  private
261
243
 
244
+ def _upload(io, location:, metadata:, upload_options: {}, close: true, delete: false, **)
245
+ storage.upload(io, location, shrine_metadata: metadata, **upload_options)
246
+ ensure
247
+ io.close if close
248
+ File.unlink(io.path) if delete && io.respond_to?(:path) && File.exist?(io.path)
249
+ end
250
+
262
251
  # Attempts to extract the appropriate filename from the IO object.
263
252
  def extract_filename(io)
264
253
  if io.respond_to?(:original_filename)
@@ -281,87 +270,35 @@ class Shrine
281
270
  io.size if io.respond_to?(:size)
282
271
  end
283
272
 
284
- # It first asserts that `io` is a valid IO object. It then extracts
285
- # metadata and generates the location, before calling the storage to
286
- # upload the IO object, passing the extracted metadata and location.
287
- # Finally it returns a Shrine::UploadedFile object which represents the
288
- # file that was uploaded.
289
- def _store(io, context)
290
- _enforce_io(io)
291
-
292
- metadata = get_metadata(io, context)
293
- metadata = metadata.merge(context[:metadata]) if context[:metadata].is_a?(Hash)
294
-
295
- location = get_location(io, context.merge(metadata: metadata))
296
-
297
- put(io, context.merge(location: location, metadata: metadata))
298
-
299
- self.class.uploaded_file(
300
- "id" => location,
301
- "storage" => storage_key.to_s,
302
- "metadata" => metadata,
303
- )
304
- end
305
-
306
- # Delegates to #remove.
307
- def _delete(uploaded_file, context)
308
- remove(uploaded_file, context)
309
- end
310
-
311
- # Delegates to #copy.
312
- def put(io, context)
313
- copy(io, context)
314
- end
315
-
316
- # Calls `#upload` on the storage, passing to it the location, metadata
317
- # and any upload options. The storage might modify the location or
318
- # metadata that were passed in. The uploaded IO is then closed.
319
- def copy(io, context)
320
- location = context[:location]
321
- metadata = context[:metadata]
322
- upload_options = context[:upload_options] || {}
323
-
324
- storage.upload(io, location, shrine_metadata: metadata, **upload_options)
325
- ensure
326
- io.close rescue nil
327
- end
328
-
329
- # Delegates to `UploadedFile#delete`.
330
- def remove(uploaded_file, context)
331
- uploaded_file.delete
332
- end
333
-
334
- # Delegates to #process.
335
- def processed(io, context)
336
- process(io, context)
337
- end
338
-
339
273
  # Generates a basic location for an uploaded file
340
274
  def basic_location(io, metadata:)
341
275
  extension = ".#{io.extension}" if io.is_a?(UploadedFile) && io.extension
342
- extension ||= File.extname(extract_filename(io).to_s).downcase
276
+ extension ||= File.extname(metadata["filename"].to_s).downcase
343
277
  basename = generate_uid(io)
344
278
 
345
279
  basename + extension
346
280
  end
347
281
 
348
- # Retrieves the location for the given IO and context. First it looks
349
- # for the `:location` option, otherwise it calls #generate_location.
350
- def get_location(io, context)
351
- location = context[:location] || generate_location(io, context)
352
- location or raise Error, "location generated for #{io.inspect} was nil (context = #{context})"
353
- end
354
-
355
282
  # If the IO object is a Shrine::UploadedFile, it simply copies over its
356
283
  # metadata, otherwise it calls #extract_metadata.
357
- def get_metadata(io, context)
358
- if io.is_a?(UploadedFile) && context[:metadata] != true
359
- io.metadata.dup
360
- elsif context[:metadata] != false
361
- extract_metadata(io, context)
284
+ def get_metadata(io, metadata: nil, **options)
285
+ if io.is_a?(UploadedFile) && metadata != true
286
+ result = io.metadata.dup
287
+ elsif metadata != false
288
+ result = extract_metadata(io, **options)
362
289
  else
363
- {}
290
+ result = {}
364
291
  end
292
+
293
+ result = result.merge(metadata) if metadata.is_a?(Hash)
294
+ result
295
+ end
296
+
297
+ # Retrieves the location for the given IO and context. First it looks
298
+ # for the `:location` option, otherwise it calls #generate_location.
299
+ def get_location(io, location: nil, **options)
300
+ location ||= generate_location(io, **options)
301
+ location or fail Error, "location generated for #{io.inspect} was nil"
365
302
  end
366
303
 
367
304
  # Asserts that the object is a valid IO object, specifically that it
data/shrine.gemspec CHANGED
@@ -26,26 +26,24 @@ direct uploads for fully asynchronous user experience.
26
26
  "bug_tracker_uri" => "https://github.com/shrinerb/shrine/issues",
27
27
  "changelog_uri" => "https://github.com/shrinerb/shrine/blob/master/CHANGELOG.md",
28
28
  "documentation_uri" => "https://shrinerb.com",
29
- "mailing_list_uri" => "https://groups.google.com/forum/#!forum/ruby-shrine",
29
+ "mailing_list_uri" => "https://discourse.shrinerb.com",
30
30
  "source_code_uri" => "https://github.com/shrinerb/shrine",
31
31
  }
32
32
 
33
33
  gem.files = Dir["README.md", "LICENSE.txt", "CHANGELOG.md", "lib/**/*.rb", "shrine.gemspec", "doc/**/*.md"]
34
34
  gem.require_path = "lib"
35
35
 
36
- gem.add_dependency "down", ">= 4.1", "< 6"
36
+ gem.add_dependency "down", "~> 5.1"
37
37
  gem.add_dependency "content_disposition", "~> 1.0"
38
38
 
39
39
  # general testing helpers
40
40
  gem.add_development_dependency "rake", ">= 11.1"
41
41
  gem.add_development_dependency "minitest", "~> 5.8"
42
- gem.add_development_dependency "minitest-hooks", "~> 1.3"
43
- gem.add_development_dependency "mocha", "~> 1.4"
44
- gem.add_development_dependency "shrine-memory", ">= 0.2.2"
42
+ gem.add_development_dependency "mocha", "~> 1.11"
45
43
 
46
44
  # for endpoint plugins
47
45
  gem.add_development_dependency "rack", "~> 2.0"
48
- gem.add_development_dependency "http-form_data", "~> 2.0"
46
+ gem.add_development_dependency "http-form_data", "~> 2.2"
49
47
  gem.add_development_dependency "rack-test_app"
50
48
 
51
49
  # for determine_mime_type plugin
@@ -63,15 +61,16 @@ direct uploads for fully asynchronous user experience.
63
61
  gem.add_development_dependency "ruby-vips", "~> 2.0" unless ENV["CI"]
64
62
 
65
63
  # for S3 storage
66
- gem.add_development_dependency "aws-sdk-s3", "~> 1.16"
64
+ gem.add_development_dependency "aws-sdk-s3", "~> 1.69"
67
65
  gem.add_development_dependency "aws-sdk-core", "~> 3.23"
66
+ gem.add_development_dependency "rexml"
68
67
 
69
68
  # for instrumentation plugin
70
69
  gem.add_development_dependency "dry-monitor"
71
- gem.add_development_dependency "activesupport", "~> 5.2.0"
70
+ gem.add_development_dependency "activesupport", RUBY_VERSION >= "2.5" ? "~> 6.0" : "~> 5.2"
72
71
 
73
72
  # for ORM plugins
74
73
  gem.add_development_dependency "sequel"
75
- gem.add_development_dependency "activerecord", "~> 5.2.0"
76
- gem.add_development_dependency "sqlite3", "~> 1.3.6" unless RUBY_ENGINE == "jruby"
74
+ gem.add_development_dependency "activerecord", RUBY_VERSION >= "2.5" ? "~> 6.0" : "~> 5.2"
75
+ gem.add_development_dependency "sqlite3", "~> 1.4" unless RUBY_ENGINE == "jruby"
77
76
  end