echo_uploads 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/echo_uploads/file.rb +44 -16
- data/lib/echo_uploads/filesystem_store.rb +21 -17
- data/lib/echo_uploads/mapped_file.rb +5 -0
- data/lib/echo_uploads/mapper.rb +35 -0
- data/lib/echo_uploads/model.rb +83 -34
- data/lib/echo_uploads/perm_file_saving.rb +65 -19
- data/lib/echo_uploads/railtie.rb +4 -1
- data/lib/echo_uploads/s3_store.rb +51 -0
- data/lib/echo_uploads/temp_file_saving.rb +26 -5
- data/lib/echo_uploads.rb +4 -1
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9302fa84aaf621556620c40904c1c8ead227b53d
|
4
|
+
data.tar.gz: 8ffbf38511536c4fee0a341c19f8a30424d3f54f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b53ba87f3dd560c8b163e2a8aba350f520eb982675a7c8629977816e473a80ea1b1d321dabc1716dade423ea20abaae833265f03b72f90be8966bc5a484ad2cc
|
7
|
+
data.tar.gz: 6110cf77bc31bc2906bb89c24fd493792ce84926ec44dc966f989cee08019a541f821ba6f937d12de731a12e2c580335a7d5af6e33728986603225e62b662b56
|
data/lib/echo_uploads/file.rb
CHANGED
@@ -9,11 +9,22 @@ module EchoUploads
|
|
9
9
|
|
10
10
|
before_destroy :delete_file_conditionally
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
attr_accessor :file
|
13
|
+
|
14
|
+
def compute_mime!(options)
|
15
|
+
if file and file.is_a?(::EchoUploads::MappedFile)
|
16
|
+
name = file.mapped_filename
|
17
|
+
else
|
18
|
+
name = original_filename
|
19
|
+
end
|
20
|
+
type = MIME::Types.type_for(name).first
|
14
21
|
self.mime_type = type ? type.content_type : 'application/octet-stream'
|
15
22
|
end
|
16
23
|
|
24
|
+
def compute_key!(file, options)
|
25
|
+
self.key = options[:key].call file
|
26
|
+
end
|
27
|
+
|
17
28
|
# Returns a proc that takes as its only argument an ActionDispatch::UploadedFile
|
18
29
|
# and returns a key string.
|
19
30
|
def self.default_key_proc
|
@@ -39,30 +50,47 @@ module EchoUploads
|
|
39
50
|
original_basename + original_extension
|
40
51
|
end
|
41
52
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
53
|
+
def path
|
54
|
+
storage.path key
|
55
|
+
end
|
56
|
+
|
57
|
+
# Pass in an attribute name, an ActionDispatch::Http::UploadedFile, and an options hash.
|
58
|
+
# Must set #file attribute first.
|
59
|
+
def persist!(attr, options)
|
60
|
+
unless(
|
61
|
+
file.is_a?(ActionDispatch::Http::UploadedFile) or
|
62
|
+
file.is_a?(Rack::Test::UploadedFile)
|
63
|
+
)
|
64
|
+
raise(
|
65
|
+
"Expected #file to be a ActionDispatch::Http::UploadedFile "+
|
66
|
+
"or Rack::Test::UploadedFile, but was #{file.inspect}"
|
67
|
+
)
|
68
|
+
end
|
46
69
|
|
47
70
|
# Configure and save the metadata object.
|
48
|
-
|
71
|
+
compute_key! file, options
|
49
72
|
self.owner_attr = attr
|
50
73
|
self.original_extension = ::File.extname(file.original_filename)
|
51
74
|
self.original_basename = ::File.basename(file.original_filename, original_extension)
|
52
|
-
compute_mime!
|
53
|
-
|
75
|
+
compute_mime! options
|
76
|
+
if options[:storage].is_a? String
|
77
|
+
self.storage_type = options[:storage]
|
78
|
+
else
|
79
|
+
self.storage_type = options[:storage].name
|
80
|
+
end
|
54
81
|
save!
|
55
82
|
|
56
83
|
# Write the file to the filestore.
|
57
|
-
|
58
|
-
|
59
|
-
storage.write key, file_to_write.tempfile
|
84
|
+
if file.is_a?(ActionDispatch::Http::UploadedFile)
|
85
|
+
storage.write key, file.tempfile
|
60
86
|
else
|
61
|
-
storage.write key,
|
87
|
+
storage.write key, file
|
62
88
|
end
|
63
|
-
|
64
|
-
|
65
|
-
|
89
|
+
|
90
|
+
# If we mapped the files, they were temporarily written to tmp/echo_uploads.
|
91
|
+
# Delete them.
|
92
|
+
if file.is_a?(::EchoUploads::MappedFile)
|
93
|
+
::File.delete file.path
|
66
94
|
end
|
67
95
|
|
68
96
|
# Prune any expired temporary files. (Unless automatic pruning was turned off in
|
@@ -1,5 +1,26 @@
|
|
1
1
|
module EchoUploads
|
2
2
|
class FilesystemStore < ::EchoUploads::AbstractStore
|
3
|
+
def delete(key)
|
4
|
+
_path = path(key)
|
5
|
+
::File.delete(_path) if ::File.exists?(_path)
|
6
|
+
end
|
7
|
+
|
8
|
+
def exists?(key)
|
9
|
+
::File.exists? path(key)
|
10
|
+
end
|
11
|
+
|
12
|
+
def open(key)
|
13
|
+
::File.open(path(key), 'rb', &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def path(key)
|
17
|
+
::File.join folder, key
|
18
|
+
end
|
19
|
+
|
20
|
+
def read(key)
|
21
|
+
File.read path(key)
|
22
|
+
end
|
23
|
+
|
3
24
|
def write(key, file)
|
4
25
|
_path = path key
|
5
26
|
unless ::File.exists?(_path)
|
@@ -14,23 +35,6 @@ module EchoUploads
|
|
14
35
|
end
|
15
36
|
end
|
16
37
|
|
17
|
-
def read(key)
|
18
|
-
File.read path(key)
|
19
|
-
end
|
20
|
-
|
21
|
-
def delete(key)
|
22
|
-
_path = path(key)
|
23
|
-
::File.delete(_path) if ::File.exists?(_path)
|
24
|
-
end
|
25
|
-
|
26
|
-
def open(key)
|
27
|
-
::File.open(path(key), 'rb', &block)
|
28
|
-
end
|
29
|
-
|
30
|
-
def path(key)
|
31
|
-
::File.join folder, key
|
32
|
-
end
|
33
|
-
|
34
38
|
private
|
35
39
|
|
36
40
|
# Can be customized in your per-environment config like this:
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module EchoUploads
|
4
|
+
class Mapper
|
5
|
+
def initialize(file)
|
6
|
+
unless(
|
7
|
+
file.is_a?(ActionDispatch::Http::UploadedFile) or
|
8
|
+
file.is_a?(Rack::Test::UploadedFile)
|
9
|
+
)
|
10
|
+
raise(
|
11
|
+
"Expected file to be a ActionDispatch::Http::UploadedFile "+
|
12
|
+
"or Rack::Test::UploadedFile, but was #{file.inspect}"
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
@uploaded_file = file
|
17
|
+
@outputs = []
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :outputs
|
21
|
+
|
22
|
+
def write(ext)
|
23
|
+
folder = ::File.join Rails.root, 'tmp/echo_uploads'
|
24
|
+
FileUtils.mkdir_p folder
|
25
|
+
path = ::File.join(folder, SecureRandom.hex(15) + ext)
|
26
|
+
yield path
|
27
|
+
file = ::File.open path, 'rb'
|
28
|
+
mapped_file = ::EchoUploads::MappedFile.new(
|
29
|
+
tempfile: file, filename: @uploaded_file.original_filename
|
30
|
+
)
|
31
|
+
mapped_file.mapped_filename = ::File.basename path
|
32
|
+
outputs << mapped_file
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/echo_uploads/model.rb
CHANGED
@@ -19,9 +19,11 @@ module EchoUploads
|
|
19
19
|
|
20
20
|
def echo_uploads_data
|
21
21
|
Base64.encode64(JSON.dump(self.class.echo_uploads_config.inject({}) do |hash, (attr, cfg)|
|
22
|
-
|
23
|
-
if
|
24
|
-
hash[attr] =
|
22
|
+
metas = send("#{attr}_tmp_metadata")
|
23
|
+
if metas
|
24
|
+
hash[attr] = metas.map do |meta|
|
25
|
+
{'id' => meta.id, 'key' => meta.key}
|
26
|
+
end
|
25
27
|
end
|
26
28
|
hash
|
27
29
|
end)).strip
|
@@ -30,17 +32,35 @@ module EchoUploads
|
|
30
32
|
# Pass in a hash that's been encoded as JSON and then Base64.
|
31
33
|
def echo_uploads_data=(data)
|
32
34
|
parsed = JSON.parse Base64.decode64(data)
|
35
|
+
# parsed will look like:
|
36
|
+
# { 'attr1' => [ {'id' => 1, 'key' => 'abc...'} ] }
|
37
|
+
unless parsed.is_a? Hash
|
38
|
+
raise ArgumentError, "Invalid JSON structure in: #{parsed.inspect}"
|
39
|
+
end
|
33
40
|
parsed.each do |attr, attr_data|
|
34
|
-
#
|
35
|
-
#
|
36
|
-
|
37
|
-
|
41
|
+
# If the :map option was passed, there may be multiple variants of the uploaded
|
42
|
+
# file. Even if not, attr_data is still a one-element array.
|
43
|
+
unless attr_data.is_a? Array
|
44
|
+
raise ArgumentError, "Invalid JSON structure in: #{parsed.inspect}"
|
45
|
+
end
|
46
|
+
attr_data.each do |variant_data|
|
47
|
+
unless variant_data.is_a? Hash
|
48
|
+
raise ArgumentError, "Invalid JSON structure in: #{parsed.inspect}"
|
49
|
+
end
|
50
|
+
if meta = ::EchoUploads::File.where(
|
51
|
+
id: variant_data['id'], key: variant_data['key'], temporary: true
|
52
|
+
).first
|
53
|
+
if send("#{attr}_tmp_metadata").nil?
|
54
|
+
send "#{attr}_tmp_metadata=", []
|
55
|
+
end
|
56
|
+
send("#{attr}_tmp_metadata") << meta
|
57
|
+
end
|
38
58
|
end
|
39
59
|
end
|
40
60
|
end
|
41
61
|
|
42
62
|
# Helper method used internally Echo Uploads.
|
43
|
-
def
|
63
|
+
def echo_uploads_map_metadata(attr, options)
|
44
64
|
meta = send("#{attr}_metadata")
|
45
65
|
meta ? yield(meta) : nil
|
46
66
|
end
|
@@ -54,16 +74,26 @@ module EchoUploads
|
|
54
74
|
# - +expires+: Length of time temporary files will be persisted. Defaults to
|
55
75
|
# +1.day+.
|
56
76
|
# - +storage+: A class that persists uploaded files to disk, to the cloud, or to
|
57
|
-
# wherever else you want. Defaults to +
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
77
|
+
# wherever else you want. Defaults to +Rails.configuration.echo_uploads.storage+,
|
78
|
+
# which in turn is +EchoUploads::FilesystemStore+ by default.
|
79
|
+
# - +map+: A Proc that accepts an ActionDispatch::Htttp::UploadedFile and an
|
80
|
+
# instance of +EchoUploads::Mapper+. It should transform the file data (e.g.
|
81
|
+
# scaling an image). It should then write the transformed data to one of more
|
82
|
+
# temporary files. To get the temporary file path(s), call +#write+ on the
|
83
|
+
# +Mapper+. See readme.md for an example. The +:map+ option can also accept a
|
84
|
+
# symbol naming an an instance method that works the same way as the previously
|
85
|
+
# described Proc.
|
86
|
+
# - +multiple+: You use the +:map+ option to write multiple versions of the file.
|
87
|
+
# E.g. multiple thumbnail sizes. If you do so, you must pass +multiple: true+.
|
88
|
+
# This will make the association with +EchoUploads::File+ a +has_many+ instead of
|
89
|
+
# a +has_one+. The first file you write in the map function becomes the default.
|
90
|
+
# E.g.: Your model is called +Widget+, and the upload file attribute is called
|
91
|
+
# +photo+. You pass +:map+ with a method that writes three files. If you call
|
92
|
+
# +Widget#photo_path+, it will return the path to the first of the three files.
|
63
93
|
def echo_upload(attr, options = {})
|
64
94
|
options = {
|
65
95
|
expires: 1.day,
|
66
|
-
storage:
|
96
|
+
storage: Rails.configuration.echo_uploads.storage,
|
67
97
|
key: ::EchoUploads::File.default_key_proc
|
68
98
|
}.merge(options)
|
69
99
|
|
@@ -80,43 +110,45 @@ module EchoUploads
|
|
80
110
|
# Define the writer method for the file attribute.
|
81
111
|
define_method("#{attr}=") do |file|
|
82
112
|
if options[:map]
|
83
|
-
|
113
|
+
mapper = ::EchoUploads::Mapper.new file
|
84
114
|
if options[:map].is_a? Proc
|
85
|
-
options[:map].call file,
|
115
|
+
options[:map].call file, mapper
|
86
116
|
else
|
87
|
-
send(options[:map], file,
|
117
|
+
send(options[:map], file, mapper)
|
88
118
|
end
|
89
|
-
|
90
|
-
|
119
|
+
# Write an array of ActionDispatch::Http::UploadedFile objects to the instance
|
120
|
+
# variable.
|
121
|
+
send "mapped_#{attr}=", mapper.outputs
|
91
122
|
end
|
92
123
|
instance_variable_set "@#{attr}", file
|
93
124
|
end
|
94
125
|
|
95
|
-
# Define the accessor methods for the mapped version of the file.
|
126
|
+
# Define the accessor methods for the mapped version(s) of the file. Returns
|
127
|
+
# an array.
|
96
128
|
attr_accessor "mapped_#{attr}"
|
97
129
|
|
130
|
+
# Define the original filename method.
|
131
|
+
define_method("#{attr}_original_filename") do
|
132
|
+
echo_uploads_map_metadata(attr, options, &:original_filename)
|
133
|
+
end
|
134
|
+
|
98
135
|
# Define the path method. This method will raise if the given storage
|
99
136
|
# class doesn't support the #path method.
|
100
137
|
define_method("#{attr}_path") do
|
101
|
-
|
102
|
-
meta.
|
138
|
+
echo_uploads_map_metadata(attr, options) do |meta|
|
139
|
+
meta.path
|
103
140
|
end
|
104
141
|
end
|
105
142
|
|
106
143
|
# Define the MIME type method.
|
107
144
|
define_method("#{attr}_mime") do
|
108
|
-
|
145
|
+
echo_uploads_map_metadata(attr, options, &:mime_type)
|
109
146
|
end
|
110
147
|
alias_method "#{attr}_mime_type", "#{attr}_mime"
|
111
148
|
|
112
|
-
# Define the original filename method.
|
113
|
-
define_method("#{attr}_original_filename") do
|
114
|
-
map_metadata(attr, &:original_filename)
|
115
|
-
end
|
116
|
-
|
117
149
|
# Define the key method
|
118
150
|
define_method("#{attr}_key") do
|
119
|
-
|
151
|
+
echo_uploads_map_metadata(attr, options, &:key)
|
120
152
|
end
|
121
153
|
|
122
154
|
# Define the has_x? method. Returns true if a permanent or temporary file has been
|
@@ -147,10 +179,27 @@ module EchoUploads
|
|
147
179
|
end
|
148
180
|
|
149
181
|
# Define the association with the metadata model.
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
182
|
+
if options[:multiple]
|
183
|
+
has_many("#{attr}_metadatas".to_sym,
|
184
|
+
->() { where(owner_attr: attr) },
|
185
|
+
as: :owner, dependent: :destroy, class_name: '::EchoUploads::File'
|
186
|
+
)
|
187
|
+
|
188
|
+
alias_method attr.to_s.pluralize, "#{attr}_metadatas"
|
189
|
+
|
190
|
+
define_method("#{attr}_metadata") do
|
191
|
+
send("#{attr}_metadatas").first
|
192
|
+
end
|
193
|
+
|
194
|
+
define_method("#{attr}_metadata=") do |val|
|
195
|
+
send("#{attr}_metadatas") << val
|
196
|
+
end
|
197
|
+
else
|
198
|
+
has_one("#{attr}_metadata".to_sym,
|
199
|
+
->() { where(owner_attr: attr) },
|
200
|
+
as: :owner, dependent: :destroy, class_name: '::EchoUploads::File'
|
201
|
+
)
|
202
|
+
end
|
154
203
|
|
155
204
|
# Define the temp attribute for the metadata model.
|
156
205
|
attr_accessor "#{attr}_tmp_metadata"
|
@@ -10,34 +10,80 @@ module EchoUploads
|
|
10
10
|
after_save do |model|
|
11
11
|
if (file = send(attr)).present?
|
12
12
|
# A file is being uploaded during this request cycle.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
# references it.
|
17
|
-
meta.delete_file_conditionally
|
13
|
+
|
14
|
+
if options[:multiple]
|
15
|
+
metas = send("#{attr}_metadatas")
|
18
16
|
else
|
19
|
-
|
20
|
-
meta = ::EchoUploads::File.new(owner: model, temporary: false)
|
21
|
-
send("#{attr}_metadata=", meta)
|
17
|
+
metas = [send("#{attr}_metadata")].compact
|
22
18
|
end
|
23
|
-
|
24
|
-
|
19
|
+
|
20
|
+
# metas is now an array of ::EchoUploads::File instances. The array may
|
21
|
+
# be empty.
|
22
|
+
|
23
|
+
if metas.any?
|
24
|
+
# Previous permanent file(s) exist. This is a new version being uploaded.
|
25
|
+
# Delete the old version(s).
|
26
|
+
metas.each(&:destroy)
|
27
|
+
end
|
28
|
+
|
29
|
+
# No previous permanent files exists. The metas array is currently empty or
|
30
|
+
# else contains deleted records. We need to rebuild that array by constructing
|
31
|
+
# (but not yet saving) new EchoUploads::File objects.
|
32
|
+
|
33
|
+
if options[:multiple]
|
34
|
+
mapped_files = send("mapped_#{attr}") ||
|
35
|
+
raise('echo_uploads called with :multiple, but :map option was missing')
|
36
|
+
metas = mapped_files.map do |mapped_file|
|
37
|
+
::EchoUploads::File.new(
|
38
|
+
owner: model, temporary: false, file: mapped_file
|
39
|
+
)
|
40
|
+
end
|
41
|
+
send("#{attr}_metadatas=", metas)
|
42
|
+
else
|
43
|
+
metas = [::EchoUploads::File.new(
|
44
|
+
owner: model, temporary: false, file: send(attr)
|
45
|
+
)]
|
46
|
+
send("#{attr}_metadata=", metas.first)
|
47
|
+
end
|
48
|
+
|
49
|
+
# metas is still an array of the EchoUploads::File instances. If the array was
|
50
|
+
# initially empty (meaning no previous permanent file existed), then it has
|
51
|
+
# since been populated.
|
52
|
+
|
53
|
+
metas.each do |meta|
|
54
|
+
meta.persist! attr, options
|
55
|
+
end
|
56
|
+
elsif metas = send("#{attr}_tmp_metadata")
|
25
57
|
# A file has not been uploaded during this request cycle. However, the
|
26
58
|
# submitted form "remembered" a temporary metadata record that was previously
|
27
|
-
# saved.
|
28
|
-
|
29
|
-
#
|
59
|
+
# saved.
|
60
|
+
|
61
|
+
# Delete any existing metadata record. (It's possible we
|
30
62
|
# were trying to replace an old version of the file, and there were validation
|
31
63
|
# errors on the first attempt.)
|
32
|
-
|
64
|
+
|
65
|
+
if options[:multiple]
|
66
|
+
model.send("#{attr}_metadatas").each(&:destroy)
|
67
|
+
elsif old = model.send("#{attr}_metadata")
|
33
68
|
old.destroy
|
34
69
|
end
|
35
70
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
meta
|
71
|
+
# We need not call persist! here, because the file is already persisted. (Nor
|
72
|
+
# could we call it, because persist! requires an
|
73
|
+
# ActionDispatch::HTTP::UploadedFile.) Mark the metadata record as permanent
|
74
|
+
# and set its owner.
|
75
|
+
metas.each do |meta|
|
76
|
+
meta.owner = model
|
77
|
+
meta.temporary = false
|
78
|
+
meta.expires_at = nil
|
79
|
+
meta.save!
|
80
|
+
end
|
81
|
+
|
82
|
+
if options[:multiple]
|
83
|
+
send("#{attr}_metadatas=", metas)
|
84
|
+
else
|
85
|
+
send("#{attr}_metadata=", metas.first)
|
86
|
+
end
|
41
87
|
end
|
42
88
|
end
|
43
89
|
end
|
data/lib/echo_uploads/railtie.rb
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Uses the official Amazon Web Services SDK gem:
|
2
|
+
# gem install aws-sdk
|
3
|
+
module EchoUploads
|
4
|
+
class S3Store < ::EchoUploads::AbstractStore
|
5
|
+
def delete(key)
|
6
|
+
bucket.objects[key].delete
|
7
|
+
end
|
8
|
+
|
9
|
+
def exists?(key)
|
10
|
+
bucket.objects[key].exists?
|
11
|
+
end
|
12
|
+
|
13
|
+
def read(key)
|
14
|
+
data = ''
|
15
|
+
bucket.objects[key].read { |chunk| data << chunk }
|
16
|
+
data
|
17
|
+
end
|
18
|
+
|
19
|
+
def url(key, options = {})
|
20
|
+
options = {method: :read}.merge(options)
|
21
|
+
bucket.objects[key].url_for options.delete(:method), options
|
22
|
+
end
|
23
|
+
|
24
|
+
def write(key, file)
|
25
|
+
bucket.objects[key].write file
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def bucket
|
31
|
+
if Rails.configuration.echo_uploads.aws
|
32
|
+
s3 = AWS::S3.new Rails.configuration.echo_uploads.aws
|
33
|
+
else
|
34
|
+
s3 = AWS::S3.new
|
35
|
+
end
|
36
|
+
bucket_name = Rails.configuration.echo_uploads.s3.bucket || raise(
|
37
|
+
'You must define config.echo_uploads.s3.bucket in your application config.'
|
38
|
+
)
|
39
|
+
if s3.buckets[bucket_name].nil?
|
40
|
+
s3.buckets.create bucket_name
|
41
|
+
end
|
42
|
+
s3.buckets[bucket_name]
|
43
|
+
end
|
44
|
+
|
45
|
+
def folder
|
46
|
+
Rails.configuration.echo_uploads.s3.folder || raise(
|
47
|
+
'You must define config.echo_uploads.s3.folder in your application config.'
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -28,11 +28,32 @@ module EchoUploads
|
|
28
28
|
# That's fine. We'll now have a permanent and a temporary one. The temporary
|
29
29
|
# one will replace the permanent one if and when the user resubmits with
|
30
30
|
# valid data.
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
|
32
|
+
# Construct an array of EchoUploads::File instances. The array might have only
|
33
|
+
# one element.
|
34
|
+
if options[:multiple]
|
35
|
+
mapped_files = send("mapped_#{attr}") ||
|
36
|
+
raise('echo_uploads called with :multiple, but :map option was missing')
|
37
|
+
metas = mapped_files.map do |mapped_file|
|
38
|
+
::EchoUploads::File.new(
|
39
|
+
owner: nil, temporary: true, expires_at: options[:expires].from_now,
|
40
|
+
file: mapped_file
|
41
|
+
)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
metas = [::EchoUploads::File.new(
|
45
|
+
owner: nil, temporary: true, expires_at: options[:expires].from_now,
|
46
|
+
file: send(attr)
|
47
|
+
)]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Persist each file. (There might only be one, though.)
|
51
|
+
metas.each do |meta|
|
52
|
+
meta.persist! attr, options
|
53
|
+
end
|
54
|
+
|
55
|
+
# Set the attr_tmp_metadata attribute so the form can remember our records.
|
56
|
+
send("#{attr}_tmp_metadata=", metas)
|
36
57
|
end
|
37
58
|
end
|
38
59
|
|
data/lib/echo_uploads.rb
CHANGED
@@ -6,5 +6,8 @@ require 'echo_uploads/perm_file_saving'
|
|
6
6
|
require 'echo_uploads/temp_file_saving'
|
7
7
|
require 'echo_uploads/model'
|
8
8
|
require 'echo_uploads/file'
|
9
|
+
require 'echo_uploads/mapper'
|
10
|
+
require 'echo_uploads/mapped_file'
|
9
11
|
require 'echo_uploads/abstract_store'
|
10
|
-
require 'echo_uploads/filesystem_store'
|
12
|
+
require 'echo_uploads/filesystem_store'
|
13
|
+
require 'echo_uploads/s3_store'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: echo_uploads
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jarrett Colby
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mime-types
|
@@ -38,7 +38,9 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
description: '
|
41
|
+
description: Gracefully handles invalid form submissions, so users don't have to resubmit
|
42
|
+
the file. Supports transforming the file before saving, e.g. scaling an image. Compatible
|
43
|
+
with any storage mechanism, including the local filesystem and the cloud.
|
42
44
|
email: jarrett@madebyhq.com
|
43
45
|
executables: []
|
44
46
|
extensions: []
|
@@ -48,9 +50,12 @@ files:
|
|
48
50
|
- lib/echo_uploads/abstract_store.rb
|
49
51
|
- lib/echo_uploads/file.rb
|
50
52
|
- lib/echo_uploads/filesystem_store.rb
|
53
|
+
- lib/echo_uploads/mapped_file.rb
|
54
|
+
- lib/echo_uploads/mapper.rb
|
51
55
|
- lib/echo_uploads/model.rb
|
52
56
|
- lib/echo_uploads/perm_file_saving.rb
|
53
57
|
- lib/echo_uploads/railtie.rb
|
58
|
+
- lib/echo_uploads/s3_store.rb
|
54
59
|
- lib/echo_uploads/temp_file_saving.rb
|
55
60
|
- lib/echo_uploads/validation.rb
|
56
61
|
homepage: https://github.com/jarrett/echo_uploads
|