papercrop 0.2.0 → 0.3.0
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 +15 -0
- data/Appraisals +10 -0
- data/README.md +26 -2
- data/lib/assets/javascripts/papercrop.js +6 -2
- data/lib/paperclip_processors/papercrop.rb +33 -0
- data/lib/papercrop.rb +2 -1
- data/lib/papercrop/helpers.rb +16 -10
- data/lib/papercrop/logger.rb +7 -0
- data/lib/papercrop/model_extension.rb +41 -17
- data/spec/fixtures/bert.jpg +0 -0
- data/spec/fixtures/mountains.jpg +0 -0
- data/spec/fixtures/mountains_expected_result.jpg +0 -0
- data/spec/helpers/form_helpers_spec.rb +20 -10
- data/spec/integration/papercrop_js_spec.rb +4 -4
- data/spec/integration/papercrop_spec.rb +6 -6
- data/spec/model_extensions/model_extension_spec.rb +74 -12
- data/spec/spec_helper.rb +3 -4
- data/spec/support/compare_images.rb +24 -0
- data/{lib → vendor}/assets/images/Jcrop.gif +0 -0
- data/{lib → vendor}/assets/javascripts/jquery.jcrop.js +0 -0
- data/{lib → vendor}/assets/stylesheets/jquery.jcrop.css +0 -0
- data/vendor/{jcrop-v0.9.10 → src.jcrop-v0.9.10}/MIT-LICENSE.txt +0 -0
- data/vendor/{jcrop-v0.9.10 → src.jcrop-v0.9.10}/css/Jcrop.gif +0 -0
- data/vendor/{jcrop-v0.9.10 → src.jcrop-v0.9.10}/css/jquery.Jcrop.css +0 -0
- data/vendor/{jcrop-v0.9.10 → src.jcrop-v0.9.10}/js/jquery.Jcrop.js +0 -0
- metadata +35 -43
- data/lib/paperclip_processors/cropper.rb +0 -28
checksums.yaml
ADDED
@@ -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=
|
data/Appraisals
ADDED
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
|
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 `
|
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
|
data/lib/papercrop.rb
CHANGED
data/lib/papercrop/helpers.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
71
|
-
|
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
|
Binary file
|
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(
|
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
|
21
|
-
assert_select @box.root, 'input#landscape_picture_original_h
|
22
|
-
assert_select @box.root, 'input#landscape_picture_box_w
|
23
|
-
assert_select @box.root,
|
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
|
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
|
52
|
-
assert_select
|
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
|
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("
|
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(
|
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(
|
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(
|
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",
|
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
|
21
|
-
find("#landscape_picture_original_h").value.should eq("768
|
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
|
26
|
-
find("#picture_crop_y").set "200
|
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(
|
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(
|
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
|
14
|
-
@landscape.picture_crop_y = 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
|
28
|
-
@landscape.picture_crop_y = 200
|
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(
|
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
|
55
|
-
@landscape.picture_crop_y = 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 =
|
64
|
-
|
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
|
72
|
-
@landscape.image_geometry(:picture).height.should eq(768
|
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
|
data/spec/spec_helper.rb
CHANGED
@@ -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
|
-
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
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.
|
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:
|
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
|
-
|
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/
|
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/
|
207
|
-
- vendor/
|
208
|
-
- vendor/
|
209
|
-
- vendor/jcrop-v0.9.10/
|
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.
|
234
|
+
rubygems_version: 2.1.0
|
246
235
|
signing_key:
|
247
|
-
specification_version:
|
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
|