dragonfly 0.7.7 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of dragonfly might be problematic. Click here for more details.

Files changed (67) hide show
  1. data/Gemfile +1 -1
  2. data/Gemfile.rails.2.3.5 +0 -1
  3. data/History.md +12 -0
  4. data/README.md +4 -2
  5. data/VERSION +1 -1
  6. data/config.ru +1 -1
  7. data/dragonfly.gemspec +256 -179
  8. data/extra_docs/Analysers.md +15 -6
  9. data/extra_docs/Configuration.md +13 -2
  10. data/extra_docs/Encoding.md +20 -7
  11. data/extra_docs/GeneralUsage.md +8 -5
  12. data/extra_docs/Generators.md +17 -7
  13. data/extra_docs/Heroku.md +1 -2
  14. data/extra_docs/MimeTypes.md +1 -1
  15. data/extra_docs/Models.md +1 -1
  16. data/extra_docs/Mongo.md +2 -2
  17. data/extra_docs/Processing.md +15 -7
  18. data/extra_docs/Rack.md +2 -3
  19. data/extra_docs/Rails2.md +2 -3
  20. data/extra_docs/Rails3.md +2 -3
  21. data/extra_docs/Sinatra.md +2 -2
  22. data/extra_docs/URLs.md +6 -4
  23. data/features/3.0.3.feature +8 -0
  24. data/features/steps/rails_steps.rb +2 -2
  25. data/features/support/env.rb +1 -1
  26. data/fixtures/files/app/views/albums/new.html.erb +4 -4
  27. data/fixtures/rails_2.3.5/template.rb +0 -1
  28. data/fixtures/{rails_3.0.0 → rails_3.0.3}/template.rb +0 -1
  29. data/irbrc.rb +1 -1
  30. data/lib/dragonfly/analysis/image_magick_analyser.rb +47 -0
  31. data/lib/dragonfly/app.rb +2 -0
  32. data/lib/dragonfly/config/image_magick.rb +41 -0
  33. data/lib/dragonfly/data_storage/file_data_store.rb +4 -2
  34. data/lib/dragonfly/data_storage/s3data_store.rb +7 -3
  35. data/lib/dragonfly/encoding/image_magick_encoder.rb +57 -0
  36. data/lib/dragonfly/generation/hash_with_css_style_keys.rb +23 -0
  37. data/lib/dragonfly/generation/image_magick_generator.rb +140 -0
  38. data/lib/dragonfly/generation/r_magick_generator.rb +0 -18
  39. data/lib/dragonfly/image_magick_utils.rb +81 -0
  40. data/lib/dragonfly/processing/image_magick_processor.rb +99 -0
  41. data/lib/dragonfly/rails/images.rb +1 -1
  42. data/lib/dragonfly/temp_object.rb +7 -6
  43. data/spec/dragonfly/analysis/image_magick_analyser_spec.rb +15 -0
  44. data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +5 -49
  45. data/spec/dragonfly/analysis/shared_analyser_spec.rb +51 -0
  46. data/spec/dragonfly/app_spec.rb +2 -0
  47. data/spec/dragonfly/data_storage/data_store_spec.rb +6 -0
  48. data/spec/dragonfly/data_storage/file_data_store_spec.rb +1 -1
  49. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +11 -1
  50. data/spec/dragonfly/deprecation_spec.rb +2 -2
  51. data/spec/dragonfly/encoding/image_magick_encoder_spec.rb +41 -0
  52. data/spec/dragonfly/encoding/r_magick_encoder_spec.rb +3 -6
  53. data/spec/dragonfly/generation/hash_with_css_style_keys_spec.rb +24 -0
  54. data/spec/dragonfly/generation/image_magick_generator_spec.rb +12 -0
  55. data/spec/dragonfly/generation/r_magick_generator_spec.rb +12 -123
  56. data/spec/dragonfly/generation/shared_generator_spec.rb +91 -0
  57. data/spec/dragonfly/image_magick_utils_spec.rb +16 -0
  58. data/spec/dragonfly/processing/image_magick_processor_spec.rb +29 -0
  59. data/spec/dragonfly/processing/r_magick_processor_spec.rb +2 -212
  60. data/spec/dragonfly/processing/shared_processing_spec.rb +215 -0
  61. data/spec/image_matchers.rb +6 -0
  62. data/spec/spec_helper.rb +11 -0
  63. data/yard/templates/default/fulldoc/html/css/common.css +9 -2
  64. data/yard/templates/default/layout/html/layout.erb +12 -1
  65. metadata +310 -11
  66. data/.gitignore +0 -15
  67. data/features/rails_3.0.0.feature +0 -8
