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 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