Thursday, January 27, 2011

Rails Dragonfly Image Upload With S3 and Test Faking with Fakeweb

A while ago, I wrote a post titled "Faking Paperclip S3 calls with Fakeweb". Since then, while working with the Rails CMS Refinery, I discovered the delights of using the simpler Rack integrated Dragonfly library for easy upload/processing of images in a Rails web application.

Here is a guide on how to set Dragonfly up to work with S3, and how to fake its Cucumber integration test calls with Fakeweb.

Assuming you already have Dragonfly configured for use by their GitHub Instructions, edit the Dragonfly initializer and add the following line:


app.configure_with(:heroku, ENV['S3_BUCKET'])


Note that it is a bit quirky that we are referencing heroku here, but all what that Dragonfly config file does is configure S3 storage to read environment variables. There are other more verbose ways of doing this that are also easy, but using that one line is easiest. In the future, it is worth contributing a config file to Dragonfly that has a better name than heroku for configuring S3.

Next, edit your Rails 3 application.rb file and add the following above your application module:

ENV['S3_BUCKET'] = "APPLICATION_BUCKET_NAME_#{Rails.env}"
ENV['S3_KEY'] = "S3_KEY"
ENV['S3_SECRET'] = "S3_SECRET"


Assuming you are using Bundler, edit Gemfile and add the following:


gem 'rack-cache', '1.0.0', :require => 'rack/cache'
gem 'dragonfly', '0.8.1'

group :test do
gem 'fakeweb'
# other test gems go here
end


Now, add the following step to your general steps file:


When /^(?:|I )attach the image "([^\"]*)" to "([^\"]*)"$/ do |file_path, field|
base_path = "http://s3.amazonaws.com/"

path = "^#{base_path}#{ENV['S3_BUCKET']}/([^/.]+/)*#{File.basename(file_path)}$"
FakeWeb.register_uri(:put, Regexp.new(path), :body => "OK")

body =<<-endstring
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ListAllMyBucketsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Owner><ID>cc51eabc6701f10ce998d1c4acce7e3b1dc5510ac392a24257c59922514c7baf</ID><DisplayName>S3_USER_NAME</DisplayName></Owner><Buckets><Bucket><Name>APPLICATION_BUCKET_NAME_test</Name><CreationDate>2011-01-20T21:42:56.000Z</CreationDate></Bucket></Buckets></ListAllMyBucketsResult>
endstring
FakeWeb.register_uri(:get, base_path, :body => body)

When "I attach the file \"#{file_path}\" to \"#{field}\""
end


The XML above provides Dragonfly with info about existing buckets in S3. This will ensure Dragonfly does not attempt to create the application bucket via a remote call. Make sure to replace APPLICATION_BUCKET_NAME_test with the right name for your test environment.

Finally, use that step in your Cucumber feature files:


When I attach the image "features/support/sample_image.jpg" to "Logo"


That is assuming of course that you have a file called sample_image.jpg nested under your Cucumber support directory.

Enjoy reaping the benefits of S3 usage with Dragonfly and test-driving it with Cucumber.

p.s. Let me know in comments if I missed a detail or if you have any questions.

3 comments:

Unknown said...

wow how helpful, really, thanks.

Milind said...

Thanks for the article...it helped.pls keep up the good work..:)

Unknown said...

uploads are generally a tricky area in web development. In this tutorial, we will learn how to use Dragonfly, a powerful Ruby gem that makes it easy and efficient to add any kind of upload functionality to a Rails project.

link: http://tut.biitbook.com/how-to-upload-files-with-ease-using-dragonfly/
link: http://biitbook.com/books/detail/how-to-upload-files-with-ease-using-dragonfly