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.