shrine 2.4.1 → 2.5.0

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.

@@ -12,22 +12,27 @@ class Shrine
12
12
  # plugin :processing
13
13
  #
14
14
  # process(:store) do |io, context|
15
- # size_700 = resize_to_limit(io.download, 700, 700)
16
- # size_500 = resize_to_limit(size_700, 500, 500)
17
- # size_300 = resize_to_limit(size_500, 300, 300)
15
+ # original = io.download
18
16
  #
19
- # {large: size_700, medium: size_500, small: size_300}
17
+ # size_800 = resize_to_limit!(original, 800, 800)
18
+ # size_500 = resize_to_limit(size_800, 500, 500)
19
+ # size_300 = resize_to_limit(size_500, 300, 300)
20
+ #
21
+ # {large: size_800, medium: size_500, small: size_300}
20
22
  # end
21
23
  #
24
+ # You probably want to load the `delete_raw` plugin to automatically
25
+ # delete processed files after they have been uploaded.
26
+ #
22
27
  # Now when you access the stored attachment through the model, a hash of
23
28
  # uploaded files will be returned:
24
29
  #
25
- # JSON.parse(user.avatar_data) #=>
26
- # # {
27
- # # "large" => {"id" => "lg043.jpg", "storage" => "store", "metadata" => {...}},
28
- # # "medium" => {"id" => "kd9fk.jpg", "storage" => "store", "metadata" => {...}},
29
- # # "small" => {"id" => "932fl.jpg", "storage" => "store", "metadata" => {...}},
30
- # # }
30
+ # user.avatar_data #=>
31
+ # # '{
32
+ # # "large": {"id":"lg043.jpg", "storage":"store", "metadata":{...}},
33
+ # # "medium": {"id":"kd9fk.jpg", "storage":"store", "metadata":{...}},
34
+ # # "small": {"id":"932fl.jpg", "storage":"store", "metadata":{...}}
35
+ # # }'
31
36
  #
32
37
  # user.avatar #=>
33
38
  # # {
@@ -36,43 +41,50 @@ class Shrine
36
41
  # # :small => #<Shrine::UploadedFile @data={"id"=>"932fl.jpg", ...}>,
37
42
  # # }
38
43
  #
39
- # # With the store_dimensions plugin
40
- # user.avatar[:large].width #=> 700
41
- # user.avatar[:medium].width #=> 500
42
- # user.avatar[:small].width #=> 300
44
+ # user.avatar[:medium] #=> #<Shrine::UploadedFile>
45
+ # user.avatar[:medium].url #=> "/uploads/store/lg043.jpg"
43
46
  #
44
- # You probably want to load the `delete_raw` plugin to automatically
45
- # delete processed files after they have been uploaded.
47
+ # The plugin also extends the `Attacher#url` to accept versions:
48
+ #
49
+ # user.avatar_url(:large)
50
+ # user.avatar_url(:small, download: true) # with URL options
46
51
  #
47
52
  # ## Original file
48
53
  #
49
- # If you want to keep the original file, you can forward it as is without
50
- # explicitly downloading it (since `Shrine::UploadedFile` itself is an
51
- # IO-like object), which might avoid downloading depending on the storage:
54
+ # If you want to keep the original file, you can include the original
55
+ # `Shrine::UploadedFile` object as one of the versions:
52
56
  #
53
57
  # process(:store) do |io, context|
54
58
  # # processing thumbnail
55
59
  # {original: io, thumbnail: thumbnail}
56
60
  # end
57
61
  #
62
+ # If both temporary and permanent storage are Amazon S3, the cached original
63
+ # will simply be copied over to permanent storage (without any downloading
64
+ # and reuploading), so in these cases the performance impact of storing the
65
+ # original file in addition to processed versions is neglibible.
66
+ #
58
67
  # ## Fallbacks
59
68
  #
60
- # The plugin also extends the `<attachmen>_url` method to accept versions,
61
- # and adds automatic fallbacks:
69
+ # If versions are processed in a background job, there will be a period
70
+ # where the user will browse the site before versions have finished
71
+ # processing. In this period `Attacher#url` will by default fall back to
72
+ # the original file.
62
73
  #
