Super Spread Sheet S³

Or little computing tricks and hacks

Category Archives: Carrierwave

Rails: Different storage for carrierwave assets depending on the environment

I am using carrierwave for storing images, one (or more) per field of a table in a database of the my Rails app. Given that heroku does not allow assets to be uploaded by the user, or dynamically for that matter, I had to use storage in the cloud. Mind you, not a bad thing. I chose AWS, s3 services to store my bucket.

Carrierwave has an extensive explanation on setting up the right configuration variables to allow the storage on the cloud. (Carrierwave in github)

The first problem I had was running the development and staging (or production) sites at the same time: Updates in one were seen in the other in the following case:

  1. Create a field with id=1 in production. The image associated with it, is stored in the cloud with id=1.
  2. Create a field with id=1 in development. the image associated with it, overrides in the cloud, the previously created image.

My solution to solve this problem was to create a second bucket, and add in the file

config/initialize/carrierwave.rb

the setting as follows:

# config/initialize/carrierwave.rb
...
  if Rails.env.production?
    config.fog_directory = 'bucket-0'
  elsif Rails.env.development?
    config.fog_directory = 'bucket-1'
  end

The next problem was that the test suite run was affecting the development data in the same way as described before. In fact, I wanted the images for the test suites simply to be stored locally and then deleted. The first approach was to include in the same file as above the following:

# config/initialize/carrierwave.rb
...
  if Rails.env.test? || Rails.env.cucumber?
    config.storage = :file
    config.enable_processing = false
    config.root = "#{Rails.root}/tmp"
  else
    config.storage = :fog
  end

  config.cache_dir = "#{Rails.root}/tmp/uploads"

And to delete after finishing the test, add to the file:

spec/spec_helper.rb

the following:

# spec/spec_helper.rb
RSpec.configure do |config|
  config.after(:all) do
    if Rails.env.test?
      FileUtils.rm_rf(Dir["#{Rails.root}/tmp/uploads"])
    end
  end
end

That only seemed to work. I think that the display was using cache data. Only recently did I notice the problem when reworking some aspects of the app. The images in development were being overwritten by the test suite.

I had to add the following to the uploader file

# app/uploaders/image_uploader.rb
...
  if Rails.env.test?
    storage :file
  else
    storage :fog
  end

Et voilá!

Advertisements

Image manipulation commands in Carrierwave

In my Rails app, I am using the gems

gem 'mini_magick'
gem 'carrierwave'

for image manipulation of files entered by users. Even though the sample files are full of useful comments, I did not find a comprehensive list of the possible commands. I needed to create exact sizes respecting ratio. The internet searches failed me today! Perhaps I was in a rush…

I found this post, which is actually describing another gem for image manipulation, describing the following commands:

resize_to_limit(width, height)

resize an image down to fit the width and height, maintaining the image ratio.

resize_to_fit(width, height)

resize an image up or down to fit the width and height, maintaining the image ratio.

resize_to_fill(width, height)

crop and resize to fit the width and height.

Deploying Rails in Heroku using AWS S3 to store carrierwave files

I am developing an app which requires users to upload pictures on updates. Heroku allows only for transient pictures, staying alive only minutes or seconds in its temporary storage. The solution was to store all the pictures in the cloud.

For this I used AWS. The steps:

  1. Create an account in AWS, which for the first year is free for the first year. The verification process is lengthy and you need a phone as you will receive an automatic call.
  2. Create a bucket, which in Linux terms is a directory. You can do this by going to services -> S3 and there you should have an option for creating a bucket.
  3. Create a IAM user. This is very important to allow you to manage access to your account giving specific permissions. Grab the credentials right then and put them in a safe place. You will not have access to them again, you need to recreate to see them.
  4. To give access privileges to that user, it seems that you have to create a IAM group and grant privileges to that group. Then add the user to the group. There might be a way to just grant access to the user but having a group is the suggested way.
  5. That is all from form the AWS side.
  6. Instead of saving the keys in a file, which you risk adding to the git repository exposing the keys, heroku suggests adding them as environment variables. That is achieved by following the instructions in the link, and breifly it looks like this:

     

    $ heroku config:set S3_KEY=THATVERYLONGSTRING
    Adding config vars and restarting app... done, v12
    S3_KEY: THATVERYLONGSTRING
    
    $ heroku config
    S3_KEY: THATVERYLONGSTRING
    (and any other environment variables that might be set)
    
    $ heroku config:get S3_KEY
    THATVERYLONGSTRING
    
    $ heroku config:unset S3_KEY
    (When you don't need it anymore)
    
  7. If running on development at the same time, do set the environment variables locally as well.
  8. Add the gems to the Gemfile:
    gem 'fog'
    gem 'fog-aws'
  9. I am not sure if both are needed, but I started with fog-aws and was getting errors of initialized variable. Once I added fog, there were no problems. The problem might had been that I am using an older version of carrierwave.
  10. Update the config/initializers/carrierwave.rb and each of the image uploaders. I used the information here and here.

     

    # config/initializers/carrierwave.rb
    
    CarrierWave.configure do |config|
      config.fog_credentials = {
        :provider              => 'AWS',
        :aws_access_key_id     => ENV['S3_KEY'],
        :aws_secret_access_key => ENV['S3_SECRET']
      }
    
      if Rails.env.test? || Rails.env.cucumber?
        config.storage = :file
        config.enable_processing = false
        config.root = "#{Rails.root}/tmp"
      else
        config.storage = :fog
      end
    
      config.cache_dir = "#{Rails.root}/tmp/uploads"
    
      config.fog_directory = ENV['S3_BUCKET_NAME']
    end
    
    #app/uploaders/image_uploader.rb
    
    class ImageAuthorUploader < CarrierWave::Uploader::Base
      storage :fog
      def store_dir
        "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
      end
    end
    
  11. And that should do it! It did for me.

A related post and video presentation on the subject by Nicholas Henry can be found here.