In the first part of this series, you learned how to use CarrierWave in your Rails application. In this second part, you will learn how to enable image uploading for your users using Devise. Devise is an authentication solution for Rails. You will also learn how to use fog, a Ruby cloud service library that will enable your application to connect to Amazon Web Services.
Enough talk—let’s get down to business.
Rails Application Setup
Generate your new rails application:
rails new myapp
Open up your Gemfile
and add the following gems:
***Gemfile*** gem carrierwave gem devise gem mini_magick gem fog
Run bundle install
to install the gems.
From your terminal, create a Pages controller
:
rails g controller Pages index
Navigate to config/routes.rb
and add a root path:
***config/routes.rb*** root to: 'pages#index'
Generate and Configure Devise
The uploading feature will be integrated into our User model for users to upload avatars. From your terminal, install devise:
rails generate devise:install
The generator will install an initializer which describes all of Devise’s configuration options. Open up app/views/layouts/application.html.erb
in your text editor and add the following code above the yield
block:
***app/views/layouts/application.html.erb*** <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p>
At this point you can generate your User model:
rails generate devise User
Next, migrate your database:
rake db:migrate
You will need to edit devise views, so it is important you generate those:
rails generate devise:views
And that will do the magic.
Using your text editor, open app/views/devise/registrations/new.html.erb
and edit it to look like this:
***app/views/devise/registrations/new.html.erb*** <h2>Sign up</h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name), :html => {multipart: :true}) do |f| %> <%= devise_error_messages! %> <div class="field"> <%= f.label :email %><br /> <%= f.email_field :email, autofocus: true %> </div> <div class="field"> <%= f.label :password %> <% if @minimum_password_length %> <em>(<%= @minimum_password_length %> characters minimum)</em> <% end %><br /> <%= f.password_field :password, autocomplete: "off" %> </div> <div class="field"> <%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation, autocomplete: "off" %> </div> <div class="field"> <%= f.label :avatar do %> <%= f.file_field :avatar %> <%= f.hidden_field :avatar_cache %> <% end %> </div> <div class="actions"> <%= f.submit "Sign up" %> </div> <% end %> <%= render "devise/shared/links" %>
Do the same for app/views/devise/registrations/edit.html.erb
:
***app/views/devise/registrations/edit.html.erb*** <h2>Edit <%= resource_name.to_s.humanize %></h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, multipart: :true }) do |f| %> <%= devise_error_messages! %> <div class="field"> <%= f.label :email %><br /> <%= f.email_field :email, autofocus: true %> </div> <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div> <% end %> <div class="field"> <%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br /> <%= f.password_field :password, autocomplete: "off" %> </div> <div class="field"> <%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation, autocomplete: "off" %> </div> <% if current_user.avatar.url.present? %> <%= image_tag(current_user.avatar.url) %> <%= f.label :remove_avatar do %> <%= f.check_box :remove_avatar %> <% end %> <% end %> <%= f.file_field :avatar %> <%= f.hidden_field :avatar_cache %> <div class="field"> <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br /> <%= f.password_field :current_password, autocomplete: "off" %> </div> <div class="actions"> <%= f.submit "Update" %> </div> <% end %> <h3>Cancel my account</h3> <p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p> <%= link_to "Back", :back %>
With that done, you will need to whitelist avatar for devise and add an avatar column to the User table. From your terminal, run migration to add a new avatar column.
rails g migration add_avatar_to_users avatar:string rake db:migrate
Add the CarrierWave avatar to your User model—your model should look like this:
***models/user.rb*** class User < ActiveRecord::Base mount_uploader :avatar, AvatarUploader devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable # User Avatar Validation validates_integrity_of :avatar validates_processing_of :avatar private def avatar_size_validation errors[:avatar] << "should be less than 500KB" if avatar.size > 0.5.megabytes end end
In the above code, you added a mount_uploader
line at the top of the User
class. There is also a validation to check the integrity and processing of the avatar, alongside a method to ensure that no image greater than 500KB is uploaded.
You need to add avatar
, avatar_cache
, and remove_avatar
to the list of accessible attributes. Doing this is easy—just open up your application_controller.rb
and make it look like this:
***app/controllers/application_controller.rb*** class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :password, :password_confirmation, :remember_me, :avatar, :avatar_cache) } devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:username, :password, :password_confirmation, :current_password, :avatar, :avatar_cache, :remove_avatar) } end end
With that done, you are ready to integrate CarrierWave
.
Setting Up CarrierWave
Using your text editor, navigate to config/initializers
and create a file named carrier_wave.rb
. Paste in the code below:
***config/initializers/carrier_wave.rb***
require 'carrierwave/orm/activerecord'
This is the initializer that is needed in loading CarrierWave after ActiveRecord.
From your terminal, generate an uploader:
rails generate uploader Avatar
This will create a new directory called uploaders in the app folder and a file inside called avatar_uploader.rb
. I have edited the contents of the file to look like what I have below:
***app/uploaders/avatar_uploader.rb*** # encoding: utf-8 class AvatarUploader < CarrierWave::Uploader::Base include CarrierWave::MiniMagick # Choose what kind of storage to use for this uploader: storage :fog # Override the directory where uploaded files will be stored. # This is a sensible default for uploaders that are meant to be mounted: def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end # Create different versions of your uploaded files: version :thumb do process :resize_to_fill => [100, 100] end version :medium do process :resize_to_fill => [300, 300] end version :small do process :resize_to_fill => [140, 140] end # Add a white list of extensions which are allowed to be uploaded. # For images you might use something like this: def extension_white_list %w(jpg jpeg gif png) end end
You need the MiniMagick
line to generate different versions of an image. I included three versions of images. MiniMagick makes the resizing into this version possible. The last code block ensures that no file extensions aside from those listed here are uploaded.
AWS Setup
For this tutorial, we will be uploading our images to Amazon Web Services. If you do not have an account yet, hop over to the sign-up page and create a free account.
When you’ve finished with that, you will need to create a bucket to store your images. When there, choose Create Bucket to open the dialog box. Enter a name for the bucket and select a region. When done, select Create.
Open your Gemfile and add this gem, and bundle install
when done.
gem 'figaro'
From your terminal, run bundle exec figaro install
. This will a create a new file config/application.yml
and append it to your application’s .gitignore
.
You need this file to keep your AWS access id and secret key safe.
To find your AWS access id and secret key, go to Amazon Web Services and click on the name of your account, which is located in the right corner of the console.
From the drop-down, select Security Credentials, and click the Continue to Security Credentials button. In the page that shows, select Access Keys (Access Key ID and Secret Access Key). Click on the Create New Access Key button to generate a new key, and copy it into an editor.
In your text editor, navigate to config/application.yml
and paste in the following:
***config/application.yml*** aws_access_id: Enter access_id here aws_access_secret_key: Enter access_key here
Replace the lines as stated above.
Navigate to config/initializers
, create a file named storage.rb
, and paste in the following:
***config/initializers/storage.rb*** CarrierWave.configure do |config| config.storage = :fog config.fog_credentials = { provider: 'AWS', aws_access_key_id: ENV["aws_access_id"], aws_secret_access_key: ENV["aws_access_secret_key"], region: 'us-west-2' } config.fog_directory = "tutsplus-avatar" config.fog_public = false end
According to the above config, the region for my bucket is us-west-2
, and the bucket’s name is tutsplus-avatar
. Replace that with information about your bucket.
Start up your rails server
and point your browser to http://localhost:3000/users/sign_up
.
Setting a Default Avatar
In your application, you might want to set a default avatar for users that choose not to upload an avatar. Doing this is easy.
Create a folder in app/assets/images
called fallback
and drop your default image in it. Using your text editor, navigate to app/uploaders/avatar_uploader.rb
and paste in the code below:
***app/uploaders/avatar_uploader.rb*** def default_url(*args) ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default-avatar.gif"].compact.join('_')) end
Be sure to change default-avatar.gif
to the name of your image.
Conclusion
Now you know how to enable image uploading for your users. This adds an important feature to your rails application. I hope you had fun. In the next part, we will have a look at PaperClip. Your feedback is welcome.
Comments