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.

Testing Braintree on Ruby on Rails

I’m using Braintree with a Ruby on Rails app via the braintree_ruby gem.

I needed to test responses in some of my tests.  Here’s how I was able to simulate some transactions.

I created a transaction, such as this:

result = Braintree::Transaction.sale( :amount => “100.00”, :payment_method_nonce => nonce_from_the_client )

To simulate different types of transactions, I changed nonce_from_the_client to the different options here putting the string in quotation marks.

To test a declined transaction, I used a nonce for a declined transaction and adjusted the amount to one that would fail, such as this:

result = Braintree::Transaction.sale( :amount => “2000.00”, :payment_method_nonce => “fake-processor-declined-visa-nonce” )

 

Simple_Form Custom Errors

I had some error messages not appearing in simple_form because the fields they were related to did not appear in the simple_form.  Normally, simple_form will highlight the field in the form that has a problem and display the error message next to it.  However, since the field related to the error wasn’t in the form, there was no message being shown.

To solve this, I had to add some additional code to display the text of the error message.

I fixed it by adding this to my simple_form forms:

<%= render 'shared/error_messages', object: f.object %>

and then created a partial, _error_messages (shared/_error_messages):

<% if object.errors.any? %>

<% end %>

Rails 4 Testing Email

My new favorite way to test email is to use the Mailcatcher gem.  It’s easy to install the Mailcatcher gem (per the instructions, do not include it in your gemfile due to the high likelihood that it will mess something up).

It’s as easy as the instructions say:

  1. gem install mailcatcher (in cmd window)
  2. mailcatcher (in cmd window)
  3. Go to http://localhost:1080/
  4. Send mail through smtp://localhost:1025
  5. Add these to you development.rb file:
    config.action_mailer.delivery_method = :smtp
    config.action_mailer.smtp_settings = { :address => "localhost", :port => 1025 }

After you do that, you can go into your rails console and send emails.  Once you send emails, when you visit http://127.0.0.1:1080/

You’ll see the emails that were sent.  The screen will look somewhat similar to an email client, and you see tabs to see the html, as well as text, versions of the email.

I’m using Sendgrid and Heroku, if you have the same set-up, you may have to comment out the config settings in your environment.rb file.

Select Drop Down in Simple_Form and Rails

I’m using the simple_form gem for forms in Rails and I needed to create a select (drop down) for a field.  In my case, it was for selecting US States, but this can be changed easily to whatever options you want to present by changing the options in the list.

In my user.rb model, I put the options (change these if your select is for something else than US States).

STATES =
[
[‘Alabama’, ‘AL’],
[‘Alaska’, ‘AK’],
[‘Arizona’, ‘AZ’],
[‘Arkansas’, ‘AR’],
[‘California’, ‘CA’],
[‘Colorado’, ‘CO’],
[‘Connecticut’, ‘CT’],
[‘Delaware’, ‘DE’],
[‘District of Columbia’, ‘DC’],
[‘Florida’, ‘FL’],
[‘Georgia’, ‘GA’],
[‘Hawaii’, ‘HI’],
[‘Idaho’, ‘ID’],
[‘Illinois’, ‘IL’],
[‘Indiana’, ‘IN’],
[‘Iowa’, ‘IA’],
[‘Kansas’, ‘KS’],
[‘Kentucky’, ‘KY’],
[‘Louisiana’, ‘LA’],
[‘Maine’, ‘ME’],
[‘Maryland’, ‘MD’],
[‘Massachusetts’, ‘MA’],
[‘Michigan’, ‘MI’],
[‘Minnesota’, ‘MN’],
[‘Mississippi’, ‘MS’],
[‘Missouri’, ‘MO’],
[‘Montana’, ‘MT’],
[‘Nebraska’, ‘NE’],
[‘Nevada’, ‘NV’],
[‘New Hampshire’, ‘NH’],
[‘New Jersey’, ‘NJ’],
[‘New Mexico’, ‘NM’],
[‘New York’, ‘NY’],
[‘North Carolina’, ‘NC’],
[‘North Dakota’, ‘ND’],
[‘Ohio’, ‘OH’],
[‘Oklahoma’, ‘OK’],
[‘Oregon’, ‘OR’],
[‘Pennsylvania’, ‘PA’],
[‘Puerto Rico’, ‘PR’],
[‘Rhode Island’, ‘RI’],
[‘South Carolina’, ‘SC’],
[‘South Dakota’, ‘SD’],
[‘Tennessee’, ‘TN’],
[‘Texas’, ‘TX’],
[‘Utah’, ‘UT’],
[‘Vermont’, ‘VT’],
[‘Virginia’, ‘VA’],
[‘Washington’, ‘WA’],
[‘West Virginia’, ‘WV’],
[‘Wisconsin’, ‘WI’],
[‘Wyoming’, ‘WY’]
]

