shrine 1.4.2 → 2.0.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.
- checksums.yaml +4 -4
- data/README.md +236 -234
- data/doc/changing_location.md +6 -4
- data/doc/creating_storages.md +4 -4
- data/doc/design.md +223 -0
- data/doc/migrating_storage.md +6 -11
- data/doc/regenerating_versions.md +22 -40
- data/lib/shrine.rb +60 -77
- data/lib/shrine/plugins/activerecord.rb +37 -14
- data/lib/shrine/plugins/background_helpers.rb +1 -0
- data/lib/shrine/plugins/backgrounding.rb +49 -37
- data/lib/shrine/plugins/backup.rb +6 -4
- data/lib/shrine/plugins/cached_attachment_data.rb +5 -5
- data/lib/shrine/plugins/data_uri.rb +9 -9
- data/lib/shrine/plugins/default_storage.rb +4 -4
- data/lib/shrine/plugins/default_url.rb +7 -1
- data/lib/shrine/plugins/default_url_options.rb +1 -1
- data/lib/shrine/plugins/delete_promoted.rb +2 -2
- data/lib/shrine/plugins/delete_raw.rb +4 -4
- data/lib/shrine/plugins/determine_mime_type.rb +50 -43
- data/lib/shrine/plugins/direct_upload.rb +10 -20
- data/lib/shrine/plugins/download_endpoint.rb +16 -13
- data/lib/shrine/plugins/dynamic_storage.rb +4 -12
- data/lib/shrine/plugins/included.rb +6 -19
- data/lib/shrine/plugins/keep_files.rb +4 -4
- data/lib/shrine/plugins/logging.rb +4 -4
- data/lib/shrine/plugins/migration_helpers.rb +37 -34
- data/lib/shrine/plugins/moving.rb +19 -32
- data/lib/shrine/plugins/parallelize.rb +5 -5
- data/lib/shrine/plugins/pretty_location.rb +2 -6
- data/lib/shrine/plugins/remote_url.rb +31 -43
- data/lib/shrine/plugins/remove_attachment.rb +5 -5
- data/lib/shrine/plugins/remove_invalid.rb +1 -1
- data/lib/shrine/plugins/restore_cached_data.rb +4 -10
- data/lib/shrine/plugins/sequel.rb +46 -21
- data/lib/shrine/plugins/store_dimensions.rb +19 -20
- data/lib/shrine/plugins/upload_options.rb +11 -9
- data/lib/shrine/plugins/validation_helpers.rb +3 -3
- data/lib/shrine/plugins/versions.rb +18 -3
- data/lib/shrine/storage/file_system.rb +9 -11
- data/lib/shrine/storage/linter.rb +1 -7
- data/lib/shrine/storage/s3.rb +25 -19
- data/lib/shrine/version.rb +3 -3
- data/shrine.gemspec +13 -3
- metadata +28 -9
- data/lib/shrine/plugins/delete_uploaded.rb +0 -3
- data/lib/shrine/plugins/keep_location.rb +0 -46
- data/lib/shrine/plugins/restore_cached.rb +0 -3
@@ -32,6 +32,9 @@ class Shrine
|
|
32
32
|
# }
|
33
33
|
# }
|
34
34
|
#
|
35
|
+
# Note that the endpoint uploads the file standalone, without any knowledge
|
36
|
+
# of the record, so `context[:record]` and `context[:name]` will be nil.
|
37
|
+
#
|
35
38
|
# Once you've uploaded the file, you need to assign the result to the
|
36
39
|
# hidden attachment field in the form. There are many great JavaScript
|
37
40
|
# libraries for file uploads, most popular being [jQuery-File-Upload].
|
@@ -156,18 +159,12 @@ class Shrine
|
|
156
159
|
uploader.plugin :rack_file
|
157
160
|
end
|
158
161
|
|
159
|
-
def self.configure(uploader,
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
uploader.opts[:direct_upload_allowed_storages] = allowed_storages
|
167
|
-
uploader.opts[:direct_upload_presign] = presign
|
168
|
-
uploader.opts[:direct_upload_presign_options] = presign_options
|
169
|
-
uploader.opts[:direct_upload_presign_location] = presign_location
|
170
|
-
uploader.opts[:direct_upload_max_size] = max_size
|
162
|
+
def self.configure(uploader, opts = {})
|
163
|
+
uploader.opts[:direct_upload_allowed_storages] = opts.fetch(:allowed_storages, uploader.opts.fetch(:direct_upload_allowed_storages, [:cache]))
|
164
|
+
uploader.opts[:direct_upload_presign] = opts.fetch(:presign, uploader.opts[:direct_upload_presign])
|
165
|
+
uploader.opts[:direct_upload_presign_options] = opts.fetch(:presign_options, uploader.opts.fetch(:direct_upload_presign_options, {}))
|
166
|
+
uploader.opts[:direct_upload_presign_location] = opts.fetch(:presign_location, uploader.opts[:direct_upload_presign_location])
|
167
|
+
uploader.opts[:direct_upload_max_size] = opts.fetch(:max_size, uploader.opts[:direct_upload_max_size])
|
171
168
|
|
172
169
|
uploader.assign_upload_endpoint(App) unless uploader.const_defined?(:UploadEndpoint)
|
173
170
|
end
|
@@ -185,12 +182,6 @@ class Shrine
|
|
185
182
|
endpoint_class.opts[:shrine_class] = self
|
186
183
|
const_set(:UploadEndpoint, endpoint_class)
|
187
184
|
end
|
188
|
-
|
189
|
-
# Returns the Roda direct upload endpoint.
|
190
|
-
def direct_endpoint
|
191
|
-
warn "Shrine.direct_endpoint is deprecated and will be removed in Shrine 2, you should use Shrine::UploadEndpoint instead."
|
192
|
-
self::UploadEndpoint
|
193
|
-
end
|
194
185
|
end
|
195
186
|
|
196
187
|
# Routes incoming requests. It first asserts that the storage is existent
|
@@ -198,7 +189,6 @@ class Shrine
|
|
198
189
|
# with the file upload and returns the uploaded file as JSON.
|
199
190
|
class App < Roda
|
200
191
|
plugin :default_headers, "Content-Type"=>"application/json"
|
201
|
-
plugin :json_parser
|
202
192
|
|
203
193
|
route do |r|
|
204
194
|
r.on ":storage" do |storage_key|
|
@@ -260,7 +250,7 @@ class Shrine
|
|
260
250
|
if presign_location
|
261
251
|
presign_location.call(request)
|
262
252
|
else
|
263
|
-
|
253
|
+
uploader.send(:generate_uid, nil) + request.params["extension"].to_s
|
264
254
|
end
|
265
255
|
end
|
266
256
|
|
@@ -47,11 +47,13 @@ class Shrine
|
|
47
47
|
#
|
48
48
|
# [Roda]: https://github.com/jeremyevans/roda
|
49
49
|
module DownloadEndpoint
|
50
|
-
def self.configure(uploader,
|
51
|
-
uploader.opts[:download_endpoint_storages] = storages
|
52
|
-
uploader.opts[:download_endpoint_prefix] = prefix
|
53
|
-
uploader.opts[:download_endpoint_disposition] = disposition
|
54
|
-
uploader.opts[:download_endpoint_host] = host
|
50
|
+
def self.configure(uploader, opts = {})
|
51
|
+
uploader.opts[:download_endpoint_storages] = opts.fetch(:storages, uploader.opts[:download_endpoint_storages])
|
52
|
+
uploader.opts[:download_endpoint_prefix] = opts.fetch(:prefix, uploader.opts[:download_endpoint_prefix])
|
53
|
+
uploader.opts[:download_endpoint_disposition] = opts.fetch(:disposition, uploader.opts.fetch(:download_endpoint_disposition, "inline"))
|
54
|
+
uploader.opts[:download_endpoint_host] = opts.fetch(:host, uploader.opts[:download_endpoint_host])
|
55
|
+
|
56
|
+
raise Error, "The :storages option is required for download_endpoint plugin" if uploader.opts[:download_endpoint_storages].nil?
|
55
57
|
|
56
58
|
uploader.assign_download_endpoint(App) unless uploader.const_defined?(:DownloadEndpoint)
|
57
59
|
end
|
@@ -84,7 +86,7 @@ class Shrine
|
|
84
86
|
id,
|
85
87
|
].join("/")
|
86
88
|
else
|
87
|
-
super
|
89
|
+
super
|
88
90
|
end
|
89
91
|
end
|
90
92
|
end
|
@@ -93,8 +95,6 @@ class Shrine
|
|
93
95
|
# and allowed. Afterwards it proceeds with the file download using
|
94
96
|
# streaming.
|
95
97
|
class App < Roda
|
96
|
-
plugin :streaming
|
97
|
-
|
98
98
|
route do |r|
|
99
99
|
r.on ":storage" do |storage_key|
|
100
100
|
@storage = get_storage(storage_key)
|
@@ -106,15 +106,18 @@ class Shrine
|
|
106
106
|
response["Content-Disposition"] = "#{disposition}; filename=#{filename.inspect}"
|
107
107
|
response["Content-Type"] = Rack::Mime.mime_type(extname)
|
108
108
|
|
109
|
-
|
110
|
-
_, content_length =
|
109
|
+
stream = get_stream(id)
|
110
|
+
_, content_length = stream.peek
|
111
111
|
response['Content-Length'] = content_length.to_s if content_length
|
112
112
|
|
113
|
-
|
114
|
-
|
115
|
-
|
113
|
+
chunks = Enumerator.new do |y|
|
114
|
+
loop do
|
115
|
+
chunk, * = stream.next
|
116
|
+
y << chunk
|
116
117
|
end
|
117
118
|
end
|
119
|
+
|
120
|
+
r.halt response.finish_with_body(chunks)
|
118
121
|
end
|
119
122
|
end
|
120
123
|
end
|
@@ -34,20 +34,12 @@ class Shrine
|
|
34
34
|
private
|
35
35
|
|
36
36
|
def resolve_dynamic_storage(name)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
dynamic_storage_cache[name] = block.call(match)
|
41
|
-
break
|
42
|
-
end
|
37
|
+
dynamic_storages.each do |regex, block|
|
38
|
+
if match = name.to_s.match(regex)
|
39
|
+
return block.call(match)
|
43
40
|
end
|
44
|
-
|
45
|
-
dynamic_storage_cache[name]
|
46
41
|
end
|
47
|
-
|
48
|
-
|
49
|
-
def dynamic_storage_cache
|
50
|
-
@dynamic_storage_cache ||= {}
|
42
|
+
nil
|
51
43
|
end
|
52
44
|
end
|
53
45
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class Shrine
|
2
2
|
module Plugins
|
3
|
-
# The included plugin allows you to hook up to the `.included` hook
|
4
|
-
#
|
5
|
-
#
|
3
|
+
# The included plugin allows you to hook up to the `.included` hook of the
|
4
|
+
# attachment module, and do additional action on the model which includes
|
5
|
+
# it.
|
6
6
|
#
|
7
7
|
# plugin :included do |name|
|
8
8
|
# define_method("#{name}_width") do
|
@@ -14,22 +14,9 @@ class Shrine
|
|
14
14
|
# end
|
15
15
|
# end
|
16
16
|
#
|
17
|
-
# The block is evaluated in the context of the model
|
18
|
-
# cannot use keywords like `def`, instead you should use the
|
19
|
-
# metaprogramming equivalents like `define_method`.
|
20
|
-
# is included to a model, it will receive the appropriate methods:
|
21
|
-
#
|
22
|
-
# class User
|
23
|
-
# include ImageUploader[:avatar]
|
24
|
-
# end
|
25
|
-
#
|
26
|
-
# user = User.new
|
27
|
-
# user.avatar_width #=> nil
|
28
|
-
# user.avatar_height #=> nil
|
29
|
-
#
|
30
|
-
# user.avatar = File.open("avatar.jpg")
|
31
|
-
# user.avatar_width #=> 300
|
32
|
-
# user.avatar_height #=> 500
|
17
|
+
# The block is evaluated in the context of the model via `instance_exec`.
|
18
|
+
# This means you cannot use keywords like `def`, instead you should use the
|
19
|
+
# metaprogramming equivalents like `define_method`.
|
33
20
|
module Included
|
34
21
|
def self.configure(uploader, &block)
|
35
22
|
uploader.opts[:included_block] = block
|
@@ -20,10 +20,10 @@ class Shrine
|
|
20
20
|
#
|
21
21
|
# [event store]: http://docs.geteventstore.com/introduction/event-sourcing-basics/
|
22
22
|
module KeepFiles
|
23
|
-
def self.configure(uploader,
|
24
|
-
uploader.opts[:keep_files]
|
25
|
-
|
26
|
-
|
23
|
+
def self.configure(uploader, opts = {})
|
24
|
+
keep_files = (uploader.opts[:keep_files] ||= [])
|
25
|
+
opts[:destroyed] ? keep_files << :destroyed : keep_files.delete(:destroyed) if opts.key?(:destroyed)
|
26
|
+
opts[:replaced] ? keep_files << :replaced : keep_files.delete(:replaced) if opts.key?(:replaced)
|
27
27
|
end
|
28
28
|
|
29
29
|
module AttacherMethods
|
@@ -48,10 +48,10 @@ class Shrine
|
|
48
48
|
uploader.plugin :hooks
|
49
49
|
end
|
50
50
|
|
51
|
-
def self.configure(uploader,
|
52
|
-
uploader.opts[:logging_logger] = logger
|
53
|
-
uploader.opts[:logging_stream] = stream
|
54
|
-
uploader.opts[:logging_format] = format
|
51
|
+
def self.configure(uploader, opts = {})
|
52
|
+
uploader.opts[:logging_logger] = opts.fetch(:logger, uploader.opts[:logging_logger])
|
53
|
+
uploader.opts[:logging_stream] = opts.fetch(:stream, uploader.opts.fetch(:logging_stream, $stdout))
|
54
|
+
uploader.opts[:logging_format] = opts.fetch(:format, uploader.opts.fetch(:logging_format, :human))
|
55
55
|
end
|
56
56
|
|
57
57
|
module ClassMethods
|
@@ -1,57 +1,60 @@
|
|
1
|
+
warn "The migration_helpers Shrine plugin is deprecated and will be removed in Shrine 3. Attacher#cached? and Attacher#stored? have been moved to base."
|
2
|
+
|
1
3
|
class Shrine
|
2
4
|
module Plugins
|
3
5
|
# The migration_helpers plugin gives the attacher additional helper methods
|
4
6
|
# which are convenient when doing file migrations.
|
5
7
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# plugin with `delegate: false`:
|
9
|
-
#
|
10
|
-
# plugin :migration_helpers, delegate: false
|
11
|
-
#
|
12
|
-
# ## `attachment_cache` and `attachment_store`
|
13
|
-
#
|
14
|
-
# These methods return cache and store uploaders used by the underlying
|
15
|
-
# attacher:
|
16
|
-
#
|
17
|
-
# user.avatar_cache #=> #<Shrine @storage_key=:cache @storage=#<Shrine::Storage::FileSystem @directory=public/uploads>>
|
18
|
-
# user.avatar_store #=> #<Shrine @storage_key=:store @storage=#<Shrine::Storage::S3:0x007fb8343397c8 @bucket=#<Aws::S3::Bucket name="foo">>>
|
19
|
-
#
|
20
|
-
# # attacher equivalents
|
21
|
-
# user.avatar_attacher.cache
|
22
|
-
# user.avatar_attacher.store
|
8
|
+
# The plugin also allows convenient delegating to these methods through the
|
9
|
+
# model, by setting `:delegate`:
|
23
10
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# These methods return true if attachment exists and is cached/stored:
|
11
|
+
# plugin :migration_helpers, delegate: true
|
27
12
|
#
|
28
|
-
#
|
29
|
-
# user.avatar_stored? # user.avatar && user.avatar_store.uploaded?(user.avatar)
|
30
|
-
#
|
31
|
-
# # attacher equivalents
|
32
|
-
# user.avatar_attacher.cached?
|
33
|
-
# user.avatar_attacher.stored?
|
34
|
-
#
|
35
|
-
# ## `update_attachment`
|
13
|
+
# ## `update_stored`
|
36
14
|
#
|
37
15
|
# This method updates the record's attachment with the result of the given
|
38
16
|
# block.
|
39
17
|
#
|
18
|
+
# user.avatar_attacher.update_stored do |avatar|
|
19
|
+
# user.avatar_attacher.store.upload(avatar) # saved to the record
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # with model delegation
|
40
23
|
# user.update_avatar do |avatar|
|
41
24
|
# user.avatar_store.upload(avatar) # saved to the record
|
42
25
|
# end
|
43
26
|
#
|
44
|
-
# # attacher equivalent
|
45
|
-
# user.avatar_attacher.update_stored { |avatar| }
|
46
|
-
#
|
47
27
|
# The block will get triggered _only_ if the attachment is present and not
|
48
28
|
# cached, *and* will save the record only if the record's attachment
|
49
29
|
# hasn't changed in the time it took to execute the block. This method is
|
50
30
|
# most useful for adding/removing versions and changing locations of files.
|
31
|
+
#
|
32
|
+
# ## `cached?` and `stored?`
|
33
|
+
#
|
34
|
+
# These methods return true if attachment exists and is cached/stored:
|
35
|
+
#
|
36
|
+
# user.avatar_attacher.cached? # user.avatar && user.avatar_attacher.cache.uploaded?(user.avatar)
|
37
|
+
# user.avatar_attacher.stored? # user.avatar && user.avatar_attacher.store.uploaded?(user.avatar)
|
38
|
+
#
|
39
|
+
# # with model delegation
|
40
|
+
# user.avatar_cached?
|
41
|
+
# user.avatar_stored?
|
42
|
+
#
|
43
|
+
# ## `attachment_cache` and `attachment_store`
|
44
|
+
#
|
45
|
+
# These methods return cache and store uploaders used by the underlying
|
46
|
+
# attacher:
|
47
|
+
#
|
48
|
+
# # these methods already exist without migration_helpers
|
49
|
+
# user.avatar_attacher.cache #=> #<Shrine @storage_key=:cache @storage=#<Shrine::Storage::FileSystem @directory=public/uploads>>
|
50
|
+
# user.avatar_attacher.store #=> #<Shrine @storage_key=:store @storage=#<Shrine::Storage::S3:0x007fb8343397c8 @bucket=#<Aws::S3::Bucket name="foo">>>
|
51
|
+
#
|
52
|
+
# # with model delegation
|
53
|
+
# user.avatar_cache
|
54
|
+
# user.avatar_store
|
51
55
|
module MigrationHelpers
|
52
|
-
def self.configure(uploader,
|
53
|
-
|
54
|
-
uploader.opts[:migration_helpers_delegate] = options.fetch(:delegate, true)
|
56
|
+
def self.configure(uploader, delegate: false)
|
57
|
+
uploader.opts[:migration_helpers_delegate] = delegate
|
55
58
|
end
|
56
59
|
|
57
60
|
module AttachmentMethods
|
@@ -1,54 +1,36 @@
|
|
1
1
|
class Shrine
|
2
2
|
module Plugins
|
3
|
-
# The moving plugin
|
4
|
-
#
|
5
|
-
#
|
3
|
+
# The moving plugin makes so that when files are supposed to be uploaded,
|
4
|
+
# they are moved instead. For example, on FileSystem moving is
|
5
|
+
# instantaneous regardless of the filesize, so it's suitable for speeding
|
6
|
+
# up uploads for larger files.
|
6
7
|
#
|
7
|
-
#
|
8
|
-
# default temporary files won't immediately get deleted (Ruby's Tempfiles
|
9
|
-
# usually get deleted only when the process ends).
|
8
|
+
# plugin :moving
|
10
9
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# The `:storages` option specifies which storages the file will be moved
|
14
|
-
# to. The above will move raw files to cache (without this plugin it's
|
15
|
-
# simply copied over). However, you may want to move cached files to
|
16
|
-
# `:store` as well:
|
17
|
-
#
|
18
|
-
# plugin :moving, storages: [:cache, :store]
|
10
|
+
# By default files will be moved whenever the storage supports it. If you
|
11
|
+
# want moving to happen only for certain storages, you can set `storages`:
|
19
12
|
#
|
20
|
-
#
|
21
|
-
# destination are on the filesystem, a `mv` command will be executed
|
22
|
-
# (making the transfer instantaneous). Some other storages may implement
|
23
|
-
# moving as well, usually only for files which are on the same storage.
|
24
|
-
# If moving isn't implemented by the storage, the file will be simply
|
25
|
-
# deleted after upload.
|
13
|
+
# plugin :moving, storages: [:cache]
|
26
14
|
module Moving
|
27
|
-
def self.configure(uploader,
|
28
|
-
uploader.opts[:moving_storages] = storages
|
15
|
+
def self.configure(uploader, opts = {})
|
16
|
+
uploader.opts[:moving_storages] = opts.fetch(:storages, uploader.opts[:moving_storages])
|
29
17
|
end
|
30
18
|
|
31
19
|
module InstanceMethods
|
32
20
|
private
|
33
21
|
|
34
|
-
#
|
35
|
-
# copying and deleting.
|
22
|
+
# Moves the file if storage supports it, otherwise defaults to copying.
|
36
23
|
def copy(io, context)
|
37
24
|
if move?(io, context)
|
38
|
-
|
39
|
-
move(io, context)
|
40
|
-
else
|
41
|
-
warn "The #{storage_key.inspect} Shrine storage doesn't support moving a #{io.inspect}. It is currently still deleted, but it won't be in Shrine 2."
|
42
|
-
super
|
43
|
-
io.delete if io.respond_to?(:delete)
|
44
|
-
end
|
25
|
+
move(io, context)
|
45
26
|
else
|
46
27
|
super
|
47
28
|
end
|
48
29
|
end
|
49
30
|
|
50
31
|
def move(io, context)
|
51
|
-
|
32
|
+
context[:upload_options] = (context[:upload_options] || {}).merge(shrine_metadata: context[:metadata])
|
33
|
+
storage.move(io, context[:location], context[:upload_options])
|
52
34
|
end
|
53
35
|
|
54
36
|
def movable?(io, context)
|
@@ -56,6 +38,11 @@ class Shrine
|
|
56
38
|
end
|
57
39
|
|
58
40
|
def move?(io, context)
|
41
|
+
moving_storage? && movable?(io, context)
|
42
|
+
end
|
43
|
+
|
44
|
+
def moving_storage?
|
45
|
+
opts[:moving_storages].nil? ||
|
59
46
|
opts[:moving_storages].include?(storage_key)
|
60
47
|
end
|
61
48
|
end
|
@@ -11,8 +11,8 @@ class Shrine
|
|
11
11
|
#
|
12
12
|
# plugin :parallelize, threads: 5
|
13
13
|
module Parallelize
|
14
|
-
def self.configure(uploader,
|
15
|
-
uploader.opts[:parallelize_threads] = threads
|
14
|
+
def self.configure(uploader, opts = {})
|
15
|
+
uploader.opts[:parallelize_threads] = opts.fetch(:threads, uploader.opts.fetch(:parallelize_threads, 3))
|
16
16
|
end
|
17
17
|
|
18
18
|
module InstanceMethods
|
@@ -43,8 +43,8 @@ class Shrine
|
|
43
43
|
end
|
44
44
|
|
45
45
|
class ThreadPool
|
46
|
-
def initialize(
|
47
|
-
@
|
46
|
+
def initialize(size)
|
47
|
+
@size = size
|
48
48
|
@tasks = Queue.new
|
49
49
|
end
|
50
50
|
|
@@ -53,7 +53,7 @@ class Shrine
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def perform
|
56
|
-
threads = @
|
56
|
+
threads = @size.times.map { spawn_thread }
|
57
57
|
threads.each(&:join)
|
58
58
|
end
|
59
59
|
|
@@ -23,8 +23,8 @@ class Shrine
|
|
23
23
|
# plugin :pretty_location, namespace: "/"
|
24
24
|
# # "blog/user/.../493g82jf23.jpg"
|
25
25
|
module PrettyLocation
|
26
|
-
def self.configure(uploader,
|
27
|
-
uploader.opts[:pretty_location_namespace] = namespace
|
26
|
+
def self.configure(uploader, opts = {})
|
27
|
+
uploader.opts[:pretty_location_namespace] = opts.fetch(:namespace, uploader.opts[:pretty_location_namespace])
|
28
28
|
end
|
29
29
|
|
30
30
|
module InstanceMethods
|
@@ -44,10 +44,6 @@ class Shrine
|
|
44
44
|
|
45
45
|
private
|
46
46
|
|
47
|
-
def generate_uid(io)
|
48
|
-
SecureRandom.hex(5)
|
49
|
-
end
|
50
|
-
|
51
47
|
def class_location(klass)
|
52
48
|
parts = klass.name.downcase.split("::")
|
53
49
|
if separator = opts[:pretty_location_namespace]
|