Capstone Project Update: Utilizing Active Storage with AWS S3 Service in Rails
Published: 2023-07-13 02:43:54

Welcome back to the update of my capstone project, where I share the highs and lows of my journey at Flatiron School. Today, I bring you a tale of perseverance, determination, and ultimately, success. After facing numerous challenges and moments of frustration, I am happy to share that I have finally been able to incorporate **Active Storage** with **AWS S3 Service** in my project.

When I first embarked on this venture, I had high hopes for incorporating Active Storage—a powerful file uploading system—into my Capstone Project. My intention was to leverage the benefits of AWS S3 Service, a scalable and reliable cloud storage solution, to enhance the functionality and user experience of my application. However, little did I know that this seemingly straightforward integration would push me to the limits of my programming knowledge.

Join me as I recount the steps I took, the resources I utilized, and the key insights I gained along the way. I hope that by sharing my experience, I can provide guidance and inspiration to fellow developers who may find themselves in a similar predicament.

## CREATE an AWS ACCOUNT

If you don't have already sign up for AWS and explore Free Tier products [here](https://portal.aws.amazon.com/billing/signup#/start/email) and fill in your information to get started.

To initiate the process of creating an S3 bucket, navigate to the AWS console and conduct a search for "S3". Once you have accessed the S3 Management Console, locate and click on the "Create Bucket" button. This action will promptly lead you to where you can define the specific details of your bucket. Choose a distinctive and easy-to-remember name, and for now, it is advisable to retain the default region setting.


![aws1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p5vxxgeebjpvpeimlhn8.png)

To ensure the security of your bucket and prevent unauthorized access, it is recommended to keep the "Block All Public Access" option selected. This ensures that your bucket remains inaccessible to individuals who should not have permission to use it. Also suggested to leave the "ACLs" option set to "Disable" and "Bucket Versioning" since it's for personal use and will not be utilized.


![aws2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/onrgvv6cg90esuinpcmi.png)

Once you have configured the above settings, click on the "Create" button, and your S3 bucket will be successfully created. However, it is important to note that configuring permissions on the bucket is still necessary. To do this, proceed to the AWS console and navigate to the Identity Access Management (IAM) section. From the side panel, select "Users".

![aws3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6rl173gbijw5enznvooq.png)

Click on "Add User" and assign a suitable name to the new user, like example "active-storage-file". It is recommended to grant this user only programmatic access. On the subsequent screen, you will need to set permissions. Choose the "Attach Existing Policies Directly" tab and search for "S3". Then, select the checkbox beside "AmazonS3FullAccess".

Next, locate and click on your User name in the AWS console, and then navigate to the Security credentials section. Scroll down the page and find the option to Generate credentials. By clicking on it, you will create a unique key ID and secret key ID. It is crucial to take note of these credentials as they will not be accessible once you exit the page.

## Install and Set Up the AWS SDK Gem

- In your Rails application's Gemfile, add the aws-sdk-s3 gem: ```
gem "aws-sdk-s3", require: false
```
- Run bundle install to install the gem.

## Configure Active Storage

- Open config/storage.yml file in your Rails application.
- Add an S3 service configuration under the amazon key, specifying your S3 credentials and bucket name. For example:

```
amazon:
  service: S3
  access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
  secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
  region: 'your-aws-region'
  bucket: 'your-bucket-name'
```
##  Set Environment Variables

To effectively manage all my credentials, I have installed the Dotenv gem, which allows me to store and access environment variables securely. Dotenv ensures that my sensitive information, including the AWS key ID and secret key ID, remains protected and separate from my application's source code.

Create ".env" file in the root directory of your project. This file will house the API key required for your development environment. It is crucial to add the ".env" file to your ".gitignore" file, preventing it from being pushed to your repository. This way, you can keep your API key confidential and avoid any unauthorized access or exposure.

## Enable Active Storage

- Open config/environments/production.rb and config/environments/development.rb in your Rails application.
- Uncomment or add the following line to enable Active Storage for production:

```
config.active_storage.service = :amazon
```

## Migrate the Database

- Run the following command to create the necessary database tables for Active Storage:

```
rails active_storage:install
rails db:migrate
```
## Use Active Storage in Your Models

- In the model where you want to use Active Storage, add the has_one_attached or has_many_attached method. For example, in my UserAdmin model:

```
class UserAdmin < ApplicationRecord
  has_one_attached :profile_pic

validate :profile_pic_validation

  def profile_pic_validation
    if profile_pic.attached?
      if profile_pic.blob.byte_size > 8.megabyte
        errors.add(:profile_pic, "file is too big")
      end

      acceptable_types = ["image/jpeg", "image/png"]
      unless acceptable_types.include?(profile_pic.blob.content_type)
        errors.add(:profile_pic, "must be a JPEG or PNG")
      end
    else
      errors.add(:profile_pic, "must be attached")
    end
  end
end
```
You can add additional custom validations such as file size, content type, and provide appropriate error messages if the validation fails.

This serializer method generates the URL for the profile picture only if it is attached to the object. If the profile picture is attached, it returns the URL path generated by rails_blob_path. Otherwise, it returns nil. This allows you to include the profile picture URL in the serialized representation of the object, if it is available.

```
class UserAdminSerializer < ActiveModel::Serializer
  include Rails.application.routes.url_helpers

  attributes :id, :first_name, :last_name, :email, :profile_pic
  
  def profile_pic
    rails_blob_path(object.profile_pic, only_path: true) if object.profile_pic.attached?
  end

end
```
## Seeding With Existing Database

This is the point at which I encountered a challenge because I had already seeded my database with an existing userAdmin. To proceed, start by creating a folder within the db directory to store the images that you intend to attach to your seeds. Then run *rails db:seed*


![seed](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dbx3cx5v7ycv530c10pe.png)

**RESOURCES**

Check out my previous blog post about my Capstone Project [here](https://dev.to/janicera2880/celebrating-a-milestone-capstone-project-unveiled-51c1).

Creating AWS Account [HERE](https://www.honeybadger.io/blog/rails-app-aws-s3/)

Installing Dotenv [HERE](https://github.com/bkeepers/dotenv)

Active Storage Setup [HERE](https://pragmaticstudio.com/tutorials/using-active-storage-in-rails)

MORE Active Storage Blogs [HERE](https://dev.to/nemwelboniface/the-ultimate-guide-to-active-storage-in-rails-2f57)