nice_text_captcha 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,73 @@
1
+ Nice Text Captcha
2
+ by Neil Smith
3
+
4
+ About
5
+ -----
6
+
7
+ This generates text-based CATPCHAs that are "nice", by my own personal
8
+ subjective definition!
9
+
10
+ Questions are purposely simple, and examples would include:
11
+
12
+ * How many letters are there in the word 'White'?
13
+ * In the word "tiger", what is the letter in the 5th position?
14
+ * How many animals are in the words: "cat", "orange", and "dolphin"?
15
+ * What is 'four' + "nine"?
16
+
17
+ Implementation
18
+ --------------
19
+
20
+ You call a method in your view which both generates a random question, and places
21
+ the possible answers into a flash[] object, stored in a SHA2 hash.
22
+
23
+ (It's stored in a flash, as Rails handles ageing the data after a request - if we
24
+ used the session, it would hang around for the duration of the session.)
25
+
26
+ Then, in your controller action, you call a method which extracts the answers from
27
+ the flash, and injects them into your model.
28
+
29
+ A validation method on the model finally checks the answer against the hashed
30
+ possible answers, and pushes an error onto the base of your model if the answer
31
+ doesn't match.
32
+
33
+ For numerical answers ("What is 'two' plus 'three'?"), both numbers ("3") and
34
+ words ("three") are accepted.
35
+
36
+ Requirements
37
+ ------------
38
+
39
+ Tested against Rails 2.3 - currently untested against Rails 3.
40
+ 'linguistics' gem for turning numbers into words and ordinals.
41
+ 'Digest/SHA2' for hashing the possible answers.
42
+
43
+ Instructions
44
+ ------------
45
+
46
+ In your model, you need to add some validation:
47
+
48
+ class Thing < ActiveRecord::Base
49
+ validate :check_nice_text_captcha
50
+ end
51
+
52
+ In your controller, you need to add a call to populate the answers from the
53
+ data in the session:
54
+
55
+ class ThingsController < ActiveRecord::Base
56
+ def create
57
+ @thing = Thing.new(params[:thing]) # as normal
58
+ validate_nice_text_captcha_for(@thing)
59
+ end
60
+ end
61
+
62
+ Finally, in your view, you need to render the question by calling nice_text_captcha,
63
+ and give the user a text box for their answer:
64
+
65
+ <% form_for(@thing) do |f| %>
66
+ <!-- normal form elements here -->
67
+
68
+ <%= f.nice_text_captcha %><br />
69
+ <%= f.text_field :nice_text_captcha %><br />
70
+
71
+ <!-- probably a submit button here -->
72
+
73
+ <% end %>
@@ -0,0 +1,21 @@
1
+ #config.gem "linguistics"
2
+ require 'linguistics'
3
+ Linguistics::use(:en)
4
+
5
+ require 'nice_text_captcha/captcha'
6
+
7
+ require 'nice_text_captcha/active_record_extensions'
8
+ require 'nice_text_captcha/action_controller_extensions'
9
+ require 'nice_text_captcha/form_builder_extensions'
10
+
11
+ require 'nice_text_captcha/types/default_question'
12
+ require 'nice_text_captcha/types/letter_position_question'
13
+ require 'nice_text_captcha/types/maths_question'
14
+ require 'nice_text_captcha/types/word_length_question'
15
+ require 'nice_text_captcha/types/words_in_list_question'
16
+
17
+ ActionView::Helpers::FormBuilder.send :include, NiceTextCaptcha::FormBuilderExtensions
18
+
19
+ ActiveRecord::Base.send :include, NiceTextCaptcha::ActiveRecordExtensions
20
+
21
+ ActionController::Base.send :include, NiceTextCaptcha::ActionControllerExtensions
@@ -0,0 +1,11 @@
1
+ module NiceTextCaptcha
2
+
3
+ module ActionControllerExtensions
4
+
5
+ def validate_nice_text_captcha_for(obj)
6
+ obj.nice_text_captcha_responses = flash[:nice_text_captcha_responses] || ["no_answers_in_session"]
7
+ end
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,31 @@
1
+ module NiceTextCaptcha
2
+
3
+ module ActiveRecordExtensions
4
+
5
+ attr_accessor :nice_text_captcha
6
+ attr_accessor :nice_text_captcha_responses
7
+
8
+ def check_nice_text_captcha
9
+
10
+ captcha_successful = @nice_text_captcha_responses.nil?
11
+ @nice_text_captcha_responses.each do |response|
12
+ captcha_successful = true if Captcha.hash(self.class.to_s.underscore, @nice_text_captcha.strip.downcase) == response
13
+ end if @nice_text_captcha_responses
14
+
15
+ if !captcha_successful
16
+ errors.add_to_base(nice_text_captcha_failure_message)
17
+ end
18
+
19
+ @nice_text_captcha = nil
20
+
21
+ end
22
+
23
+ protected
24
+
25
+ def nice_text_captcha_failure_message
26
+ "Please ensure you answer the security question (CAPTCHA) correctly"
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,30 @@
1
+ module NiceTextCaptcha
2
+
3
+ class Captcha
4
+
5
+ attr_reader :question
6
+ attr_reader :answers
7
+
8
+ def initialize
9
+ captcha = [
10
+ Types::MathsQuestion,
11
+ Types::LetterPositionQuestion,
12
+ Types::WordsInListQuestion,
13
+ Types::WordLengthQuestion,
14
+ ].rand.new
15
+ @question = captcha.question
16
+ @answers = captcha.answers
17
+ end
18
+
19
+ def encrypted_answers_for(object_name)
20
+ answers.collect { |answer| Captcha.hash(object_name, answer) }
21
+ end
22
+
23
+ def self.hash(obj, answer)
24
+ str = obj.to_s + answer.to_s + ActionController::Base.session_options[:secret].to_s
25
+ Digest::SHA2.hexdigest(str)
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,13 @@
1
+ module NiceTextCaptcha
2
+
3
+ module FormBuilderExtensions
4
+
5
+ def nice_text_captcha
6
+ captcha = NiceTextCaptcha::Captcha.new
7
+ @template.flash[:nice_text_captcha_responses] = captcha.encrypted_answers_for(@object_name)
8
+ captcha.question
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,42 @@
1
+ module NiceTextCaptcha::Types
2
+ class DefaultQuestion
3
+
4
+ ANIMALS = ["dog", "cat", "dolphin", "mouse", "horse", "penguin", "panda", "tiger", "lion", "zebra"]
5
+ COLOURS = ["black", "white", "red", "green", "blue", "pink", "purple", "orange", "yellow", "brown"]
6
+
7
+ attr_reader :question
8
+ attr_reader :answers
9
+
10
+ private
11
+
12
+ def fifty_fifty?
13
+ rand(2) == 0
14
+ end
15
+
16
+ def number_to_word(number)
17
+ number.to_i.en.numwords
18
+ end
19
+
20
+ def number_to_ordinal(number)
21
+ number.to_i.en.ordinal
22
+ end
23
+
24
+ def randomly_quote(word)
25
+ q = ["'", "\""].rand
26
+ q + word + q
27
+ end
28
+
29
+ def randomly_capitalise(word)
30
+ fifty_fifty? ? word : word.capitalize
31
+ end
32
+
33
+ def randomly_quote_and_capitalise(word)
34
+ randomly_quote(randomly_capitalise(word))
35
+ end
36
+
37
+ def randomly_sort(list)
38
+ list.sort { |x,y| rand(100) <=> rand(100) }
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,32 @@
1
+ class NiceTextCaptcha::Types::LetterPositionQuestion < NiceTextCaptcha::Types::DefaultQuestion
2
+
3
+ def initialize
4
+ word = [ANIMALS + COLOURS].flatten.rand
5
+ position = rand(word.length) + 1
6
+ answer = word[position - 1, 1]
7
+
8
+ position_string = number_to_ordinal(position)
9
+
10
+ position_string = "first" if position == 1 and fifty_fifty?
11
+ position_string = "second" if position == 2 and fifty_fifty?
12
+ position_string = (fifty_fifty? ? "final" : "last") if position == word.length and fifty_fifty?
13
+ position_string = "second to last" if position == word.length - 1 and fifty_fifty?
14
+
15
+ @question = format_question(randomly_quote_and_capitalise(word), position_string)
16
+ @answers = [answer]
17
+ end
18
+
19
+ def format_question(word, position)
20
+ [
21
+ "What is the %s letter in the word %s?" % [position, word],
22
+ "What's the %s letter in the word %s?" % [position, word],
23
+ "The %s letter in %s is what?" % [position, word],
24
+ "The %s letter in the word %s is?" % [position, word],
25
+ "In the word %s, what is the %s letter?" % [word, position],
26
+ "In the word %s, what is the letter in the %s position?" % [word, position],
27
+ ].rand
28
+ end
29
+
30
+ private
31
+
32
+ end
@@ -0,0 +1,36 @@
1
+ class NiceTextCaptcha::Types::MathsQuestion < NiceTextCaptcha::Types::DefaultQuestion
2
+
3
+ def initialize
4
+ number_1 = one_to_nine
5
+ number_2 = one_to_nine
6
+ answer = number_1 + number_2
7
+
8
+ @question = format_question(number_or_word(number_1), number_or_word(number_2))
9
+ @answers = [answer, number_to_word(answer)]
10
+ end
11
+
12
+ def format_question(one, two)
13
+ [
14
+ "What is %s plus %s?" % [one, two],
15
+ "What is %s + %s?" % [one, two],
16
+ "What is %s added to %s?" % [one, two],
17
+ "What do you get if you add the numbers %s and %s?" % [one, two],
18
+ "What is the result of adding %s and %s?" % [one, two],
19
+ ].rand
20
+ end
21
+
22
+ private
23
+
24
+ def one_to_nine
25
+ rand(9) + 1
26
+ end
27
+
28
+ def number_or_word(number)
29
+ if fifty_fifty?
30
+ number
31
+ else
32
+ randomly_quote_and_capitalise(number_to_word(number))
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,20 @@
1
+ class NiceTextCaptcha::Types::WordLengthQuestion < NiceTextCaptcha::Types::DefaultQuestion
2
+
3
+ def initialize
4
+ word = [ANIMALS + COLOURS].flatten.rand
5
+ answer = word.length
6
+
7
+ @question = format_question(randomly_quote_and_capitalise(word))
8
+ @answers = [answer, number_to_word(answer)]
9
+ end
10
+
11
+ def format_question(word)
12
+ [
13
+ "How many letters are in the word %s?" % word,
14
+ "How many letters are there in the word %s?" % word,
15
+ "How many letters are there in %s?" % word,
16
+ "In the word %s, how many letters are there?" % word,
17
+ ].rand
18
+ end
19
+
20
+ end
@@ -0,0 +1,38 @@
1
+ class NiceTextCaptcha::Types::WordsInListQuestion < NiceTextCaptcha::Types::DefaultQuestion
2
+
3
+ def initialize
4
+ animals = randomly_sort(ANIMALS)
5
+ colours = randomly_sort(COLOURS)
6
+
7
+ answer = rand(3) + 1
8
+
9
+ if fifty_fifty?
10
+ words = animals[0 .. answer-1]
11
+ words << colours[0 .. rand(3)]
12
+ noun = "animals"
13
+ else
14
+ words = colours[0 .. answer-1]
15
+ words << animals[0 .. rand(3)]
16
+ noun = "colours"
17
+ end
18
+
19
+ words.flatten!
20
+ words.collect! { |w| randomly_quote_and_capitalise(w) }
21
+
22
+ @question = format_question(noun, randomly_sort(words))
23
+
24
+ @answers = [answer, number_to_word(answer)]
25
+ end
26
+
27
+ def format_question(noun, word_list)
28
+ collective_noun = ["list", "words", "list of words", "word list"].rand
29
+ collective_noun = "following " + collective_noun if fifty_fifty?
30
+
31
+ [
32
+ "How many %s are in the %s: %s?" % [noun, collective_noun, word_list.to_sentence],
33
+ "How many %s are there in the %s: %s?" % [noun, collective_noun, word_list.to_sentence],
34
+ "In the %s: %s - how many %s are there?" % [collective_noun, word_list.to_sentence, noun],
35
+ ].rand
36
+ end
37
+
38
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nice_text_captcha
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Neil Smith
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-06 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description:
23
+ email: neil.jx.smith@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README
30
+ files:
31
+ - README
32
+ - lib/nice_text_captcha/action_controller_extensions.rb
33
+ - lib/nice_text_captcha/active_record_extensions.rb
34
+ - lib/nice_text_captcha/captcha.rb
35
+ - lib/nice_text_captcha/form_builder_extensions.rb
36
+ - lib/nice_text_captcha/types/default_question.rb
37
+ - lib/nice_text_captcha/types/letter_position_question.rb
38
+ - lib/nice_text_captcha/types/maths_question.rb
39
+ - lib/nice_text_captcha/types/word_length_question.rb
40
+ - lib/nice_text_captcha/types/words_in_list_question.rb
41
+ - lib/nice_text_captcha.rb
42
+ has_rdoc: true
43
+ homepage:
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --main
49
+ - README
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.7
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Generates Text Catpchas, that are 'nice' by my own personal definition!
77
+ test_files: []
78
+