shrine 2.18.1 → 2.19.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.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +53 -1
- data/README.md +96 -137
- data/doc/advantages.md +4 -4
- data/doc/attacher.md +1 -2
- data/doc/carrierwave.md +3 -2
- data/doc/creating_storages.md +0 -20
- data/doc/design.md +1 -1
- data/doc/metadata.md +62 -36
- data/doc/paperclip.md +7 -6
- data/doc/plugins/data_uri.md +50 -4
- data/doc/plugins/derivation_endpoint.md +24 -0
- data/doc/plugins/determine_mime_type.md +47 -5
- data/doc/plugins/infer_extension.md +45 -9
- data/doc/plugins/instrumentation.md +170 -0
- data/doc/plugins/presign_endpoint.md +1 -1
- data/doc/plugins/pretty_location.md +23 -0
- data/doc/plugins/remote_url.md +59 -8
- data/doc/plugins/signature.md +54 -7
- data/doc/plugins/store_dimensions.md +69 -4
- data/doc/plugins/upload_endpoint.md +2 -2
- data/doc/plugins/validation_helpers.md +71 -29
- data/doc/refile.md +1 -1
- data/doc/release_notes/2.18.0.md +2 -2
- data/doc/release_notes/2.19.0.md +263 -0
- data/doc/storage/file_system.md +26 -8
- data/doc/testing.md +10 -10
- data/lib/shrine.rb +32 -16
- data/lib/shrine/attacher.rb +3 -0
- data/lib/shrine/attachment.rb +3 -0
- data/lib/shrine/plugins/add_metadata.rb +12 -16
- data/lib/shrine/plugins/backup.rb +2 -0
- data/lib/shrine/plugins/copy.rb +2 -0
- data/lib/shrine/plugins/data_uri.rb +56 -28
- data/lib/shrine/plugins/derivation_endpoint.rb +61 -27
- data/lib/shrine/plugins/determine_mime_type.rb +27 -5
- data/lib/shrine/plugins/infer_extension.rb +26 -5
- data/lib/shrine/plugins/instrumentation.rb +300 -0
- data/lib/shrine/plugins/logging.rb +2 -0
- data/lib/shrine/plugins/moving.rb +2 -0
- data/lib/shrine/plugins/pretty_location.rb +21 -12
- data/lib/shrine/plugins/rack_file.rb +23 -18
- data/lib/shrine/plugins/refresh_metadata.rb +4 -4
- data/lib/shrine/plugins/remote_url.rb +42 -23
- data/lib/shrine/plugins/signature.rb +32 -1
- data/lib/shrine/plugins/store_dimensions.rb +54 -9
- data/lib/shrine/plugins/validation_helpers.rb +148 -47
- data/lib/shrine/storage/file_system.rb +32 -15
- data/lib/shrine/storage/linter.rb +0 -13
- data/lib/shrine/storage/s3.rb +2 -5
- data/lib/shrine/uploaded_file.rb +8 -0
- data/lib/shrine/version.rb +2 -2
- data/shrine.gemspec +18 -3
- metadata +58 -27
data/doc/storage/file_system.md
CHANGED
@@ -11,7 +11,7 @@ storage.url("image.jpg") #=> "/uploads/image.jpg"
|
|
11
11
|
```
|
12
12
|
|
13
13
|
This storage will upload all files to "public/uploads", and the URLs of the
|
14
|
-
uploaded files will start with "/uploads
|
14
|
+
uploaded files will start with "/uploads/\*". This way you can use FileSystem
|
15
15
|
for both cache and store, one having the prefix "uploads/cache" and other
|
16
16
|
"uploads/store". If you're uploading files to the `public` directory itself,
|
17
17
|
you need to set `:prefix` to `"/"`:
|
@@ -29,12 +29,6 @@ storage = Shrine::Storage::FileSystem.new(Dir.tmpdir)
|
|
29
29
|
storage.url("image.jpg") #=> "/var/folders/k7/6zx6dx6x7ys3rv3srh0nyfj00000gn/T/image.jpg"
|
30
30
|
```
|
31
31
|
|
32
|
-
In general you can always retrieve path to the file using `#path`:
|
33
|
-
|
34
|
-
```rb
|
35
|
-
storage.path("image.jpg") #=> #<Pathname:public/image.jpg>
|
36
|
-
```
|
37
|
-
|
38
32
|
## Host
|
39
33
|
|
40
34
|
It's generally a good idea to serve your files via a CDN, so an additional
|
@@ -58,6 +52,28 @@ storage.url("image.jpg", host: "http://943.23.43.1")
|
|
58
52
|
#=> "http://943.23.43.1/opt/files/image.jpg"
|
59
53
|
```
|
60
54
|
|
55
|
+
## Moving
|
56
|
+
|
57
|
+
If you're uploading files on disk and want to improve performance, you can tell
|
58
|
+
the `FileSystem#upload` method to **move** files instead of copying them:
|
59
|
+
|
60
|
+
```rb
|
61
|
+
storage.upload(file, "/path/to/destination", move: true) # performs the `mv` command
|
62
|
+
|
63
|
+
File.exist?(file.path) #=> false
|
64
|
+
```
|
65
|
+
|
66
|
+
If you want to make this option default, you can use the
|
67
|
+
[`upload_options`][upload_options] plugin.
|
68
|
+
|
69
|
+
## Path
|
70
|
+
|
71
|
+
You can retrieve path to the file using `#path`:
|
72
|
+
|
73
|
+
```rb
|
74
|
+
storage.path("image.jpg") #=> #<Pathname:public/image.jpg>
|
75
|
+
```
|
76
|
+
|
61
77
|
## Clearing cache
|
62
78
|
|
63
79
|
If you're using FileSystem as cache, you will probably want to periodically
|
@@ -66,7 +82,7 @@ periodically:
|
|
66
82
|
|
67
83
|
```rb
|
68
84
|
file_system = Shrine.storages[:cache]
|
69
|
-
file_system.clear!
|
85
|
+
file_system.clear! { |path| path.mtime < Time.now - 7*24*60*60 } # delete files older than 1 week
|
70
86
|
```
|
71
87
|
|
72
88
|
## Permissions
|
@@ -94,3 +110,5 @@ use it for cache, since Heroku wipes this directory between app restarts. This
|
|
94
110
|
also means that deploying the app can cancel someone's uploading if you're
|
95
111
|
using backgrounding. Also, by default you cannot generate URLs to files in the
|
96
112
|
"tmp" directory, but you can with the `download_endpoint` plugin.
|
113
|
+
|
114
|
+
[upload_options]: /doc/plugins/upload_options.md#readme
|
data/doc/testing.md
CHANGED
@@ -54,14 +54,14 @@ Shrine.storages = {
|
|
54
54
|
}
|
55
55
|
```
|
56
56
|
|
57
|
-
If you're using AWS S3 storage, you can use [
|
57
|
+
If you're using AWS S3 storage, you can use [MinIO] (explained below) instead
|
58
58
|
of S3, both in test and development environment. Alternatively, you can [stub
|
59
59
|
aws-sdk-s3 requests][aws-sdk-ruby stubs] in tests.
|
60
60
|
|
61
|
-
###
|
61
|
+
### MinIO
|
62
62
|
|
63
|
-
[
|
64
|
-
you can run locally. The advantage of using
|
63
|
+
[MinIO] is an open source object storage server with AWS S3 compatible API which
|
64
|
+
you can run locally. The advantage of using MinIO for your development and test
|
65
65
|
environments is that all AWS S3 functionality should still continue to work,
|
66
66
|
including direct uploads, so you don't need to update your code.
|
67
67
|
|
@@ -71,17 +71,17 @@ If you're on a Mac you can install it with Homebrew:
|
|
71
71
|
$ brew install minio/stable/minio
|
72
72
|
```
|
73
73
|
|
74
|
-
Afterwards you can start the
|
74
|
+
Afterwards you can start the MinIO server and give it a directory where it will
|
75
75
|
store the data:
|
76
76
|
|
77
77
|
```
|
78
78
|
$ minio server data/
|
79
79
|
```
|
80
80
|
|
81
|
-
This command will print out the credentials for the running
|
82
|
-
well as a link to the
|
81
|
+
This command will print out the credentials for the running MinIO server, as
|
82
|
+
well as a link to the MinIO web interface. Follow that link and create a new
|
83
83
|
bucket. Once you've done that, you can configure `Shrine::Storage::S3` to use
|
84
|
-
your
|
84
|
+
your MinIO server:
|
85
85
|
|
86
86
|
```rb
|
87
87
|
Shrine::Storage::S3.new(
|
@@ -94,7 +94,7 @@ Shrine::Storage::S3.new(
|
|
94
94
|
)
|
95
95
|
```
|
96
96
|
|
97
|
-
The `:endpoint` option will make aws-sdk-s3 point all URLs to your
|
97
|
+
The `:endpoint` option will make aws-sdk-s3 point all URLs to your MinIO server
|
98
98
|
(instead of `s3.amazonaws.com`), and `:force_path_style` tells it not to use
|
99
99
|
subdomains when generating URLs.
|
100
100
|
|
@@ -285,4 +285,4 @@ isolation.
|
|
285
285
|
[Rack::Test]: https://github.com/brynary/rack-test
|
286
286
|
[Rack::TestApp]: https://github.com/kwatch/rack-test_app
|
287
287
|
[aws-sdk-ruby stubs]: http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/ClientStubs.html
|
288
|
-
[
|
288
|
+
[MinIO]: https://min.io/
|
data/lib/shrine.rb
CHANGED
@@ -9,6 +9,7 @@ require "shrine/plugins"
|
|
9
9
|
require "securerandom"
|
10
10
|
require "json"
|
11
11
|
require "tempfile"
|
12
|
+
require "logger"
|
12
13
|
|
13
14
|
# Core class that represents uploader.
|
14
15
|
# Base implementation is defined in InstanceMethods and ClassMethods.
|
@@ -37,6 +38,8 @@ class Shrine
|
|
37
38
|
|
38
39
|
@opts = {}
|
39
40
|
@storages = {}
|
41
|
+
@logger = Logger.new(STDOUT)
|
42
|
+
@logger.formatter = -> (*, message) { "#{message}\n" }
|
40
43
|
|
41
44
|
module ClassMethods
|
42
45
|
# Generic options for this class, plugins store their options here.
|
@@ -45,6 +48,9 @@ class Shrine
|
|
45
48
|
# A hash of storages with their symbol identifiers.
|
46
49
|
attr_accessor :storages
|
47
50
|
|
51
|
+
# A logger instance.
|
52
|
+
attr_accessor :logger
|
53
|
+
|
48
54
|
# When inheriting Shrine, copy the instance variables into the subclass,
|
49
55
|
# and create subclasses of core classes.
|
50
56
|
def inherited(subclass)
|
@@ -100,7 +106,7 @@ class Shrine
|
|
100
106
|
# model class. Example:
|
101
107
|
#
|
102
108
|
# class Photo
|
103
|
-
# include Shrine
|
109
|
+
# include Shrine::Attachment(:image) # creates a Shrine::Attachment object
|
104
110
|
# end
|
105
111
|
def Attachment(name, *args)
|
106
112
|
self::Attachment.new(name, *args)
|
@@ -154,9 +160,14 @@ class Shrine
|
|
154
160
|
end
|
155
161
|
end
|
156
162
|
|
157
|
-
# Prints a
|
163
|
+
# Prints a warning to the logger.
|
164
|
+
def warn(message)
|
165
|
+
Shrine.logger.warn "SHRINE WARNING: #{message}"
|
166
|
+
end
|
167
|
+
|
168
|
+
# Prints a deprecation warning to the logger.
|
158
169
|
def deprecation(message)
|
159
|
-
warn "SHRINE DEPRECATION WARNING: #{message}"
|
170
|
+
Shrine.logger.warn "SHRINE DEPRECATION WARNING: #{message}"
|
160
171
|
end
|
161
172
|
end
|
162
173
|
|
@@ -233,11 +244,7 @@ class Shrine
|
|
233
244
|
# file extension. Can be overriden in uploaders for generating custom
|
234
245
|
# location.
|
235
246
|
def generate_location(io, context = {})
|
236
|
-
|
237
|
-
extension ||= File.extname(extract_filename(io).to_s).downcase
|
238
|
-
basename = generate_uid(io)
|
239
|
-
|
240
|
-
basename + extension
|
247
|
+
basic_location(io)
|
241
248
|
end
|
242
249
|
|
243
250
|
# Extracts filename, size and MIME type from the file, which is later
|
@@ -264,7 +271,7 @@ class Shrine
|
|
264
271
|
# Attempts to extract the MIME type from the IO object.
|
265
272
|
def extract_mime_type(io)
|
266
273
|
if io.respond_to?(:content_type) && io.content_type
|
267
|
-
warn "The \"mime_type\" Shrine metadata field will be set from the \"Content-Type\" request header, which might not hold the actual MIME type of the file. It is recommended to load the determine_mime_type plugin which determines MIME type from file content."
|
274
|
+
Shrine.warn "The \"mime_type\" Shrine metadata field will be set from the \"Content-Type\" request header, which might not hold the actual MIME type of the file. It is recommended to load the determine_mime_type plugin which determines MIME type from file content."
|
268
275
|
io.content_type.split(";").first # exclude media type parameters
|
269
276
|
end
|
270
277
|
end
|
@@ -283,7 +290,7 @@ class Shrine
|
|
283
290
|
_enforce_io(io)
|
284
291
|
|
285
292
|
metadata = get_metadata(io, context)
|
286
|
-
metadata = metadata.merge(context[:metadata]) if context[:metadata]
|
293
|
+
metadata = metadata.merge(context[:metadata]) if context[:metadata].is_a?(Hash)
|
287
294
|
|
288
295
|
location = get_location(io, context.merge(metadata: metadata))
|
289
296
|
|
@@ -329,6 +336,15 @@ class Shrine
|
|
329
336
|
process(io, context)
|
330
337
|
end
|
331
338
|
|
339
|
+
# Generates a basic location for an uploaded file
|
340
|
+
def basic_location(io)
|
341
|
+
extension = ".#{io.extension}" if io.is_a?(UploadedFile) && io.extension
|
342
|
+
extension ||= File.extname(extract_filename(io).to_s).downcase
|
343
|
+
basename = generate_uid(io)
|
344
|
+
|
345
|
+
basename + extension
|
346
|
+
end
|
347
|
+
|
332
348
|
# Retrieves the location for the given IO and context. First it looks
|
333
349
|
# for the `:location` option, otherwise it calls #generate_location.
|
334
350
|
def get_location(io, context)
|
@@ -339,10 +355,12 @@ class Shrine
|
|
339
355
|
# If the IO object is a Shrine::UploadedFile, it simply copies over its
|
340
356
|
# metadata, otherwise it calls #extract_metadata.
|
341
357
|
def get_metadata(io, context)
|
342
|
-
if io.is_a?(UploadedFile)
|
358
|
+
if io.is_a?(UploadedFile) && context[:metadata] != true
|
343
359
|
io.metadata.dup
|
344
|
-
|
360
|
+
elsif context[:metadata] != false
|
345
361
|
extract_metadata(io, context)
|
362
|
+
else
|
363
|
+
{}
|
346
364
|
end
|
347
365
|
end
|
348
366
|
|
@@ -360,9 +378,7 @@ class Shrine
|
|
360
378
|
SecureRandom.hex
|
361
379
|
end
|
362
380
|
end
|
363
|
-
end
|
364
381
|
|
365
|
-
|
366
|
-
|
367
|
-
core_class.extend core_class.const_get(:ClassMethods)
|
382
|
+
extend ClassMethods
|
383
|
+
include InstanceMethods
|
368
384
|
end
|
data/lib/shrine/attacher.rb
CHANGED
data/lib/shrine/attachment.rb
CHANGED
@@ -7,12 +7,12 @@ class Shrine
|
|
7
7
|
# [doc/plugins/add_metadata.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/add_metadata.md
|
8
8
|
module AddMetadata
|
9
9
|
def self.configure(uploader)
|
10
|
-
uploader.opts[:
|
10
|
+
uploader.opts[:add_metadata_definitions] ||= []
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
-
def add_metadata(name = nil,
|
15
|
-
opts[:
|
14
|
+
def add_metadata(name = nil, &block)
|
15
|
+
opts[:add_metadata_definitions] << [name, block]
|
16
16
|
|
17
17
|
metadata_method(name) if name
|
18
18
|
end
|
@@ -24,7 +24,7 @@ class Shrine
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def _metadata_method(name)
|
27
|
-
|
27
|
+
FileMethods.send(:define_method, name) do
|
28
28
|
metadata[name.to_s]
|
29
29
|
end
|
30
30
|
end
|
@@ -43,28 +43,24 @@ class Shrine
|
|
43
43
|
private
|
44
44
|
|
45
45
|
def extract_custom_metadata(io, context)
|
46
|
-
opts[:
|
47
|
-
result
|
48
|
-
metadata = {}
|
46
|
+
opts[:add_metadata_definitions].each do |name, block|
|
47
|
+
result = instance_exec(io, context, &block)
|
49
48
|
|
50
49
|
if name
|
51
|
-
metadata
|
50
|
+
context[:metadata].merge! name.to_s => result
|
52
51
|
else
|
53
|
-
metadata.merge!(
|
52
|
+
context[:metadata].merge! result.transform_keys(&:to_s) if result
|
54
53
|
end
|
55
54
|
|
56
|
-
# convert symbol keys to strings
|
57
|
-
metadata.keys.each do |key|
|
58
|
-
metadata[key.to_s] = metadata.delete(key) if key.is_a?(Symbol)
|
59
|
-
end
|
60
|
-
|
61
|
-
context[:metadata].merge!(metadata)
|
62
|
-
|
63
55
|
# rewind between metadata blocks
|
64
56
|
io.rewind
|
65
57
|
end
|
66
58
|
end
|
67
59
|
end
|
60
|
+
|
61
|
+
module FileMethods
|
62
|
+
# methods will be dynamically defined here through `Shrine.add_metadata`
|
63
|
+
end
|
68
64
|
end
|
69
65
|
|
70
66
|
register_plugin(:add_metadata, AddMetadata)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
Shrine.deprecation("The backup plugin has been deprecated, the new preferred way to implement mirroring is via the instrumentation plugin – see https://github.com/shrinerb/shrine/wiki/Mirroring-Uploads. The backup plugin will be removed in Shrine 3.")
|
4
|
+
|
3
5
|
class Shrine
|
4
6
|
module Plugins
|
5
7
|
# Documentation lives in [doc/plugins/backup.md] on GitHub.
|
data/lib/shrine/plugins/copy.rb
CHANGED
@@ -20,11 +20,24 @@ class Shrine
|
|
20
20
|
CONTENT_SEPARATOR = /,/
|
21
21
|
DEFAULT_CONTENT_TYPE = "text/plain"
|
22
22
|
|
23
|
+
LOG_SUBSCRIBER = -> (event) do
|
24
|
+
Shrine.logger.info "Data URI (#{event.duration}ms) – #{{
|
25
|
+
uploader: event[:uploader],
|
26
|
+
}.inspect}"
|
27
|
+
end
|
28
|
+
|
23
29
|
def self.configure(uploader, opts = {})
|
24
|
-
uploader.opts[:
|
25
|
-
uploader.opts[:
|
30
|
+
uploader.opts[:data_uri] ||= { log_subscriber: LOG_SUBSCRIBER }
|
31
|
+
uploader.opts[:data_uri].merge!(opts)
|
32
|
+
|
33
|
+
if uploader.opts[:data_uri][:filename]
|
34
|
+
Shrine.deprecation("The :filename option is deprecated for the data_uri plugin, and will be removed in Shrine 3. Use the infer_extension plugin instead.")
|
35
|
+
end
|
26
36
|
|
27
|
-
|
37
|
+
# instrumentation plugin integration
|
38
|
+
if uploader.respond_to?(:subscribe)
|
39
|
+
uploader.subscribe(:data_uri, &uploader.opts[:data_uri][:log_subscriber])
|
40
|
+
end
|
28
41
|
end
|
29
42
|
|
30
43
|
module ClassMethods
|
@@ -36,20 +49,25 @@ class Shrine
|
|
36
49
|
# io.size #=> 21
|
37
50
|
# io.read # decoded content
|
38
51
|
def data_uri(uri, filename: nil)
|
39
|
-
|
52
|
+
instrument_data_uri(uri) do
|
53
|
+
info = parse_data_uri(uri)
|
54
|
+
create_data_file(info, filename: filename)
|
55
|
+
end
|
56
|
+
end
|
40
57
|
|
58
|
+
private
|
59
|
+
|
60
|
+
def create_data_file(info, filename: nil)
|
41
61
|
content_type = info[:content_type] || DEFAULT_CONTENT_TYPE
|
42
62
|
content = info[:base64] ? Base64.decode64(info[:data]) : CGI.unescape(info[:data])
|
43
|
-
filename = opts[:
|
63
|
+
filename = opts[:data_uri][:filename].call(content_type) if opts[:data_uri][:filename]
|
44
64
|
|
45
|
-
data_file = DataFile.new(content, content_type: content_type, filename: filename)
|
65
|
+
data_file = Shrine::DataFile.new(content, content_type: content_type, filename: filename)
|
46
66
|
info[:data].clear
|
47
67
|
|
48
68
|
data_file
|
49
69
|
end
|
50
70
|
|
51
|
-
private
|
52
|
-
|
53
71
|
def parse_data_uri(uri)
|
54
72
|
scanner = StringScanner.new(uri)
|
55
73
|
scanner.scan(DATA_REGEXP) or raise ParseError, "data URI has invalid format"
|
@@ -60,6 +78,13 @@ class Shrine
|
|
60
78
|
|
61
79
|
{ content_type: media_type, base64: !!base64, data: content }
|
62
80
|
end
|
81
|
+
|
82
|
+
# Sends a `data_uri.shrine` event for instrumentation plugin.
|
83
|
+
def instrument_data_uri(uri, &block)
|
84
|
+
return yield unless respond_to?(:instrument)
|
85
|
+
|
86
|
+
instrument(:data_uri, data_uri: uri, &block)
|
87
|
+
end
|
63
88
|
end
|
64
89
|
|
65
90
|
module AttachmentMethods
|
@@ -87,7 +112,7 @@ class Shrine
|
|
87
112
|
data_file = shrine_class.data_uri(uri)
|
88
113
|
assign(data_file, **options)
|
89
114
|
rescue ParseError => error
|
90
|
-
message = shrine_class.opts[:
|
115
|
+
message = shrine_class.opts[:data_uri][:error_message] || error.message
|
91
116
|
message = message.call(uri) if message.respond_to?(:call)
|
92
117
|
errors.replace [message]
|
93
118
|
@data_uri = uri
|
@@ -118,30 +143,33 @@ class Shrine
|
|
118
143
|
result
|
119
144
|
end
|
120
145
|
end
|
146
|
+
end
|
121
147
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
def initialize(content, content_type: nil, filename: nil)
|
126
|
-
@content_type = content_type
|
127
|
-
@original_filename = filename
|
128
|
-
@io = StringIO.new(content)
|
129
|
-
end
|
148
|
+
register_plugin(:data_uri, DataUri)
|
149
|
+
end
|
130
150
|
|
131
|
-
|
132
|
-
|
133
|
-
end
|
151
|
+
class DataFile
|
152
|
+
attr_reader :content_type, :original_filename
|
134
153
|
|
135
|
-
|
136
|
-
|
154
|
+
def initialize(content, content_type: nil, filename: nil)
|
155
|
+
@content_type = content_type
|
156
|
+
@original_filename = filename
|
157
|
+
@io = StringIO.new(content)
|
158
|
+
end
|
137
159
|
|
138
|
-
|
139
|
-
|
140
|
-
@io.string.clear # deallocate string
|
141
|
-
end
|
142
|
-
end
|
160
|
+
def to_io
|
161
|
+
@io
|
143
162
|
end
|
144
163
|
|
145
|
-
|
164
|
+
extend Forwardable
|
165
|
+
delegate [:read, :size, :rewind, :eof?] => :@io
|
166
|
+
|
167
|
+
def close
|
168
|
+
@io.close
|
169
|
+
@io.string.clear # deallocate string
|
170
|
+
end
|
146
171
|
end
|
172
|
+
|
173
|
+
Plugins::DataUri.const_set(:DataFile, DataFile)
|
174
|
+
Plugins::DataUri.deprecate_constant(:DataFile)
|
147
175
|
end
|