mini_paperclip 0.1.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 934c31a21e7ff961a58d0d22023b0d17095b83087a22845b30e85102bba1667b
4
- data.tar.gz: 003a3f115224cf645d81fe98dfd1664ea3386aa48f7436791a1bf6b14c0b864a
3
+ metadata.gz: 6d115f886793d57d62835f40e27768ff04588d3e1f17e7fb430b11f2b146e2ed
4
+ data.tar.gz: 6d1557f279f9bf007b90d06272e7e19a1829f38b1c0f6a4ce079c06e410fca58
5
5
  SHA512:
6
- metadata.gz: 60d2cb3ddfcff65b7edcf3519d7752f5a5aafb03bdcf15ba487b6c423af234feb13224b609d5bba1cd3bba3939f70f9aaa8b7a9209ecdef8e414e829a946ea4a
7
- data.tar.gz: d16ef4a7638188af8963b87ab41c0045cf1a73547558b0d9864ba7efa713f30e86164c81ae8cb19ecc998c1b250dafa99d2c5556cf27e80eddf8cd53d8a867a4
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
@@ -8,6 +8,7 @@
8
8
  /spec/temp/
9
9
  /tmp/
10
10
  Gemfile.lock
11
+ /gemfiles/*.gemfile.lock
11
12
 
12
13
  # rspec failure tracking
13
14
  .rspec_status
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
- gem "sqlite3"
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/:attribute/:id/:hash.:extension`
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[/:custom_style/] = -> (attachment, style) {
120
- "-#{style}"
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
 
@@ -0,0 +1,7 @@
1
+ gem "rake", "~> 12.0"
2
+ gem "rspec", "~> 3.0"
3
+ gem "rack-test"
4
+ gem "webmock"
5
+ gem "tapp"
6
+ gem "sqlite3"
7
+ gem "pretty_backtrace"
@@ -1,10 +1,5 @@
1
1
  source "https://rubygems.org"
2
2
  gemspec path: '../'
3
3
 
4
- gem "rake", "~> 12.0"
5
- gem "rspec", "~> 3.0"
6
- gem "rack-test"
7
- gem "webmock"
8
- gem "tapp"
9
4
  gem 'activerecord', '~> 5.2.0'
10
- gem "sqlite3"
5
+ eval_gemfile 'development.gemfile'
@@ -1,10 +1,5 @@
1
1
  source "https://rubygems.org"
2
2
  gemspec path: '../'
3
3
 
4
- gem "rake", "~> 12.0"
5
- gem "rspec", "~> 3.0"
6
- gem "rack-test"
7
- gem "webmock"
8
- gem "tapp"
9
4
  gem 'activerecord', '~> 6.0.0'
10
- gem "sqlite3"
5
+ eval_gemfile 'development.gemfile'
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+ gemspec path: '../'
3
+
4
+ gem 'activerecord', git: "https://github.com/rails/rails", branch: :main
5
+ eval_gemfile 'development.gemfile'
@@ -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
- /:class/ => ->(*) { class_result },
27
- /:attachment/ => ->(*) { attachment_result },
28
- /:hash/ => ->(_, style) { hash_key(style) },
29
- /:extension/ => ->(*) { File.extname(@record.read_attribute("#{@attachment_name}_file_name"))[1..-1] },
30
- /:id/ => ->(*) { @record.id },
31
- /:updated_at/ => ->(*) { @record.read_attribute("#{@attachment_name}_updated_at").to_i },
32
- /:style/ => ->(_, style) { style }
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/:attribute/:id/:style/:updated_at",
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
- UnsupporedError = Class.new(StandardError)
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(record, attachment_name, @config)
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").to_i
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
- # clear
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
- # copy
72
- @record.write_attribute("#{@attachment_name}_file_name", file.record.read_attribute("#{@attachment_name}_file_name"))
73
- @record.write_attribute("#{@attachment_name}_content_type", file.record.read_attribute("#{@attachment_name}_content_type"))
74
- @record.write_attribute("#{@attachment_name}_file_size", file.record.read_attribute("#{@attachment_name}_file_size"))
75
- @record.write_attribute("#{@attachment_name}_updated_at", Time.current)
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
- # e.g. ActionDispatch::Http::UploadedFile
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
- # e.g. File
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
- # download from url
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
- # data-uri
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 UnsupporedError, "attachment for \"#{file[0..100]}\" is not supported"
88
+ raise UnsupportedError, "attachment for \"#{file[0..100]}\" is not supported"
124
89
  end
125
90
  else
126
- raise UnsupporedError, "attachment for #{file.class} is not supported"
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 = Attachment.new(self, attachment_name, option_config)
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
- def initialize(record, attachment_name, config)
6
- @record = record
7
- @attachment_name = attachment_name
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(attachment, style, &block) }
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 attachment
30
- @record.public_send(@attachment_name)
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
- interpolate(@config.hash_data, style),
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(record, attachment_name, config)
9
- @record = record
10
- @attachment_name = attachment_name
8
+ def initialize(attachment, config)
9
+ @attachment = attachment
11
10
  @config = config
12
- @interpolator = Interpolator.new(record, attachment_name, config)
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 @record.public_send(@attachment_name)&.file?
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 #{path}")
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
- end
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: @record.read_attribute("#{@attachment_name}_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
- end
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
- res = Aws::S3::Client.new.head_object(
29
+ Aws::S3::Client.new.head_object(
42
30
  bucket: @config.s3_bucket_name,
43
31
  key: s3_object_key(style),
44
32
  )
45
- res.content_length.to_i != 0
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, { count: count })
14
- record.errors.add(attachment_file_size_name, :less_than, { count: count })
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
- geometry_string = MiniMagick::Tool::Identify.new do |identify|
26
- identify.format "%w,%h"
27
- identify << value.waiting_write_file.path
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(attribute, :geometry, {
37
- actual_width: width,
38
- actual_height: height,
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniPaperclip
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.1"
5
5
  end
@@ -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.0
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: 2020-11-26 00:00:00.000000000 Z
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/ruby.yml"
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/rails_60.gemfile.lock
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.2
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