@@ -0,0 +1,99 @@
1
+ module Dragonfly
2
+ module Processing
3
+ class ImageMagickProcessor
4
+
5
+ GRAVITIES = {
6
+ 'nw' => 'NorthWest',
7
+ 'n' => 'North',
8
+ 'ne' => 'NorthEast',
9
+ 'w' => 'West',
10
+ 'c' => 'Center',
11
+ 'e' => 'East',
12
+ 'sw' => 'SouthWest',
13
+ 's' => 'South',
14
+ 'se' => 'SouthEast'
15
+ }
16
+
17
+ # Geometry string patterns
18
+ RESIZE_GEOMETRY = /^\d*x\d*[><%^!]?$|^\d+@$/ # e.g. '300x200!'
19
+ CROPPED_RESIZE_GEOMETRY = /^(\d+)x(\d+)#(\w{1,2})?$/ # e.g. '20x50#ne'
20
+ CROP_GEOMETRY = /^(\d+)x(\d+)([+-]\d+)?([+-]\d+)?(\w{1,2})?$/ # e.g. '30x30+10+10'
21
+ THUMB_GEOMETRY = Regexp.union RESIZE_GEOMETRY, CROPPED_RESIZE_GEOMETRY, CROP_GEOMETRY
22
+
23
+ include ImageMagickUtils
24
+
25
+ def resize(temp_object, geometry)
26
+ convert(temp_object, "-resize '#{geometry}'")
27
+ end
28
+
29
+ def crop(temp_object, opts={})
30
+ width = opts[:width]
31
+ height = opts[:height]
32
+ gravity = GRAVITIES[opts[:gravity]]
33
+ x = "#{opts[:x] || 0}"
34
+ x = '+' + x unless x[/^[+-]/]
35
+ y = "#{opts[:y] || 0}"
36
+ y = '+' + y unless y[/^[+-]/]
37
+
38
+ convert(temp_object, "-crop #{width}x#{height}#{x}#{y}#{" -gravity #{gravity}" if gravity}")
39
+ end
40
+
41
+ def flip(temp_object)
42
+ convert(temp_object, "-flip")
43
+ end
44
+
45
+ def flop(temp_object)
46
+ convert(temp_object, "-flop")
47
+ end
48
+
49
+ def greyscale(temp_object)
50
+ convert(temp_object, "-colorspace Gray")
51
+ end
52
+ alias grayscale greyscale
53
+
54
+ def resize_and_crop(temp_object, opts={})
55
+ attrs = identify(temp_object)
56
+ current_width = attrs[:width].to_i
57
+ current_height = attrs[:height].to_i
58
+
59
+ width = opts[:width] ? opts[:width].to_i : current_width
60
+ height = opts[:height] ? opts[:height].to_i : current_height
61
+ gravity = opts[:gravity] || 'c'
62
+
63
+ if width != current_width || height != current_height
64
+ scale = [width.to_f / current_width, height.to_f / current_height].max
65
+ temp_object = TempObject.new(resize(temp_object, "#{(scale * current_width).ceil}x#{(scale * current_height).ceil}"))
66
+ end
67
+
68
+ crop(temp_object, :width => width, :height => height, :gravity => gravity)
69
+ end
70
+
71
+ def rotate(temp_object, amount, opts={})
72
+ convert(temp_object, "-rotate '#{amount}#{opts[:qualifier]}'")
73
+ end
74
+
75
+ def thumb(temp_object, geometry)
76
+ case geometry
77
+ when RESIZE_GEOMETRY
78
+ resize(temp_object, geometry)
79
+ when CROPPED_RESIZE_GEOMETRY
80
+ resize_and_crop(temp_object, :width => $1, :height => $2, :gravity => $3)
81
+ when CROP_GEOMETRY
82
+ crop(temp_object,
83
+ :width => $1,
84
+ :height => $2,
85
+ :x => $3,
86
+ :y => $4,
87
+ :gravity => $5
88
+ )
89
+ else raise ArgumentError, "Didn't recognise the geometry string #{geometry}"
90
+ end
91
+ end
92
+
93
+ def convert(temp_object, args='', format=nil)
94
+ format ? [super, {:format => format.to_sym}] : super
95
+ end
96
+
97
+ end
98
+ end
99
+ end
@@ -5,7 +5,7 @@ require 'uri'
5
5
  ### The dragonfly app ###
6
6
  app = Dragonfly[:images]
7
7
  app.configure_with(:rails)
8
- app.configure_with(:rmagick)
8
+ app.configure_with(:imagemagick)
9
9
 
10
10
  ### Extend active record ###
11
11
  app.define_macro(ActiveRecord::Base, :image_accessor)
@@ -163,20 +163,21 @@ module Dragonfly
163
163
  private
164
164
 
165
165
  def initialize_from_object!(obj)
166
- case obj
167
- when TempObject
166
+ if obj.is_a? TempObject
168
167
  @initialized_data = obj.initialized_data
169
168
  @initialized_tempfile = copy_to_tempfile(obj.initialized_tempfile.path) if obj.initialized_tempfile
170
169
  @initialized_file = obj.initialized_file
171
- when String
170
+ elsif obj.is_a? String
172
171
  @initialized_data = obj
173
- when Tempfile
172
+ elsif obj.is_a? Tempfile
174
173
  @initialized_tempfile = obj
175
- when File
174
+ elsif obj.is_a? File
176
175
  @initialized_file = obj
177
176
  self.name = File.basename(obj.path)
177
+ elsif obj.respond_to?(:tempfile)
178
+ @initialized_tempfile = obj.tempfile
178
179
  else
179
- raise ArgumentError, "#{self.class.name} must be initialized with a String, a File, a Tempfile, or another TempObject"
180
+ raise ArgumentError, "#{self.class.name} must be initialized with a String, a File, a Tempfile, another TempObject, or something that responds to .tempfile"
180
181
  end
181
182
  self.name = obj.original_filename if obj.respond_to?(:original_filename)
182
183
  end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ require 'dragonfly/analysis/shared_analyser_spec'
3
+
4
+ describe Dragonfly::Analysis::ImageMagickAnalyser do
5
+
6
+ before(:each) do
7
+ image_path = File.dirname(__FILE__) + '/../../../samples/beach.png'
8
+ @image = Dragonfly::TempObject.new(File.new(image_path))
9
+ @analyser = Dragonfly::Analysis::ImageMagickAnalyser.new
10
+ @analyser.log = Logger.new(LOG_FILE)
11
+ end
12
+
13
+ it_should_behave_like "image analyser methods"
14
+
15
+ end
@@ -1,71 +1,27 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require 'spec_helper'
2
+ require 'dragonfly/analysis/shared_analyser_spec'
2
3
 
3
4
  describe Dragonfly::Analysis::RMagickAnalyser do
4
5
 
5
6
  before(:each) do
6
7
  image_path = File.dirname(__FILE__) + '/../../../samples/beach.png'
7
- @beach = Dragonfly::TempObject.new(File.new(image_path))
8
+ @image = Dragonfly::TempObject.new(File.new(image_path))
8
9
  @analyser = Dragonfly::Analysis::RMagickAnalyser.new
9
10
  @analyser.log = Logger.new(LOG_FILE)
10
11
  end
11
-
12
- describe "analysis methods", :shared => true do
13
-
14
- it "should return the width" do
15
- @analyser.width(@beach).should == 280
16
- end
17
-
18
- it "should return the height" do
19
- @analyser.height(@beach).should == 355
20
- end
21
-
22
- it "should return the aspect ratio" do
23
- @analyser.aspect_ratio(@beach).should == (280.0/355.0)
24
- end
25
-
26
- it "should say if it's portrait" do
27
- @analyser.portrait?(@beach).should be_true
28
- end
29
-
30
- it "should say if it's landscape" do
31
- @analyser.landscape?(@beach).should be_false
32
- end
33
-
34
- it "should return the number of colours" do
35
- @analyser.number_of_colours(@beach).should == 34703
36
- end
37
-
38
- it "should return the depth" do
39
- @analyser.depth(@beach).should == 8
40
- end
41
-
42
- it "should return the format" do
43
- @analyser.format(@beach).should == :png
44
- end
45
-
46
- end
47
12
 
48
13
  describe "when using the filesystem" do
49
14
  before(:each) do
50
15
  @analyser.use_filesystem = true
51
16
  end
52
- it_should_behave_like "analysis methods"
17
+ it_should_behave_like "image analyser methods"
53
18
  end
54
19
 
55
20
  describe "when not using the filesystem" do
56
21
  before(:each) do
57
22
  @analyser.use_filesystem = false
58
23
  end
59
- it_should_behave_like "analysis methods"
60
- end
61
-
62
- %w(width height aspect_ratio number_of_colours depth format portrait? landscape?).each do |meth|
63
- it "should throw unable_to_handle in #{meth.inspect} if it's not an image file" do
64
- temp_object = Dragonfly::TempObject.new('blah')
65
- lambda{
66
- @analyser.send(meth, temp_object)
67
- }.should throw_symbol(:unable_to_handle)
68
- end
24
+ it_should_behave_like "image analyser methods"
69
25
  end
70
26
 
71
27
  end
@@ -0,0 +1,51 @@
1
+ # NEEDS:
2
+ #
3
+ # @image
4
+ # @processor
5
+ #
6
+ describe "image analyser methods", :shared => true do
7
+
8
+ it "should return the width" do
9
+ @analyser.width(@image).should == 280
10
+ end
11
+
12
+ it "should return the height" do
13
+ @analyser.height(@image).should == 355
14
+ end
15
+
16
+ it "should return the aspect ratio" do
17
+ @analyser.aspect_ratio(@image).should == (280.0/355.0)
18
+ end
19
+
20
+ it "should say if it's portrait" do
21
+ @analyser.portrait?(@image).should be_true
22
+ end
23
+
24
+ it "should say if it's landscape" do
25
+ @analyser.landscape?(@image).should be_false
26
+ end
27
+
28
+ it "should return the number of colours" do
29
+ @analyser.number_of_colours(@image).should == 34703
30
+ end
31
+
32
+ it "should return the depth" do
33
+ @analyser.depth(@image).should == 8
34
+ end
35
+
36
+ it "should return the format" do
37
+ @analyser.format(@image).should == :png
38
+ end
39
+
40
+ %w(width height aspect_ratio number_of_colours depth format portrait? landscape?).each do |meth|
41
+ it "should throw unable_to_handle in #{meth.inspect} if it's not an image file" do
42
+ suppressing_stderr do
43
+ temp_object = Dragonfly::TempObject.new('blah')
44
+ lambda{
45
+ @analyser.send(meth, temp_object)
46
+ }.should throw_symbol(:unable_to_handle)
47
+ end
48
+ end
49
+ end
50
+
51
+ end
@@ -235,6 +235,8 @@ describe Dragonfly::App do
235
235
  end
236
236
 
237
237
  {
238
+ :imagemagick => Dragonfly::Config::ImageMagick,
239
+ :image_magick => Dragonfly::Config::ImageMagick,
238
240
  :rmagick => Dragonfly::Config::RMagick,
239
241
  :r_magick => Dragonfly::Config::RMagick,
240
242
  :rails => Dragonfly::Config::Rails,
@@ -13,6 +13,12 @@ describe "data_store", :shared => true do
13
13
  temp_object2 = Dragonfly::TempObject.new('gollum')
14
14
  @data_store.store(@temp_object).should_not == @data_store.store(temp_object2)
15
15
  end
16
+ it "should return a unique identifier for each storage even when the first is deleted" do
17
+ uid1 = @data_store.store(@temp_object)
18
+ @data_store.destroy(uid1)
19
+ uid2 = @data_store.store(@temp_object)
20
+ uid1.should_not == uid2
21
+ end
16
22
  it "should allow for passing in options as a second argument" do
17
23
  @data_store.store(@temp_object, :some => :option)
18
24
  end
@@ -33,7 +33,7 @@ describe Dragonfly::DataStorage::FileDataStore do
33
33
  before(:each) do
34
34
  # Set 'now' to a date in the past
35
35
  Time.stub!(:now).and_return Time.mktime(1984,"may",4,14,28,1)
36
- @file_pattern_prefix_without_root = '1984/05/04/'
36
+ @file_pattern_prefix_without_root = '1984/05/04/14_28_01_0_'
37
37
  @file_pattern_prefix = "#{@data_store.root_path}/#{@file_pattern_prefix_without_root}"
38
38
  end
39
39
 
@@ -4,7 +4,10 @@ require 'yaml'
4
4
 
5
5
  describe Dragonfly::DataStorage::S3DataStore do
6
6
 
7
- # Change this to test it with an actual internet connection
7
+ # To run these tests, put a file ".s3_spec.yml" in the dragonfly root dir, like this:
8
+ # key: XXXXXXXXXX
9
+ # secret: XXXXXXXXXX
10
+ # enabled: true
8
11
  if File.exist?(file = File.expand_path('../../../../.s3_spec.yml', __FILE__))
9
12
  config = YAML.load_file(file)
10
13
  KEY = config['key']
@@ -53,6 +56,13 @@ describe Dragonfly::DataStorage::S3DataStore do
53
56
  data, extra = @data_store.retrieve(uid)
54
57
  data.should == 'eggheads'
55
58
  end
59
+
60
+ it "should work fine when not using the filesystem" do
61
+ @data_store.use_filesystem = false
62
+ temp_object = Dragonfly::TempObject.new('gollum')
63
+ uid = @data_store.store(temp_object)
64
+ @data_store.retrieve(uid).should == ["gollum", {:meta=>{}, :format=>nil, :name=>nil}]
65
+ end
56
66
  end
57
67
 
58
68
  end
@@ -5,8 +5,8 @@ describe "Deprecated stuff" do
5
5
  describe "job urls" do
6
6
 
7
7
  before(:each) do
8
- @app = test_app.configure_with(:rmagick) do |c|
9
- c.log = Logger.new($stdout)
8
+ @app = test_app.configure_with(:imagemagick) do |c|
9
+ # c.log = Logger.new($stdout)
10
10
  end
11
11
  @job = @app.fetch('eggs')
12
12
  end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dragonfly::Encoding::ImageMagickEncoder do
4
+
5
+ before(:all) do
6
+ sample_file = File.dirname(__FILE__) + '/../../../samples/beach.png' # 280x355, 135KB
7
+ @image = Dragonfly::TempObject.new(File.new(sample_file))
8
+ @encoder = Dragonfly::Encoding::ImageMagickEncoder.new
9
+ end
10
+
11
+ describe "#encode" do
12
+
13
+ it "should encode the image to the correct format" do
14
+ image = @encoder.encode(@image, :gif)
15
+ image.should have_format('gif')
16
+ end
17
+
18
+ it "should throw :unable_to_handle if the format is not handleable" do
19
+ lambda{
20
+ @encoder.encode(@image, :goofy)
21
+ }.should throw_symbol(:unable_to_handle)
22
+ end
23
+
24
+ it "should do nothing if the image is already in the correct format" do
25
+ image = @encoder.encode(@image, :png)
26
+ image.should == @image
27
+ end
28
+
29
+ it "should allow for extra args" do
30
+ image = @encoder.encode(@image, :jpg, '-quality 1')
31
+ image.should have_format('jpeg')
32
+ image.should have_size('1.45KB')
33
+ end
34
+
35
+ it "should still work even if the image is already in the correct format and args are given" do
36
+ image = @encoder.encode(@image, :png, '-quality 1')
37
+ image.should_not == @image
38
+ end
39
+ end
40
+
41
+ end
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe Dragonfly::Encoding::RMagickEncoder do
4
4
 
@@ -16,12 +16,9 @@ describe Dragonfly::Encoding::RMagickEncoder do
16
16
  end
17
17
 
18
18
  it "should throw :unable_to_handle if the format is not handleable" do
19
- test_string = "I'm a string"
20
- catch :unable_to_handle do
19
+ lambda{
21
20
  @encoder.encode(@image, :goofy)
22
- test_string = "This line should not happen"
23
- end
24
- test_string.should == "I'm a string"
21
+ }.should throw_symbol(:unable_to_handle)
25
22
  end
26
23
 
27
24
  it "should do nothing if the image is already in the correct format" do
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dragonfly::Generation::HashWithCssStyleKeys do
4
+
5
+ before(:each) do
6
+ @hash = Dragonfly::Generation::HashWithCssStyleKeys[
7
+ :font_style => 'normal',
8
+ :'font-weight' => 'bold',
9
+ 'font_colour' => 'white',
10
+ 'font-size' => 23,
11
+ :hello => 'there'
12
+ ]
13
+ end
14
+
15
+ describe "accessing using underscore symbol style" do
16
+ it{ @hash[:font_style].should == 'normal' }
17
+ it{ @hash[:font_weight].should == 'bold' }
18
+ it{ @hash[:font_colour].should == 'white' }
19
+ it{ @hash[:font_size].should == 23 }
20
+ it{ @hash[:hello].should == 'there' }
21
+ it{ @hash[:non_existent_key].should be_nil }
22
+ end
23
+
24
+ end