mini_paperclip 0.1.0 → 0.3.1
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.
- checksums.yaml +4 -4
- data/.github/workflows/{ruby.yml → ci.yml} +4 -1
- data/.gitignore +1 -0
- data/Gemfile +1 -7
- data/README.md +5 -3
- data/gemfiles/development.gemfile +7 -0
- data/gemfiles/rails_52.gemfile +1 -6
- data/gemfiles/rails_60.gemfile +1 -6
- data/gemfiles/rails_head.gemfile +5 -0
- data/lib/mini_paperclip.rb +10 -8
- data/lib/mini_paperclip/attachment.rb +119 -68
- data/lib/mini_paperclip/class_methods.rb +7 -1
- data/lib/mini_paperclip/config.rb +2 -1
- data/lib/mini_paperclip/interpolator.rb +15 -9
- data/lib/mini_paperclip/shoulda/matchers/validate_attachment_geometry_matcher.rb +1 -0
- data/lib/mini_paperclip/storage/base.rb +9 -9
- data/lib/mini_paperclip/storage/filesystem.rb +17 -10
- data/lib/mini_paperclip/storage/s3.rb +34 -16
- data/lib/mini_paperclip/validators/file_size_validator.rb +2 -2
- data/lib/mini_paperclip/validators/geometry_validator.rb +15 -11
- data/lib/mini_paperclip/version.rb +1 -1
- data/mini_paperclip.gemspec +1 -0
- metadata +20 -6
- data/gemfiles/rails_52.gemfile.lock +0 -98
- data/gemfiles/rails_60.gemfile.lock +0 -98
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d115f886793d57d62835f40e27768ff04588d3e1f17e7fb430b11f2b146e2ed
|
4
|
+
data.tar.gz: 6d1557f279f9bf007b90d06272e7e19a1829f38b1c0f6a4ce079c06e410fca58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b8c590434edbc1bac00c85028f44d8de3ff67a001643a0613718af5f60bcd06862d012867f3137cb758a48b4db6d8db699df80e3b9838f64dd082e105ddbbf7
|
7
|
+
data.tar.gz: 04524d26fb7e8f96e631c82d1ecce996505b66d60d4c47d0016f48e66ec6cc427eb7a28a7241afa4c894ecb08e4afe92c466bd7d15b4a34d3e5e269dfcc06665
|
@@ -36,5 +36,8 @@ jobs:
|
|
36
36
|
ruby-version: ${{ matrix.ruby }}
|
37
37
|
- name: Install dependencies
|
38
38
|
run: bundle install --gemfile=${{ matrix.gemfile }}
|
39
|
-
- name: Run tests
|
39
|
+
- name: Run tests and Print coverage
|
40
|
+
env:
|
41
|
+
COVERAGE: 1
|
42
|
+
LOGLEVEL: info
|
40
43
|
run: bundle exec --gemfile=${{ matrix.gemfile }} rake
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -2,11 +2,5 @@ source "https://rubygems.org"
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in mini_paperclip.gemspec
|
4
4
|
gemspec
|
5
|
-
|
6
|
-
gem "rake", "~> 12.0"
|
7
|
-
gem "rspec", "~> 3.0"
|
8
|
-
gem "rack-test"
|
9
|
-
gem "webmock"
|
10
|
-
gem "tapp"
|
11
5
|
gem "activerecord"
|
12
|
-
|
6
|
+
eval_gemfile 'gemfiles/development.gemfile'
|
data/README.md
CHANGED
@@ -71,6 +71,7 @@ MiniPaperclip.config.tap do |config|
|
|
71
71
|
config.s3_acl # s3 object acl
|
72
72
|
config.s3_cache_control # Set this value to Cache-Control header when put-object
|
73
73
|
config.interpolates # minimum templates using by `String#gsub!`
|
74
|
+
config.keep_old_files # Delete old attached file if it's false (default false)
|
74
75
|
config.read_timeout # timeout when attachment set url
|
75
76
|
config.logger # You can set logger object.
|
76
77
|
end
|
@@ -104,7 +105,7 @@ end
|
|
104
105
|
|
105
106
|
Interpolate is a simple template system like this.
|
106
107
|
|
107
|
-
template: `:class/:
|
108
|
+
template: `:class/:attachment/:id/:hash.:extension`
|
108
109
|
result: `books/images/1234/abcdef1234567.png`
|
109
110
|
|
110
111
|
You can check default interpolates.
|
@@ -116,8 +117,9 @@ p MiniPaperclip.config.interpolaters
|
|
116
117
|
You can add any interpolate key and process.
|
117
118
|
|
118
119
|
```
|
119
|
-
MiniPaperclip.config.interpolates[
|
120
|
-
|
120
|
+
MiniPaperclip.config.interpolates[':custom_style'] = -> (style) {
|
121
|
+
# This block is called by the scope in the instance of the Interpolator
|
122
|
+
# You can also call `attachment` and `config` in this block
|
121
123
|
}
|
122
124
|
```
|
123
125
|
|
data/gemfiles/rails_52.gemfile
CHANGED
data/gemfiles/rails_60.gemfile
CHANGED
data/lib/mini_paperclip.rb
CHANGED
@@ -8,6 +8,7 @@ require "active_support/number_helper"
|
|
8
8
|
require "mini_magick"
|
9
9
|
require "mimemagic"
|
10
10
|
require "aws-sdk-s3"
|
11
|
+
require "image_size"
|
11
12
|
|
12
13
|
require "mini_paperclip/attachment"
|
13
14
|
require "mini_paperclip/class_methods"
|
@@ -23,16 +24,17 @@ module MiniPaperclip
|
|
23
24
|
@config ||= Config.new(
|
24
25
|
# defaults
|
25
26
|
interpolates: {
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
':class' => ->(_) { class_result },
|
28
|
+
':attachment' => ->(_) { attachment_result },
|
29
|
+
':hash' => ->(style) { hash_key(style) },
|
30
|
+
':extension' => ->(_) { extension },
|
31
|
+
':id' => ->(_) { @attachment.record.id },
|
32
|
+
':updated_at' => ->(_) { attachment.updated_at&.to_i },
|
33
|
+
':style' => ->(style) { style }
|
33
34
|
},
|
34
|
-
hash_data: ":class/:
|
35
|
+
hash_data: ":class/:attachment/:id/:style/:updated_at",
|
35
36
|
url_missing_path: ":attachment/:style/missing.png",
|
37
|
+
keep_old_files: false,
|
36
38
|
read_timeout: 60,
|
37
39
|
logger: Logger.new($stdout),
|
38
40
|
)
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module MiniPaperclip
|
4
4
|
class Attachment
|
5
|
-
|
5
|
+
UnsupportedError = Class.new(StandardError)
|
6
6
|
|
7
7
|
attr_reader :record, :attachment_name, :config, :storage,
|
8
8
|
:waiting_write_file, :meta_content_type
|
@@ -15,7 +15,7 @@ module MiniPaperclip
|
|
15
15
|
@meta_content_type = nil
|
16
16
|
@dirty = false
|
17
17
|
@storage = Storage.const_get(@config.storage.to_s.camelcase)
|
18
|
-
.new(
|
18
|
+
.new(self, @config)
|
19
19
|
end
|
20
20
|
|
21
21
|
def original_filename
|
@@ -31,7 +31,7 @@ module MiniPaperclip
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def updated_at
|
34
|
-
@record.read_attribute("#{@attachment_name}_updated_at")
|
34
|
+
@record.read_attribute("#{@attachment_name}_updated_at")
|
35
35
|
end
|
36
36
|
|
37
37
|
def file?
|
@@ -61,94 +61,51 @@ module MiniPaperclip
|
|
61
61
|
@waiting_write_file = nil
|
62
62
|
@meta_content_type = nil
|
63
63
|
|
64
|
+
if present?
|
65
|
+
push_delete_files
|
66
|
+
end
|
67
|
+
|
64
68
|
if file.nil?
|
65
|
-
|
66
|
-
@record.write_attribute("#{@attachment_name}_file_name", nil)
|
67
|
-
@record.write_attribute("#{@attachment_name}_content_type", nil)
|
68
|
-
@record.write_attribute("#{@attachment_name}_file_size", nil)
|
69
|
-
@record.write_attribute("#{@attachment_name}_updated_at", nil)
|
69
|
+
assign_nil
|
70
70
|
elsif file.instance_of?(Attachment)
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
@waiting_copy_attachment = file
|
71
|
+
if file.present?
|
72
|
+
assign_attachment(file)
|
73
|
+
else
|
74
|
+
assign_nil
|
75
|
+
end
|
77
76
|
elsif file.respond_to?(:original_filename)
|
78
|
-
|
79
|
-
@record.write_attribute("#{@attachment_name}_file_name", file.original_filename)
|
80
|
-
@record.write_attribute("#{@attachment_name}_content_type", strict_content_type(file.to_io))
|
81
|
-
@record.write_attribute("#{@attachment_name}_file_size", file.size)
|
82
|
-
@record.write_attribute("#{@attachment_name}_updated_at", Time.current)
|
83
|
-
@waiting_write_file = build_tempfile(file.tap(&:rewind))
|
84
|
-
@meta_content_type = file.content_type
|
77
|
+
assign_uploaded_file(file)
|
85
78
|
elsif file.respond_to?(:path)
|
86
|
-
|
87
|
-
@record.write_attribute("#{@attachment_name}_file_name", File.basename(file.path))
|
88
|
-
@record.write_attribute("#{@attachment_name}_content_type", strict_content_type(file))
|
89
|
-
@record.write_attribute("#{@attachment_name}_file_size", file.size)
|
90
|
-
@record.write_attribute("#{@attachment_name}_updated_at", Time.current)
|
91
|
-
@waiting_write_file = build_tempfile(file.tap(&:rewind))
|
79
|
+
assign_file(file)
|
92
80
|
elsif file.instance_of?(String)
|
93
81
|
if file.empty?
|
94
82
|
# do nothing
|
95
83
|
elsif file.start_with?('http')
|
96
|
-
|
97
|
-
open_uri_option = {
|
98
|
-
read_timeout: MiniPaperclip.config.read_timeout || 60
|
99
|
-
}
|
100
|
-
uri = URI.parse(file)
|
101
|
-
uri.open(open_uri_option) do |io|
|
102
|
-
@record.write_attribute("#{@attachment_name}_file_name", File.basename(uri.path))
|
103
|
-
@record.write_attribute("#{@attachment_name}_content_type", strict_content_type(io))
|
104
|
-
@record.write_attribute("#{@attachment_name}_file_size", io.size)
|
105
|
-
@record.write_attribute("#{@attachment_name}_updated_at", Time.current)
|
106
|
-
@waiting_write_file = build_tempfile(io.tap(&:rewind))
|
107
|
-
@meta_content_type = io.meta["content-type"]
|
108
|
-
end
|
84
|
+
assign_http(file)
|
109
85
|
elsif file.start_with?('data:')
|
110
|
-
|
111
|
-
match_data = file.match(/\Adata:([-\w]+\/[-\w\+\.]+)?;base64,(.*)/m)
|
112
|
-
if match_data.nil?
|
113
|
-
raise UnsupporedError, "attachment for \"#{file[0..100]}\" is not supported"
|
114
|
-
end
|
115
|
-
raw = Base64.decode64(match_data[2])
|
116
|
-
@record.write_attribute("#{@attachment_name}_file_name", nil)
|
117
|
-
@record.write_attribute("#{@attachment_name}_content_type", strict_content_type(StringIO.new(raw)))
|
118
|
-
@record.write_attribute("#{@attachment_name}_file_size", raw.bytesize)
|
119
|
-
@record.write_attribute("#{@attachment_name}_updated_at", Time.current)
|
120
|
-
@waiting_write_file = build_tempfile(StringIO.new(raw))
|
121
|
-
@meta_content_type = match_data[1]
|
86
|
+
assign_data_uri(file)
|
122
87
|
else
|
123
|
-
raise
|
88
|
+
raise UnsupportedError, "attachment for \"#{file[0..100]}\" is not supported"
|
124
89
|
end
|
125
90
|
else
|
126
|
-
raise
|
91
|
+
raise UnsupportedError, "attachment for #{file.class} is not supported"
|
127
92
|
end
|
128
93
|
end
|
129
94
|
|
130
95
|
def process_and_store
|
131
96
|
return unless file?
|
132
|
-
|
133
|
-
if @waiting_copy_attachment
|
134
|
-
debug("start attachment copy")
|
135
|
-
@storage.copy(:original, @waiting_copy_attachment)
|
136
|
-
@config.styles&.each do |style, size_arg|
|
137
|
-
@storage.copy(style, @waiting_copy_attachment)
|
138
|
-
end
|
139
|
-
@waiting_copy_attachment = nil
|
140
|
-
return
|
141
|
-
end
|
142
|
-
|
143
|
-
return if @waiting_write_file.nil?
|
97
|
+
return unless @waiting_write_file
|
144
98
|
|
145
99
|
begin
|
146
100
|
debug("start attachment styles process")
|
147
101
|
@storage.write(:original, @waiting_write_file)
|
148
102
|
@config.styles&.each do |style, size_arg|
|
149
103
|
Tempfile.create([style.to_s, File.extname(@waiting_write_file.path)]) do |temp|
|
104
|
+
temp.binmode
|
150
105
|
MiniMagick::Tool::Convert.new do |convert|
|
151
106
|
convert << @waiting_write_file.path
|
107
|
+
convert.coalesce if animated?
|
108
|
+
convert.auto_orient
|
152
109
|
if size_arg.end_with?('#')
|
153
110
|
# crop option
|
154
111
|
convert.resize("#{size_arg[0..-2]}^")
|
@@ -157,20 +114,114 @@ module MiniPaperclip
|
|
157
114
|
else
|
158
115
|
convert.resize(size_arg)
|
159
116
|
end
|
117
|
+
convert.layers("optimize") if animated?
|
160
118
|
convert << temp.path
|
161
119
|
end
|
162
120
|
@storage.write(style, temp)
|
163
121
|
end
|
164
122
|
end
|
123
|
+
|
124
|
+
# should delete after write for copy
|
125
|
+
if !@config.keep_old_files
|
126
|
+
do_delete_files
|
127
|
+
end
|
128
|
+
|
165
129
|
ensure
|
166
|
-
@waiting_write_file.close!
|
130
|
+
if @waiting_write_file.respond_to?(:close!)
|
131
|
+
@waiting_write_file.close!
|
132
|
+
elsif @waiting_write_file.respond_to?(:close)
|
133
|
+
@waiting_write_file.close
|
134
|
+
end
|
167
135
|
end
|
168
136
|
@waiting_write_file = nil
|
169
137
|
end
|
170
138
|
|
139
|
+
def push_delete_files
|
140
|
+
@storage.push_delete_file(:original)
|
141
|
+
@config.styles&.each_key do |style|
|
142
|
+
@storage.push_delete_file(style)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def do_delete_files
|
147
|
+
@storage.do_delete_files
|
148
|
+
end
|
149
|
+
|
150
|
+
def animated?
|
151
|
+
content_type == 'image/gif'
|
152
|
+
end
|
153
|
+
|
171
154
|
private
|
172
155
|
|
156
|
+
def assign_nil
|
157
|
+
# clear
|
158
|
+
@record.write_attribute("#{@attachment_name}_file_name", nil)
|
159
|
+
@record.write_attribute("#{@attachment_name}_content_type", nil)
|
160
|
+
@record.write_attribute("#{@attachment_name}_file_size", nil)
|
161
|
+
@record.write_attribute("#{@attachment_name}_updated_at", nil)
|
162
|
+
end
|
163
|
+
|
164
|
+
def assign_attachment(attachment)
|
165
|
+
# copy
|
166
|
+
@waiting_write_file = attachment.storage.open(:original)
|
167
|
+
@record.write_attribute("#{@attachment_name}_file_name", attachment.original_filename)
|
168
|
+
@record.write_attribute("#{@attachment_name}_content_type", attachment.content_type)
|
169
|
+
@record.write_attribute("#{@attachment_name}_file_size", attachment.size)
|
170
|
+
@record.write_attribute("#{@attachment_name}_updated_at", Time.current)
|
171
|
+
end
|
172
|
+
|
173
|
+
def assign_uploaded_file(file)
|
174
|
+
# e.g. ActionDispatch::Http::UploadedFile
|
175
|
+
@record.write_attribute("#{@attachment_name}_file_name", file.original_filename)
|
176
|
+
@record.write_attribute("#{@attachment_name}_content_type", strict_content_type(file.to_io))
|
177
|
+
@record.write_attribute("#{@attachment_name}_file_size", file.size)
|
178
|
+
@record.write_attribute("#{@attachment_name}_updated_at", Time.current)
|
179
|
+
@waiting_write_file = build_tempfile(file.tap(&:rewind))
|
180
|
+
@meta_content_type = file.content_type
|
181
|
+
end
|
182
|
+
|
183
|
+
def assign_file(file)
|
184
|
+
# e.g. File
|
185
|
+
@record.write_attribute("#{@attachment_name}_file_name", File.basename(file.path))
|
186
|
+
@record.write_attribute("#{@attachment_name}_content_type", strict_content_type(file))
|
187
|
+
@record.write_attribute("#{@attachment_name}_file_size", file.size)
|
188
|
+
@record.write_attribute("#{@attachment_name}_updated_at", Time.current)
|
189
|
+
@waiting_write_file = build_tempfile(file.tap(&:rewind))
|
190
|
+
end
|
191
|
+
|
192
|
+
def assign_http(url)
|
193
|
+
# download from url
|
194
|
+
open_uri_option = {
|
195
|
+
read_timeout: MiniPaperclip.config.read_timeout || 60
|
196
|
+
}
|
197
|
+
uri = URI.parse(url)
|
198
|
+
uri.open(open_uri_option) do |io|
|
199
|
+
@record.write_attribute("#{@attachment_name}_file_name", File.basename(uri.path))
|
200
|
+
@record.write_attribute("#{@attachment_name}_content_type", strict_content_type(io))
|
201
|
+
@record.write_attribute("#{@attachment_name}_file_size", io.size)
|
202
|
+
@record.write_attribute("#{@attachment_name}_updated_at", Time.current)
|
203
|
+
@waiting_write_file = build_tempfile(io.tap(&:rewind))
|
204
|
+
@meta_content_type = io.meta["content-type"]
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def assign_data_uri(data_uri)
|
209
|
+
# data-uri
|
210
|
+
match_data = data_uri.match(/\Adata:([-\w]+\/[-\w\+\.]+)?;base64,(.*)/m)
|
211
|
+
if match_data.nil?
|
212
|
+
raise UnsupportedError, "attachment for \"#{data_uri[0..100]}\" is not supported"
|
213
|
+
end
|
214
|
+
raw = Base64.decode64(match_data[2])
|
215
|
+
@record.write_attribute("#{@attachment_name}_file_name", nil)
|
216
|
+
@record.write_attribute("#{@attachment_name}_content_type", strict_content_type(StringIO.new(raw)))
|
217
|
+
@record.write_attribute("#{@attachment_name}_file_size", raw.bytesize)
|
218
|
+
@record.write_attribute("#{@attachment_name}_updated_at", Time.current)
|
219
|
+
@waiting_write_file = build_tempfile(StringIO.new(raw))
|
220
|
+
@meta_content_type = match_data[1]
|
221
|
+
end
|
222
|
+
|
173
223
|
def strict_content_type(io)
|
224
|
+
io.rewind
|
174
225
|
MimeMagic.by_magic(io)&.type
|
175
226
|
end
|
176
227
|
|
@@ -184,7 +235,7 @@ module MiniPaperclip
|
|
184
235
|
end
|
185
236
|
|
186
237
|
def debug(str)
|
187
|
-
MiniPaperclip.config.logger.debug(str)
|
238
|
+
MiniPaperclip.config.logger.debug("[mini_paperclip] #{str}")
|
188
239
|
end
|
189
240
|
end
|
190
241
|
end
|
@@ -8,7 +8,7 @@ module MiniPaperclip
|
|
8
8
|
instance_variable_set("@#{attachment_name}", Attachment.new(self, attachment_name, option_config))
|
9
9
|
end
|
10
10
|
define_method("#{attachment_name}=") do |file|
|
11
|
-
a =
|
11
|
+
a = public_send(attachment_name)
|
12
12
|
a.assign(file)
|
13
13
|
instance_variable_set("@#{attachment_name}", a)
|
14
14
|
end
|
@@ -19,6 +19,12 @@ module MiniPaperclip
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
22
|
+
before_destroy do
|
23
|
+
public_send(attachment_name).push_delete_files
|
24
|
+
end
|
25
|
+
after_commit(on: :destroy) do
|
26
|
+
public_send(attachment_name).do_delete_files
|
27
|
+
end
|
22
28
|
validates_with Validators::MediaTypeSpoofValidator, {
|
23
29
|
attributes: attachment_name,
|
24
30
|
if: -> { instance_variable_get("@#{attachment_name}")&.dirty? }
|
@@ -16,6 +16,7 @@ module MiniPaperclip
|
|
16
16
|
:s3_acl,
|
17
17
|
:s3_cache_control,
|
18
18
|
:interpolates,
|
19
|
+
:keep_old_files,
|
19
20
|
:read_timeout,
|
20
21
|
:logger,
|
21
22
|
keyword_init: true,
|
@@ -25,7 +26,7 @@ module MiniPaperclip
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def merge!(hash)
|
28
|
-
to_h.deep_merge(hash).each { |k, v| self[k] = v }
|
29
|
+
to_h.deep_merge(hash.to_h).each { |k, v| self[k] = v }
|
29
30
|
self
|
30
31
|
end
|
31
32
|
end
|
@@ -2,16 +2,17 @@
|
|
2
2
|
|
3
3
|
module MiniPaperclip
|
4
4
|
class Interpolator
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
attr_reader :attachment, :config
|
6
|
+
|
7
|
+
def initialize(attachment, config)
|
8
|
+
@attachment = attachment
|
8
9
|
@config = config
|
9
10
|
end
|
10
11
|
|
11
12
|
def interpolate(template, style)
|
12
13
|
template.dup.tap do |t|
|
13
14
|
@config.interpolates&.each do |matcher, block|
|
14
|
-
t.gsub!(matcher) { instance_exec(
|
15
|
+
t.gsub!(matcher) { instance_exec(style, &block) }
|
15
16
|
end
|
16
17
|
end
|
17
18
|
end
|
@@ -19,23 +20,28 @@ module MiniPaperclip
|
|
19
20
|
private
|
20
21
|
|
21
22
|
def class_result
|
22
|
-
@record.class.name.underscore.pluralize
|
23
|
+
@attachment.record.class.name.underscore.pluralize
|
23
24
|
end
|
24
25
|
|
25
26
|
def attachment_result
|
26
|
-
@attachment_name.to_s.downcase.pluralize
|
27
|
+
@attachment.attachment_name.to_s.downcase.pluralize
|
27
28
|
end
|
28
29
|
|
29
|
-
def
|
30
|
-
|
30
|
+
def extension
|
31
|
+
attachment.original_filename &&
|
32
|
+
File.extname(attachment.original_filename)[1..-1]
|
31
33
|
end
|
32
34
|
|
33
35
|
def hash_key(style)
|
34
36
|
OpenSSL::HMAC.hexdigest(
|
35
37
|
OpenSSL::Digest::SHA1.new,
|
36
38
|
@config.hash_secret,
|
37
|
-
|
39
|
+
interpolated_hash_data(style),
|
38
40
|
)
|
39
41
|
end
|
42
|
+
|
43
|
+
def interpolated_hash_data(style)
|
44
|
+
interpolate(@config.hash_data, style)
|
45
|
+
end
|
40
46
|
end
|
41
47
|
end
|
@@ -96,6 +96,7 @@ module MiniPaperclip
|
|
96
96
|
|
97
97
|
def create_dummy_image(width:, height:)
|
98
98
|
Tempfile.create(['MiniPaperclip::Shoulda::Matchers::ValidateAttachmentGeometryMatcher', ".#{@format}"]) do |f|
|
99
|
+
f.binmode
|
99
100
|
MiniMagick::Tool::Convert.new do |convert|
|
100
101
|
convert.size("#{width}x#{height}")
|
101
102
|
convert.xc("none")
|
@@ -3,13 +3,13 @@
|
|
3
3
|
module MiniPaperclip
|
4
4
|
module Storage
|
5
5
|
class Base
|
6
|
-
attr_reader :config
|
6
|
+
attr_reader :attachment, :config, :interpolator
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@
|
10
|
-
@attachment_name = attachment_name
|
8
|
+
def initialize(attachment, config)
|
9
|
+
@attachment = attachment
|
11
10
|
@config = config
|
12
|
-
@interpolator = Interpolator.new(
|
11
|
+
@interpolator = Interpolator.new(attachment, config)
|
12
|
+
@deletes = []
|
13
13
|
end
|
14
14
|
|
15
15
|
def url_for_read(style)
|
@@ -17,10 +17,10 @@ module MiniPaperclip
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def path_for(style)
|
20
|
-
template = if @
|
21
|
-
@config.url_path
|
22
|
-
else
|
20
|
+
template = if @attachment.original_filename.nil?
|
23
21
|
@config.url_missing_path
|
22
|
+
else
|
23
|
+
@config.url_path
|
24
24
|
end
|
25
25
|
interpolate(template, style)
|
26
26
|
end
|
@@ -32,7 +32,7 @@ module MiniPaperclip
|
|
32
32
|
private
|
33
33
|
|
34
34
|
def debug(str)
|
35
|
-
MiniPaperclip.config.logger.debug(str)
|
35
|
+
MiniPaperclip.config.logger.debug("[mini_paperclip] #{str}")
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -5,17 +5,10 @@ module MiniPaperclip
|
|
5
5
|
class Filesystem < Base
|
6
6
|
def write(style, file)
|
7
7
|
path = file_path(style)
|
8
|
-
debug("writing by filesystem to
|
8
|
+
debug("writing by filesystem from:#{file.path} to:#{path}")
|
9
9
|
FileUtils.mkdir_p(File.dirname(path))
|
10
|
-
FileUtils.cp(file.path, path)
|
11
|
-
|
12
|
-
|
13
|
-
def copy(style, from_attachment)
|
14
|
-
raise "not supported" unless from_attachment.storage.instance_of?(Filesystem)
|
15
|
-
to_path = file_path(style)
|
16
|
-
from_path = from_attachment.storage.file_path(style)
|
17
|
-
debug("copying by filesystem from:#{from_path} to:#{to_path}")
|
18
|
-
FileUtils.cp(from_path, to_path)
|
10
|
+
FileUtils.cp(file.path, path) if file.path != path
|
11
|
+
@deletes.delete(path) # cancel deletion if overwrite
|
19
12
|
end
|
20
13
|
|
21
14
|
def file_path(style)
|
@@ -29,6 +22,20 @@ module MiniPaperclip
|
|
29
22
|
def exists?(style)
|
30
23
|
File.exists?(file_path(style))
|
31
24
|
end
|
25
|
+
|
26
|
+
def push_delete_file(style)
|
27
|
+
@deletes.push(file_path(style))
|
28
|
+
end
|
29
|
+
|
30
|
+
def do_delete_files
|
31
|
+
return if @deletes.empty?
|
32
|
+
debug("deleting by filesystem #{@deletes}")
|
33
|
+
FileUtils.rm_f(@deletes)
|
34
|
+
end
|
35
|
+
|
36
|
+
def open(style, &block)
|
37
|
+
File.open(file_path(style), 'r', &block)
|
38
|
+
end
|
32
39
|
end
|
33
40
|
end
|
34
41
|
end
|
@@ -8,24 +8,12 @@ module MiniPaperclip
|
|
8
8
|
Aws::S3::Client.new.put_object(
|
9
9
|
acl: @config.s3_acl,
|
10
10
|
cache_control: @config.s3_cache_control,
|
11
|
-
content_type: @
|
11
|
+
content_type: @attachment.content_type,
|
12
12
|
body: file.tap(&:rewind),
|
13
13
|
bucket: @config.s3_bucket_name,
|
14
14
|
key: s3_object_key(style),
|
15
15
|
)
|
16
|
-
|
17
|
-
|
18
|
-
def copy(style, from_attachment)
|
19
|
-
raise "not supported yet" unless from_attachment.storage.instance_of?(S3)
|
20
|
-
debug("copying by S3 to bucket:#{@config.s3_bucket_name},key:#{s3_object_key(style)}")
|
21
|
-
Aws::S3::Client.new.copy_object(
|
22
|
-
acl: @config.s3_acl,
|
23
|
-
cache_control: @config.s3_cache_control,
|
24
|
-
content_type: @record.read_attribute("#{@attachment_name}_content_type"),
|
25
|
-
copy_source: from_attachment.storage.object_key(style),
|
26
|
-
bucket: @config.s3_bucket_name,
|
27
|
-
key: s3_object_key(style),
|
28
|
-
)
|
16
|
+
@deletes.delete({ key: s3_object_key(style) }) # cancel deletion if overwrite
|
29
17
|
end
|
30
18
|
|
31
19
|
def s3_object_key(style)
|
@@ -38,11 +26,41 @@ module MiniPaperclip
|
|
38
26
|
end
|
39
27
|
|
40
28
|
def exists?(style)
|
41
|
-
|
29
|
+
Aws::S3::Client.new.head_object(
|
42
30
|
bucket: @config.s3_bucket_name,
|
43
31
|
key: s3_object_key(style),
|
44
32
|
)
|
45
|
-
|
33
|
+
true
|
34
|
+
rescue Aws::S3::Errors::NotFound
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def push_delete_file(style)
|
39
|
+
@deletes.push({ key: s3_object_key(style) })
|
40
|
+
end
|
41
|
+
|
42
|
+
def do_delete_files
|
43
|
+
return if @deletes.empty?
|
44
|
+
debug("deleting by S3 to bucket:#{@config.s3_bucket_name},objects:#{@deletes}")
|
45
|
+
Aws::S3::Client.new.delete_objects(
|
46
|
+
bucket: @config.s3_bucket_name,
|
47
|
+
delete: {
|
48
|
+
objects: @deletes,
|
49
|
+
quiet: true,
|
50
|
+
}
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
def open(style)
|
55
|
+
Tempfile.new(['MiniPaperclip::Storage::S3']).tap do |response_target|
|
56
|
+
response_target.binmode
|
57
|
+
Aws::S3::Client.new.get_object(
|
58
|
+
bucket: @config.s3_bucket_name,
|
59
|
+
key: s3_object_key(style),
|
60
|
+
response_target: response_target,
|
61
|
+
)
|
62
|
+
yield response_target if block_given?
|
63
|
+
end
|
46
64
|
end
|
47
65
|
end
|
48
66
|
end
|
@@ -10,8 +10,8 @@ module MiniPaperclip
|
|
10
10
|
if check_value = options[:less_than]
|
11
11
|
unless attachment_file_size < check_value
|
12
12
|
count = ActiveSupport::NumberHelper.number_to_human_size(check_value)
|
13
|
-
record.errors.add(attribute, :less_than,
|
14
|
-
record.errors.add(attachment_file_size_name, :less_than,
|
13
|
+
record.errors.add(attribute, :less_than, count: count)
|
14
|
+
record.errors.add(attachment_file_size_name, :less_than, count: count)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -16,29 +16,33 @@ module MiniPaperclip
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
19
20
|
# validate_attachment :image,
|
20
21
|
# geometry: {
|
21
22
|
# width: { less_than_or_equal_to: 3000 },
|
22
23
|
# height: { less_than_or_equal_to: 3000 } }
|
23
24
|
def validate_each(record, attribute, value)
|
24
25
|
return unless value.waiting_write_file
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
value.waiting_write_file.rewind
|
27
|
+
image_size = ImageSize.new(value.waiting_write_file)
|
28
|
+
# invalid format should not relate geometry
|
29
|
+
unless image_size.format
|
30
|
+
MiniPaperclip.config.logger.info("[mini_paperclip] cannot get image_size from #{value.waiting_write_file.inspect}")
|
31
|
+
return
|
28
32
|
end
|
29
|
-
return unless !geometry_string.empty?
|
30
|
-
width, height = geometry_string.split(',').map(&:to_i)
|
31
33
|
|
32
34
|
expected_width_less_than_or_equal_to = options.dig(:width, :less_than_or_equal_to)
|
33
35
|
expected_height_less_than_or_equal_to = options.dig(:height, :less_than_or_equal_to)
|
34
|
-
unless (!expected_width_less_than_or_equal_to || width <= expected_width_less_than_or_equal_to) &&
|
35
|
-
(!expected_height_less_than_or_equal_to || height <= expected_height_less_than_or_equal_to)
|
36
|
-
record.errors.add(
|
37
|
-
|
38
|
-
|
36
|
+
unless (!expected_width_less_than_or_equal_to || image_size.width <= expected_width_less_than_or_equal_to) &&
|
37
|
+
(!expected_height_less_than_or_equal_to || image_size.height <= expected_height_less_than_or_equal_to)
|
38
|
+
record.errors.add(
|
39
|
+
attribute,
|
40
|
+
:geometry,
|
41
|
+
actual_width: image_size.width,
|
42
|
+
actual_height: image_size.height,
|
39
43
|
expected_width_less_than_or_equal_to: expected_width_less_than_or_equal_to,
|
40
44
|
expected_height_less_than_or_equal_to: expected_height_less_than_or_equal_to,
|
41
|
-
|
45
|
+
)
|
42
46
|
end
|
43
47
|
end
|
44
48
|
end
|
data/mini_paperclip.gemspec
CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.add_runtime_dependency "activemodel"
|
18
18
|
spec.add_runtime_dependency "activesupport"
|
19
19
|
spec.add_runtime_dependency "aws-sdk-s3"
|
20
|
+
spec.add_runtime_dependency "image_size"
|
20
21
|
|
21
22
|
spec.metadata["homepage_uri"] = spec.homepage
|
22
23
|
spec.metadata["source_code_uri"] = spec.homepage
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini_paperclip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ksss
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mini_magick
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: image_size
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
83
97
|
description: Subset from paperclip because paperclip has deprecated
|
84
98
|
email:
|
85
99
|
- co000ri@gmail.com
|
@@ -87,7 +101,7 @@ executables: []
|
|
87
101
|
extensions: []
|
88
102
|
extra_rdoc_files: []
|
89
103
|
files:
|
90
|
-
- ".github/workflows/
|
104
|
+
- ".github/workflows/ci.yml"
|
91
105
|
- ".gitignore"
|
92
106
|
- ".rspec"
|
93
107
|
- CODE_OF_CONDUCT.md
|
@@ -97,10 +111,10 @@ files:
|
|
97
111
|
- Rakefile
|
98
112
|
- bin/console
|
99
113
|
- bin/setup
|
114
|
+
- gemfiles/development.gemfile
|
100
115
|
- gemfiles/rails_52.gemfile
|
101
|
-
- gemfiles/rails_52.gemfile.lock
|
102
116
|
- gemfiles/rails_60.gemfile
|
103
|
-
- gemfiles/
|
117
|
+
- gemfiles/rails_head.gemfile
|
104
118
|
- lib/mini_paperclip.rb
|
105
119
|
- lib/mini_paperclip/attachment.rb
|
106
120
|
- lib/mini_paperclip/class_methods.rb
|
@@ -145,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
159
|
- !ruby/object:Gem::Version
|
146
160
|
version: '0'
|
147
161
|
requirements: []
|
148
|
-
rubygems_version: 3.1.
|
162
|
+
rubygems_version: 3.1.4
|
149
163
|
signing_key:
|
150
164
|
specification_version: 4
|
151
165
|
summary: MiniPaperclip is a mini paperclip
|
@@ -1,98 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: ..
|
3
|
-
specs:
|
4
|
-
mini_paperclip (0.1.0)
|
5
|
-
activemodel
|
6
|
-
activesupport
|
7
|
-
aws-sdk-s3
|
8
|
-
mimemagic
|
9
|
-
mini_magick
|
10
|
-
|
11
|
-
GEM
|
12
|
-
remote: https://rubygems.org/
|
13
|
-
specs:
|
14
|
-
activemodel (5.2.4.4)
|
15
|
-
activesupport (= 5.2.4.4)
|
16
|
-
activerecord (5.2.4.4)
|
17
|
-
activemodel (= 5.2.4.4)
|
18
|
-
activesupport (= 5.2.4.4)
|
19
|
-
arel (>= 9.0)
|
20
|
-
activesupport (5.2.4.4)
|
21
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
22
|
-
i18n (>= 0.7, < 2)
|
23
|
-
minitest (~> 5.1)
|
24
|
-
tzinfo (~> 1.1)
|
25
|
-
addressable (2.7.0)
|
26
|
-
public_suffix (>= 2.0.2, < 5.0)
|
27
|
-
arel (9.0.0)
|
28
|
-
aws-eventstream (1.1.0)
|
29
|
-
aws-partitions (1.399.0)
|
30
|
-
aws-sdk-core (3.109.3)
|
31
|
-
aws-eventstream (~> 1, >= 1.0.2)
|
32
|
-
aws-partitions (~> 1, >= 1.239.0)
|
33
|
-
aws-sigv4 (~> 1.1)
|
34
|
-
jmespath (~> 1.0)
|
35
|
-
aws-sdk-kms (1.39.0)
|
36
|
-
aws-sdk-core (~> 3, >= 3.109.0)
|
37
|
-
aws-sigv4 (~> 1.1)
|
38
|
-
aws-sdk-s3 (1.85.0)
|
39
|
-
aws-sdk-core (~> 3, >= 3.109.0)
|
40
|
-
aws-sdk-kms (~> 1)
|
41
|
-
aws-sigv4 (~> 1.1)
|
42
|
-
aws-sigv4 (1.2.2)
|
43
|
-
aws-eventstream (~> 1, >= 1.0.2)
|
44
|
-
concurrent-ruby (1.1.7)
|
45
|
-
crack (0.4.4)
|
46
|
-
diff-lcs (1.4.4)
|
47
|
-
hashdiff (1.0.1)
|
48
|
-
i18n (1.8.5)
|
49
|
-
concurrent-ruby (~> 1.0)
|
50
|
-
jmespath (1.4.0)
|
51
|
-
mimemagic (0.3.5)
|
52
|
-
mini_magick (4.11.0)
|
53
|
-
minitest (5.14.2)
|
54
|
-
public_suffix (4.0.6)
|
55
|
-
rack (2.2.3)
|
56
|
-
rack-test (1.1.0)
|
57
|
-
rack (>= 1.0, < 3)
|
58
|
-
rake (12.3.3)
|
59
|
-
rspec (3.10.0)
|
60
|
-
rspec-core (~> 3.10.0)
|
61
|
-
rspec-expectations (~> 3.10.0)
|
62
|
-
rspec-mocks (~> 3.10.0)
|
63
|
-
rspec-core (3.10.0)
|
64
|
-
rspec-support (~> 3.10.0)
|
65
|
-
rspec-expectations (3.10.0)
|
66
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
67
|
-
rspec-support (~> 3.10.0)
|
68
|
-
rspec-mocks (3.10.0)
|
69
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
70
|
-
rspec-support (~> 3.10.0)
|
71
|
-
rspec-support (3.10.0)
|
72
|
-
sqlite3 (1.4.2)
|
73
|
-
tapp (1.5.1)
|
74
|
-
thor
|
75
|
-
thor (1.0.1)
|
76
|
-
thread_safe (0.3.6)
|
77
|
-
tzinfo (1.2.8)
|
78
|
-
thread_safe (~> 0.1)
|
79
|
-
webmock (3.10.0)
|
80
|
-
addressable (>= 2.3.6)
|
81
|
-
crack (>= 0.3.2)
|
82
|
-
hashdiff (>= 0.4.0, < 2.0.0)
|
83
|
-
|
84
|
-
PLATFORMS
|
85
|
-
ruby
|
86
|
-
|
87
|
-
DEPENDENCIES
|
88
|
-
activerecord (~> 5.2.0)
|
89
|
-
mini_paperclip!
|
90
|
-
rack-test
|
91
|
-
rake (~> 12.0)
|
92
|
-
rspec (~> 3.0)
|
93
|
-
sqlite3
|
94
|
-
tapp
|
95
|
-
webmock
|
96
|
-
|
97
|
-
BUNDLED WITH
|
98
|
-
2.1.4
|
@@ -1,98 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: ..
|
3
|
-
specs:
|
4
|
-
mini_paperclip (0.1.0)
|
5
|
-
activemodel
|
6
|
-
activesupport
|
7
|
-
aws-sdk-s3
|
8
|
-
mimemagic
|
9
|
-
mini_magick
|
10
|
-
|
11
|
-
GEM
|
12
|
-
remote: https://rubygems.org/
|
13
|
-
specs:
|
14
|
-
activemodel (6.0.3.4)
|
15
|
-
activesupport (= 6.0.3.4)
|
16
|
-
activerecord (6.0.3.4)
|
17
|
-
activemodel (= 6.0.3.4)
|
18
|
-
activesupport (= 6.0.3.4)
|
19
|
-
activesupport (6.0.3.4)
|
20
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
21
|
-
i18n (>= 0.7, < 2)
|
22
|
-
minitest (~> 5.1)
|
23
|
-
tzinfo (~> 1.1)
|
24
|
-
zeitwerk (~> 2.2, >= 2.2.2)
|
25
|
-
addressable (2.7.0)
|
26
|
-
public_suffix (>= 2.0.2, < 5.0)
|
27
|
-
aws-eventstream (1.1.0)
|
28
|
-
aws-partitions (1.399.0)
|
29
|
-
aws-sdk-core (3.109.3)
|
30
|
-
aws-eventstream (~> 1, >= 1.0.2)
|
31
|
-
aws-partitions (~> 1, >= 1.239.0)
|
32
|
-
aws-sigv4 (~> 1.1)
|
33
|
-
jmespath (~> 1.0)
|
34
|
-
aws-sdk-kms (1.39.0)
|
35
|
-
aws-sdk-core (~> 3, >= 3.109.0)
|
36
|
-
aws-sigv4 (~> 1.1)
|
37
|
-
aws-sdk-s3 (1.85.0)
|
38
|
-
aws-sdk-core (~> 3, >= 3.109.0)
|
39
|
-
aws-sdk-kms (~> 1)
|
40
|
-
aws-sigv4 (~> 1.1)
|
41
|
-
aws-sigv4 (1.2.2)
|
42
|
-
aws-eventstream (~> 1, >= 1.0.2)
|
43
|
-
concurrent-ruby (1.1.7)
|
44
|
-
crack (0.4.4)
|
45
|
-
diff-lcs (1.4.4)
|
46
|
-
hashdiff (1.0.1)
|
47
|
-
i18n (1.8.5)
|
48
|
-
concurrent-ruby (~> 1.0)
|
49
|
-
jmespath (1.4.0)
|
50
|
-
mimemagic (0.3.5)
|
51
|
-
mini_magick (4.11.0)
|
52
|
-
minitest (5.14.2)
|
53
|
-
public_suffix (4.0.6)
|
54
|
-
rack (2.2.3)
|
55
|
-
rack-test (1.1.0)
|
56
|
-
rack (>= 1.0, < 3)
|
57
|
-
rake (12.3.3)
|
58
|
-
rspec (3.10.0)
|
59
|
-
rspec-core (~> 3.10.0)
|
60
|
-
rspec-expectations (~> 3.10.0)
|
61
|
-
rspec-mocks (~> 3.10.0)
|
62
|
-
rspec-core (3.10.0)
|
63
|
-
rspec-support (~> 3.10.0)
|
64
|
-
rspec-expectations (3.10.0)
|
65
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
66
|
-
rspec-support (~> 3.10.0)
|
67
|
-
rspec-mocks (3.10.0)
|
68
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
69
|
-
rspec-support (~> 3.10.0)
|
70
|
-
rspec-support (3.10.0)
|
71
|
-
sqlite3 (1.4.2)
|
72
|
-
tapp (1.5.1)
|
73
|
-
thor
|
74
|
-
thor (1.0.1)
|
75
|
-
thread_safe (0.3.6)
|
76
|
-
tzinfo (1.2.8)
|
77
|
-
thread_safe (~> 0.1)
|
78
|
-
webmock (3.10.0)
|
79
|
-
addressable (>= 2.3.6)
|
80
|
-
crack (>= 0.3.2)
|
81
|
-
hashdiff (>= 0.4.0, < 2.0.0)
|
82
|
-
zeitwerk (2.4.1)
|
83
|
-
|
84
|
-
PLATFORMS
|
85
|
-
ruby
|
86
|
-
|
87
|
-
DEPENDENCIES
|
88
|
-
activerecord (~> 6.0.0)
|
89
|
-
mini_paperclip!
|
90
|
-
rack-test
|
91
|
-
rake (~> 12.0)
|
92
|
-
rspec (~> 3.0)
|
93
|
-
sqlite3
|
94
|
-
tapp
|
95
|
-
webmock
|
96
|
-
|
97
|
-
BUNDLED WITH
|
98
|
-
2.1.4
|