attachs 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -3
  3. data/lib/attachs/attachment.rb +6 -0
  4. data/lib/attachs/processors/base.rb +25 -0
  5. data/lib/attachs/processors/thumbnail.rb +69 -0
  6. data/lib/attachs/storages/base.rb +4 -8
  7. data/lib/attachs/storages/local.rb +11 -12
  8. data/lib/attachs/storages/s3.rb +14 -13
  9. data/lib/attachs/version.rb +1 -1
  10. data/lib/attachs.rb +6 -4
  11. data/lib/tasks/attachs.rake +2 -2
  12. data/test/dummy/log/test.log +7308 -0
  13. data/test/dummy/public/big/180x150.gif +0 -0
  14. data/test/dummy/public/medium/180x150.gif +0 -0
  15. data/test/dummy/public/original/180x150.gif +0 -0
  16. data/test/dummy/public/original/file.txt +1 -0
  17. data/test/dummy/public/resized.gif +0 -0
  18. data/test/dummy/public/small/180x150.gif +0 -0
  19. data/test/dummy/public/storage/image/5461/big/11/180x150.gif +0 -0
  20. data/test/dummy/public/storage/image/5461/big/9/180x150.gif +0 -0
  21. data/test/dummy/public/storage/image/5461/medium/11/180x150.gif +0 -0
  22. data/test/dummy/public/storage/image/5461/medium/9/180x150.gif +0 -0
  23. data/test/dummy/public/storage/image/5461/original/11/180x150.gif +0 -0
  24. data/test/dummy/public/storage/image/5461/original/9/180x150.gif +0 -0
  25. data/test/dummy/public/storage/image/5461/small/11/180x150.gif +0 -0
  26. data/test/dummy/public/storage/image/5461/small/9/180x150.gif +0 -0
  27. data/test/dummy/public/storage/text/11/original/11/file.txt +1 -0
  28. data/test/dummy/public/storage/text/11/original/9/file.txt +1 -0
  29. data/test/local_storage_test.rb +15 -15
  30. data/test/processor_test.rb +46 -0
  31. data/test/s3_storage_tes.rb +18 -22
  32. data/test/tasks_test.rb +3 -11
  33. metadata +38 -5
  34. data/lib/attachs/tools/magick.rb +0 -52
  35. data/test/magick_tool_test.rb +0 -42
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 174763ef00e837f637a02dadcf2c6e732304c911
4
- data.tar.gz: 543f20a4cfbc0097daa613ec66bf31459191c260
3
+ metadata.gz: 393185c42c0aec2c323f8d958fa832825ab2b931
4
+ data.tar.gz: 3d7edbd5e15fbd5ee1eba21bbf9563f345d94fb1
5
5
  SHA512:
6
- metadata.gz: 5fc324cf2d3df019d8d37501e9af50eb1169d3e88b6ba11c6be97afdfd22377d3dd8391e03fb170d051f852b1116da68ce195de93d22bab1f98edf5986bee561
7
- data.tar.gz: a57ec77ecc61acebed6512bce22a18c0152fc26b9f488f86daeed88f055e44b4284388644bba3ef7500eeecb7f99469e86f94435a356c627b1f77f99800f4c94
6
+ metadata.gz: 2c2d7d87ff89da949369dad068fde6a325fc386eb64f84d839a7f5e09f2fd35fbec582491e905bca766e5979422cef1e67a43c073f907a19889892ad5400e444
7
+ data.tar.gz: 3696bfd7933332c93cf6747fb1b6600f3e7e1b1a2072be2f625279e1e2a289a9bd0dd523ea0d8785d28483fa5d15b335a54647d016d87210135da8ed9b4fc810
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Gem Version](https://badge.fury.io/rb/attachs.svg)](http://badge.fury.io/rb/attachs) [![Code Climate](https://codeclimate.com/github/museways/attachs/badges/gpa.svg)](https://codeclimate.com/github/museways/attachs) [![Build Status](https://travis-ci.org/museways/attachs.svg?branch=master)](https://travis-ci.org/museways/attachs)
1
+ [![Gem Version](https://badge.fury.io/rb/attachs.svg)](http://badge.fury.io/rb/attachs) [![Code Climate](https://codeclimate.com/github/museways/attachs/badges/gpa.svg)](https://codeclimate.com/github/museways/attachs) [![Build Status](https://travis-ci.org/museways/attachs.svg?branch=master)](https://travis-ci.org/museways/attachs) [![Dependency Status](https://gemnasium.com/museways/attachs.svg)](https://gemnasium.com/museways/attachs)
2
2
 
3
3
  # Attachs
4
4
 
@@ -33,6 +33,7 @@ Attachs.configure do |config|
33
33
  config.global_styles = []
34
34
  config.global_convert_options= ''
35
35
  config.convert_options = {}
36
+ config.default_processors = [:thumbnail]
36
37
  config.default_storage = :local
37
38
  config.default_path = '/:timestamp-:filename'
38
39
  config.base_url = ''
@@ -224,6 +225,37 @@ Attachs.configure do |config|
224
225
  end
225
226
  ```
226
227
 
228
+ ## Processors
229
+
230
+ To create a custom processor:
231
+ ```ruby
232
+ class Attachs::Processors::CustomThumbnail
233
+
234
+ def initialize(attachment, source)
235
+ # Custom initialization
236
+ end
237
+
238
+ def process(style, destination)
239
+ # Custom logic
240
+ end
241
+
242
+ end
243
+ ```
244
+
245
+ To change the processors in the model:
246
+ ```ruby
247
+ class User < ActiveRecord::Base
248
+ has_attached_file :avatar, processors: [:custom_thumbnail]
249
+ end
250
+ ```
251
+
252
+ To change the default processors:
253
+ ```ruby
254
+ Attachs.configure do |config|
255
+ config.default_processors = [:custom_thumbnail]
256
+ end
257
+ ```
258
+
227
259
  ## CDN
228
260
 
229
261
  To configure a cdn:
@@ -237,12 +269,12 @@ end
237
269
 
238
270
  To refresh all the styles of some attachment:
239
271
  ```
240
- bundle exec rake attachs:refresh:all CLASS=User ATTACHMENT=avatar
272
+ bundle exec rake attachs:refresh:all class=user attachment=avatar
241
273
  ```
242
274
 
243
275
  To refresh missing styles of some attachment:
244
276
  ```
245
- bundle exec rake attachs:refresh:missing CLASS=User ATTACHMENT=avatar
277
+ bundle exec rake attachs:refresh:missing class=user attachment=avatar
246
278
  ```
247
279
 
248
280
  ## Credits
@@ -38,6 +38,12 @@ module Attachs
38
38
  @private ||= options[:private] == true
39
39
  end
40
40
 
41
+ def processors
42
+ @processors ||= (options[:processors] || Attachs.config.default_processors).map do |processor|
43
+ "Attachs::Processors::#{processor.to_s.classify}".constantize
44
+ end
45
+ end
46
+
41
47
  def public?
42
48
  !private?
43
49
  end
@@ -0,0 +1,25 @@
1
+ module Attachs
2
+ module Processors
3
+ class Base
4
+
5
+ def initialize(attachment, source)
6
+ @attachment = attachment
7
+ @source = source
8
+ end
9
+
10
+ protected
11
+
12
+ attr_reader :attachment, :source
13
+
14
+ def run(cmd)
15
+ stdout, stderr, status = Open3.capture3(cmd)
16
+ if status.success?
17
+ stdout.strip
18
+ else
19
+ false
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,69 @@
1
+ module Attachs
2
+ module Processors
3
+ class Thumbnail < Base
4
+
5
+ def initialize(attachment, source)
6
+ super
7
+ @width, @height = dimensions(source)
8
+ end
9
+
10
+ def process(style, destination)
11
+ new_width, new_height, strategy, options = geometry(style)
12
+ resize source, width, height, new_width, new_height, strategy, options, destination
13
+ end
14
+
15
+ protected
16
+
17
+ attr_reader :width, :height
18
+
19
+ def geometry(style)
20
+ geometry = Attachs.config.styles[style]
21
+ width, height = geometry.scan(/[^x]+/).map(&:to_i)
22
+ case geometry[/!|#/]
23
+ when '#'
24
+ strategy = 'cover'
25
+ when '!'
26
+ strategy = 'force'
27
+ else
28
+ strategy = 'contain'
29
+ end
30
+ options = Attachs.config.convert_options[style]
31
+ [width, height, strategy, options]
32
+ end
33
+
34
+ def resize(source, width, height, new_width, new_height, strategy, custom_options, destination)
35
+ case strategy
36
+ when 'cover'
37
+ ratio = [new_width.to_f/width, new_height.to_f/height].max
38
+ options = "-resize #{(ratio*width).ceil}x#{(ratio*height).ceil} -gravity center -crop #{new_width}x#{new_height}+0+0"
39
+ when 'force'
40
+ options = "-resize #{new_width}x#{new_height}\!"
41
+ when 'contain'
42
+ options = "-resize #{new_width}x#{new_height}"
43
+ end
44
+ if global_options = Attachs.config.global_convert_options
45
+ options << " #{global_options}"
46
+ end
47
+ if custom_options
48
+ options << " #{custom_options}"
49
+ end
50
+ convert source, options, destination
51
+ end
52
+
53
+ def dimensions(source)
54
+ if output = identify(source, '-format %wx%h')
55
+ output.split('x').map(&:to_i)
56
+ end
57
+ end
58
+
59
+ def convert(source, options, destination)
60
+ run "convert '#{source}' #{options} '#{destination}'"
61
+ end
62
+
63
+ def identify(source, options)
64
+ run "identify #{options} '#{source}'"
65
+ end
66
+
67
+ end
68
+ end
69
+ end
@@ -10,12 +10,8 @@ module Attachs
10
10
 
11
11
  attr_reader :attachment
12
12
 
13
- def resize(*args)
14
- Attachs::Tools::Magick.resize(*args)
15
- end
16
-
17
13
  def template
18
- @template = begin
14
+ @template ||= begin
19
15
  if attachment.exists?
20
16
  (attachment.options[:path] || Attachs.config.default_path).dup
21
17
  else
@@ -52,13 +48,13 @@ module Attachs
52
48
  when :type
53
49
  attachment.content_type.split('/').first.parameterize
54
50
  when :timestamp
55
- (attachment.updated_at.to_f * 10000000000).to_i
51
+ attachment.updated_at.to_i
56
52
  when :class
57
53
  attachment.record.class.name.parameterize
58
54
  when :id
59
55
  attachment.record.id
60
- when :param
61
- attachment.record.to_param
56
+ when :attribute
57
+ attachment.attribute.to_s.parameterize
62
58
  end.to_s
63
59
  end
64
60
  end
@@ -4,7 +4,7 @@ module Attachs
4
4
 
5
5
  def url(style=:original)
6
6
  if attachment.url?
7
- base_url.join(path(style)).to_s
7
+ "#{base_url.join(path(style))}?#{attachment.updated_at.to_i}"
8
8
  end
9
9
  end
10
10
 
@@ -24,13 +24,16 @@ module Attachs
24
24
 
25
25
  def process_styles(force=false)
26
26
  if attachment.image?
27
- attachment.styles.each do |style|
28
- if force == true
29
- delete realpath(style)
30
- end
31
- unless File.exist? realpath(style)
32
- FileUtils.mkdir_p realpath(style).dirname
33
- resize realpath, style, realpath(style)
27
+ attachment.processors.each do |klass|
28
+ processor = klass.new(attachment, realpath)
29
+ attachment.styles.each do |style|
30
+ if force == true
31
+ delete realpath(style)
32
+ end
33
+ unless File.exist? realpath(style)
34
+ FileUtils.mkdir_p realpath(style).dirname
35
+ processor.process style, realpath(style)
36
+ end
34
37
  end
35
38
  end
36
39
  end
@@ -51,10 +54,6 @@ module Attachs
51
54
 
52
55
  protected
53
56
 
54
- def move(origin, destination)
55
- FileUtils.mv base_path.join(origin), base_path.join(destination)
56
- end
57
-
58
57
  def delete(realpath)
59
58
  if File.exist? realpath
60
59
  File.delete realpath
@@ -7,7 +7,7 @@ module Attachs
7
7
  options = args.extract_options!
8
8
  style = (args[0] || :original)
9
9
  if Attachs.config.base_url.present?
10
- Pathname.new(Attachs.config.base_url, path(style)).to_s
10
+ Pathname.new(Attachs.config.base_url).join(path(style)).to_s
11
11
  else
12
12
  if options[:ssl].present?
13
13
  secure = options[:ssl]
@@ -17,6 +17,8 @@ module Attachs
17
17
  secure = Attachs.config.s3[:ssl]
18
18
  end
19
19
  object(style).public_url(secure: secure).to_s
20
+ end.tap do |url|
21
+ url << "?#{attachment.updated_at.to_i}"
20
22
  end
21
23
  end
22
24
  end
@@ -38,14 +40,17 @@ module Attachs
38
40
  end
39
41
  cache[path] = download
40
42
  end
41
- attachment.styles.each do |style|
42
- if force == true
43
- object(style).delete
44
- end
45
- unless object(style).exists?
46
- tmp = Tempfile.new('s3')
47
- resize cache[path].path, style, tmp.path
48
- stream tmp, path(style)
43
+ attachment.processors.each do |klass|
44
+ processor = klass.new(attachment, cache[path].path)
45
+ attachment.styles.each do |style|
46
+ if force == true
47
+ object(style).delete
48
+ end
49
+ unless object(style).exists?
50
+ tmp = Tempfile.new('s3')
51
+ processor.process style, tmp.path
52
+ stream tmp, path(style)
53
+ end
49
54
  end
50
55
  end
51
56
  end
@@ -66,10 +71,6 @@ module Attachs
66
71
 
67
72
  protected
68
73
 
69
- def move(origin, destination)
70
- bucket.objects[origin].move_to(destination)
71
- end
72
-
73
74
  def cache
74
75
  @cache ||= {}
75
76
  end
@@ -1,5 +1,5 @@
1
1
  module Attachs
2
2
 
3
- VERSION = '0.4.0'
3
+ VERSION = '0.4.1'
4
4
 
5
5
  end
data/lib/attachs.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'open3'
2
2
  require 'attachs/attachment'
3
- require 'attachs/tools/magick'
3
+ require 'attachs/processors/base'
4
+ require 'attachs/processors/thumbnail'
4
5
  require 'attachs/storages/base'
5
6
  require 'attachs/storages/local'
6
7
  require 'attachs/storages/s3'
@@ -26,15 +27,16 @@ module Attachs
26
27
  def config
27
28
  @config ||= begin
28
29
  ActiveSupport::OrderedOptions.new.tap do |config|
30
+ config.s3 = { ssl: false }
31
+ config.base_url = ''
29
32
  config.styles = {}
30
33
  config.interpolations = {}
34
+ config.convert_options = {}
31
35
  config.global_styles = []
32
36
  config.global_convert_options= ''
33
- config.convert_options = {}
34
37
  config.default_storage = :local
38
+ config.default_processors = [:thumbnail]
35
39
  config.default_path = '/:timestamp-:filename'
36
- config.base_url = ''
37
- config.s3 = { ssl: false }
38
40
  end
39
41
  end
40
42
  end
@@ -1,8 +1,8 @@
1
1
  module Attachs
2
2
  module Task
3
3
  def self.process(force)
4
- model = ENV['CLASS'].classify.constantize
5
- attachment = ENV['ATTACHMENT'].to_sym
4
+ model = (ENV['class'] || ENV['CLASS']).classify.constantize
5
+ attachment = (ENV['attachment'] || ENV['ATTACHMENT']).to_sym
6
6
  model.find_each do |record|
7
7
  model.attachments.each do |attr, options|
8
8
  if attr == attachment