63
- # user.avatar_url(:medium)
74
+ # user.avatar #=> #<Shrine::UploadedFile>
75
+ # user.avatar_url(:large) # falls back to `user.avatar_url`
64
76
  #
65
- # # returns URL of that version if versions have been created,
66
- # # otherwise returns original URL if attachment exists,
67
- # # otherwise returns nil
77
+ # This behaviour is convenient if you want to gracefully degrade to the
78
+ # cached file until the background job has finished processing. However, if
79
+ # you would rather provide your own default URLs for versions, you can
80
+ # disable this fallback:
68
81
  #
69
- # This behaviour is convenient when using background jobs, as it allows you
70
- # to gracefully degrade before the background job finishes.
82
+ # plugin :versions, fallback_to_original: false
71
83
  #
72
- # If you already have some versions processed in the foreground when a
73
- # background job is kicked off (with the `recache` plugin), the
74
- # `<attachment>_url` method won't know which version to use as a fallback.
75
- # In that case you can specify `:fallbacks` when loading the plugin:
84
+ # If you already have some versions processed in the foreground after a
85
+ # background job is kicked off (with the `recache` plugin), you can have
86
+ # URLs for versions that are yet to be processed fall back to existing
87
+ # versions:
76
88
  #
77
89
  # plugin :versions, fallbacks: {
78
90
  # :thumb_2x => :thumb,
@@ -84,22 +96,17 @@ class Shrine
84
96
  # user.avatar_url(:thumb_2x) # returns :thumb URL until :thumb_2x becomes available
85
97
  # user.avatar_url(:large_2x) # returns :large URL until :large_2x becomes available
86
98
  #
87
- # Any additional options will be properly forwarded to the underlying
88
- # storage:
89
- #
90
- # user.avatar_url(:medium, download: true)
91
- #
92
99
  # ## Context
93
100
  #
94
- # The `context` will now also include the version name, which you can use
95
- # when generating a location or a default URL:
101
+ # The version name will be available via `:version` when generating
102
+ # location or a default URL.
96
103
  #
97
104
  # def generate_location(io, context)
98
105
  # "uploads/#{context[:version]}-#{super}"
99
106
  # end
100
107
  #
101
- # plugin :default_url do |context|
102
- # "/images/defaults/#{context[:version]}.jpg"
108
+ # Attacher.default_url do |options|
109
+ # "/images/defaults/#{options[:version]}.jpg"
103
110
  # end
104
111
  #
105
112
  # [image_processing]: https://github.com/janko-m/image_processing
@@ -114,6 +121,7 @@ class Shrine
114
121
 
115
122
  uploader.opts[:version_names] = opts.fetch(:names, uploader.opts[:version_names])
116
123
  uploader.opts[:version_fallbacks] = opts.fetch(:fallbacks, uploader.opts.fetch(:version_fallbacks, {}))
124
+ uploader.opts[:versions_fallback_to_original] = opts.fetch(:fallback_to_original, uploader.opts.fetch(:versions_fallback_to_original, true))
117
125
  end
118
126
 
119
127
  module ClassMethods
@@ -163,6 +171,10 @@ class Shrine
163
171
  def _store(io, context)
164
172
  if (hash = io).is_a?(Hash)
165
173
  raise Error, ":location is not applicable to versions" if context.key?(:location)
174
+ if duplicates = hash.keys.group_by{|k| hash[k]}.values.reject(&:one?).first
175
+ raise Error, "The following version keys point to the same IO object: #{duplicates.inspect}. Shrine cannot upload the same IO object multiple times."
176
+ end
177
+
166
178
  hash.inject({}) do |result, (name, version)|
167
179
  result.update(name => _store(version, version: name, **context))
168
180
  end
@@ -186,29 +198,39 @@ class Shrine
186
198
  # Smart versioned URLs, which include the version name in the default
187
199
  # URL, and properly forwards any options to the underlying storage.
188
200
  def url(version = nil, **options)
189
- if get.is_a?(Hash)
201
+ attachment = get
202
+
203
+ if attachment.is_a?(Hash)
190
204
  if version
191
- if file = get[version]
192
- file.url(**options)
205
+ if attachment.key?(version)
206
+ attachment[version].url(**options)
193
207
  elsif fallback = shrine_class.version_fallbacks[version]
194
208
  url(fallback, **options)
