|
# Source: |
|
# |
|
# http://gist.github.com/235097 |
|
# http://coderack.org/users/techiferous/middlewares/89-rackspellcheck |
|
# |
|
# |
|
# If you'd like this packaged up as a gem, send me a note. You can get |
|
# in touch with me at http://www.techiferous.com/about |
|
|
|
require 'nokogiri' |
|
require 'ispell' |
|
|
|
module Rack |
|
class SpellCheck |
|
|
|
def initialize(app, options = {}) |
|
@app = app |
|
@options = options |
|
# The way ispell parses words means that it's hard to skip URLs. |
|
# Ignoring these "words" eases the pain somewhat. |
|
@ignore = ["www", "com", "http", "org", "html"] |
|
if options[:ignore].is_a? Array |
|
@ignore += options[:ignore].map(&:downcase) |
|
end |
|
end |
|
|
|
def call(env) |
|
@doc = nil |
|
@request = Rack::Request.new(env) |
|
status, @headers, @body = @app.call(env) |
|
if html? |
|
find_spelling_errors |
|
highlight_spelling_errors |
|
update_content_length |
|
end |
|
[status, @headers, @body] |
|
end |
|
|
|
private |
|
|
|
def find_spelling_errors |
|
@speller = Ispell.new('ispell', 'english') # note: we are forking a process |
|
doc.at_css("body").traverse do |node| |
|
if node.text? |
|
node.content = spellcheck(node.content) |
|
end |
|
end |
|
@speller.destroy! # stop the process |
|
@body = doc.to_html |
|
end |
|
|
|
def highlight_spelling_errors |
|
style = "background-color: yellow; color: red; font-weight: bold;" |
|
@body.gsub!('changethistobeginningtaglater', "<span style=\"#{style}\">") |
|
@body.gsub!('changethistoendingtaglater', '</span>') |
|
end |
|
|
|
def spellcheck(text) |
|
results = @speller.spellcheck(text) |
|
new_text = text |
|
results.each do |res| |
|
case res.type |
|
when :miss, :guess, :none |
|
unless @ignore.include?(res.original.downcase) |
|
new_text.gsub!(res.original, |
|
"changethistobeginningtaglater#{res.original}changethistoendingtaglater") |
|
end |
|
end |
|
end |
|
new_text |
|
end |
|
|
|
def html? |
|
@headers["Content-Type"] && @headers["Content-Type"].include?("text/html") |
|
end |
|
|
|
def doc |
|
@doc ||= Nokogiri::HTML(body_to_string) |
|
end |
|
|
|
def body_to_string |
|
s = "" |
|
@body.each { |x| s << x } |
|
s |
|
end |
|
|
|
def update_content_length |
|
@headers['Content-Length'] = Rack::Utils.bytesize(@body).to_s |
|
end |
|
|
|
end |
|
end |