Now in my simple_form, I put this:

<%= simple_form_for(@user) do |f| %>
<%= f.input :indiv_state, collection: User::STATES %>
<%= f.button :submit %>
<% end %>

Fixing “SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed” Error on Windows

When I tried to implement OmniAuth, I received this error “SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed”.  Essentially, I was getting this error because the code was searching for an SSL certificate on my PC.

I didn’t have a SSL certificate for my PC, fortunately I found a work around to fix this.

Tracking Conversions in Google Analytics for Devise User Sign-Ups

I wanted to track user sign-ups as a goal, while using the Devise gem.  The Devise gem redirects users to the root path as a default, which I wanted to keep.  The problem with that is that I didn’t want to every visit to the root page as a conversion, only the ones that were a redirect after the user registered.

Here’s what I ended up doing:

In the users#registrations_controller.rb, I added this flash:

flash[:user_signup] = true

then in the root path view, I added:

<% if flash[:user_signup] == true %>
<%= render :partial => “shared/google_analytics_user_sign_up” %>  # edited google analytics code
<% else %>
<%= render :partial => “shared/google_analytics” %> # regular google analytics code
<% end %>

In the google_analytics_user_sign_up partial, I have this:

<script>
(function(i,s,o,g,r,a,m){i[‘GoogleAnalyticsObject’]=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,’script’,’//www.google-analytics.com/analytics.js’,’ga’);

ga(‘create’, ‘UA-99999999-1’, ‘foo.com’);
ga(‘send’, ‘pageview’, ‘/special/path’);
</script>
<!– Google Analytics end –>

In Google Analytics, add it as a goal, in the destination add:

/special/path

You can name the path whatever you want, as long as they match in the code and in Google Analytics.  Just make sure that it’s unique, it’s not the path of another url someone might go to.

Creating a Blog for Rails App

I wanted to create a blog for my Rails app and investigated ways to do this.  You can always start a free blog on Blogger, WordPress or Tumblr, but I wanted the blog to be a part of my domain.

There are gems to help do this.  I found the Bloggy and PostMarkdown gems.  Although they seem to integrate well with Rails, I wanted to use an existing CMS platform so I didn’t have to mess with the code of my app and to make creating posts easier. Adding markdown files to my app, generating views, and then pushing every time I wanted to make a blog update deterred me.

Instead I found a way to use WordPress for my blog here.  I liked the I could leverage WordPress to make posting easier, as well as the ability to use plug-ins people have built for assisting with SEO.  The nice thing about this set-up is that the blog uses your domain (i.e. http://www.yourdomain.com/blog) address and it doesn’t drain your resources because it is running as a separate (free) Heroku app.

The biggest downsides I’ve found are that you need a place to store your files for your posts (S3) since you can’t write to a Heroku file system when posting through WordPress.  Also, you need to push files, such as templates and plug-ins.

 

ActiveAdmin: Turning Hashes/ActiveRecord Hashes to Field Values

After I installed ActiveAdmin gem, everything worked great except I had an issue where the dropdown menus were showing ActiveRecord hashes like this:

#<Category:0x698a648>

Instead of showing a name like the other fields.  It seems that the default is to show a field called “name” in the dropdown menus in ActiveAdmin, and if you don’t have such a field it shows the ActiveRecord instead.  Changing that was easy, after I found this StackOverflow question.

All you have to do is go into the model and add a method for to_s, like this:

def to_s
# field name you want to show in the drop down
end

for me, I put:

def to_s
id
end

Whenever Gem Without Capistrano

The Whenever gem simplifies the process of using scheduled tasks without learning CRON.  Stated another way, it allows you to schedule CRON jobs without knowing CRON’s abbreviations and syntax by allowing you to enter your scheduled tasks in a schedule.rb file using straightforward language.

When you deploy using Capistrano you can put code in your deploy.rb file so that Whenever tells CRON of any scheduling changes.

However, if you’re like me, and not deploying with Capistrano than you just have to remember to use this command in Linux/Ubuntu:

whenever -i

To confirm that your task was scheduled, you can enter:

crontab -l

This should indicate that your tasks in schedule.rb were scheduled to run via CRON.