shrine 2.19.3 → 3.6.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 (211) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +523 -41
  3. data/LICENSE.txt +1 -1
  4. data/README.md +83 -979
  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 +103 -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 +1156 -0
  20. data/doc/metadata.md +190 -109
  21. data/doc/multiple_files.md +93 -30
  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 +186 -101
  34. data/doc/plugins/derivatives.md +839 -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 +16 -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 +188 -170
  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 +5 -1
  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/release_notes/3.5.0.md +63 -0
  116. data/doc/release_notes/3.6.0.md +23 -0
  117. data/doc/retrieving_uploads.md +5 -2
  118. data/doc/securing_uploads.md +60 -37
  119. data/doc/storage/file_system.md +20 -3
  120. data/doc/storage/memory.md +19 -0
  121. data/doc/storage/s3.md +122 -78
  122. data/doc/testing.md +141 -133
  123. data/doc/upgrading_to_3.md +708 -0
  124. data/doc/validation.md +54 -90
  125. data/lib/shrine/attacher.rb +292 -169
  126. data/lib/shrine/attachment.rb +13 -46
  127. data/lib/shrine/plugins/_persistence.rb +93 -0
  128. data/lib/shrine/plugins/activerecord.rb +77 -34
  129. data/lib/shrine/plugins/add_metadata.rb +25 -17
  130. data/lib/shrine/plugins/atomic_helpers.rb +119 -0
  131. data/lib/shrine/plugins/backgrounding.rb +77 -113
  132. data/lib/shrine/plugins/cached_attachment_data.rb +6 -15
  133. data/lib/shrine/plugins/column.rb +102 -0
  134. data/lib/shrine/plugins/data_uri.rb +38 -36
  135. data/lib/shrine/plugins/default_storage.rb +45 -15
  136. data/lib/shrine/plugins/default_url.rb +12 -24
  137. data/lib/shrine/plugins/default_url_options.rb +3 -30
  138. data/lib/shrine/plugins/delete_raw.rb +10 -16
  139. data/lib/shrine/plugins/derivation_endpoint.rb +130 -171
  140. data/lib/shrine/plugins/derivatives.rb +645 -0
  141. data/lib/shrine/plugins/determine_mime_type.rb +9 -21
  142. data/lib/shrine/plugins/download_endpoint.rb +118 -133
  143. data/lib/shrine/plugins/dynamic_storage.rb +5 -11
  144. data/lib/shrine/plugins/entity.rb +158 -0
  145. data/lib/shrine/plugins/form_assign.rb +108 -0
  146. data/lib/shrine/plugins/included.rb +6 -6
  147. data/lib/shrine/plugins/infer_extension.rb +17 -20
  148. data/lib/shrine/plugins/instrumentation.rb +59 -43
  149. data/lib/shrine/plugins/keep_files.rb +3 -15
  150. data/lib/shrine/plugins/metadata_attributes.rb +28 -19
  151. data/lib/shrine/plugins/mirroring.rb +142 -0
  152. data/lib/shrine/plugins/model.rb +160 -0
  153. data/lib/shrine/plugins/module_include.rb +3 -3
  154. data/lib/shrine/plugins/multi_cache.rb +27 -0
  155. data/lib/shrine/plugins/presign_endpoint.rb +27 -28
  156. data/lib/shrine/plugins/pretty_location.rb +15 -9
  157. data/lib/shrine/plugins/processing.rb +22 -9
  158. data/lib/shrine/plugins/rack_file.rb +2 -42
  159. data/lib/shrine/plugins/rack_response.rb +21 -10
  160. data/lib/shrine/plugins/recache.rb +6 -5
  161. data/lib/shrine/plugins/refresh_metadata.rb +13 -11
  162. data/lib/shrine/plugins/remote_url.rb +49 -49
  163. data/lib/shrine/plugins/remove_attachment.rb +12 -6
  164. data/lib/shrine/plugins/remove_invalid.rb +19 -8
  165. data/lib/shrine/plugins/restore_cached_data.rb +13 -7
  166. data/lib/shrine/plugins/sequel.rb +86 -36
  167. data/lib/shrine/plugins/signature.rb +10 -16
  168. data/lib/shrine/plugins/store_dimensions.rb +35 -40
  169. data/lib/shrine/plugins/tempfile.rb +1 -3
  170. data/lib/shrine/plugins/type_predicates.rb +113 -0
  171. data/lib/shrine/plugins/upload_endpoint.rb +28 -24
  172. data/lib/shrine/plugins/upload_options.rb +14 -15
  173. data/lib/shrine/plugins/url_options.rb +31 -0
  174. data/lib/shrine/plugins/validation.rb +80 -0
  175. data/lib/shrine/plugins/validation_helpers.rb +35 -58
  176. data/lib/shrine/plugins/versions.rb +107 -87
  177. data/lib/shrine/plugins.rb +22 -0
  178. data/lib/shrine/storage/file_system.rb +46 -64
  179. data/lib/shrine/storage/linter.rb +42 -7
  180. data/lib/shrine/storage/memory.rb +49 -0
  181. data/lib/shrine/storage/s3.rb +173 -160
  182. data/lib/shrine/uploaded_file.rb +32 -32
  183. data/lib/shrine/version.rb +3 -3
  184. data/lib/shrine.rb +87 -150
  185. data/shrine.gemspec +11 -12
  186. metadata +92 -82
  187. data/doc/migrating_storage.md +0 -76
  188. data/doc/plugins/backup.md +0 -31
  189. data/doc/plugins/copy.md +0 -24
  190. data/doc/plugins/delete_promoted.md +0 -12
  191. data/doc/plugins/direct_upload.md +0 -172
  192. data/doc/plugins/hooks.md +0 -58
  193. data/doc/plugins/logging.md +0 -42
  194. data/doc/plugins/migration_helpers.md +0 -60
  195. data/doc/plugins/moving.md +0 -19
  196. data/doc/plugins/multi_delete.md +0 -20
  197. data/doc/plugins/parallelize.md +0 -16
  198. data/doc/plugins/parsed_json.md +0 -23
  199. data/doc/regenerating_versions.md +0 -143
  200. data/lib/shrine/plugins/background_helpers.rb +0 -5
  201. data/lib/shrine/plugins/backup.rb +0 -90
  202. data/lib/shrine/plugins/copy.rb +0 -50
  203. data/lib/shrine/plugins/delete_promoted.rb +0 -20
  204. data/lib/shrine/plugins/direct_upload.rb +0 -217
  205. data/lib/shrine/plugins/hooks.rb +0 -90
  206. data/lib/shrine/plugins/logging.rb +0 -142
  207. data/lib/shrine/plugins/migration_helpers.rb +0 -70
  208. data/lib/shrine/plugins/moving.rb +0 -57
  209. data/lib/shrine/plugins/multi_delete.rb +0 -32
  210. data/lib/shrine/plugins/parallelize.rb +0 -78
  211. 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,13 +233,26 @@ 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)
265
254
  io.original_filename
266
- elsif io.respond_to?(:path)
255
+ elsif io.respond_to?(:path) && io.path
267
256
  File.basename(io.path)
268
257
  end
269
258
  end
@@ -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,27 +26,25 @@ 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"
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
- gem.add_development_dependency "rack", "~> 2.0"
48
- gem.add_development_dependency "http-form_data", "~> 2.0"
49
- gem.add_development_dependency "rack-test_app"
45
+ gem.add_development_dependency "rack", ">= 2", "< 4"
46
+ gem.add_development_dependency "http-form_data", "~> 2.2"
47
+ gem.add_development_dependency "rack-test", "~> 2.1"
50
48
 
51
49
  # for determine_mime_type plugin
52
50
  gem.add_development_dependency "mimemagic", ">= 0.3.2"
@@ -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.7" ? "~> 7.0" : 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_ENGINE == "jruby" ? "~> 7.0.0" : RUBY_VERSION >= "2.7" ? "~> 7.0" : RUBY_VERSION >= "2.5" ? "~> 6.0" : "~> 5.2"
75
+ gem.add_development_dependency "sqlite3", "~> 1.4" unless RUBY_ENGINE == "jruby"
77
76
  end