papercrop 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Zjk4MGM0YzdiN2Q3NjNjNGEwN2UzMmI3OGExOWU3YzA5NjNlYzcyYQ==
5
+ data.tar.gz: !binary |-
6
+ OTU0MDE4ODM4YmNkNjQ5MTIxMDFiOWJhOGZjMzM0MzE3ZTBmNmUwZQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YjRjOTM4MDZkYThjZTNlZGUwMTJmMzQxYjFlMGYxNzdjYzg2MGY3YTEzMDY1
10
+ OGI5MTY0NTI0ZDA5NzEzMTZlNTdhYWUzMjU3MDE1YmE2YzVjNDYxZjViZTJi
11
+ ODZmNTY2MWY1OTcwYTBmM2FhOTc0NDA3ZjJlYmU4YzAxN2UzNzA=
12
+ data.tar.gz: !binary |-
13
+ M2YzNmQ1NjUwMjEyMDY2MTIxMWIyYjQ0Yzk3NGM4ZTg2NmNkNjM4Mjk5ODkz
14
+ ZjM3NGQ4ZTAzMWU2MjFkZGE5ZGQ4OTBlMTg4MGJiMmFjYjJhZGY1NWE4NWQ3
15
+ MDNhNDEzZmQ1YmU3YTRhNDgwYmFmMzc2NzE1NmMzMmMyYzYwYmY=
@@ -0,0 +1,10 @@
1
+
2
+ appraise "rails_3_2" do
3
+ gem "rails", "3.2.13"
4
+ gem "paperclip", "3.4.0"
5
+ end
6
+
7
+ appraise "rails_4" do
8
+ gem "rails", "4.0.2"
9
+ gem "paperclip", "4.1.0"
10
+ end
data/README.md CHANGED
@@ -50,13 +50,37 @@ Regardless of the width, the preview box and the cropping area will have the asp
50
50
 
51
51
  If you're rendering it on ajax ensure to call init_papercrop() in js after loading the crop form to make things work properly.
52
52
 
53
+ ### Advanced features
54
+
55
+ **Unlock aspect ratio**
56
+
57
+ You can unlock the aspect ratio if you pass false as argument. NOTE: *preview will be disabled*
58
+
59
+ crop_attached_file :snapshot, :aspect => false
60
+
61
+ Regardless the model, you can always redefine/unlock aspect from the helper if you need to.
62
+
63
+ f.cropbox :snapshot, :width => 500, :aspect => 4..3
64
+
65
+ **Chaining processors**
66
+
67
+ Maybe you want to chain some custom processors to do amazing effects like crop+rotate images. Papercrop will add its processor in last place unless you declare it in the attachment definition
68
+
69
+ has_attached_file :landscape, :styles => {:big => '2000x1500'},
70
+ :processors => [:papercrop, :rotator]
71
+
53
72
  ### Running the Tests
54
73
 
