Recently I built a Rails blog with a WYSIWYG editor, CKEditor, that allows a user to upload pictures and post articles via the text editor. CKEditor is one of the full-featured text editors that developers can easly integrate into their applications. For uploads, I chose Paperclip, a powerful file attachment library for ActiveRecord. Since Heroku uses an ephemeral filesystem, static assets such as picture uploads must be stored elsewhere. Therefore, I'll use AWS' Simple Storage Simple, S3.

Adding CKEditor to a Rails app

This article assumes you've created a basic Rails app with a Postgres database for both development and production. If you don't already have a Rails blog app, we'll quickly scaffold one without comments. This will give us the basic structure to implement everything we need. In your text editor change to the app directory, create a basic blog, migrate the database and initialize a git repository.

cd your_rails_app
rails g scaffold Article title:string body:text
rake db:migrate
git init

Skipping ahead a bit to something to which we'll return, create an empty aws.yml file in the app's config directory. We'll need it later for credentials. Important: add this file to the .gitignore file in the root directory of your app. You will not want to save this file to your remote git repository when committing your changes. Commit your changes.

Next, add the gems we'll need to our Gemfile. The AWS gem simplifies the coding for the interaction of our app with AWS' services.

gem 'ckeditor'
gem 'paperclip'
gem 'rails_12factor', group: :production
gem 'aws-sdk', '~> 2.6', '>= 2.6.6'

After saving the Gemfile:

bundle install

Now we need to generate the CKEditor and Paperclip models and migrate those changes.

rails generate ckeditor:install --orm=active_record --backend=paperclip
rake db:migrate

Take a look in app/models/ckeditor/attachment_file.rb and app/models/ckeditor/picture.rb. The ckeditor installer has taken care of the models for our file upload and storage.

# app/models/ckeditor/attachment_file.rb
class Ckeditor::AttachmentFile < Ckeditor::Asset
  has_attached_file :data,
                    url: '/ckeditor_assets/attachments/:id/:filename',
                    path: ':rails_root/public/ckeditor_assets/attachments/:id/:filename'

  validates_attachment_presence :data
  validates_attachment_size :data, less_than: 100.megabytes
  do_not_validate_attachment_file_type :data

  def url_thumb
    @url_thumb ||= Ckeditor::Utils.filethumb(filename)
  end
end
# app/models/ckeditor/picture.rb
class Ckeditor::Picture < Ckeditor::Asset
  has_attached_file :data,
                    url: '/ckeditor_assets/pictures/:id/:style_:basename.:extension',
                    path: ':rails_root/public/ckeditor_assets/pictures/:id/:style_:basename.:extension',
                    styles: { content: '800>', thumb: '118x100#' }

  validates_attachment_presence :data
  validates_attachment_size :data, less_than: 2.megabytes
  validates_attachment_content_type :data, content_type: /\Aimage/

  def url_content
    url(:content)
  end
end

Include the CKEditor javascripts in app/assets/javascripts/application.js.

//= require ckeditor/init

For precompiling the CKEditor add the following in config/initializers/assets.rb and afterward restart the server.

Rails.application.config.assets.precompile += %w( ckeditor/* )

Now to actually use the CKEditor, add the cktext_area form helper to your app. Depending on your form, it could look something like this. Consult the gem documentation for further configuration details.

# app/views/articles/_form.html.erb
<%= form_for(@article) do |f| %>
                                 ...   
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  
    <%= f.label :body %><br>
    <%= f.cktext_area :body %>

    <%= f.submit %> 
<% end %>

When you look at your articles/new route in the browser, the CKEditor will be called in the form area specified.

Setting up S3

If you don't have an AWS account, create one. Then, going to your AWS console, create an S3 bucket following the AWS naming recommendations. This will be the repository for your uploaded pictures. Choose the US Standard region and make note of the region's AWS identifier. As of this writing the Standard Region's identifier was us-east-1. Consult AWS Endpoint listings for a complete list of identifiers. Note: You may be able to use a server closer to your geographical area, but there have been some reports that Heroku servers only access the east coast servers.

Now you will need to create your AWS security credentials to access your AWS services and buckets. Log into AWS services, if not already, and look for the security credentials link in the dropdown menu under your account name. After opening up the Access Keys menu, create and download the Access Key Id and the Secret Access Key.

Deploy to Heroku

If not already deployed, commit your changes to git and push your app to Heroku. Make sure you run heroku run rake db:migrate to intialize your Heroku database or you will receive an error on page load. Deploying to Heroku is not within the scope of this article, but Heroku's documentation is straightforward and clear.

Configure Rails app for S3 access

In the aws.yml file add the following AWS credentials:

access_key_id: YOUR_ACCESS_KEY
secret_access_key: YOUR_SECRET_ID

Add the following code to config/environments/development.rb filling in your region and bucket name.

config.paperclip_defaults = {
  storage: :s3,
  s3_region: 'your-region-name',
  bucket:'your-bucket-name',
  s3_credentials: "#{Rails.root}/config/aws.yml"
  }

Add the following code to config/environments.production.rb:

config.paperclip_defaults = {
      storage: :s3,
      s3_credentials: {
        bucket: ENV.fetch('S3_BUCKET_NAME'),
        access_key_id: ENV.fetch('AWS_ACCESS_KEY_ID'),
        secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY'),
        s3_region: ENV.fetch('AWS_REGION'),
      }
    }
    # Secret key base for non-existent secrets.yml file
    config.secret_key_base = ENV["SECRET_KEY_BASE"]

The parameters in the ENV.fetch methods reference the Heroku production configuration variables you'll next set. Copy the parameters exactly as they appear above.

Configure Heroku for S3

Now that the config/environments.production.rb file is referencing the internal Heroku configuration variables, we need to put the S3 credentials in Heroku's configuration variables. It can be done either through your Heroku app's online settings or through the Heroku CLI in your local terminal. The following instruction are for the command line.

Add the S3 access key, secret access key, S3 bucket name, and, importantly, the region. Heroku's servers will return confirmation of the changes.

heroku config:set AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=yyy
heroku config:set S3_BUCKET_NAME=your-bucket-name
Heroku config:set AWS_REGION=your-region
Re-deploy your app to Heroku.

Conclusion

Voila!

You should now be able to add blog posts with formatted pictures in both development and production modes, storing the static assets on AWS' S3 servers while using Heroku for your Rails' production server.