Setting up ActiveStorage and Cloudinary

How to install ActiveStorage and upload to Cloudinary

There are a lot of steps (and copying and pasting) in order to setup ActiveStorage and Cloudinary in your Rails application. Let's try to do it together.

Adding the Gems

We'll need a place to put our Cloudinary API key since we don't want to push it to Github. We're going to use the gem dotenv-rails for this. So let's add that and Cloudinary to our Gemfile.

# Gemfile
gem 'dotenv-rails', groups: [:development, :test]
gem 'cloudinary', '~> 1.16.0'

Since we changed our Gemfile, we need to bundle install. We'll also need to create the .env file and add it into our .gitignore. Let's copy and paste this in your Terminal.

bundle install
touch .env
echo '.env*' >> .gitignore

Providing your API keys

Cloudinary env

Let's go to your Cloudinary console to get *your* API environment variable. Copy it and paste it into your .env file.

# .env
CLOUDINARY_URL=cloudinary://298522699261255:Qa1ZfO4syfbOC-***********************8

While we're setting the secret key, let's not forget to let Heroku know as well. Let's paste our CLOUDINARY_URL in the Terminal.

heroku config:set CLOUDINARY_URL=cloudinary://298....

Installing ActiveStorage

There's a lot of "magic" happening behind the scenees with ActiveStorage. It order for it to work, it needs a bit of configuration so be careful with the copy and pasting here. First, the migrations

rails active_storage:install
rails db:migrate

Now for the config files. Make sure you're in the correct file, there are three.

# config/storage.yml
cloudinary:
  service: Cloudinary
# You can remove everything else in this file.

Replace :local by :cloudinary in the config.

⚠️ Make sure you do not have this line twice.

# config/environments/development.rb
config.active_storage.service = :cloudinary

And also in the production file

# config/environments/production.rb
config.active_storage.service = :cloudinary

Attaching a photo to a model

This will allow users to upload a photo to our model.

Make sure you've restarted your Rails server.

class Cocktail < ApplicationRecord
  has_one_attached :photo
end

Now for the form and strong parameters.

<!-- app/views/cocktails/_form.html.erb -->
<%= simple_form_for @cocktail do |f| %>
  <!-- [...] -->
  <%= f.input :photo, as: :file %>
  <!-- [...] -->
<% end %>
# app/controllers/articles_controller.rb
def cocktail_params
  params.require(:cocktail).permit(:name, :photo)
end

Using the image in the view

This can be tricky so you need to be careful. There's two different ways we can use the new images

cl_image_tag builds an img tag.

cl_image_path builds a url "https://res.cloudinary.com/..."

Adding a photo as a background image

<div class="card-category" style="background-image: linear-gradient(rgba(0,0,0,0.3), rgba(0,0,0,0.3)), url(<%= cocktail.photo.attached? ? (cl_image_path cocktail.photo.key) : "http://loremflickr.com/300/260/cocktail" %>)">
  <%= cocktail.name %>
</div>

Notice what went into the card url. We have a conditional to make sure there's a photo attached. cocktail.photo.attached? ? truthy : falsy

Link to the CSS

Adding a photo as an image

<% if cocktail.photo.attached? %>
  <%= cl_image_tag cocktail.photo.key, alt: "cocktail image", class: 'anthing_you_want' %>
<% else %>
  <%= image_tag "http://loremflickr.com/300/260/cocktail", alt: "cocktail image", class: 'anthing_you_want' %>
<% end %>

Attaching Photos in your seeds

require "open-uri" # at the top

puts 'Creating cocktails... (this could take a minute)'
30.times do
  file = URI.open('http://loremflickr.com/300/260/cocktail')
  cocktail = Cocktail.create!(name: 'SOMETHING_FROM_FAKER')
  cocktail.photo.attach(io: file, filename: 'cocktail.png', content_type: 'image/png')
end
puts "...created #{Cocktail.count} cocktails"

Active Storage Validations

If you want to require your user to upload an image, you can follow the Active Storage Validations gem setup.