The Ruby file API has always struck me as inelegant (i.e., I’m constantly looking up its syntax). So I wrote String#to_file to make the common operation of writing a string to a file easy:
class String
def to_file(filename)
File.open(filename, 'w') {|f| f.write self }
end
endNow when you do this:
"some string".to_file("testing.txt")You’ll get a file called “testing.txt” that contains “some string”. Easy, no?
You can use the same method to download files:
require 'open-uri'
url = 'http://blog.stackoverflow.com/audio/stackoverflow-podcast-001.mp3'
open(url).read.to_file(url.split('/').last)And boom, you’ve downloaded the file to “stackoverflow-podcast-001.mp3”
Here’s a simple Post model:
class Post < ActiveRecord::Base
has_many :comments, :dependent => :destroy
validates_presence_of :title
endNow check out what happens when I try to save an invalid post (I’m logging to stdout so you can see the SQL):
[Dev]> post.comments.count
=> 1
[Dev]> post.update_attributes(:title => nil, :comments => [])
Comment Load (0.2ms) SELECT "comments".* FROM "comments" WHERE ("comments".post_id = 2)
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE ("posts"."id" = 2) LIMIT 1
SQL (0.1ms) DELETE FROM "comments" WHERE ("comments"."id" = 1)
=> false
[Dev]> post.errors.full_messages
=> ["Title can't be blank"]
[Dev]> post.comments.count
=> 0The update fails, of course, because the post’s title cannot be blank. However, note this line of SQL:
SQL (0.1ms) DELETE FROM "comments" WHERE ("comments"."id" = 1)Even though this save failed, Rails still deleted all the post’s comments! And it had nothing to do with the update_attributes method; merely setting the comments attribute kills all existing comments:
[Dev]> post.comments.count
=> 1
[Dev]> post.comments = []
Comment Load (0.2ms) SELECT "comments".* FROM "comments" WHERE ("comments".post_id = 2)
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE ("posts"."id" = 2) LIMIT 1
SQL (0.1ms) DELETE FROM "comments" WHERE ("comments"."id" = 1)
=> []
[Dev]> post.comments.count
=> 0This is extremely counter-intuitive: post.title = "whatever" doesn’t affect the post’s title until I save, and neither should post.comments = some_comments.
Yes: enclose all statements that set an attribute corresponding to a has_x association in a transaction:
Post.transaction do
post.comments = some_comments
post.save!
endThis way, if save! throws an exception, the transaction will roll back the changes you made to the post’s comments
It’s annoying and error-prone to have to remember to use transactions; hopefully Rails' default behavior changes someday.
Apple made an annoying change to the way the iPhone camera works in iOS4. If you’ve ever been emailed a photo from an iPhone 4, you’ve experienced this rage guy situation:
EXIF is a standard method for storing metadata in image files. What’s metadata? Data about the photo (as opposed to the data that makes up the photo itself) — example pieces of metadata stored in EXIF are:
Before EXIF, if you wanted to store the date a photo was taken with the photo, you’d have to print it on the photo itself (ugly!):

EXIF allows you to store this kind of data without affecting the way a photo looks (kind of like writing a date on the back of a print)
Here’s what happens:
The iPhone camera sensor's "up" is the right edge of the phone (when held with the home button at the bottom).
When the iPhone takes a photo, it writes the photo data with the default sensor orientation (i.e., as if the photo had been taken with the phone rotated such that the home button is on the right.)
Why doesn’t the iPhone write the photo data with the correct orientation? In fact, it seems like there shouldn’t even be the option to specify rotation in EXIF! I.e., if there were only one way of specifying a photo’s orientation (the orientation of the photo data itself), consumers wouldn’t have to worry about whether their photo viewing software knew about EXIF.
The way to fix this (and this is what the previous iPhone OS did) is to rotate the actual photo data when it comes off the sensor; unfortunately this operation is computationally expensive, and so I assume Apple stopped doing it so the iPhone would save photos faster (perhaps this is one reason the iOS4 camera app is so much more responsive)
Here’s how I fixed this problem for InstaGal, which uses Paperclip for attachments and is hosted on Heroku.
The basic idea is to use the EXIF orientation information to rotate the actual photo before saving. Fortunately, the ImageMagick library (which Paperclip uses) has a convenience “auto-orient” method that saves you the trouble of manually reading the EXIF data and making the appropriate rotation.
How do we get Paperclip to use ImageMagick’s auto-orientation? Google around and you’ll see recommendations to add :convert_options => { :all => '-auto-orient' } to your Paperclip configuration like so:
has_attached_file :image, :storage => :s3,
:styles => { :medium => "600x600>", :small => "320x320>", :thumb => "100x100#" },
:convert_options => { :all => '-auto-orient' },
:s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:path => "/:style/:id_:filename"This supposedly passes the “auto-orient” option to ImageMagick before it creates your thumbnails, but it didn’t work for me on Heroku, and messing with ImageMagick command line options is a bit hacky.
Instead, we’ll use RMagick (the Ruby version of ImageMagick) in a separate image processor.
First, create the file config/initializers/auto_orient.rb:
module Paperclip
class AutoOrient < Paperclip::Processor
require 'RMagick' # Make sure to update your gem file
def initialize(file, options = {}, *args)
@file = file
end
def make( *args )
img = Magick::Image.read("#{File.expand_path(@file.path)}")[0]
img.auto_orient!
temp = Tempfile.new(@file.path.split('/').last)
img.write(temp.path)
return temp
end
end
endNow, update your Paperclip settings:
has_attached_file :image, :storage => :s3,
:styles => { :original => '5000x5000>', :medium => "600x600>", :small => "320x320>", :thumb => "100x100#" },
:processors => [:auto_orient, :thumbnail],
:s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:path => "/:style/:id_filename"Note:
And that’s it! Paperclip will automatically correct the orientation of your attached photos before resizing and saving them.