55
- We are using a dummy application to handle some of our test cases. You can find this in the `/test_app` directory and should be able to run this as a regular Rails 4 app _(using the `rails s` command)_ if you're interested in taking a look. You may need to create the mock database for the `test_app` before your tests will start to pass. This means you need to run `rake db:create db:migrate db:test:prepare` from within the `test_app` directory.
74
+ We are using dummy applications to handle some of our test cases with different Gemfiles using [Appraisal](https://github.com/thoughtbot/appraisal). You can find them in the `/test_apps` directory and should be able to run them as a regular Rails app _(using the `rails s` command)_ if you're interested in taking a look. You may need to create mock databases for the `test_apps` before your tests will start to pass. This means you need to run the classics `rake db:create db:migrate db:test:prepare` through appraisal from the root directory.
75
+
76
+ appraisal rails_3_2 rake db:create db:migrate
77
+ appraisal rails_4 rake db:create db:migrate
78
+
79
+ Append RAILS_ENV=test to both commands to prepare each database for testing
56
80
 
57
81
  In order to fully test our gem, we needed to use [Selenium](http://docs.seleniumhq.org/download/). Getting this setup is beyond the scope of this Readme.
58
82
 
59
- Once you have everything setup, you should be able `bundle exec rake` from the root directory have everything run. If you've installed Selenium properly, you should see an automated instance of your browser _(eg. Firefox)_ pop up and run through some of the integration tests.
83
+ Once you have everything setup, you should be able `appraisal rake` from the root directory have everything run. If you've installed Selenium properly, you should see an automated instance of your browser _(eg. Firefox)_ pop up and run through some of the integration tests.
60
84
 
61
85
  That's all!
62
86
 
@@ -9,10 +9,14 @@
9
9
  var aspect = $("input#" + attachment + "_aspect").val();
10
10
  var width = $(this).width();
11
11
 
12
+ if (aspect === 'false') {
13
+ aspect = false
14
+ }
15
+
12
16
  update_crop = function(coords) {
13
17
  var preview_width, rx, ry;
14
18
 
15
- if (preview) {
19
+ if (preview && aspect) {
16
20
  preview_width = $("#" + attachment + "_crop_preview_wrapper").width();
17
21
 
18
22
  rx = preview_width / coords.w;
@@ -36,7 +40,7 @@
36
40
  onChange : update_crop,
37
41
  onSelect : update_crop,
38
42
  setSelect : [0, 0, 250, 250],
39
- aspectRatio : aspect,
43
+ aspectRatio : aspect === false ? undefined : aspect,
40
44
  boxWidth : $("input[id$='_" + attachment + "_box_w']").val()
41
45
  }, function() {
42
46
  jcrop_api = this;
@@ -0,0 +1,33 @@
1
+ require "paperclip"
2
+
3
+ module Paperclip
4
+ class Papercrop < Thumbnail
5
+
6
+ def transformation_command
7
+ if crop_command
8
+ crop_command + super.join(' ').sub(/ -crop \S+/, '').split(' ')
9
+ else
10
+ super
11
+ end
12
+ end
13
+
14
+
15
+ def crop_command
16
+ target = @attachment.instance
17
+
18
+ if target.cropping?(@attachment.name)
19
+ begin
20
+ w = Integer(target.send :"#{@attachment.name}_crop_w")
21
+ h = Integer(target.send :"#{@attachment.name}_crop_h")
22
+ x = Integer(target.send :"#{@attachment.name}_crop_x")
23
+ y = Integer(target.send :"#{@attachment.name}_crop_y")
24
+ ["-crop", "#{w}x#{h}+#{x}+#{y}"]
25
+ rescue Exception => e
26
+ ::Papercrop.log("[papercrop] #{@attachment.name} crop w/h/x/y were non-integer. Error: #{e.to_s}")
27
+ return
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -2,4 +2,5 @@ require 'papercrop/engine'
2
2
  require 'papercrop/reg_exp'
3
3
  require 'papercrop/model_extension'
4
4
  require 'papercrop/helpers'
5
- require 'paperclip_processors/cropper'
5
+ require 'papercrop/logger'
6
+ require 'paperclip_processors/papercrop'
@@ -1,10 +1,10 @@
1
1
  module Papercrop
2
2
  module Helpers
3
-
3
+
4
4
  # Form helper to render the cropping preview box of an attachment.
5
- # Box width can be handled by setting the :width option.
5
+ # Box width can be handled by setting the :width option.
6
6
  # Width is 100 by default. Height is calculated by the aspect ratio.
7
- #
7
+ #
8
8
  # crop_preview :avatar
9
9
  # crop_preview :avatar, :width => 150
10
10
  #
@@ -13,7 +13,7 @@ module Papercrop
13
13
  def crop_preview(attachment, opts = {})
14
14
  attachment = attachment.to_sym
15
15
  width = opts[:width] || 100
16
- height = (width / self.object.send(:"#{attachment}_aspect")).round
16
+ height = (width / self.object.send(:"#{attachment}_aspect")).round
17
17
 
18
18
  if self.object.send(attachment).class == Paperclip::Attachment
19
19
  wrapper_options = {
@@ -34,20 +34,26 @@ module Papercrop
34
34
  #
35
35
  # cropbox :avatar, :width => 650
36
36
  #
37
+ # You can override the aspect ratio used by Jcrop, and even unlock it by setting :aspect to a new value or false
38
+ #
39
+ # cropbox :avatar, :width => 650, :aspect => false
40
+ #
37
41
  # @param attachment [Symbol] attachment name
38
42
  # @param opts [Hash]
39
43
  def cropbox(attachment, opts = {})
40
44
  attachment = attachment.to_sym
41
- original_width = self.object.image_geometry(attachment, :original).width
42
- original_height = self.object.image_geometry(attachment, :original).height
43
- box_width = opts[:width] || original_width
45
+ original_width = self.object.image_geometry(attachment, :original).width.to_i
46
+ original_height = self.object.image_geometry(attachment, :original).height.to_i
47
+ box_width = opts.fetch :width, original_width
48
+ aspect = opts.fetch :aspect, self.object.send(:"#{attachment}_aspect")
44
49
 
45
50
  if self.object.send(attachment).class == Paperclip::Attachment
46
51
  box = self.hidden_field(:"#{attachment}_original_w", :value => original_width)
47
52
  box << self.hidden_field(:"#{attachment}_original_h", :value => original_height)
48
53
  box << self.hidden_field(:"#{attachment}_box_w", :value => box_width)
49
-
50
- for attribute in [:crop_x, :crop_y, :crop_w, :crop_h, :aspect] do
54
+ box << self.hidden_field(:"#{attachment}_aspect", :value => aspect, :id => "#{attachment}_aspect")
55
+
56
+ for attribute in [:crop_x, :crop_y, :crop_w, :crop_h] do
51
57
  box << self.hidden_field(:"#{attachment}_#{attribute}", :id => "#{attachment}_#{attribute}")
52
58
  end
53
59
 
@@ -64,4 +70,4 @@ if defined? ActionView::Helpers::FormBuilder
64
70
  ActionView::Helpers::FormBuilder.class_eval do
65
71
  include Papercrop::Helpers
66
72
  end
67
- end
73
+ end
@@ -0,0 +1,7 @@
1
+ module Papercrop
2
+
3
+ # Paperclip log wrapper
4
+ def self.log(text)
5
+ Paperclip.log(text)
6
+ end
7
+ end
@@ -7,42 +7,63 @@ module Papercrop
7
7
  #
8
8
  # crop_attached_file :avatar
9
9
  #
10
- # You can also define an aspect ratio for the crop and preview box through opts[:aspect]
10
+ # You can also define an initial aspect ratio for the crop and preview box through opts[:aspect]
11
11
  #
12
12
  # crop_attached_file :avatar, :aspect => "4:3"
13
+ #
14
+ # Or unlock it
15
+ #
16
+ # crop_attached_file :avatar, :aspect => false
13
17
  #
14
18
  # @param attachment_name [Symbol] Name of the desired attachment to crop
15
19
  # @param opts [Hash]
20
+ # @option opts [Range, String, FlaseClass] :aspect
16
21
  def crop_attached_file(attachment_name, opts = {})
22
+ opts = opts.dup
23
+
17
24
  [:crop_x, :crop_y, :crop_w, :crop_h, :original_w, :original_h, :box_w, :aspect, :cropped_geometries].each do |a|
18
25
  attr_accessor :"#{attachment_name}_#{a}"
19
26
  end
20
27
 
21
- if opts[:aspect].kind_of?(String) && opts[:aspect] =~ Papercrop::RegExp::ASPECT
22
- opts[:aspect] = Range.new *opts[:aspect].split(':').map(&:to_i)
23
- end
24
-
25
- unless opts[:aspect].kind_of?(Range)
26
- opts[:aspect] = 1..1
27
- end
28
+ aspect = normalize_aspect opts[:aspect]
28
29
 
29
30
  send :define_method, :"#{attachment_name}_aspect" do
30
- opts[:aspect].first.to_f / opts[:aspect].last.to_f
31
+ aspect.first.to_f / aspect.last.to_f if aspect
31
32
  end
32
33
 
33
34
  if respond_to? :attachment_definitions
34
- # for Paperclip <= 3.4
35
+ # for Paperclip <= 3.4
35
36
  definitions = attachment_definitions
36
37
  else
37
38
  # for Paperclip >= 3.5
38
39
  definitions = Paperclip::Tasks::Attachments.instance.definitions_for(self)
39
40
  end
40
41
 
41
- definitions[attachment_name][:processors] ||= []
42
- definitions[attachment_name][:processors] << :cropper
42
+ processors = definitions[attachment_name][:processors] ||= []
43
+ unless processors.include? :papercrop
44
+ processors << :papercrop
45
+ end
43
46
 
44
47
  after_update :"reprocess_to_crop_#{attachment_name}_attachment"
45
48
  end
49
+
50
+
51
+ # Returns a valid and normalized value for aspect ratio
52
+ # It will return 1.. if aspect is nil or a invalid string
53
+ # @param aspect [Range, String, FalseClass]
54
+ #
55
+ # @return [Range]
56
+ def normalize_aspect(aspect)
57
+ if aspect.kind_of?(String) && aspect =~ Papercrop::RegExp::ASPECT
58
+ Range.new *aspect.split(':').map(&:to_i)
59
+ elsif aspect.kind_of?(Range)
60
+ return aspect.first.to_i..aspect.last.to_i
61
+ elsif aspect == false
62
+ return aspect
63
+ else
64
+ 1..1
65
+ end
66
+ end
46
67
  end
47
68
 
48
69
 
@@ -50,7 +71,7 @@ module Papercrop
50
71
 
51
72
  # Asks if the attachment received a crop process
52
73
  # @param attachment_name [Symbol]
53
- #
74
+ #
54
75
  # @return [Boolean]
55
76
  def cropping?(attachment_name)
56
77
  !self.send(:"#{attachment_name}_crop_x").blank? &&
@@ -66,9 +87,12 @@ module Papercrop
66
87
  # @param style = :original [Symbol] attachment style
67
88
  # @return [Paperclip::Geometry]
68
89
  def image_geometry(attachment_name, style = :original)
69
- @geometry ||= {}
70
- path = (self.send(attachment_name).options[:storage] == :s3) ? self.send(attachment_name).url(style) : self.send(attachment_name).path(style)
71
- @geometry[style] ||= Paperclip::Geometry.from_file(path)
90
+ @geometry ||= {}
91
+ @geometry[attachment_name] ||= {}
92
+
93
+ path = (self.send(attachment_name).options[:storage] == :filesystem) ? self.send(attachment_name).path(style) : self.send(attachment_name).url(style)
94
+
95
+ @geometry[attachment_name][style] ||= Paperclip::Geometry.from_file(path)
72
96
  end
73
97
 
74
98
 
@@ -121,7 +145,7 @@ end
121
145
 
122
146
 
123
147
  # Mongoid support
124
- if defined? Mongoid::Document
148
+ if defined? Mongoid::Document
125
149
  Mongoid::Document::ClassMethods.module_eval do
126
150
  include Papercrop::ModelExtension::ClassMethods
127
151
  end
Binary file
@@ -4,7 +4,7 @@ describe "Form Helpers" do
4
4
 
5
5
  before do
6
6
  @landscape = Landscape.new(:name => "Mountains")
7
- @landscape.picture = open("test_app/test/fixtures/matterhorn.jpg")
7
+ @landscape.picture = open(mountains_img_path)
8
8
  @landscape.save
9
9
 
10
10
  @box = nil
@@ -17,10 +17,10 @@ describe "Form Helpers" do
17
17
  end
18
18
  @box = HTML::Document.new(@box)
19
19
 
20
- assert_select @box.root, 'input#landscape_picture_original_w', :value => "1024.0"
21
- assert_select @box.root, 'input#landscape_picture_original_h', :value => "768.0"
22
- assert_select @box.root, 'input#landscape_picture_box_w', :value => "1024.0"
23
- assert_select @box.root, 'input#picture_aspect', :value => @landscape.picture_aspect.to_s
20
+ assert_select @box.root, 'input#landscape_picture_original_w[value="1024"]'
21
+ assert_select @box.root, 'input#landscape_picture_original_h[value="768"]'
22
+ assert_select @box.root, 'input#landscape_picture_box_w[value="1024"]'
23
+ assert_select @box.root, "input#picture_aspect[value=\"#{@landscape.picture_aspect.to_s}\"]"
24
24
  assert_select @box.root, 'input#picture_crop_x'
25
25
  assert_select @box.root, 'input#picture_crop_y'
26
26
  assert_select @box.root, 'input#picture_crop_w'
@@ -38,7 +38,17 @@ describe "Form Helpers" do
38
38
  end
39
39
  @box = HTML::Document.new(@box)
40
40
 
41
- assert_select @box.root, 'input#landscape_picture_box_w', :value => "400"
41
+ assert_select @box.root, 'input#landscape_picture_box_w[value="400"]'
42
+ end
43
+
44
+
45
+ it "builds the crop box with unlocked aspect flag" do
46
+ form_for @landscape do |f|
47
+ @box = f.cropbox(:picture, :width => 400, :aspect => false)
48
+ end
49
+ @box = HTML::Document.new(@box)
50
+
51
+ assert_select @box.root, 'input#picture_aspect[value="false"]'
42
52
  end
43
53
 
44
54
 
@@ -48,8 +58,8 @@ describe "Form Helpers" do
48
58
  end
49
59
  @box = HTML::Document.new(@box)
50
60
 
51
- assert_select @box.root, 'div#picture_crop_preview_wrapper', :style => "width:100px; height:75px; overflow:hidden" do
52
- assert_select 'img#picture_crop_preview', :src => @landscape.picture.path(:original)
61
+ assert_select @box.root, 'div#picture_crop_preview_wrapper[style="width:100px; height:75px; overflow:hidden"]' do
62
+ assert_select "img#picture_crop_preview[src=\"#{@landscape.picture.url(:original)}\"]"
53
63
  end
54
64
  end
55
65
 
@@ -60,6 +70,6 @@ describe "Form Helpers" do
60
70
  end
61
71
  @box = HTML::Document.new(@box)
62
72
 
63
- assert_select @box.root, 'div#picture_crop_preview_wrapper', :style => "width:40px; height:30px; overflow:hidden"
73
+ assert_select @box.root, 'div#picture_crop_preview_wrapper[style="width:40px; height:30px; overflow:hidden"]'
64
74
  end
65
- end
75
+ end
@@ -8,7 +8,7 @@ describe "Image crop with JS", :js => true do
8
8
  click_link "New Landscape"
9
9
 
10
10
  fill_in "Name", :with => "Mountains"
11
- find("#landscape_picture").native.send_keys(File.expand_path("../../../test_app/test/fixtures/matterhorn.jpg", __FILE__))
11
+ find("#landscape_picture").native.send_keys(File.expand_path("../../../#{mountains_img_path}", __FILE__))
12
12
  click_button "Create Landscape"
13
13
 
14
14
  sleep 2
@@ -19,7 +19,7 @@ describe "Image crop with JS", :js => true do
19
19
  click_button "Crop image"
20
20
 
21
21
  sleep 1
22
- compare_images(CROPPED_IMG_PATH, Landscape.last.picture.path(:medium)).round(2).should eq(0.0)
22
+ compare_images(expected_mountains_img_path, Landscape.last.picture.path(:medium)).round(2).should eq(0.0)
23
23
  end
24
24
 
25
25
 
@@ -29,7 +29,7 @@ describe "Image crop with JS", :js => true do
29
29
  click_link "New Landscape"
30
30
 
31
31
  fill_in "Name", :with => "Mountains"
32
- find("#landscape_picture").native.send_keys(File.expand_path("../../../test_app/test/fixtures/matterhorn.jpg", __FILE__))
32
+ find("#landscape_picture").native.send_keys(File.expand_path(File.expand_path("../../../#{mountains_img_path}", __FILE__), __FILE__))
33
33
  click_button "Create Landscape"
34
34
 
35
35
  click_button "Crop image"
@@ -45,6 +45,6 @@ describe "Image crop with JS", :js => true do
45
45
  click_button "Crop image"
46
46
 
47
47
  sleep 1
48
- compare_images(CROPPED_IMG_PATH, Landscape.last.picture.path(:medium)).round(2).should eq(0.0)
48
+ compare_images(expected_mountains_img_path, Landscape.last.picture.path(:medium)).round(2).should eq(0.0)
49
49
  end
50
50
  end
@@ -8,7 +8,7 @@ describe "Image crop" do
8
8
  click_link "New Landscape"
9
9
 
10
10
  fill_in "Name", :with => "Mountains"
11
- attach_file "Picture", "test_app/test/fixtures/matterhorn.jpg"
11
+ attach_file "Picture", mountains_img_path
12
12
  click_button "Create Landscape"
13
13
 
14
14
  page.should have_css("#picture_crop_preview_wrapper")
@@ -17,18 +17,18 @@ describe "Image crop" do
17
17
 
18
18
  page.should have_css("#landscape_picture_original_w")
19
19
 
20
- find("#landscape_picture_original_w").value.should eq("1024.0")
21
- find("#landscape_picture_original_h").value.should eq("768.0")
20
+ find("#landscape_picture_original_w").value.should eq("1024")
21
+ find("#landscape_picture_original_h").value.should eq("768")
22
22
  find("#landscape_picture_box_w").value.should eq("600")
23
23
  find("#picture_aspect").value.should eq((4.0 / 3.0).to_s)
24
24
 
25
- find("#picture_crop_x").set "300.0"
26
- find("#picture_crop_y").set "200.0"
25
+ find("#picture_crop_x").set "300"
26
+ find("#picture_crop_y").set "200"
27
27
  find("#picture_crop_w").set "400"
28
28
  find("#picture_crop_h").set "300"
29
29
 
30
30
  click_button "Crop image"
31
31
 
32
- compare_images(CROPPED_IMG_PATH, Landscape.last.picture.path(:medium)).round(2).should eq(0.0)
32
+ compare_images(expected_mountains_img_path, Landscape.last.picture.path(:medium)).round(2).should eq(0.0)
33
33
  end
34
34
  end
@@ -4,14 +4,19 @@ describe "Model Extension" do
4
4
 
5
5
  before do
6
6
  @landscape = Landscape.new(:name => "Mountains")
7
- @landscape.picture = open("test_app/test/fixtures/matterhorn.jpg")
7
+ @landscape.picture = open(mountains_img_path)
8
8
  @landscape.save
9
+
10
+ @user = User.new(:name => "Bert", :email => "bert@sesame.street")
11
+ @user.avatar = open(bert_img_path)
12
+ @user.signature = open(mountains_img_path)
13
+ @user.save
9
14
  end
10
15
 
11
16
 
12
17
  it "clears the crop attributes" do
13
- @landscape.picture_crop_x = 0.0
14
- @landscape.picture_crop_y = 0.0
18
+ @landscape.picture_crop_x = 0
19
+ @landscape.picture_crop_y = 0
15
20
  @landscape.picture_crop_w = 400
16
21
  @landscape.picture_crop_h = 300
17
22
  @landscape.reset_crop_attributes_of(:picture)
@@ -24,13 +29,13 @@ describe "Model Extension" do
24
29
 
25
30
 
26
31
  it "crops images" do
27
- @landscape.picture_crop_x = 300.0
28
- @landscape.picture_crop_y = 200.0
32
+ @landscape.picture_crop_x = 300
33
+ @landscape.picture_crop_y = 200
29
34
  @landscape.picture_crop_w = 400
30
35
  @landscape.picture_crop_h = 300
31
36
  @landscape.save
32
37
  # Rounding to account for different versions of imagemagick
33
- compare_images(CROPPED_IMG_PATH, @landscape.picture.path(:medium)).round(2).should eq(0.0)
38
+ compare_images(expected_mountains_img_path, @landscape.picture.path(:medium)).round(2).should eq(0.0)
34
39
  end
35
40
 
36
41
 
@@ -51,25 +56,73 @@ describe "Model Extension" do
51
56
 
52
57
  it "knows when to crop" do
53
58
  @landscape.cropping?(:picture).should be(false)
54
- @landscape.picture_crop_x = 0.0
55
- @landscape.picture_crop_y = 0.0
59
+ @landscape.picture_crop_x = 0
60
+ @landscape.picture_crop_y = 0
56
61
  @landscape.picture_crop_w = 400
57
62
  @landscape.picture_crop_h = 300
58
63
  @landscape.cropping?(:picture).should be(true)
59
64
  end
60
65
 
61
66
 
67
+ it "normalizes aspect ratio" do
68
+ Landscape.normalize_aspect(4..3).should eq(4..3)
69
+ Landscape.normalize_aspect("4:3").should eq(4..3)
70
+ Landscape.normalize_aspect("4".."3").should eq(4..3)
71
+ Landscape.normalize_aspect(4.0..3.0).should eq(4..3)
72
+ Landscape.normalize_aspect(false).should eq(false)
73
+ Landscape.normalize_aspect(nil).should eq(1..1)
74
+ Landscape.normalize_aspect(true).should eq(1..1)
75
+ Landscape.normalize_aspect("foo").should eq(1..1)
76
+ end
77
+
78
+
62
79
  it "registers the post processor" do
63
- definitions = Paperclip::AttachmentRegistry.definitions_for(Landscape)
64
- definitions[:picture][:processors].should eq([:cropper])
80
+ definitions = retrieve_attachment_definitions_for(Landscape)
81
+
82
+ definitions[:picture][:processors].should eq([:papercrop])
83
+ end
84
+
85
+
86
+ it "does not register the processor if it's already there" do
87
+ Landscape.has_attached_file :processed, :styles => {:medium => "200x200#"}, :processors => [:papercrop, :rotator]
88
+ Landscape.crop_attached_file :processed
89
+
90
+ definitions = retrieve_attachment_definitions_for(Landscape)
91
+
92
+ definitions[:processed][:processors].should eq([:papercrop, :rotator])
93
+
94
+ Landscape._update_callbacks.delete_if {|e| e.instance_values['filter'] == :reprocess_to_crop_processed_attachment }
65
95
  end
66
96
 
67
97
 
68
98
  it "returns image properties" do
69
99
  @landscape.picture_aspect.should eq(4.0 / 3.0)
70
100
 
71
- @landscape.image_geometry(:picture).width.should eq(1024.0)
72
- @landscape.image_geometry(:picture).height.should eq(768.0)
101
+ @landscape.image_geometry(:picture).width.should eq(1024)
102
+ @landscape.image_geometry(:picture).height.should eq(768)
103
+ end
104
+
105
+
106
+ it "returns image geometry for two attachments" do
107
+ @user.image_geometry(:avatar).width.should eq(900)
108
+ @user.image_geometry(:avatar).height.should eq(900)
109
+ @user.image_geometry(:avatar, :medium).width.should eq(200)
110
+ @user.image_geometry(:avatar, :medium).height.should eq(200)
111
+ @user.image_geometry(:signature).width.should eq(1024)
112
+ @user.image_geometry(:signature).height.should eq(768)
113
+ @user.image_geometry(:signature, :medium).width.should eq(496)
114
+ @user.image_geometry(:signature, :medium).height.should eq(279)
115
+ end
116
+
117
+
118
+ it "sanitizes processor" do
119
+ Papercrop.expects(:log).with('[papercrop] picture crop w/h/x/y were non-integer. Error: invalid value for Integer(): "evil code"').twice
120
+
121
+ @landscape.picture_crop_x = "evil code"
122
+ @landscape.picture_crop_y = 200
123
+ @landscape.picture_crop_w = 400
124
+ @landscape.picture_crop_h = 300
125
+ @landscape.save
73
126
  end
74
127
 
75
128
 
@@ -80,4 +133,13 @@ describe "Model Extension" do
80
133
 
81
134
  @landscape.image_geometry(:picture)
82
135
  end
136
+
137
+
138
+ it "returns url for fog storage" do
139
+ @landscape.picture.expects(:url).returns("http://some-aws-s3-fog-storage-url").once
140
+ @landscape.picture.expects(:options).returns(:storage => :fog).once
141
+ Paperclip::Geometry.expects(:from_file).with("http://some-aws-s3-fog-storage-url").once
142
+
143
+ @landscape.image_geometry(:picture)
144
+ end
83
145
  end
@@ -1,6 +1,8 @@
1
1
  # This file is copied to spec/ when you run 'rails generate rspec:install'
2
2
  ENV["RAILS_ENV"] = 'test'
3
- require File.expand_path("../../test_app/config/environment", __FILE__)
3
+ ENV["TEST_APP"] ||= 'rails_4'
4
+
5
+ require File.expand_path("../../test_apps/#{ENV["TEST_APP"]}/config/environment", __FILE__)
4
6
  require 'rspec/rails'
5
7
  require 'rspec/autorun'
6
8
  require 'database_cleaner'
@@ -9,9 +11,6 @@ require 'database_cleaner'
9
11
  # in spec/support/ and its subdirectories.
10
12
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
11
13
 
12
- # Previously cropped image. Used for comparing test cropped images with RMagick
13
- CROPPED_IMG_PATH = "test_app/test/fixtures/test_img.jpg"
14
-
15
14
  RSpec.configure do |config|
16
15
  # ## Mock Framework
17
16
  #
@@ -5,4 +5,28 @@ def compare_images(test_image_path, cropped_image_path)
5
5
  target_img = Magick::Image::read(cropped_image_path).first
6
6
 
7
7
  test_img.compare_channel(target_img, Magick::MeanAbsoluteErrorMetric).second
8
+ end
9
+
10
+
11
+ def retrieve_attachment_definitions_for(model_class)
12
+ if model_class.respond_to?(:attachment_definitions)
13
+ model_class.attachment_definitions
14
+ else
15
+ Paperclip::AttachmentRegistry.definitions_for(model_class)
16
+ end
17
+ end
18
+
19
+
20
+ def mountains_img_path
21
+ "spec/fixtures/mountains.jpg"
22
+ end
23
+
24
+
25
+ def expected_mountains_img_path
26
+ "spec/fixtures/mountains_expected_result.jpg"
27
+ end
28
+
29
+
30
+ def bert_img_path
31
+ "spec/fixtures/bert.jpg"
8
32
  end
File without changes
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: papercrop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
5
- prerelease:
4
+ version: 0.3.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Ruben Santamaria
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-02-28 00:00:00.000000000 Z
11
+ date: 2015-09-23 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rails
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ! '>='
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ! '>='
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: jquery-rails
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ! '>='
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ! '>='
44
39
  - !ruby/object:Gem::Version
@@ -46,7 +41,6 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: paperclip
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ! '>='
52
46
  - !ruby/object:Gem::Version
@@ -54,7 +48,6 @@ dependencies:
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ! '>='
60
53
  - !ruby/object:Gem::Version
@@ -62,7 +55,6 @@ dependencies:
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: rspec-rails
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
59
  - - ~>
68
60
  - !ruby/object:Gem::Version
@@ -70,7 +62,6 @@ dependencies:
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
66
  - - ~>
76
67
  - !ruby/object:Gem::Version
@@ -78,7 +69,6 @@ dependencies:
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: capybara
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
73
  - - ! '>='
84
74
  - !ruby/object:Gem::Version
@@ -86,7 +76,6 @@ dependencies:
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
80
  - - ! '>='
92
81
  - !ruby/object:Gem::Version
@@ -94,7 +83,6 @@ dependencies:
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: mocha
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
87
  - - ! '>='
100
88
  - !ruby/object:Gem::Version
@@ -102,7 +90,6 @@ dependencies:
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
94
  - - ! '>='
108
95
  - !ruby/object:Gem::Version
@@ -110,7 +97,6 @@ dependencies:
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: rmagick
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
101
  - - ! '>='
116
102
  - !ruby/object:Gem::Version
@@ -118,7 +104,6 @@ dependencies:
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
108
  - - ! '>='
124
109
  - !ruby/object:Gem::Version
@@ -126,7 +111,6 @@ dependencies:
126
111
  - !ruby/object:Gem::Dependency
127
112
  name: sass
128
113
  requirement: !ruby/object:Gem::Requirement
129
- none: false
130
114
  requirements:
131
115
  - - ! '>='
132
116
  - !ruby/object:Gem::Version
@@ -134,7 +118,6 @@ dependencies:
134
118
  type: :development
135
119
  prerelease: false
136
120
  version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
121
  requirements:
139
122
  - - ! '>='
140
123
  - !ruby/object:Gem::Version
@@ -142,7 +125,6 @@ dependencies:
142
125
  - !ruby/object:Gem::Dependency
143
126
  name: sqlite3
144
127
  requirement: !ruby/object:Gem::Requirement
145
- none: false
146
128
  requirements:
147
129
  - - ! '>='
148
130
  - !ruby/object:Gem::Version
@@ -150,7 +132,6 @@ dependencies:
150
132
  type: :development
151
133
  prerelease: false
152
134
  version_requirements: !ruby/object:Gem::Requirement
153
- none: false
154
135
  requirements:
155
136
  - - ! '>='
156
137
  - !ruby/object:Gem::Version
@@ -158,7 +139,6 @@ dependencies:
158
139
  - !ruby/object:Gem::Dependency
159
140
  name: database_cleaner
160
141
  requirement: !ruby/object:Gem::Requirement
161
- none: false
162
142
  requirements:
163
143
  - - ! '>='
164
144
  - !ruby/object:Gem::Version
@@ -166,7 +146,6 @@ dependencies:
166
146
  type: :development
167
147
  prerelease: false
168
148
  version_requirements: !ruby/object:Gem::Requirement
169
- none: false
170
149
  requirements:
171
150
  - - ! '>='
172
151
  - !ruby/object:Gem::Version
@@ -174,7 +153,6 @@ dependencies:
174
153
  - !ruby/object:Gem::Dependency
175
154
  name: selenium-webdriver
176
155
  requirement: !ruby/object:Gem::Requirement
177
- none: false
178
156
  requirements:
179
157
  - - ! '>='
180
158
  - !ruby/object:Gem::Version
@@ -182,7 +160,20 @@ dependencies:
182
160
  type: :development
183
161
  prerelease: false
184
162
  version_requirements: !ruby/object:Gem::Requirement
185
- none: false
163
+ requirements:
164
+ - - ! '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: appraisal
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
186
177
  requirements:
187
178
  - - ! '>='
188
179
  - !ruby/object:Gem::Version
@@ -193,21 +184,26 @@ executables: []
193
184
  extensions: []
194
185
  extra_rdoc_files: []
195
186
  files:
196
- - lib/assets/images/Jcrop.gif
197
- - lib/assets/javascripts/jquery.jcrop.js
198
187
  - lib/assets/javascripts/papercrop.js
199
- - lib/assets/stylesheets/jquery.jcrop.css
200
- - lib/paperclip_processors/cropper.rb
188
+ - lib/paperclip_processors/papercrop.rb
201
189
  - lib/papercrop/engine.rb
202
190
  - lib/papercrop/helpers.rb
191
+ - lib/papercrop/logger.rb
203
192
  - lib/papercrop/model_extension.rb
204
193
  - lib/papercrop/reg_exp.rb
205
194
  - lib/papercrop.rb
206
- - vendor/jcrop-v0.9.10/css/Jcrop.gif
207
- - vendor/jcrop-v0.9.10/css/jquery.Jcrop.css
208
- - vendor/jcrop-v0.9.10/js/jquery.Jcrop.js
209
- - vendor/jcrop-v0.9.10/MIT-LICENSE.txt
195
+ - vendor/assets/images/Jcrop.gif
196
+ - vendor/assets/javascripts/jquery.jcrop.js
197
+ - vendor/assets/stylesheets/jquery.jcrop.css
198
+ - vendor/src.jcrop-v0.9.10/css/Jcrop.gif
199
+ - vendor/src.jcrop-v0.9.10/css/jquery.Jcrop.css
200
+ - vendor/src.jcrop-v0.9.10/js/jquery.Jcrop.js
201
+ - vendor/src.jcrop-v0.9.10/MIT-LICENSE.txt
210
202
  - README.md
203
+ - Appraisals
204
+ - spec/fixtures/bert.jpg
205
+ - spec/fixtures/mountains.jpg
206
+ - spec/fixtures/mountains_expected_result.jpg
211
207
  - spec/helpers/form_helpers_spec.rb
212
208
  - spec/integration/papercrop_js_spec.rb
213
209
  - spec/integration/papercrop_spec.rb
@@ -218,35 +214,31 @@ files:
218
214
  homepage: https://github.com/rsantamaria/papercrop
219
215
  licenses:
220
216
  - MIT
217
+ metadata: {}
221
218
  post_install_message:
222
219
  rdoc_options: []
223
220
  require_paths:
224
221
  - lib
225
222
  required_ruby_version: !ruby/object:Gem::Requirement
226
- none: false
227
223
  requirements:
228
224
  - - ! '>='
229
225
  - !ruby/object:Gem::Version
230
226
  version: '0'
231
- segments:
232
- - 0
233
- hash: 3120983817575737500
234
227
  required_rubygems_version: !ruby/object:Gem::Requirement
235
- none: false
236
228
  requirements:
237
229
  - - ! '>='
238
230
  - !ruby/object:Gem::Version
239
231
  version: '0'
240
- segments:
241
- - 0
242
- hash: 3120983817575737500
243
232
  requirements: []
244
233
  rubyforge_project:
245
- rubygems_version: 1.8.25
234
+ rubygems_version: 2.1.0
246
235
  signing_key:
247
- specification_version: 3
236
+ specification_version: 4
248
237
  summary: Paperclip extension for cropping images
249
238
  test_files:
239
+ - spec/fixtures/bert.jpg
240
+ - spec/fixtures/mountains.jpg
241
+ - spec/fixtures/mountains_expected_result.jpg
250
242
  - spec/helpers/form_helpers_spec.rb
251
243
  - spec/integration/papercrop_js_spec.rb
252
244
  - spec/integration/papercrop_spec.rb
@@ -1,28 +0,0 @@
1
- require "paperclip"
2
-
3
- module Paperclip
4
- class Cropper < Thumbnail
5
-
6
- def transformation_command
7
- if crop_command
8
- crop_command + super.join(' ').sub(/ -crop \S+/, '').split(' ')
9
- else
10
- super
11
- end
12
- end
13
-
14
-
15
- def crop_command
16
- target = @attachment.instance
17
-
18
- if target.cropping?(@attachment.name)
19
- w = target.send :"#{@attachment.name}_crop_w"
20
- h = target.send :"#{@attachment.name}_crop_h"
21
- x = target.send :"#{@attachment.name}_crop_x"
22
- y = target.send :"#{@attachment.name}_crop_y"
23
- ["-crop", "#{w}x#{h}+#{x}+#{y}"]
24
- end
25
- end
26
-
27
- end
28
- end