Leaving the beaten track with RSpec
Posted by Craig Ambrose on December 05, 2007 at 07:16 AM
I’ve spent today learning RSpec. To get my head around it, I decided to spec a controller action which I’d already written and tested with Test::Unit. It’s a pretty basic CRUD action, requiring login and ownership of the record.
Here’s the spec and test code side by side:
I’m not sure which one was easier. The RSpec version is certainly a lot longer. Having said that, the Test::Unit one uses fixtures, which aren’t included in the dump.
In RSpec’s favour, is that the specifications run much faster (as they don’t use the database), and they also better decouple the controller from the model, as it’s a more true “unit” test. Having said that, I believe that the RSpec version might actually be a little bit more fragile, as it would fail if the code under test changed to perform the same operation using different methods (such as calling ActiveRecord create, instead of new and save).
Also, some of the things I specified in terms of expectations are actually order dependent, which is not captured in my specs, so if I deliberately messed up the order of some of the code, I might get false positives with my specifications.
For the record, here’s the code under test.
before_filter :login_required, :only => [:new, :create, :edit, :update]
before_filter :load_user
before_filter :current_user_can_edit_user, :only => [:new, :create, :edit, :update]
before_filter :load_user_base_tabs
def create
@photo_set = PhotoSet.new(params[:photo_set])
@photo_set.profile = @profile
success = @photo_set.save respond_to do |format|
format.html do
if success
redirect_to user_photo_set_path(@user, @photo_set)
else
render :action => 'new'
end
end
end
end
Image cropping with Mini Magick and attachment_fu
Posted by Craig Ambrose on December 03, 2007 at 01:07 AM
As I mentioned in my last post, I’ve recently switched a lot of my code from RMagic to Mini Magick. The API provided by mini magick isn’t quite as nice, and you have to jump through a few hoops to get it to do what you want, so I thought I’d post an example.
Whenever clients talk about image thumbnailing, what they actually want is for the image to be cropped square, and then resized down to thumbnail size. They never, ever, want you to provide a thumbnail that isn’t square, or to scale the image out of proportion in order to stretch it into a square shape. It’s always crop and scale.
Strangely, this isn’t what attachment_fu does out of the box, with either rmagick or mini magick. Others have discussed this with regard to rmagick, but I couldn’t find a good mini-magick solution that worked, so here’s why I’ve come up with.
Replace the resize_image method in attachment_fu’s mini_magick_processor.rb file with the following:
def resize_image(img, size)
size = size.first if size.is_a?(Array) && size.length == 1
if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
if size.is_a?(Fixnum)
resize_and_crop(img, size)
else
size[0] == size[1] ? resize_and_crop(img, size[0]) : img.resize(size.join('x'))
end
else
img.resize(size.to_s)
end
self.temp_path = img
end
def resize_and_crop(image, square_size)
if image[:width] < image[:height]
shave_off = ((image[:height] - image[:width])/2).round
image.shave("0x#{shave_off}")
elsif image[:width] > image[:height]
shave_off = ((image[:width] - image[:height])/2).round
image.shave("#{shave_off}x0")
end
image.resize("#{square_size}x#{square_size}")
return image
end
The resize_image method is changed a little to ensure that resize_and_crop is called if both requested dimensions are the same. This occurs if you specify it as a single number, or as an array of two numbers that are the same.
eg::thumb => [80,80]
