My First Foray into Active Storage
Published: 2021-01-05 05:51:54

After spending the first few weeks at Flatiron School mostly stuck in the plain ole boring terminal, I was ready for some color by the time the Rails project came around. Needing to utilize a has many and has many through relationship and wanting to build on my relationship with antiques and flea markets (I love them and could browse all day every day), I came up with a concept for users to post both flea markets and antiques purchased at different markets. It is a way for users to catalog their collections and also get a sneak peak at what types of antiques different flea markets have to offer. And the most exciting part - photos of antiques are involved! 

Now all I had to do was figure out how to integrate photo uploading to my project... I proceeded to set all of my models and controller actions up so that antiques and markets could be created, read, updated, and deleted by users but I had no idea what to do for inputting photos. My first attempt was to create a column in the database titled photos with the data type of string thinking that I could take the users input of a URL that points to the photo they want to upload. Then when it was time to display the photo, I would put that URL into an `<a>` tag. But then the user has to first save their photo somewhere else then copy and paste the URL.. There had to be a better way! 

Insert Active Storage. 🙌 🙌 🙌 🙌 

And blobs! More on that in a bit. 

Active Storage is a way to upload files to a cloud sharing service like Amazon S3 or Google Cloud and attach the files to Active Record objects. This way the files, photos in my case, can be called upon as if they were another attribute on my model: `antique.antique_photo` <<Feels like magic because the set up honestly couldn't be easier! The documentation is super straight forward so I encourage anyone who wants to add photo uploading capabilities to their Rails project to [check it out](https://edgeguides.rubyonrails.org/active_storage_overview.html) but I wanted to write a quick overview of the way the files are stored. 

Active Storage works by creating two tables in your database: active_storage_attachments and active_storage_blobs. 

On the **active_storage_blob table**, each row is referred to as a blob and contains information and details about the file that is uploaded such as the file's name, content type. 
 
**Active_storage_attachments** is a polymorphic join table that stores three things - 
1. blob_id (the row id of the chosen file on the active_storage_blob table)
2. record_type (this is the model that you want to have the photo)
3. record_id (this is the id of the particular instance of the model that the photo will be tied to) 

To set up Active Storage - 
First run `$ bin/rails active-storage:install` and then `$ bin/rails db:migrate` to add the tables. 

Then, inside `config/storage.yml` declare the Active Storage Services - what service you will use to store the files. In my case, as this is just a project, I used the local disk but the documentation references other services.
```
local:
  service: Disk
  root: <%= Rails.root.join("storage") %>
```

Inside `config/environments/development.rb`, tell Active Storage to use the local disk with 
```
config.active_storage.service = :local
```

That's it for the file set up for local storage. All that is left is implementing it into the MVC structure. 
In your **model** that you want to have a photo, declare the relationship with either has one or has many:
```
class Antique < ApplicationRecord
  has_one_attached :antique_photo
end  
```
```
class Market < ApplicationRecord
  has_many_attached :market_photos
end  
```

To their respective **controller**, add `:antique_photo` and `:market_photos` to the strong params private method. 

Update your form in the **view** to add the ability for users to select their file and upload it:
```
<%= form_with model: @antique, local: true do |f| %>
    <%= f.label :antique_photo, "Add a photo of your antique: " %>
    <%= f.file_field :antique_photo %>
    .....
    <%= f.submit "add antique", :class => "button_submit sage" %>
<% end %>
```

And that's it! As mentioned earlier, the photo can be called by `@antique.antique_photo` and used to render it in the browser as `<img><%= image_tag @antique.antique_photo %></img>`. 
It is important to note here that you can refer to your photos by whatever name you'd like (:avatar, :picture, :profile_picture, etc) just make sure that you reference it the same way in the model, view, and controller. 

Using Active Storage might not be the best way to add photos in every case but it worked for my first foray into it and I am happy that my users can showcase their antique collections with photos! 
Here is the documentation: https://edgeguides.rubyonrails.org/active_storage_overview.html
This is a really helpful, more in depth overview: https://pragmaticstudio.com/tutorials/using-active-storage-in-rails 
And here is my project: https://github.com/madelinemc/oui-flea-antiquerie