195
209
  else
196
210
  default_url(**options, version: version)
197
211
  end
198
212
  else
199
- raise Error, "must call #{name}_url with the name of the version"
213
+ raise Error, "must call Shrine::Attacher#url with the name of the version"
200
214
  end
201
215
  else
202
- if get || version.nil?
203
- super(**options)
216
+ if version
217
+ if attachment && fallback_to_original?
218
+ attachment.url(**options)
219
+ else
220
+ default_url(**options, version: version)
221
+ end
204
222
  else
205
- default_url(**options, version: version)
223
+ super(**options)
206
224
  end
207
225
  end
208
226
  end
209
227
 
210
228
  private
211
229
 
230
+ def fallback_to_original?
231
+ shrine_class.opts[:versions_fallback_to_original]
232
+ end
233
+
212
234
  def assign_cached(value)
213
235
  cached_file = uploaded_file(value)
214
236
  warn "Assigning cached hash of files is deprecated for security reasons and will be removed in Shrine 3." if cached_file.is_a?(Hash)
@@ -42,8 +42,8 @@ class Shrine
42
42
  # ## Clearing cache
43
43
  #
44
44
  # If you're using FileSystem as cache, you will probably want to
45
- # periodically delete old files which aren't used anymore. You can put
46
- # the following in a periodic Rake task:
45
+ # periodically delete old files which aren't used anymore. You can run
46
+ # something like this periodically:
47
47
  #
48
48
  # file_system = Shrine.storages[:cache]
49
49
  # file_system.clear!(older_than: Time.now - 7*24*60*60) # delete files older than 1 week
@@ -187,7 +187,7 @@ class Shrine
187
187
  if name == :download
188
188
  warn "Shrine::Storage::FileSystem#download is deprecated and will be removed in Shrine 3."
189
189
  require "down"
190
- open(id) { |file| Down.copy_to_tempfile(id, file) }
190
+ open(*args) { |file| Down.copy_to_tempfile(*args, file) }
191
191
  else
192
192
  super
193
193
  end
@@ -85,7 +85,7 @@ class Shrine
85
85
  storage.delete(id)
86
86
  error :delete, "file still #exists? after deleting" if storage.exists?(id)
87
87
  begin
88
- storage.delete(SecureRandom.hex)
88
+ storage.delete(id)
89
89
  rescue => exception
90
90
  error :delete, "shouldn't fail if the file doesn't exist, but raised #{exception.class}"
91
91
  end
@@ -39,7 +39,7 @@ class Shrine
39
39
  # [copying] and [presigning].
40
40
  #
41
41
  # You can also generate upload options per upload with the `upload_options`
42
- # plugin:
42
+ # plugin
43
43
  #
44
44
  # class MyUploader < Shrine
45
45
  # plugin :upload_options, store: ->(io, context) do
@@ -51,9 +51,13 @@ class Shrine
51
51
  # end
52
52
  # end
53
53
  #
54
- # Note that, unlike the `:upload_options` storage option, the
55
- # `upload_options` plugin won't forward the given options for generating
56
- # presigns, since presigns are generated using the storage directly.
54
+ # or when using the uploader directly
55
+ #
56
+ # uploader.upload(file, upload_options: {acl: "public-read"})
57
+ #
58
+ # Note that, unlike the `:upload_options` storage option, upload options
59
+ # given on the uploader level won't be forwarded for generating presigns,
60
+ # since presigns are generated using the storage directly.
57
61
  #
58
62
  # ## URL options
59
63
  #
@@ -5,8 +5,8 @@ class Shrine
5
5
 
6
6
  module VERSION
7
7
  MAJOR = 2
8
- MINOR = 4
9
- TINY = 1
8
+ MINOR = 5
9
+ TINY = 0
10
10
  PRE = nil
11
11
 
12
12
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shrine
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.1
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janko Marohnić
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-17 00:00:00.000000000 Z
11
+ date: 2016-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: down
@@ -301,6 +301,7 @@ files:
301
301
  - doc/design.md
302
302
  - doc/direct_s3.md
303
303
  - doc/migrating_storage.md
304
+ - doc/multiple_files.md
304
305
  - doc/paperclip.md
305
306
  - doc/refile.md
306
307
  - doc/regenerating_versions.md