attachs 0.4.0 → 0.4.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.
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