How to set max image size and weight/height using Dragonfly? Max size will be different for different models, so i would like to set it in model lvl.
According to this post, http://blog.new-bamboo.co.uk/2010/1/13/dragonfly, you can do it with the following, where 400 is the max
class Album
validates_property :width, :of => :cover_image, :in => (0..400)
end
Related
I'm using Paperclip in my project but some of my users are complaining that it's incorrectly rotating some images.
For some reasons I can't even imagine I figured it out that some files are with wrong exif orientation attributes. I was looking and I saw that paperclip calls ImageMagick by default using -auto-orient. I saw that the Thumbnail processor has an option to turn auto-orient on or off.
But I couldn't find a way to pass this to the Processor.
This is the code I have:
has_attached_file :photo,
styles: { :square => "400x400#" }
Does anyone now how to do that?
Thanks!
In the end I created a new processor which extends from the paperclip default Thumbnail processor to send the correct options.
class WithouAutoOrientProcessor < Paperclip::Thumbnail
def initialize(file, options = {}, attachment = nil)
options[:auto_orient] = false
super
end
end
And in the model I added
has_attached_file :photo,
styles: { :square => "400x400#" },
processors: [:WithouAutoOrientProcessor]
Although it is a valid option to add your own processor, this is how you pass the option to the processor:
In your styles hash replace your dimension strings with another hash
Put your old dimensions in the key geometry into this hash
The other key/value pairs are the options passed to the processor
You can of course pass auto_orient: false, too
Applying this to your model's code:
has_attached_file :photo,
styles: { square: { geometry: "400x400#", auto_orient: false } }
Ok I'm really quite stuck on this and it might be nice to throw around some ideas, this may be a long'ish post.
Firstly let me try and explain what I'm trying to do. Currently I have a model called Book with a nested model called Snippets. I have a column called size in my Book model which defines if it is [0 => 'Short', 1 => 'Medium', 2 => 'Long']. I've also got a total word count in my Book controller which will give me the amount of words in every snippet. Now what I want to try and do is depending on the size [0,1,2] define a different limit on the word count. Example shown below
Size (content length validation) | Word Count
Short (500) per creation | 20,000 words in total
Medium (700) per creation | 50,000 words in total
Long (1000) per creation | 100,000 words in total
current_word_count - total_word_count depending on size [short,med,long]
So depending on the size defined in Book which I have working now I would like the total amount of words for snippets in that book to be defined by model including all current posts so for example if I have a short book and I have 10,000 words already in snippets there should be 10,000 left. The reason I have thought it through this way was because not every user will always post the maximum required.
Now for the code.
First the models:
Book.rb
class Book < ActiveRecord::Base
has_many :snippets
attr_accessible :title, :book_id, :size
def get_word_count
#word_count = 0
self.snippets.each.do |c|
#word_count += c.content.scan(/\w+/).size
end
def short?
size == 0
end
def medium?
size == 1
end
def long?
size == 2
end
end
Snippet.rb
class Snippet < ActiveRecord::Base
before_create :check_limit
belongs_to :book
attr_accessible :content, :book_id
validates :book_id, presence: true
#validates_length_of :content, less_than: 200, if: book.small?
#validates_length_of :content, less_than: 500, if: book.medium?
#validates_length_of :content, less_than: 1000, if: book.long?
def check_limit
if book.word_limit_reached?
errors.add :base, 'Snippet limit reached.'
return false
end
return true
end
end
Seeing as this is a database action I won't really need to touch the controller just yet until I have defined these rules. I've been sat here trying various things but as I'm still new to Rails I'm just trying to do this to get my head around the code and the things you can do.
As always I appreciate your help and feedback.
Ahh, custom validation goodness, this is what I would do:
book.rb
class Book < ActiveRecord::Base
has_many :snippets
def get_word_count
#word_count = []
self.snippets.each do |c|
#word_count << c.content.scan(/\w+/).size
end
#word_count = #word_count.inject(:+)
# Similar to what you had before, but this creates an array, and adds each
# content size to the array, the inject(:+) is a method done on an array to sum
# each element of the array, doesn't work with older versions of ruby, but it's safe
# 1.9.2 and up
end
def short?
size == 0
end
def medium?
size == 1
end
def long?
size == 2
end
end
Snippet.rb
class Snippet < ActiveRecord::Base
belongs_to :book
validate :size_limit
# Here I created a constant containing a hash with your book limitation parameters
# This way you can compare against it easily
BOOK_SIZE = {
0 => {"per" => 500, "total" => 20000},
1 => {"per" => 700, "total" => 50000},
2 => {"per" => 1000, "total" => 100000}
}
def size_limit
# Sets the book size, which is the same as the hash key for your constant BOOK_SIZE
book_limit = self.book.size
# Scans the content attribute and sets the word count
word_count = self.content.scan(/\w+/).size
# This is getting the total word count, for all the snippets and the new snippet
# the user wants to save
current_snippets_size = (self.book.get_word_count || 0) + word_count
# This is where your validation magic happens. There are 2 comparisons done and if they
# don't both pass, add an error that can be passed back to the controller.
# 1st If your content attribute is smaller than allowed for that particular book
# 2nd If your total snippets content for all previous and current snippet are less than
# allowed by the total book size defined in the constant
errors.add(:content, "Content size is too big") unless word_count < BOOK_SIZE[book_limit]['per'] && current_snippets_size < BOOK_SIZE[book_limit]['total']
end
end
What is the best way to validate a cost/price input by a user, validation rules below:
Examples of formats allowed .23, .2, 1.23, 0.25, 5, 6.3 (maximum of two digits after decimal point)
Minimum value of 0.01
Maximum value of 9.99
Check the price and verify the format
#rails 3
validates :price, :format => { :with => /\A\d+(?:\.\d{0,2})?\z/ }, :numericality => {:greater_than => 0, :less_than => 10}
#rails 2
validates_numericality_of :price, :greater_than => 0, :less_than => 10
validates_format_of :price, :with => /\A\d+(?:\.\d{0,2})?\z/
For client side validations you can use a jQuery plugin like this one that allows you to define different valid formats for a given input.
For server side validations and according to this question/answer maybe you should use a decimal column for price in which you can define values for precision and scale, scale solves the two digits after decimal point restriction.
Then to validate the numericality, minimum and maximum value you can use the next validation method:
validates_numericality_of :price, :greater_than => 0, :less_than => 10
You can build custom validations.Lets say, for example the second case:
validate :price_has_to_be_greater_than_minimum
def price_has_to_be_greater_than_minimum
errors.add(:price, "price has to be greater than 0.01") if
!price.blank? and price > 0.01
end
More on this, in the Rails Guides, here.
I want attachment_fu to resize my thumbnails in a similar way to how flickr, facebook and twitter handle this: If I want a 100x100 thumbnail I want the thumbnail to be exactly 100x100 with any excess cropped off so that the aspect ratio is preserved.
Any ideas?
To set up the 100x100 thumbnails, add the following to your model:
has_attachment :content_type => :image,
:storage => IMAGE_STORAGE,
:max_size => 20.megabytes,
:thumbnails => {
:thumb => '100x100>',
:large => '800x600>',
}
(In this example, I am creating a 100x100 thumbnail, and also an 800x600 'large' size, in additional to keeping the original size.)
Also, keep in mind that the thumbnail might not be exactly 100x100; it will have a maximum dimension of 100x100. This means that if the original has an aspect ration of 4:3, the thumbnail would be 100x75. I'm not exactly sure if that is what you meant by "exactly 100x100 with any excess cropped off so that the aspect ratio is preserved."
Add this to your model
protected
# Override image resizing method
def resize_image(img, size)
# resize_image take size in a number of formats, we just want
# Strings in the form of "crop: WxH"
if (size.is_a?(String) && size =~ /^crop: (\d*)x(\d*)/i) ||
(size.is_a?(Array) && size.first.is_a?(String) &&
size.first =~ /^crop: (\d*)x(\d*)/i)
img.crop_resized!($1.to_i, $2.to_i)
# We need to save the resized image in the same way the
# orignal does.
self.temp_path = write_to_temp_file(img.to_blob)
else
super # Otherwise let attachment_fu handle it
end
end
and change the thumbnail size to:
:thumbnails => {:thumb => 'crop: 100x100' }
source:
http://stuff-things.net/2008/02/21/quick-and-dirty-cropping-images-with-attachment_fu/
There's a cropping directive that can be given in the specification:
has_attachment :content_type => :image,
:thumbnails => {
:thumb => '100x100#'
}
Memonic: '#' looks like the crop tool.
Edit: Correction
has_attachment :content_type => :image,
:thumbnails => {
:thumb => '100x100!'
}
The previous method was for Paperclip which has a different notation.
My solution was to delve into the attachment_fu plugin folder (vendor/plugins) and edit the rmagick_processor.rb file. First I renamed resize_image to resize_image_internal, then added:
def resize_image(img, size)
# resize_image take size in a number of formats, we just want
# Strings in the form of "square: WxH"
if (size.is_a?(String) && size =~ /^square: (\d*)x(\d*)/i) ||
(size.is_a?(Array) && size.first.is_a?(String) &&
size.first =~ /^square: (\d*)x(\d*)/i)
iw, ih = img.columns, img.rows
aspect = iw.to_f / ih.to_f
if aspect > 1
shave_off = (iw - ih) / 2
img.shave!(shave_off, 0)
else
shave_off = (ih-iw) / 2
img.shave!(0, shave_off)
end
resize_image_internal(img, "#{$1}x#{$2}!")
else
resize_image_internal(img, size) # Otherwise let attachment_fu handle it
end
end
I can now use 'square: 100x100' as my geometry string. Note that the above code assumes the required output is square.
I'm looking for a way to determine image orientation preferably with Paperclip, but is it even possible or do I need to user RMagick or another image library for this?
Case scenario: When a user uploads an image i want to check the orientation/size/dimensions to determine if the image is in portrait/landscape or square and save this attribute to the model.
Here's what I generally do in my image models. Perhaps it will help:
I use IM's -auto-orient option when converting. This ensures images are always rotated properly after upload
I read the EXIF data after processing and get the width and height (among other things)
You can then just have an instance method that outputs an orientation string based on width and height
has_attached_file :attachment,
:styles => {
:large => "900x600>",
:medium => "600x400>",
:square => "100x100#",
:small => "300x200>" },
:convert_options => { :all => '-auto-orient' },
:storage => :s3,
:s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:s3_permissions => 'public-read',
:s3_protocol => 'https',
:path => "images/:id_partition/:basename_:style.:extension"
after_attachment_post_process :post_process_photo
def post_process_photo
imgfile = EXIFR::JPEG.new(attachment.queued_for_write[:original].path)
return unless imgfile
self.width = imgfile.width
self.height = imgfile.height
self.model = imgfile.model
self.date_time = imgfile.date_time
self.exposure_time = imgfile.exposure_time.to_s
self.f_number = imgfile.f_number.to_f
self.focal_length = imgfile.focal_length.to_s
self.description = imgfile.image_description
end
Thanks for the answer jonnii.
Although I did find what I was looking for in the PaperClip::Geometry module.
This worked find:
class Image < ActiveRecord::Base
after_save :set_orientation
has_attached_file :data, :styles => { :large => "685x", :thumb => "100x100#" }
validates_attachment_content_type :data, :content_type => ['image/jpeg', 'image/pjpeg'], :message => "has to be in jpeg format"
private
def set_orientation
self.orientation = Paperclip::Geometry.from_file(self.data.to_file).horizontal? ? 'horizontal' : 'vertical'
end
end
This of course makes both vertical and square images have the vertical attribute but that's what I wanted anyway.
When I take a photo with my camera the dimensions of the image are the same regardless if the photo is landscape or portrait. However, my camera is smart enough to rotate the image for me! How thoughtful! The way this work is that is uses something called exif data which is meta data placed on the image by the camera. It includes stuff like: the type of camera, when the photo was taken, orientation etc...
With paperclip you can set up callbacks, specifically what you'll want to do is have a callback on before_post_process that checks the orientation of the image by reading the exif data using a library (you can find a list here: http://blog.simplificator.com/2008/01/14/ruby-and-exif-data/), and then rotating the image clockwise or counterclockwise 90 degrees (you won't know which way they rotated the camera when they took the photo).
I hope this helps!