Working With Cloudinary in Rails App
Cloudinary is a SaaS technology company headquartered in Santa Clara, California, with an office in Israel. The company provides a cloud-based image and video management services. It enables users to upload, store, manage, manipulate, and deliver images and video for websites and apps.
How We integrated Cloudinary to my Rails App
Under config/initializers, we created a new file named cloudinary.rb. In this file, we copied the following configuration:
Cloudinary.config do |config| config.cloud_name = ENV["CLOUD_NAME"] config.api_key = ENV["CLOUD_API_KEY"] config.api_secret = ENV["CLOUD_API_SECRET"] config.secure = true config.cdn_subdomain = true end
In the fields for cloud_name, api_key, and api_secret, we replaced them with the information from the Cloudinary account details.
We added the line below in your Gemfile. This installs cloudinary in my application.
gem 'cloudinary'
Then in the controller, we went ahead and implemented the upload feature as shown below
def update_profile_image current_user = User.find_by(:id => params[:id]) upload_image = Cloudinary::Uploader.upload(params["image_url"]) user = { image_url: upload_image["url"] } begin current_user.update!(user) render json: { url: upload_image["url"] }, status: :ok rescue => exception render json: exception, status: :bad_request end end
Once I uploaded successfully, Cloudinary sent a response containing the URL strings of the image. Which I stored in my Rails database
HOW WE TESTED UPLOADING THROUGH CLOUDINARY
We used Rspec for testing.
First, we created a folder called fixtures inside the spec folder.
We then got a random image we wanted to upload and called it test.png
We copied the test image into the fixtures folder. `spec/fixtures/test.png`
The test we wanted to achieve was that a url is added to the image_url field of the user model.
In order to test this, we just had to hit the update endpoint with the file in the params.
it ‘adds a url to the image_url field of a user’ do file = Rack::Test::UploadedFile.new(Rails.root.join("spec", "fixtures", "test.png"), "image/jpg") new_user = FactoryBot.create(:user) user = User.find_by_id(new_user.id) expect(user.image_url).to be nil patch "/user/profile_picture/#", params: { image_url: file }, headers: auth_headers user = User.find_by_id(new_user.id) expect(user.image_url).to be_truthy end
Factory bot is being used to create the user, after we are querying to get that user as it is in the database. Initially, the user doesn’t have a url, and then after we hit the endpoint, there’s now a url added.
This test passes but it still has a minor problem. This will actually upload the image to cloudinary for every test that we run. To avoid this, we need to stub the cloudinary service.
Stubbing cloudinary service
We used webmock to mock the service.
Add webmock to the gem file.
gem 'webmock'
We used webmock to block all outgoing requests.
require "webmock/rspec" Webmock.disable_net_connect!(allow_localhost: true)
After adding this, the test should fail because we can no longer make requests to external services.
Now, we can stub a response from cloudinary by adding the following line before the request.
stub_request(:any, /api.cloudinary.com/).to_return(body: { url: 'fake_url'}.to_json)
The stub is for any http request such as get, post, update, etc. It returns the response body with a fake_url.Now the test should be passing.The tests can then be modified to make sure that the fake_url is actually being added to the image_url field and not just testing it to be truthy.