Paperclip, Heroku and Amazon S3 credentials 4

Posted by Slobodan Kovačević on January 24, 2011

Setting up Paperclip to use Amazon’s S3 is as simple as setting :storage => :s3 and providing right credentials to Paperclip by setting :s3_credentials option. Best way to provide S3 credentials is to use an YML file (usually config/s3.yml) which allows you to set different credentials for each environment. For example:

# config/s3.yml
development:
  access_key_id: XYZXYZXYZ
  secret_access_key: XYZXYZXYZ
  bucket: mygreatapp-development
production:
  access_key_id: XYZXYZXYZ
  secret_access_key: XYZXYZXYZ
  bucket: mygreatapp-production

Of course you want to treat s3.yml same as database.yml – i.e. you don’t want to track it with git and you want for each person/server to have it’s own.

However, consider this: you are working on Open Source app in a public git repository and you are deploying it on Heroku. Heroku doesn’t allow you to create files (unless they are in git repository) and you can’t commit s3.yml with your credentials to public repository.

One solution is to define different :s3_credentials hash in one of the environment files or to load different YML file for each environment and generate hash from it. Downside is that you need to have a separate YML file for each environment and/or you need to convert YML to hash. Other solution could be to have separate local branch from which you will push to Heroku. Problem with this is that you have to have a local branch for deploying. This means if there are multiple developers who deploy to production each should have separate local branch.

Much simpler way to deploy Paperclip with different S3 credentials for each environment (with one of the environment being deployed on Heroku; and repository being public) is to create s3.yml file as usual (and don’t commit it to git), but define values only for local environment.

For production deployment on Heroku you can write initializer which will set :s3_credentials from ENV variables.

# initializers/s3.rb
if Rails.env == "production"
  # set credentials from ENV hash
  S3_CREDENTIALS = { :access_key_id => ENV['S3_KEY'], :secret_access_key => ENV['S3_SECRET'], :bucket => "sharedearth-production"}
else
  # get credentials from YML file
  S3_CREDENTIALS = Rails.root.join("config/s3.yml")
end

# in your model
has_attached_file :photo, :storage => :s3, :s3_credentials => S3_CREDENTIALS

and you can easily set persistant ENV vars on Heroku with:

$ heroku config:add S3_KEY=XYZXYZ S3_SECRET=XYZXYZ

(according to Heroku docs)

Share

Rails 3 reading list

Posted by Slobodan Kovačević on March 29, 2010

I’ve been planning to catchup with all the new Rails 3 stuff. To get me started I’ve compiled a small Rails 3 related reading list.

  1. Ruby on Rails 3.0 Release Notes
  2. Active Record Query Interface 3.0
  3. The Skinny on Scopes (Formerly named_scope)
  4. Rails 3 Beautiful Code
  5. Railscasts – rails-3.0 episodes
  6. jQuery with Rails 3
  7. The Rails Module (in Rails 3)

Once I’m done with it I plan to get even more from:

Share

Expected x.rb to define X (LoadError)

Posted by Slobodan Kovačević on April 15, 2009

I have been working on extending Rails’ I18n Simple backend to make it work with Serbian grammar (post on that will follow soon), but I kept getting an error:

Expected ./lib/serbian_simple.rb to define SerbianSimple (LoadError)

I’ve just spent an hour trying to figure out why this keeps happening and I found that there’s a lot of people with similar problem.

It seems that the problem appears when Rails tries to autoload files. In my case there was a simple solution – I just added require ‘serbian_simple.rb’ in environment.rb to manually load the file.

Share

RESTful admin namespaced controller using scaffolding 35

Posted by Slobodan Kovačević on September 17, 2008

Most of my clients prefer to have a separate admin section. In turn, I like to have separate controllers for admin section and front-end in my Rails app. This is not as straightforward as it might seem, especially if you like to use scaffolding for admin controller.

The goal is to get 2 separate RESTful controllers, admin & front-end controller, one model and for admin pages to have scaffolding.

Here is the easiest way I found so far to accomplish this. This example generates categories model and controllers for it.

./script/generate controller admin/categories
./script/generate scaffold category name:string

This will generate an empty controller in admin namespace and a scaffolded resource for front-end controller.

Now we have everything generated we just need to make it work with admin controller and not with front-end.

  • move all templates from app/views/categories to app/views/admin/categories
  • copy all functions from categories_controller.rb to admin/categories_controller.rb
  • add namespace for admin controller in routes.rb:map.namespace :admin do |admin|
    admin.resources :categories
    end
  • in admin/categories_controller.rb replace in 3 places redirect_to calls to work with admin namespace. It will have something like redirect_to(@category), but to work with namespace it needs to have redirect_to([:admin, @category])
  • make similar changes in all templates, i.e. make it work within an admin namespace. You need to make following changes:
    • form_for(@category) => form_for([:admin, @category])
    • <%= link_to ‘Show’, @category %> => <%= link_to ‘Show’, [:admin, @category] %>
    • categories_path => admin_categories_path
    • edit_category_path(@category) => edit_admin_category_path(@category)
    • new_category_path => new_admin_category_path

That’s it. Now you’ll have /admin/categories for all administrative tasks and you have a free controller for front-end actions.

You might wonder why not just generate scaffold for admin/categories… The reason is that you’ll also get a model that is namespaced in admin (i.e. model would be Admin::Category). Scaffolded views also wouldn’t work as it seems that generator doesn’t take into account the fact that you are using a namespace.

Share

Mosso hosting cloud 5

Posted by Slobodan Kovačević on September 17, 2008

Mosso’s hosting cloud at $100 / month seems like a good solution to get a scalable server. However one thing bugs me, actually two things…

First one: they offer FTP only access. Meaning you cannot deploy sites directly from code repositories (i.e. git or svn). That sucks.

Second thing that bugs me: for $100 you get quite a lot of computing power which can be used to run multiple sites – but you are only allowed to have one Rails app running. Only one. If you want additional Rails apps (for example to have a test server) you need to pay an extra fee.

I know it’s cloud computing and that you have to be able to run it with any additional configuration (that’s why I think it’s ok that you have to freeze your gems in Rails apps, because you cannot install any gems yourself)… but not being able to checkout my code from repository and having to upload the whole app each time you make changes is really annoying.

In their defense, the support guy said that they are working on it, but he could give me an ETA when they’ll allow something like that.

Share