Using Ruby on Rails 5.2 Active Storage
Tags: rails, tutorial
Published: 2019-08-06 14:54:02

**Active Storage**, now bundled with **Ruby on Rails 5.2**, allows you to easily handle your file upload, either to cloud platforms, such as **Amazon S3** or **Google Cloud Storage**, or even upload right into your application directory, linking your uploads to your **Active Record** models with ease.

In order to start using **Active Storage**, you should have an existing **Ruby on Rails 5.2** or greater application (freshly installed or upgraded), open your terminal in your application directory and type the following command:

```bash
rails active_storage:install
```

This will generate a brand new migration for the two necessary database tables. After this, all you need to do is run the migrations with:

```bash
rails db:migrate
```

After this everything is ready to start using **Active Storage**.

## Setting up

To start you should declare in your `config/storage.yml` file your **Active Storage** services, one for each service that you use in your application.

> Please, note that for the sake of this article, I’ll just be showing configurations for uploading the files to the same directory as your application, in your local disk. If you prefer to use it to upload to any cloud provider please refer to the [docs](https://guides.rubyonrails.org/v5.2/active_storage_overview.html).

```yaml
local:
    service: Disk
    root: <%= Rails.root.join("storage") %>
```

After you declared every service that you’ll be using it’s time to tell your application what service should be used on each environment. In this article, I’m going to configure my **development** environment to use my **local** storage service. In order to achieve this add the following line to `config/environments/development.rb`:

```yaml
# Store files locally.
config.active_storage.service = :local
```

## Use it

Now that we managed to configure **Active Storage** it’s time to start using it! One of the advantages of it is to easily create a relationship between the uploaded asset and your **Active Record** models, as stated at the beginning of this article, so we’re to take advantage of it. I have a **Speakers** model in which I intend to add an avatar to each of the **speakers**.

We’ll start by informing my **Speakers** model that it will have a `one-to-one` relationship with the corresponding uploaded avatar. My model will look like:

```ruby
class Speaker < ApplicationRecord
    has_one_attached :avatar
end
```

Then my **Speakers** controller will accept an avatar to be uploaded. My `speaker_params` will now look like:

```ruby
def speaker_params
    params.permit(:name, :company, :email, :avatar)
end
```

The file should now look like this:

```ruby
class SpeakersController < APIController
    before_action :set_speaker, only: %i[show update destroy]    def index
        @speakers = Speaker.all
        render json: @speakers, status: :ok
    end    def create
        @speaker = Speaker.create!(speaker_params)
        render json: @speaker, status: :created
    end    def show
        render json: @speaker, status: :ok
    end    def update
        @speaker.update(speaker_params)
        head :no_content
    end    def destroy
        @speaker.destroy
        head :no_content
    end    private    def speaker_params
        params.permit(:name, :company, :email, **:avatar**, :social_media)
    end    def set_speaker
        @speaker = Speaker.find(params[:id])
    end
end
```

I have to specify on my **request** body a field named **avatar** and its value will be a file, that’s going to be used as the speaker avatar.

After you finish your **POST** request and you navigate to the `storage` directory you’ll see a newly created directory containing your uploaded file. If you wish to verify that the file is linked to your speaker you can start `rails console` and type:

```bash
irb(main):001:0> speaker = Speaker.find(1)
=> #<Speaker id: 1, name: “Rafael”, email: “rafael.almeida@xing.com”, company: “XING”, social_media: nil, created_at: “2018–10–02 09:58:54”, updated_at: “2018–10–02 09:58:54”>
irb(main):002:0> app.url_for(speaker.avatar)
=> “http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--372a32ffdde03c2f2a37f407e63d10c909fd52ba/xing-photo.jpg"
```

_Et voilá_, **Active Storage** is configured and ready to be used.

## Bonus tip

If you execute a **GET** request on your **API** you’ll get the information about your **speaker** that you defined on your migration, like the example below:

```json
{
    "id": 2,
    "name": "Rafael",
    "email": "rafael.almeida@xing.com",
    "company": "XING",
    "social_media": null,
    "created_at": "2018-10-02T09:59:28.494Z",
    "updated_at": "2018-10-02T09:59:28.497Z"
}
```

However, you also might want to get the associated avatar. We can use a **serializer** in order to achieve what we intend. You’ll need to add `gem 'active_model_serializers'` and then include the following line in every **controller** that you want to get override with the **serializer**: `include ActionController::Serialization`. After that you can use `rails g serializer speaker` and **Rails** will generate the file for you.

In order to get the associated **avatar** your `serializer` should look like:

```ruby
class SpeakerSerializer < ActiveModel::Serializer
  attributes :id, :name, :email, :company, :avatar

  def avatar
    rails_blob_path(object.avatar, only_path: true) if object.avatar.attached?
  end
end
```

Now if you re-run your **GET** request you’ll see something like:

```json
{
    "speaker": {
        "id": 2,
        "name": "Rafael",
        "email": "rafael.almeida@xing.com",
        "company": "XING",
        "avatar": "http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCdz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19–515a0de8817b3529b5d3d168871cebf6ccee0463/xing-photo.jpg"
    }
}
```

You’re now able to see the avatar associated with every speaker.