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.

References

Stubing external service

Uploading a file to cloudinary

Tezza_2020_02_24_152932868.jpg

Emmanuel Omona

co - author

Previous
Previous

First Client Demo

Next
Next

My Experience with Docker