Skip to content

Instantly share code, notes, and snippets.

@jnewman12
Last active June 16, 2019 09:44
Show Gist options
  • Save jnewman12/3ccb71e8f2a3385a27e4e80cad994c8a to your computer and use it in GitHub Desktop.
Save jnewman12/3ccb71e8f2a3385a27e4e80cad994c8a to your computer and use it in GitHub Desktop.
WDI DTLA Active Storage Intro

File Uploads with Rails and Active Storage

rails + active-storage


Lesson Objectives

  • set up an account on Amazon Web Services to store uploaded images inside AWS S3
  • set up rails app to handle file uploads
  • understand the how and why, of a rails model with an image attribute

What is Active Storage

From the rails docs

Active Storage facilitates uploading files to a cloud storage service like Amazon S3, Google Cloud Storage, or Microsoft Azure Storage and attaching those files to Active Record objects. It comes with a local disk-based service for development and testing and supports mirroring files to subordinate services for backups and migrations.

This is a big deal because before we had to use an external library (paperclip, carrier wave, etc) that facilitated these uploads. But now, rails does this by default, just using rails version 5.2.0!


AWS Set up

  • after we have set up our dependencies, we will need to set up our accounts on AWS. Let's do that!
  • unfortunately, Amazon has changed their AWS sign up process from when I set up my original account, and we now have some additional hoops to jump through.
    • first, navigate to this page: https://portal.aws.amazon.com/gp/aws/developer/registration/index.html?nc2=h_ct
    • once there, it will ask you to enter your existing amazon information or to make a new account to use AWS
    • from there, go through the sign up process. You will need to input your info, a credit card, and a phone number to verify. With the credit card option, select the basic plan and you will not be charged

App Set up

  • with our AWS account set up, let's continue with our code.
  • for this example, we are going to make a new rails app inside your favorite workspace and name it what you would like
  • for the purpose of this example, I am going to call mine, asset_upload
  • let's setup our app! run rails new asset_upload -T --database=postgresql inside your workspace where you want your repo
  • once we have set up our app, let's cd into it, and open with code .

Now generally, from here, we would have to add some gems and other config files, but with using rails 5.2.0, and getting the subsequent Active Storage with it, we don't need to add anything additionally!


Getting our AWS Keys

