= ActAsTextcaptcha ActsAsTextcaptcha provides spam protection for your Rails models using logic questions from the excellent {Text CAPTCHA}[http://textcaptcha.com/] web service (by {Rob Tuley}[http://openknot.com/me/] of {Openknot}[http://openknot.com/]). It is also possible to configure your own questions and answers instead of using this API (or as a fall back in the unlikely event of the web service being unavailable) The gem makes use of {bcrypt}[http://bcrypt-ruby.rubyforge.org/] encryption when comparing a guess with the correct answer(s). This gem is actively maintained, has good test coverage and is compatible with Rails 2.3.* and 3. If you have any issues {please report them here}[https://github.com/matthutchinson/acts_as_textcaptcha/issues]. Text CAPTCHA's logic questions are aimed at a child's age of 7, so can be solved easily by all but the most cognitively impaired users. As they involve human logic, such questions cannot be solved by a robot. There are both advantages and disadvantages for using logic questions over image based captchas, {find out more at Text CAPTCHA}[http://textcaptcha.com/why]. == Demo Here's a {fully working demo on heroku}[http://textcaptcha.heroku.com]! == Installing === Rails 3 Just add the following to your Gemfile, then `bundle install`; gem 'acts_as_textcaptcha' === Rails 2.3.* Add this to your environment.rb file, then `gem install acts_as_textcaptcha`; config.gem 'acts_as_textcaptcha' === As a plugin Or install as a plugin with; rails plugin install git@github.com:matthutchinson/acts_as_textcaptcha.git OR script/plugin install git@github.com:matthutchinson/acts_as_textcaptcha.git If you do decide to install this as a Rails plugin, you'll have to manually include the bcrypt-ruby gem in your Gemfile or environment config. Like so; gem 'bcrypt-ruby', :require => 'bcrypt' OR config.gem 'bcrypt-ruby', :lib => 'bcrypt' == Setting up *NOTE:* The procedure for configuring your app *changed significantly* from v2.* to v3.* If you are having problems please carefully check the instructions below. First {grab an API key for your website}[http://textcaptcha.com/api] then open Rails console (or any irb session) and generate a new BCrypt salt by typing; require 'bcrypt';BCrypt::Engine.generate_salt Next configure which models are to be spam protected like so; class Comment < ActiveRecord::Base # (this is the most basic way to configure the gem, with an API key and Salt only) acts_as_textcaptcha :api_key => 'PASTE_YOUR_TEXTCAPTCHA_API_KEY_HERE', :bcrypt_salt => 'PASTE_YOUR_BCRYPT_SALT_HERE' end Next in your controller's *new* action you'll want to generate the logic question for your model, like so; def new @comment = Comment.new @comment.textcaptcha end Finally, in your form view add the spam question and answer fields using the textcaptcha_fields helper. You are free to arrange the HTML within this helper as you like; <%= textcaptcha_fields(f) do %>
<%= f.label :spam_answer, @comment.spam_question %>
<%= f.text_field :spam_answer, :value => '' %>
<% end %> *NOTE:* For Rails 2.* this step is slightly different (<%= changes to <%); <% textcaptcha_fields(f) do %>
<%= f.label :spam_answer, @comment.spam_question %>
<%= f.text_field :spam_answer, :value => '' %>
<% end %> *NOTE:* If you'd rather NOT use this helper and prefer to write all your own view code, see the html produced from the textcaptcha_fields method in the gem source code. == More Configurations The gem can be configured for models individually (as shown above) or with a config/textcaptcha.yml file for the whole app. A config must have a valid bcrypt_salt, and an api_key and/or an array of questions defined. === Hash class Comment < ActiveRecord::Base acts_as_textcaptcha :api_key => 'YOUR_TEXTCAPTCHA_API_KEY', :bcrypt_salt => 'YOUR_BCRYPT_SALT', :bcrypt_cost => '3', :questions => [{ 'question' => '1+1', 'answers' => '2,two' }, { 'question' => 'The green hat is what color?', 'answers' => 'green' }] end * *api_key* (get from http://textcaptcha.com/api) * *bcrypt_salt* - used to encrypt the valid possible answers * *bcrypt_cost* - an optional logarithmic number which determines how computationally expensive the bcrypt hash will be (a cost of 4 is twice as much work as a cost of 3 - the default is 10) * *questions* - an array of question and answer hashes (see above) A random question from this array will be asked if the web service fails OR if no API key has been set. Multiple answers to the same question are comma separated (e.g. 2,two) so don't use commas in your answers! === YAML config The gem comes with a handy rake task to copy over a {textcaptcha.yml}[http://github.com/matthutchinson/acts_as_textcaptcha/raw/master/config/textcaptcha.yml] template to your Rails config directory. It will also generate a random BCrypt Salt when you first run it. rake textcaptcha:config *NOTE:* If you are on Rails 2.3.*, you'll have to add the following to your Rakefile to make this task available; `# load textcaptcha rake tasks Dir["#{Gem.searcher.find('acts_as_textcaptcha').full_gem_path}/lib/tasks/**/*.rake"].each { |ext| load ext } if Gem.searcher.find('acts_as_textcaptcha')` === Using _without_ the Text CAPTCHA web service To use only your own logic questions simply ommit the api_key from your config and define at least 1 logic question/answer. == Translations Recent versions of the gem use standard Rails I18n translation for the error message generated by acts_as_textcaptcha, with a fall-back to English. Unfortunately in some versions of Rails 2.3.* this translation fall-back fails. For this case (and when translating) setup your own locale file with this hierarchy (assuming your model is Comment); en: activerecord: errors: models: comment: attributes: spam_answer: incorrect_answer: "is incorrect, try another question instead" activemodel: attributes: comment: spam_answer: "Spam answer" *NOTE:* currently the Text CAPTCHA API web service only offers logic questions in English. == Testing and docs In development you can run the tests and rdoc tasks like so; * rake test (all tests) * rake test:coverage (all tests with code coverage reporting) * rake rdoc (generate docs) == What does the code do? The gem contains two parts, a module for your ActiveRecord models, and a single view helper method. A call to @model.textcaptcha in your controller will query the Text CAPTCHA web service. A GET request is made with Net::HTTP and parsed using the default Rails ActiveSupport::XMLMini backend (or the standard XML::Parser in older versions of Rails). A spam_question and an encrypted array of possible answers are assigned to the model. validate_textcaptcha is called on @model.validate and checks that the @model.spam_answer matches one of the possible answers (decrypted). This validation is _only_ carried out on new records, i.e. never on edit, only on create. All attempted spam answers are case-insensitive and have trailing/leading white-space removed. perform_textcaptcha? is a simple utility method (that can be overridden in your model) It allows you to define whether the spam check should be carried out at all. For example, to turn off spam checking for logged in users. If an error or timeout occurs in loading or parsing the API, ActsAsTextcaptcha will fall back to choose a random logic question defined in your options. If the web service fails or no API key is specified AND no alternate questions are configured, the @model will not require spam checking and will pass as valid. For more details on the code please check the {documentation}[http://rdoc.info/projects/matthutchinson/acts_as_textcaptcha]. Tests are written with {MiniTest}[https://rubygems.org/gems/minitest] and code coverage is provided by {SimpleCov}[https://github.com/colszowka/simplecov] == Requirements What do you need? * {Rails}[http://github.com/rails/rails] >= 2.3.2 (including Rails 3) * {Ruby}[http://ruby-lang.org/] >= 1.8.7 (also tested with REE and Ruby 1.9.2) * {bcrypt-ruby}[http://bcrypt-ruby.rubyforge.org/] gem (to securely encrypt spam answers) * {Text CAPTCHA API key}[http://textcaptcha.com/register] (_optional_, since you can define your own logic questions, see below for details) * {MiniTest}[https://rubygems.org/gems/minitest] (_optional_ if you want to run the tests, built into Ruby 1.9) == Links * {Documentation}[http://rdoc.info/projects/matthutchinson/acts_as_textcaptcha] * {Demo}[http://textcaptcha.heroku.com] * {Code}[http://github.com/matthutchinson/acts_as_textcaptcha] * {Wiki}[http://wiki.github.com/matthutchinson/acts_as_textcaptcha/] * {Bug Tracker}[http://github.com/matthutchinson/acts_as_textcaptcha/issues] * {Gem}[http://rubygems.org/gems/acts_as_textcaptcha] == Who's who? * {ActsAsTextcaptcha}[http://github.com/matthutchinson/acts_as_textcaptcha] and {little robot drawing}[http://www.flickr.com/photos/hiddenloop/4541195635/] by {Matthew Hutchinson}[http://matthewhutchinson.net] * {Text CAPTCHA}[http://textcaptcha.com] API and service by {Rob Tuley}[http://openknot.com/me/] at {Openknot}[http://openknot.com] * {bcrypt-ruby}[http://bcrypt-ruby.rubyforge.org/] Gem by {Coda Hale}[http://codahale.com] == Todo * Achieve 100% test coverage, currently at 93% == Usage This gem is used in a number of production websites and apps. It was originally extracted from code developed for {Bugle}[http://bugleblogs.com] * {matthewhutchinson.net}[http://matthewhutchinson.net] * {pmFAQtory.com}[http://pmfaqtory.com] * {The FAQtory}[http://faqtory.heroku.com] * {DPT Watch, San Francisco}[http://www.dptwatch.com] (if you're happily using acts_as_textcaptcha in production, let me know and I'll add you to this list)