Follow Along as I Stumble on the Path to Learning RoR

Gems

Seeding DB with Paperclip Images with CSV

I needed to seed my app, hosted on Heroku, with new users.  Since I wanted to created a lot of the them, I wanted to CSV.  My users have avatars, so I wanted to upload those for the users as well.

Here’s how I did it:

1. I stored the original images I wanted to use for the avatars on AWS S3 (make sure that they are set with public permission to read them.

2. I created a CSV that had columns (in this order) for the users: username, email, password, password_confirmation, avatar.  In the avatar field, I put the URL of the S3 image.  I named this CSV file “user_data” and put it in the DB directory.

3. Using the answer to this Stack Overflow question, I created a file (I named user_seed.rake) in lib/tasks.  The code in the file I used was:

require ‘csv’

namespace :csv do

desc “Import CSV Data”
task :import_stuff => :environment do

csv_file_path = ‘db/user_data.csv’

CSV.foreach(csv_file_path) do |row|
User.create!({
:username => row[0],
:email => row[1],
:password => row[2],
:password_confirmation => row[3],
:avatar => URI.parse(row[4])
})
puts “Row added!”
end
end

end

4.  Run rake csv:import_stuff


Using CloudFront with S3 and Paperclip

I wanted to cache my S3 images for my Rails app, so I changed my app to serve images using AWS CloudFront, a CDN.

Doing this was pretty simple.

(1) Set-up a AWS CloudFront instance and your S3 bucket as your origin

(2) Change your Paperclip settings to use the new CloudFront instance.  I did this in production.rb (make sure neither the url or s3_host_alias values are being written by Paperclip settings in your model):

config.paperclip_defaults = {
storage: :s3,
:url => “:s3_alias_url”,
:s3_host_alias => “your-cloud-front-url.cloudfront.net”,
:path => “:rails_env/:class/:attachment/:id/:style/:filename”,
:bucket => “your-bucket-name”,
s3_credentials: {
bucket: ENV[‘AWS_S3_BUCKET’],
access_key_id: ENV[‘AWS_ACCESS_KEY_ID’],
secret_access_key: ENV[‘AWS_SECRET_ACCESS_KEY’],
}
}

and that’s it!

Now, if you check the URL of your images, it should see that their source is prefaced with your CloudFront URL.


Sendgrid Settings

I tried following Sendgrid’s instructions, but they didn’t work.  I was getting this error:

Errno::ECONNREFUSED – Connection refused – connect(2) for “localhost” port 25

I fixed it by adding these settings:

config/environments/production.rb

config.action_mailer.raise_delivery_errors = true config.action_mailer.default_url_options = { :host => ‘sitename.com’ } config.action_mailer.perform_deliveries = true ActionMailer::Base.smtp_settings = { :address => ‘smtp.sendgrid.net’, :port => ‘587’, :authentication => :plain, :user_name => ENV[‘SENDGRID_USERNAME’], :password => ENV[‘SENDGRID_PASSWORD’], :domain => ‘heroku.com’ } ActionMailer::Base.delivery_method = :smtp


Adding a Sitemap to Google for Sites on Heroku

I’m using the awesome Sitemap Generator gem for Rails  and also hosting my app on Heroku.  Since I’m updating my sitemap daily, and Heroku forces me to write to somewhere else, I need to create the sitemap nightly on Amazon Web Services (S3), instead.

The problem I ran into is that when I went to Google Webmaster Tools, it only allowed me to specify a url for the sitemap beginning with the name of my domain.  However, since the sitemap is hosted on AWS, it has an address that starts with an Amazon URL.

I found an answer here.

Basically what you need to do is to add a second property in Google Webmaster Tools for your S3 bucket.  I added a property to my directory with the sitemap (i.e. http://s3.amazonaws.com/mybucket/sitemaps/) and then verified that I owned the property by placing the Google supplied HTML file in the sitemaps folder on S3.

The last step is adding the URL to the sitemap in settings for the new [S3] property that was added to Google Webmaster Tools.


Setting Paperclip Image URL

I had to find the URL of Paperclip images in order to use them in meta tags.  This is what I ended up doing (where User is the model and image the attachment name):

user.image.url(:thumbnail)


Removing Requirement for “Current Password” in Devise

I’m using the Devise gem and I wanted to only require the “Current Password” field for account updates when the password was being changed.  For other changes, I didn’t want to require the field.  This requirement was also important for me because some of my Users sign-up using Facebook.  In these cases, they might not have a password and would not be able to make any account changes.

I found my answer in this SO question and this Devise article.

First, you need a Registrations controller for your resource (in my case, the resource being used with Devise is my User model), if you don’t have one already:

in controllers:

  1. Create users/registrations_controller.rb
    Users::RegistrationsController < Devise::RegistrationsController
    
    def update
    
    self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
    
       if resource.update_with_password(params[resource_name])
         set_flash_message :notice, :updated if is_navigational_format?
         sign_in resource_name, resource, :bypass => true
         respond_with resource, :location => after_update_path_for(resource)
       else
         clean_up_passwords(resource)
         respond_with_navigational(resource){ render_with_scope :edit }
       end
    
    end
  2. Add this to routes.rb
    devise_for :users, controllers: {registrations: 'registrations'}

Next, add new methods to your model (user.rb in my case):

#to remove the current password check if updating a profile originally gotten via oauth (fb, twitter)


  def update_with_password(params={})
    if params[:password].blank?
      params.delete(:current_password)
      self.update_without_password(params)
    else
      self.verify_password_and_update(params)
    end
  end

  def update_without_password(params={})

    params.delete(:password)
    params.delete(:password_confirmation)
    result = update_attributes(params)
    clean_up_passwords
    result
  end
  def verify_password_and_update(params)
    #devises' update_with_password 
    # https://github.com/plataformatec/devise/blob/master/lib/devise/models/database_authenticatable.rb
    current_password = params.delete(:current_password)

    if params[:password].blank?
      params.delete(:password)
      params.delete(:password_confirmation) if params[:password_confirmation].blank?
    end

    result = if valid_password?(current_password)
      update_attributes(params)
    else
      self.attributes = params
      self.valid?
      self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
      false
    end

    clean_up_passwords
    result
  end

Adding WYSIWYG / Rich Text Editor to Rails App

I wanted to allow my users to format text input into a text area, so I decided to use CKEditor.  Luckily, there’s a great gem for this.  For my Rails app (Rails 4), all I had to do to get started was:

  1. Add this to the gemfile: gem ‘ckeditor’
  2. bundle install
  3. Add this to application.js: //= require ckeditor/init
  4. Adjust my text area in the form, I’m using the Simple Form gem (the documentation shows how to integrate with different form types): <%= f.input :text, label: “Lesson Text”, :as => :ckeditor, input_html: { :ckeditor => {:toolbar => ‘Full’} %>
  5. In config/initializers/assets.rb add this: Rails.application.config.assets.precompile += %w( ckeditor/* )

That’s all you need to do to get started with CKEditor.  However, this default comes with a ton of options for editing text.  For my site, it was too much.  For example, I did not want to allow some formatting changes, such as font size, font type, or color just so I could keep a consistent look across my app.  Also, I didn’t want to allow people to be able to upload files using the editor (although you can – the documentation shows you how to do this by tying the upload to Carrierwave or Paperclip).

So I needed to create a custom toolbar configuration.  Luckily, the gem makes this easy.

In app/assets/javascripts/ckeditor/config.js you need to add configuration.  The easiest way for me to do this was to copy the configuration from the gem (in the gem’s files – app > assets > javascripts > ckeditor) into the file, and then cut out what is not needed:

/*
Copyright (c) 2003-2011, CKSource – Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/

CKEDITOR.editorConfig = function( config )
{
// Define changes to default configuration here. For example:
// config.language = ‘fr’;
// config.uiColor = ‘#AADC6E’;

/* Filebrowser routes */
// The location of an external file browser, that should be launched when “Browse Server” button is pressed.
config.filebrowserBrowseUrl = “/ckeditor/attachment_files”;

// The location of an external file browser, that should be launched when “Browse Server” button is pressed in the Flash dialog.
config.filebrowserFlashBrowseUrl = “/ckeditor/attachment_files”;

// The location of a script that handles file uploads in the Flash dialog.
config.filebrowserFlashUploadUrl = “/ckeditor/attachment_files”;

// The location of an external file browser, that should be launched when “Browse Server” button is pressed in the Link tab of Image dialog.
config.filebrowserImageBrowseLinkUrl = “/ckeditor/pictures”;

// The location of an external file browser, that should be launched when “Browse Server” button is pressed in the Image dialog.
config.filebrowserImageBrowseUrl = “/ckeditor/pictures”;

// The location of a script that handles file uploads in the Image dialog.
config.filebrowserImageUploadUrl = “/ckeditor/pictures”;

// The location of a script that handles file uploads.
config.filebrowserUploadUrl = “/ckeditor/attachment_files”;

config.allowedContent = true;

// Toolbar groups configuration.
config.toolbar = [
{ name: ‘document’, groups: [ ‘mode’, ‘document’, ‘doctools’ ], items: [ ‘Source’] },
{ name: ‘clipboard’, groups: [ ‘clipboard’, ‘undo’ ], items: [ ‘Cut’, ‘Copy’, ‘Paste’, ‘PasteText’, ‘PasteFromWord’, ‘-‘, ‘Undo’, ‘Redo’ ] },
// { name: ‘editing’, groups: [ ‘find’, ‘selection’, ‘spellchecker’ ], items: [ ‘Find’, ‘Replace’, ‘-‘, ‘SelectAll’, ‘-‘, ‘Scayt’ ] },
// { name: ‘forms’, items: [ ‘Form’, ‘Checkbox’, ‘Radio’, ‘TextField’, ‘Textarea’, ‘Select’, ‘Button’, ‘ImageButton’, ‘HiddenField’ ] },
{ name: ‘links’, items: [ ‘Link’, ‘Unlink’, ‘Anchor’ ] },
{ name: ‘insert’, items: [ ‘Image’, ‘Flash’, ‘Table’, ‘HorizontalRule’, ‘SpecialChar’ ] },
{ name: ‘paragraph’, groups: [ ‘list’, ‘indent’, ‘blocks’, ‘align’, ‘bidi’ ], items: [ ‘NumberedList’, ‘BulletedList’, ‘-‘, ‘Outdent’, ‘Indent’, ‘-‘, ‘Blockquote’, ‘CreateDiv’, ‘-‘, ‘JustifyLeft’, ‘JustifyCenter’, ‘JustifyRight’, ‘JustifyBlock’ ] },
‘/’,
{ name: ‘styles’, items: [ ‘Styles’, ‘Format’, ‘Font’, ‘FontSize’ ] },
{ name: ‘colors’, items: [ ‘TextColor’, ‘BGColor’ ] },
{ name: ‘basicstyles’, groups: [ ‘basicstyles’, ‘cleanup’ ], items: [ ‘Bold’, ‘Italic’, ‘Underline’, ‘Strike’, ‘Subscript’, ‘Superscript’, ‘-‘, ‘RemoveFormat’ ] }
];

config.toolbar_mini = [
{ name: ‘paragraph’, groups: [ ‘list’, ‘indent’, ‘blocks’, ‘align’, ‘bidi’ ], items: [ ‘NumberedList’, ‘BulletedList’, ‘-‘, ‘Outdent’, ‘Indent’, ‘-‘, ‘Blockquote’, ‘CreateDiv’, ‘-‘, ‘JustifyLeft’, ‘JustifyCenter’, ‘JustifyRight’, ‘JustifyBlock’ ] },
{ name: ‘styles’, items: [ ‘Font’, ‘FontSize’ ] },
{ name: ‘colors’, items: [ ‘TextColor’, ‘BGColor’ ] },
{ name: ‘basicstyles’, groups: [ ‘basicstyles’, ‘cleanup’ ], items: [ ‘Bold’, ‘Italic’, ‘Underline’, ‘Strike’, ‘Subscript’, ‘Superscript’, ‘-‘, ‘RemoveFormat’ ] },
{ name: ‘insert’, items: [ ‘Image’, ‘Table’, ‘HorizontalRule’, ‘SpecialChar’ ] }
];
}

What I ended up with was this:

CKEDITOR.editorConfig = function( config )
{
// Toolbar groups configuration.
config.toolbar_mini = [
{ items: [ ‘Bold’, ‘Italic’, ‘Underline’, ‘Strike’] },
{ name: ‘paragraph’, groups: [ ‘list’, ‘indent’, ‘blocks’, ‘align’, ‘bidi’ ], items: [ ‘NumberedList’, ‘BulletedList’, ‘-‘, ‘Blockquote’ ] },
];
};

And then, I updated the toolbar configuration in my view: <%= f.input :text, label: “Lesson Text”, :as => :ckeditor, input_html: { :ckeditor => {:toolbar => ‘mini‘} %>

You can find additional configuration options for CKEditor here and here.