Before we add to our config file, we need to get something called Access Keys from AWS. Let's do that!

  • navigate back to your AWS account and sign into the AWS Console (https://console.aws.amazon.com) if you don't already have it opened
  • once signed in, find the All Services carot, then find storage, and in that list, look for s3. once you find it, click on it.
  • once inside our S3 section, let's create our first bucket. Find the blue create bucket button and click on it
  • when you click on it, it will pop up a modal with some info for your to fill out
    • the name for our bucket must be unique, as it is shared across ALL OF AMAZON! try to find a unique name that you will remember
    • for the region, select US West N. California
  • once sucessfully created, you will be brought to a page that will show you some info about your bucket. Next, lets grab our keys.
  • hit this link https://console.aws.amazon.com/iam/home?nc2=h_m_sc#security_credential which will take you to the page for our credentials. On page load a modal will appear, just click continue to security credentials
    • once on that page, scroll down to the middle and click on Access Keys (Access Key ID and Secret Access Key) and click create new access key
      • once that button is clicked, it will generate a file with your info and it will download it. Open it up. this will give you your access key, and your secret key
    • once you have your access keys, make sure to remember a couple things before we dive into your code (write it down somewhere)
      • your bucket name, and the region you made it in

Application Code

With all that set up, lets now dive into our actual application. We're going to make a single table application, with a model called user, that will have the following attributes

  • name: string
  • description: text
  • images (will be seperate table)

to do this, we need to generate a blank migration file and model file, we can accomplish this with

  • rails g model User

now we have a blank migration file, so let's add some code to edit it to set up our database properly.

Inside of our migration let's add the following code so our def change method looks like

def change
    create_table :users do |t|
      t.string :name
      t.string :description

      t.timestamps null: false
    end
end

from here lets set up our database with the following command

  • rails db:create && rails db:migrate

With our basic application code set up, we need to now set up our active storage related code while it's fresh for us.

Components of Active Storage

In working with active storage, we need to only work with a couple files

  • config/storage.yml
  • config/credentials.yml.enc
  • active_storage database tables

First, let's add our active_storage tables. What these tables are, are where we will add all our image related data. To enable this feature, we just have to tell rails to do so. Go to your console, in your project folder, and run

  • rails active_storage:install:migrations
  • rails db:migrate

Now, if you open your schema.rb file, you should be able to see 3 tables: users, active_storage_attachments, and active_storage_blobs

This migration adds two tables - active_storage_blobs and active_storage_attachments. These are for the 2 models Blob and Attachment. Blob stores metadata like filename, content-type, byte size and checksum. The actual file is stored in the storage service or disk depending on your settings, which we will be changing.

A Blob can be attached to one or more Active Record objects through the Attachment join model, but we won't worry about that for now.

While we're in our schema/model/migration state of mind, let's add 1 line to our user.rb file so that it looks like

class User < ApplicationRecord
  has_one_attached :image
end

What this line is doing, is enabling active_storage to work it's metaprogramming magic to map our image between the attachment table and the user table without us adding an image attribute to our users table. What this now means, is we have a method upon our user class called image, which is responsible for rendering related images.

Our controller

  • with a model set up, lets now add some code to our controllers and our routes
  • inside of our routes.rb file, let's delete all of the comments and just add the single line
    • resources :users
  • now for practice, let's manually create a users controller
  • once we have our controller, let's just add a couple methods to our file like so
class UsersController < ApplicationController
  def new
  end
  
  def create
  end
  
  def show
  end
end
  • the final step is to now add our view!
  • go inside of app/views and make a new folder called users. Inside users, create 2 files; a new.html.erb and show.html.erb files
  • with all of this, let's fire up our server with rails s and navigate to http://localhost:3000

Adding credentials to our files

So we have our AWS keys set up, a base application w/ active_storage enabled, our model/controller/routes set up. Now, we just need to bring it all together by adding some more config code in the proper places to use active storage.

Modifying and encrypting files

The first thing we need to do, is to edit a new file rails gives us called credentials.yml.enc. What this file does is to store all of our application secret's (like access keys and ids) and encrypts them for us. So grab your rootkey.csv file downloaded from AWS and lets add our keys

  • first open that file with rails credentials:edit EDITOR='code --wait'

When that file opens up, you'll see aws at the top. Uncomment them, and keep them spaced w/ 2 spaces (not 4) and don't use tabs. Once you have your access_key_id and secret_access_key added, save the file, and exit out of it from your editor (you must exit it for changes to take place). Once your exit out of that file, you should see a message in your terminal (the window you just ran the command above in) that says New credentials encrypted and saved.

Next, let's open up our storage.yml file. In that file, find the amazon option, and we should edit the file to look like

amazon:
  service: S3
  access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
  secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
  region: us-west-1
  bucket: assetuploadexample  

With our encrypted file, and our storage.yml set up. Let's now open up development.rb and production.rb. Inside both of those files, let's add the following line (should be line 31 in development.rb and line 42 in production.rb) and add

  • config.active_storage.service = :amazon

This will tell rails to use the amazon option inside our storage.yml.

The final thing here we need to add is the aws gem to our gemfiles. So open up your Gemfile and at the bottom add the line

  • gem 'aws-sdk-s3'

Then go back to your terminal and run bundle install

Controllers and Views

Now that we have our base config set up, let's add the following to our controller and views. Inside of UsersController lets add

class UsersController < ApplicationController

  def new
    @user = User.new
  end

  def create
    user = User.create! params.require(:user).permit(:name, :description)
    user.image.attach(params[:user][:image])
    redirect_to user    
  end

  def show
    @user = User.find(params[:id])
  end

  def destroy
  end
end

and inside of new.html.erb add

<%= form_with model: @user, local: true  do |form| %>
  <%= form.label :name%><br>
  <%= form.text_field :name %><br><br>
  <%= form.label :description%><br>
  <%= form.text_field :description %><br><br>
  <%= form.file_field :image %><br>
  <br><br>
  <%= form.submit %>
<% end %>

and inside of show.html.erb add

<p><%= @user.name %></p>
<br>
<p><%= @user.description %></p>
<br>
<%= image_tag @user.image %>

Now, go to your localhost:3000/users/new and fill out the form. If everything goes well, we should be re-directed to localhost:3000/users/1 and see an image there!

Deploying

Now let's deploy this to production so we can briefly see a concept about something rails has called master keys. Go to your terminal and run

  • heroku create

This will generate a random url slug for your app. Next, let's find our app's master key. Go back to your text editor, and do a command + p for a file called master.key. Once you have that file, go back to your terminal and in your project directory, type

  • heroku config:set RAILS_MASTER_KEY=<master_key> where master_key is your string that was in that file (no brackets).

Next run the following commands to push to heroku

  • git add .
  • git commit -m "some commit"
  • git push heroku master && heroku open

Once heroku opens your application, go to /users/new, and double check you are able to upload an image. If upon form submit you are redirected back to the show, and see an image, it worked!


Your turn

With all of the code we currently have, get with your partner and add the rest of the code necessary to CRUD our resource!


Links

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment