# Level Up Your Rails Forms with Active Storage

*Author's Note: This post is related to the  [Rails Fitness application](https://github.com/Dusch4593/flatiron_fitness_rails_app)  that I started earlier this summer. I wrote a multi-post series about building this app and you can read the prelude to Part 1  [here](https://blog.mydevdiary.net/prelude-to-upcoming-rails-project-series-ckarmzr1r04txbbs1vc2629g7) . *


While reviewing the code, I decided I wanted a user to be able to upload images to go with a **routine** and/ or its **exercises**. To achieve this goal, I decided to configure image uploads and attachments through Active Storage.


![active_storage.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1597259824005/OoGaTQp-L.png)

# What Is Active Storage? 
 [Active Storage](https://edgeguides.rubyonrails.org/active_storage_overview.html) is a component of Rails that lets a user upload and attach one or more files to an Active Record object.

More specifically, Active Storage lets you: 

* Transform image uploads via  [ImageMagick](https://imagemagick.org/index.php) 
* Render non-image files such as PDF and video

# Setup
The only real requirement for setting up Active Storage properly is for the version of Rails to be 5.2 or higher. Other than that, the setup process is pretty simple. 

### 1. Set up the migrations
Run `bundle exec rails active_storage:install`, then `bundle exec rails db:migrate` and take a look at the following code added to `db/scheme.rb`: 

```
  create_table "active_storage_attachments", force: :cascade do |t|
    t.string "name", null: false
    t.string "record_type", null: false
    t.integer "record_id", null: false
    t.integer "blob_id", null: false
    t.datetime "created_at", null: false
    t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
    t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
  end

  create_table "active_storage_blobs", force: :cascade do |t|
    t.string "key", null: false
    t.string "filename", null: false
    t.string "content_type"
    t.text "metadata"
    t.bigint "byte_size", null: false
    t.string "checksum", null: false
    t.datetime "created_at", null: false
    t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
  end
```
Two new tables are created afterward. The first, `active_storage_attachments`, according the Rails Guides article on Active Storage, is a "polymorphic join table that stores your model's class name". It is the connection between the model at hand and the other table. `activestorage_blob` contains all of the key metadata pertaining to the uploaded file. The "blob" stores information such as `filename`, `content_type`, and `metadata`.

### 2. Attach the file(s) to a model
Now that we've migrated the necessary tables related to Active Storage, we need to set up the mappings between a given record and the file(s) that might be attached to it. There are two distinct ways to set up attachments in models 

With `has_one_attached`, we are establishing a *one-to-one* connection between an Active Record object and a single file (represented as an instance of `ActiveStorage::Attached::One`)

```
class Exercise < ApplicationRecord
  has_many :routine_exercises, dependent: :destroy
  has_many :routines, through: :routine_exercises
  has_one_attached :image

  def exercises_attributes=(exercise)
    if exercise[:name] != "" && exercise[:exercise_type] != "" && exercise[:description] != ""
      new_exercise = self.exercises.build
      new_exercise.name = exercise[:name]
      new_exercise.exercise_type = exercise[:exercise_type]
      new_exercise.description = exercise[:description]
      new_exercise.image.attach(exercise[:image]) # ^^^
    end
  end
end

```
**^^^**If there are custom attribute setters (like in the snippet above), then the file(s) needs to be attached to new instance being built. 

With `has_many_attached`, we are setting up a *many-to-many* connection between the Active Record object and multiple files (represented altogether as an instance of `ActiveStorage::Attached::Many`)

### 3. Incorporate Active Storage in controllers
If you're creating and updating records with  [strong params](https://medium.com/@tbrisker/strong-parameters-in-rails-down-the-rabbit-hole-2426d331625), then the Active Storage instance variable must be added in the list of permitted params attributes. 

```
class ExercisesController extends ApplicationController 
  . . .
  
  private 
  # in this example, `image` is the AS variable we're adding to the strong params
  def exercise_params  
    params.require(:exercise).permit(:id, :name, :exercise_type, :description, :image, 
   :routine_exercises_attributes => [:id, :routine_id, :sets, :reps])
  end
end
```

If you're not using strong params, then the process of passing the Active Storage variable in the controller is taken care of. (Make sure it's attached, though! --> `image.attached?`)

### 4. Add a `file_field` in the new/edit views

You'll then want to incorporate uploading images via Active Storage in the actual forms. Whether the form is inside the actual views or separated into a partial, you'll want to add code similar to this: 

```
# app/views/exercises/_form.erb
# inside the form

form.file_field(:image)
```
 [See the code](https://github.com/Dusch4593/flatiron_fitness_rails_app/blob/master/app/views/routines/_form.erb)  for an actual example. 

When the server is started, the rendered form will look something like this: 


![Screen Shot 2020-08-18 at 3.37.16 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1597779475921/ctKJuEl_9.png)

Do you notice the new button for uploading an image? We've successfully set up Active Storage!

# Usage 
After setting up, actually using Active Storage is a breeze. If we were to fill out a routine/ edit form like normal (except we're now uploading images) and submit, the new routine looks like this: 

![Screen Shot 2020-08-18 at 3.58.47 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1597780765367/9T_mtiyCl.png)




# Cost/ Benefit Analysis
*The Pros: *
Active Storage is the official Rails tool used for attaching files to Active Record models. 

With the help of the special `active_storage_blobs` and `active_storage_attachments` tables, we can attach a file to a model *without adding a separate column to the model's table itself*. 


*The Cons: *
As of right now, I haven't really found any cons for Active Storage!

# Conclusion
If you're looking fora quick way to upload images to go with your Active Record models in your Rails projects, this is the solution for you. Active Storage is easy to set up and implement. It's a tool I'll definitely keep close to me. 

Happy Coding! 
Brandon 

# Resources
 [Active Storage Overview (Rails Guides)](https://edgeguides.rubyonrails.org/active_storage_overview.html) 

 [Pros and Cons of Active Storage (StackOverflow thread)](https://stackoverflow.com/questions/56987522/pros-cons-of-active-storage-compare-to-carrierwave-paperclip) 

 [Rails API](https://api.rubyonrails.org/) 
