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.
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
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....
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
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
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
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 %>
require "open-uri" # at the top
puts 'Creating cocktails... (this could take a minute)'
30.times do
cocktail = Cocktail.create!(name: 'SOMETHING_FROM_FAKER')
file = URI.open('http://loremflickr.com/300/260/cocktail')
cocktail.photo.attach(io: file, filename: 'cocktail.png', content_type: 'image/png')
end
puts "...created #{Cocktail.count} cocktails"
If you want to require your user to upload an image, you can follow the Active Storage Validations gem setup.