FCKEditor (with spell check!) on Rails 14
Ever seen Josh Charles' article on Integrating FCKEditor with your Ruby on Rails application? It's a really good step-by-step tutorial on how to get the popular FCKEditor working smoothly with Rails. (If you don't know what FCKEditor is - it's basically a Javascript "rich" text box with a variety of options - kinda like MS Word toolbars.)
Anyway, I really needed FCKEditor + spell check for a project I've been working on. So, with Josh's help, I spent a few weekend hours putting together a solution. FCKEditor comes with integration with Aspell "out-of-the-box" for PHP and Perl already, so a simple translation of the existing PHP code does the trick.
This assumes a model called Document, and a document_controller, and that the FCKEditor code lives in /path/to/your/rails/application/public/javascripts.
0) Follow the steps in Josh's article to write a helper method for the editor, so you can simply do something like <%= fckeditor_text_field 'document', 'text' %> to use it on your form.
1) We want FCKEditor to use our own "spell" action, instead of the default PHP code, so change one line in javascripts/editor/dialog/fck_spellerpages/spellerpages/spellchecker.js from
this.spellCheckScript = 'server-scripts/spellchecker.php'
to
this.spellCheckScript = '/document/spell'
2) When you hit the spell check button in the editor, it posts the form and gives you the stuff in the text box (escaped html tags and all) in @params[:textinputs][0]. We only want to spell check the raw text, so in the controller, use CGI.unescape to unescape the string that you have (which will look something like "Ice%20Ice%20Baby"). Then, use ActionView's helper method strip_tags to remove all html tags.
require 'cgi'
class DocumentController < ApplicationController
include ActionView::Helpers::TextHelper
def spell
@original_text = @params[:textinputs][0]
unescaped_text = CGI.unescape(@params[:textinputs][0])
plain_text = strip_tags(unescaped_text)
@words = Document.spell_check(plain_text, @user)
end
end
3) Now, in the Document model (document.rb), we need to write the spell_check method. Basically, we write the text that's passed to this function, write it to a text file, and execute Aspell to do the actual spell checking. You get back some output that needs to be parsed to retrieve the results. Ultimately, there's Javascript (see step 4) that actually displays the results of the spell check, and it expects two arrays - one that represents all misspelled words, and another that holds the corresponding suggestions. Warning - yucky string manipulation/hacking lies ahead:
def self.spell_check(text, user)
# shell out to aspell and retrieve the results
# aspell_program = 'c:\program files\aspell\bin\aspell' # windows
aspell_program = 'aspell' # every other OS on the planet
temp_file = "#{somewhere}/spell.txt"
File.open(temp_file, "wb") { |f| f.write(text) }
command_line = "\"#{aspell_program}\" -a --lang=en_US --encoding=utf-8 -H < #{temp_file} 2>
&1"
output = IO.popen(command_line) { |f| f.read }
lines = output.split("\n")
# build an array of stuff to put in the javascript
words = []
lines.each do |line|
if line[0,1] == '&'
details = line.split(' ', 5)
words << [escape(details[1]), get_suggestions(details[4])]
end
end
words
end
def self.get_suggestions(value)
value.split(', ').collect {|x| "'#{escape(x)}'"}.join(', ')
end
# properly escape single-quote for javascript
def self.escape(x)
x.gsub(/'/, '\\\\\'')
end
4) Finally, the view (spell.rhtml), actually displays the results of the spell check.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<%= stylesheet_link_tag "spellercss" %>
<%= javascript_include_tag "wordWindow" %>
<script language="javascript">
var suggs = new Array();
var words = new Array();
var textinputs = new Array();
var error;
textinputs[0] = decodeURIComponent('<%= @original_text %>');
words[0] = [];
suggs[0] = [];
<% @words.length.times do |n| %>
words[0][<%=n%>] = '<%= @words[n][0] %>';
suggs[0][<%=n%>] = [<%= @words[n][1] %>];
<% end %>
var wordWindowObj = new wordWindow();
wordWindowObj.originalSpellings = words;
wordWindowObj.suggestions = suggs;
wordWindowObj.textInputs = textinputs;
function init_spell() {
// check if any error occured during server-side processing
if( error ) {
alert( error );
} else {
// call the init_spell() function in the parent frameset
if (parent.frames.length) {
parent.init_spell( wordWindowObj );
} else {
alert('This page was loaded outside of a frameset. It might not display properly');
}
}
}
</script>
</head>
<body onLoad="init_spell();" bgcolor="#ffffff">
<script type="text/javascript">
wordWindowObj.writeBody();
</script>
</body>
</html>
Yay!
Update: As David Kessler points out, you'll also need to copy over one JavaScript file for this to work. Copy wordWindow.js from public\javascripts\editor\dialog\fck_spellerpages\spellerpages to public\javascripts.




I am attempting to follow the directions on http://www.softiesonrails.com/articles/2006/02/14/fckeditor-with-spell-check-on-rails.
I worked through several issues and have finally gotten the correct results passed to spell.rhtml. The final error that I am getting is:
ActionController::RoutingError (Recognition failed for "/javascripts/wordWindow.js"):
If I copy the ‘wordWindow.js’ file from ‘\public\javascripts\editor\dialog\fck_spellerpages\spellerpages\’ to ‘\public\javascripts\’ Rails is happy but the ‘originalSpellings’, ‘suggestions’, and the ‘textInputs’ fail to render on the screen. I am able to add some text to the body of speller.rhtml and it renders. I have also checked all of the values received from the controller and they are correct.
I would appreciate any help that you could offer. Thank you for your time and assistance.
Major brain freeze resulted in some missing code. I've updated the post.
temp_file = "#{somewhere}/spell.txt" File.open(temp_file, "wb") { |f| f.write(text) }Maybe it's just me, but isn't there a horrible race condition here? What happens if two users want to spell check a document at the same time?
Ideally, you'd make a call to a system function like mkstemp (on Linux). Since Ruby doesn't expose and API for this function, a quick and dirty way would be to make a really simple C program that simply calls the mkstemp function and spits the filename back out on standard output. You'd probably have to keep that program running until you're done with the file, so that the kernel doesn't nuke the temporary file.
It'd be nice if Ruby provided this in its standard library, but as far as I know it does not.
Jared: no, you're absolutely right. I should have pointed out that in my app, "somewhere" is a directory that I've created for each user (my app is a line-of-business app that requires users to be logged-in). If it was a public web app, a better solution would have to be implemented.
Thanks for the write-up :)
That's awesome!
salidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditorsalidafckeditor
The default when I downloaded fckeditor was to use iespell instead of aspell. To use aspell you have to change your fckconfig.js file and tell it to use aspell instead of iespell by setting
FCKConfig.SpellChecker = 'SpellerPages'
jzhmkj
Its awesome, thank you!
Try this:
http://www.thesolutioncafe.com/fckeditorspellchecker.html
Three lines of code... and you have a spell checker for FCKEditor!
When i run rake fckeditor:install It show:Don’t know how to build task ‘fckeditor:install’ I don’t know why
In all theoretical sciences, the phenomena are a representation of, for example, the phenomena.
Since all of the Categories are hypothetical, it remains a mystery why, in reference to ends, philosophy is the clue to the discovery of our disjunctive judgements, and the noumena are what first give rise to, in accordance with the principles of the Transcendental Deduction, the things in themselves.