acts_as_textcaptcha 2.2.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/Gemfile +1 -3
- data/LICENSE +2 -2
- data/README.rdoc +86 -88
- data/Rakefile +17 -7
- data/acts_as_textcaptcha.gemspec +9 -10
- data/config/textcaptcha.yml +11 -11
- data/init.rb +2 -2
- data/lib/acts_as_textcaptcha.rb +1 -1
- data/lib/acts_as_textcaptcha/framework/{rails3.rb → rails.rb} +2 -2
- data/lib/acts_as_textcaptcha/framework/rails2.rb +1 -1
- data/lib/acts_as_textcaptcha/textcaptcha.rb +85 -67
- data/lib/acts_as_textcaptcha/textcaptcha_helper.rb +20 -4
- data/lib/acts_as_textcaptcha/version.rb +2 -2
- data/lib/tasks/textcaptcha.rake +14 -11
- data/{spec → test}/schema.rb +1 -1
- data/test/test_helper.rb +27 -0
- data/test/test_models.rb +42 -0
- data/test/textcaptcha_helper_test.rb +50 -0
- data/test/textcaptcha_test.rb +188 -0
- metadata +34 -52
- data/spec/acts_as_textcaptcha_spec.rb +0 -227
- data/spec/database.yml +0 -21
- data/spec/spec.opts +0 -2
- data/spec/spec_helper.rb +0 -8
@@ -1,9 +1,25 @@
|
|
1
1
|
module ActsAsTextcaptcha
|
2
2
|
module TextcaptchaHelper
|
3
3
|
|
4
|
-
#
|
5
|
-
def
|
6
|
-
|
4
|
+
# builds html fields for spam question, answer and hidden encrypted spam answers
|
5
|
+
def textcaptcha_fields(f, &block)
|
6
|
+
model = f.object
|
7
|
+
captcha_html = ''
|
8
|
+
if model.perform_textcaptcha?
|
9
|
+
captcha_html += f.hidden_field(:spam_answers)
|
10
|
+
if model.spam_answer
|
11
|
+
captcha_html += f.hidden_field(:spam_answer)
|
12
|
+
elsif model.spam_question
|
13
|
+
captcha_html += capture(&block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Rails 2 compatability
|
18
|
+
if Rails::VERSION::MAJOR < 3
|
19
|
+
concat captcha_html, &block.binding
|
20
|
+
else
|
21
|
+
captcha_html.html_safe
|
22
|
+
end
|
7
23
|
end
|
8
24
|
end
|
9
|
-
end
|
25
|
+
end
|
@@ -1,3 +1,3 @@
|
|
1
1
|
module ActsAsTextcaptcha
|
2
|
-
VERSION = "
|
3
|
-
end
|
2
|
+
VERSION = "3.0.0"
|
3
|
+
end
|
data/lib/tasks/textcaptcha.rake
CHANGED
@@ -1,23 +1,26 @@
|
|
1
|
+
require 'bcrypt'
|
2
|
+
|
1
3
|
namespace :textcaptcha do
|
2
|
-
desc "Adds a textcaptcha.yml template config file to ./config "
|
3
|
-
task :generate_config do
|
4
4
|
|
5
|
+
desc "Creates a template config file in config/textcaptcha.yml"
|
6
|
+
task :config do
|
5
7
|
src = File.join(File.dirname(__FILE__), '../..', 'config', 'textcaptcha.yml')
|
6
8
|
dest = File.join(Rails.root, 'config', 'textcaptcha.yml')
|
7
9
|
if File.exist?(dest)
|
8
|
-
puts "\
|
10
|
+
puts "\nOoops, a textcaptcha config file at #{dest} already exists ... aborting.\n\n"
|
9
11
|
else
|
10
|
-
|
12
|
+
config = ''
|
13
|
+
salt = BCrypt::Engine.generate_salt
|
11
14
|
f = File.open(src, 'r')
|
12
|
-
f.each_line { |line|
|
13
|
-
|
14
|
-
|
15
|
+
f.each_line { |line| config += line }
|
16
|
+
config.gsub!(/RAKE_GENERATED_SALT_PLACEHOLDER/, salt)
|
17
|
+
config.gsub!(/ api_key:(.*)# for gem test purposes only$/, " api_key: PASTE_YOUR_TEXTCAPCHA_API_KEY_HERE")
|
18
|
+
config.gsub!(/ bcrypt_salt:(.*)# for gem test purposes only$/, " bcrypt_salt: #{salt}")
|
15
19
|
|
16
20
|
f = File.new(dest, 'w')
|
17
|
-
f.write(
|
21
|
+
f.write(config)
|
18
22
|
f.close
|
19
|
-
puts "\ntextcaptcha.yml generated at #{dest} (with a new BCrypt salt)
|
23
|
+
puts "\ntextcaptcha.yml generated at #{dest} (with a new BCrypt salt)\nNOTE: edit this file and add your textcaptcha api key, grab one from http://textcaptcha.com/api\n\n"
|
20
24
|
end
|
21
|
-
|
22
25
|
end
|
23
|
-
end
|
26
|
+
end
|
data/{spec → test}/schema.rb
RENAMED
data/test/test_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)+'./../lib'))
|
2
|
+
|
3
|
+
ENV['RAILS_ENV'] = 'test'
|
4
|
+
|
5
|
+
if ENV['COVERAGE']
|
6
|
+
require "simplecov"
|
7
|
+
SimpleCov.start do
|
8
|
+
add_filter '/test/'
|
9
|
+
end
|
10
|
+
SimpleCov.at_exit do
|
11
|
+
SimpleCov.result.format!
|
12
|
+
`open ./coverage/index.html` if RUBY_PLATFORM =~ /darwin/
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'minitest/autorun'
|
17
|
+
require 'fakeweb'
|
18
|
+
|
19
|
+
require 'active_record'
|
20
|
+
require 'rails'
|
21
|
+
|
22
|
+
require 'acts_as_textcaptcha'
|
23
|
+
require './test/test_models'
|
24
|
+
|
25
|
+
# load and initialize test db schema
|
26
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => 'acts_as_textcaptcha.sqlite3.db')
|
27
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
data/test/test_models.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# models for use in tests
|
2
|
+
|
3
|
+
class Widget < ActiveRecord::Base
|
4
|
+
# uses textcaptcha.yml file for configuration
|
5
|
+
acts_as_textcaptcha
|
6
|
+
end
|
7
|
+
|
8
|
+
class Comment < ActiveRecord::Base
|
9
|
+
# inline options (symbol keys) with api_key only
|
10
|
+
acts_as_textcaptcha :api_key => '8u5ixtdnq9csc84cok0owswgo',
|
11
|
+
:bcrypt_salt => '$2a$10$j0bmycH.SVfD1b5mpEGPpe'
|
12
|
+
end
|
13
|
+
|
14
|
+
class Review < ActiveRecord::Base
|
15
|
+
# inline options with all possible options
|
16
|
+
acts_as_textcaptcha 'api_key' => '8u5ixtdnq9csc84cok0owswgo',
|
17
|
+
'bcrypt_salt' => '$2a$10$j0bmycH.SVfD1b5mpEGPpe',
|
18
|
+
'bcrypt_cost' => '3',
|
19
|
+
'questions' => [{ 'question' => 'The green hat is what color?', 'answers' => 'green' }]
|
20
|
+
end
|
21
|
+
|
22
|
+
class Note < ActiveRecord::Base
|
23
|
+
# inline options (string keys) with user defined questions only (no textcaptcha service)
|
24
|
+
acts_as_textcaptcha 'questions' => [{ 'question' => '1+1', 'answers' => '2,two' }],
|
25
|
+
'bcrypt_salt' => '$2a$10$j0bmycH.SVfD1b5mpEGPpe'
|
26
|
+
|
27
|
+
# allows toggling perform_textcaptcha on/off (default on)
|
28
|
+
attr_accessor :turn_off_captcha
|
29
|
+
|
30
|
+
def perform_textcaptcha?
|
31
|
+
!turn_off_captcha
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Contact
|
36
|
+
# non active record object (symbol keys), no API used
|
37
|
+
include ActiveModel::Validations
|
38
|
+
include ActiveModel::Conversion
|
39
|
+
extend ActsAsTextcaptcha::Textcaptcha
|
40
|
+
acts_as_textcaptcha :questions => [{ :question => 'one+1', :answers => '2,two' }],
|
41
|
+
:bcrypt_salt => '$2a$10$j0bmycH.SVfD1b5mpEGPpe'
|
42
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)+'/test_helper')
|
2
|
+
require 'action_controller'
|
3
|
+
require 'action_view'
|
4
|
+
|
5
|
+
class NotesController < ActionController::Base; end
|
6
|
+
|
7
|
+
class Template < ActionView::Base
|
8
|
+
def protect_against_forgery?; false; end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
describe 'TextcaptchaHelper' do
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
@controller = NotesController.new
|
16
|
+
@note = Note.new
|
17
|
+
@note.textcaptcha
|
18
|
+
end
|
19
|
+
|
20
|
+
def render_template(assigns)
|
21
|
+
template = <<-ERB
|
22
|
+
<%= form_for(@note, :url => '/') do |f| %>
|
23
|
+
<%= textcaptcha_fields(f) do %>
|
24
|
+
<div class="field textcaptcha">
|
25
|
+
<%= f.label :spam_answer, @note.spam_question %><br/>
|
26
|
+
<%= f.text_field :spam_answer, :value => '' %>
|
27
|
+
</div>
|
28
|
+
<% end %>
|
29
|
+
<% end %>
|
30
|
+
ERB
|
31
|
+
|
32
|
+
Template.new([], assigns, @controller).render(:inline => template)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should render question and answer fields, with hidden spam_answers field' do
|
36
|
+
html = render_template({:note => @note})
|
37
|
+
|
38
|
+
html.must_match /\<label for\=\"note\_spam\_answer\"\>1\+1\<\/label\>/
|
39
|
+
html.must_match /\<input id\=\"note_spam_answers\" name\=\"note\[spam\_answers\]\" type\=\"hidden\" value\=\"(.*)\" \/\>/
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should render hidden answer and spam_answer fields when question has been answered OK (and not ask question)' do
|
43
|
+
@note.spam_answer = 2
|
44
|
+
html = render_template({:note => @note})
|
45
|
+
|
46
|
+
html.wont_match /\<label for\=\"note\_spam\_answer\"\>1\+1\<\/label\>/
|
47
|
+
html.must_match /\<input id\=\"note_spam_answers\" name\=\"note\[spam\_answers\]\" type\=\"hidden\" value\=\"(.*)\" \/\>/
|
48
|
+
html.must_match /\<input id\=\"note_spam_answer\" name\=\"note\[spam_answer\]\" type\=\"hidden\" value\=\"2\" \/\>/
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)+'/test_helper')
|
2
|
+
|
3
|
+
describe 'Textcaptcha' do
|
4
|
+
|
5
|
+
describe 'validations' do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@note = Note.new
|
9
|
+
@note.textcaptcha
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should validate an ActiveRecord object (with multiple correct answers)' do
|
13
|
+
@note.spam_question.must_equal('1+1')
|
14
|
+
@note.valid?.must_equal false
|
15
|
+
@note.errors[:spam_answer].first.must_equal('is incorrect, try another question instead')
|
16
|
+
|
17
|
+
@note.spam_answer = 'two'
|
18
|
+
@note.valid?.must_equal true
|
19
|
+
@note.errors[:spam_answer].must_be_empty
|
20
|
+
|
21
|
+
@note.spam_answer = '2'
|
22
|
+
@note.valid?.must_equal true
|
23
|
+
@note.errors[:spam_answer].must_be_empty
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should strip whitespace and downcase spam answer' do
|
27
|
+
@note.spam_answer = ' tWo '
|
28
|
+
@note.valid?.must_equal true
|
29
|
+
@note.errors[:spam_answer].must_be_empty
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should always be valid when record has been saved' do
|
33
|
+
@note.spam_answer = '2'
|
34
|
+
@note.save!
|
35
|
+
@note.textcaptcha
|
36
|
+
|
37
|
+
@note.spam_answer = 'wrong answer'
|
38
|
+
@note.new_record?.must_equal false
|
39
|
+
@note.valid?.must_equal true
|
40
|
+
@note.errors[:spam_answer].must_be_empty
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should always be valid when perform_textcaptcha? is false' do
|
44
|
+
@note.turn_off_captcha = true
|
45
|
+
@note.valid?.must_equal true
|
46
|
+
@note.errors[:spam_answer].must_be_empty
|
47
|
+
|
48
|
+
@note.save.must_equal true
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should validate a non ActiveRecord object' do
|
52
|
+
@contact = Contact.new
|
53
|
+
@contact.textcaptcha
|
54
|
+
|
55
|
+
@contact.spam_question.must_equal('one+1')
|
56
|
+
@contact.spam_answer = 'wrong'
|
57
|
+
@contact.valid?.must_equal false
|
58
|
+
|
59
|
+
@contact.spam_answer = 'two'
|
60
|
+
@contact.valid?.must_equal true
|
61
|
+
@contact.errors[:spam_answer].must_be_empty
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe 'encryption' do
|
66
|
+
|
67
|
+
before(:each) do
|
68
|
+
@note = Note.new
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should encrypt spam_answers (joined by - seperator) MD5 digested and using BCrypt engine with salt' do
|
72
|
+
@note.spam_answers.must_be_nil
|
73
|
+
@note.textcaptcha
|
74
|
+
encrypted_answers = [2,' TwO '].collect { |answer| BCrypt::Engine.hash_secret(Digest::MD5.hexdigest(answer.to_s.strip.downcase), '$2a$10$j0bmycH.SVfD1b5mpEGPpe', 1) }.join('-')
|
75
|
+
@note.spam_answers.must_equal('$2a$10$j0bmycH.SVfD1b5mpEGPpePFe1wBxOn7Brr9lMuLRxv6lg4ZYjJ22-$2a$10$j0bmycH.SVfD1b5mpEGPpe8v5mqqpDaExuS/hZu8Xkq8krYL/T8P.')
|
76
|
+
@note.spam_answers.must_equal(encrypted_answers)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should raise error if bcyrpt salt is invalid' do
|
80
|
+
@note.textcaptcha_config[:bcrypt_salt] = 'bad salt'
|
81
|
+
proc { @note.textcaptcha }.must_raise BCrypt::Errors::InvalidSalt
|
82
|
+
@note.textcaptcha_config[:bcrypt_salt] ='$2a$10$j0bmycH.SVfD1b5mpEGPpe'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'textcaptcha API' do
|
87
|
+
|
88
|
+
after(:each) do
|
89
|
+
FakeWeb.clean_registry
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should generate spam question from the service' do
|
93
|
+
@review = Review.new
|
94
|
+
|
95
|
+
@review.textcaptcha
|
96
|
+
@review.spam_question.wont_be_nil
|
97
|
+
@review.spam_question.wont_equal('The green hat is what color?')
|
98
|
+
|
99
|
+
@review.spam_answers.wont_be_nil
|
100
|
+
|
101
|
+
@review.valid?.must_equal false
|
102
|
+
@review.errors[:spam_answer].first.must_equal('is incorrect, try another question instead')
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should parse a single answer from XML response' do
|
106
|
+
@review = Review.new
|
107
|
+
question = 'If tomorrow is Saturday, what day is today?'
|
108
|
+
body = "<captcha><question>#{question}</question><answer>f6f7fec07f372b7bd5eb196bbca0f3f4</answer></captcha>"
|
109
|
+
FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :body => body)
|
110
|
+
|
111
|
+
@review.textcaptcha
|
112
|
+
@review.spam_question.must_equal(question)
|
113
|
+
@review.spam_answers.must_equal('$2a$10$j0bmycH.SVfD1b5mpEGPpecvhlumIBvWXI4HQWk0xa74DebZDx772')
|
114
|
+
@review.spam_answers.split('-').length.must_equal(1)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should parse multiple answers from XML response' do
|
118
|
+
@review = Review.new
|
119
|
+
question = 'If tomorrow is Saturday, what day is today?'
|
120
|
+
body = "<captcha><question>#{question}</question><answer>1</answer><answer>2</answer><answer>3</answer></captcha>"
|
121
|
+
FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :body => body)
|
122
|
+
|
123
|
+
@review.textcaptcha
|
124
|
+
@review.spam_question.must_equal(question)
|
125
|
+
@review.spam_answers.split('-').length.must_equal(3)
|
126
|
+
end
|
127
|
+
|
128
|
+
describe 'service is unavailable' do
|
129
|
+
|
130
|
+
describe 'should fallback to a user defined question' do
|
131
|
+
|
132
|
+
before(:each) do
|
133
|
+
@review = Review.new
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'when errors occur' do
|
137
|
+
[SocketError, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Errno::ECONNREFUSED, Errno::ETIMEDOUT,
|
138
|
+
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, URI::InvalidURIError].each do |error|
|
139
|
+
FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :exception => error)
|
140
|
+
@review.textcaptcha
|
141
|
+
@review.spam_question.must_equal('The green hat is what color?')
|
142
|
+
@review.spam_answers.wont_be_nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'when response is OK but body cannot be parsed as XML' do
|
147
|
+
FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :body => 'here be gibberish')
|
148
|
+
@review.textcaptcha
|
149
|
+
@review.spam_question.must_equal('The green hat is what color?')
|
150
|
+
@review.spam_answers.wont_be_nil
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'when response is OK but empty' do
|
154
|
+
FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :body => '')
|
155
|
+
@review.textcaptcha
|
156
|
+
@review.spam_question.must_equal('The green hat is what color?')
|
157
|
+
@review.spam_answers.wont_be_nil
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should not generate any spam question or answer when no user defined questions set' do
|
163
|
+
@comment = Comment.new
|
164
|
+
|
165
|
+
FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :exception => SocketError)
|
166
|
+
@comment.textcaptcha
|
167
|
+
@comment.spam_question.must_equal 'ActsAsTextcaptcha >> no API key (or questions) set and/or the textcaptcha service is currently unavailable (answer ok to bypass)'
|
168
|
+
@comment.spam_answers.must_equal 'ok'
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe 'configuration' do
|
173
|
+
|
174
|
+
it 'should be configured with inline hash' do
|
175
|
+
Review.textcaptcha_config.must_equal({ :api_key => '8u5ixtdnq9csc84cok0owswgo',
|
176
|
+
:bcrypt_salt => '$2a$10$j0bmycH.SVfD1b5mpEGPpe',
|
177
|
+
:bcrypt_cost => '3',
|
178
|
+
:questions => [{ 'question' => 'The green hat is what color?', 'answers' => 'green' }]})
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should be configured with textcaptcha.yml' do
|
182
|
+
Widget.textcaptcha_config[:api_key].must_equal '6eh1co0j12mi2ogcoggkkok4o'
|
183
|
+
Widget.textcaptcha_config[:bcrypt_salt].must_equal '$2a$10$qhSefD6gKtmq6M0AzXk4CO'
|
184
|
+
Widget.textcaptcha_config[:bcrypt_cost].must_equal 1
|
185
|
+
Widget.textcaptcha_config[:questions].length.must_equal 10
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_textcaptcha
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,23 +9,23 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-08-06 00:00:00.000000000 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bcrypt-ruby
|
17
|
-
requirement: &
|
17
|
+
requirement: &2156333440 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 2.1.
|
22
|
+
version: 2.1.4
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *2156333440
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: rails
|
28
|
-
requirement: &
|
28
|
+
requirement: &2156333020 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,21 +33,10 @@ dependencies:
|
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
name: activerecord
|
39
|
-
requirement: &2152243040 !ruby/object:Gem::Requirement
|
40
|
-
none: false
|
41
|
-
requirements:
|
42
|
-
- - ! '>='
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
version: '0'
|
45
|
-
type: :development
|
46
|
-
prerelease: false
|
47
|
-
version_requirements: *2152243040
|
36
|
+
version_requirements: *2156333020
|
48
37
|
- !ruby/object:Gem::Dependency
|
49
38
|
name: bundler
|
50
|
-
requirement: &
|
39
|
+
requirement: &2156332560 !ruby/object:Gem::Requirement
|
51
40
|
none: false
|
52
41
|
requirements:
|
53
42
|
- - ! '>='
|
@@ -55,10 +44,10 @@ dependencies:
|
|
55
44
|
version: '0'
|
56
45
|
type: :development
|
57
46
|
prerelease: false
|
58
|
-
version_requirements: *
|
47
|
+
version_requirements: *2156332560
|
59
48
|
- !ruby/object:Gem::Dependency
|
60
|
-
name:
|
61
|
-
requirement: &
|
49
|
+
name: simplecov
|
50
|
+
requirement: &2156332140 !ruby/object:Gem::Requirement
|
62
51
|
none: false
|
63
52
|
requirements:
|
64
53
|
- - ! '>='
|
@@ -66,10 +55,10 @@ dependencies:
|
|
66
55
|
version: '0'
|
67
56
|
type: :development
|
68
57
|
prerelease: false
|
69
|
-
version_requirements: *
|
58
|
+
version_requirements: *2156332140
|
70
59
|
- !ruby/object:Gem::Dependency
|
71
|
-
name:
|
72
|
-
requirement: &
|
60
|
+
name: rdoc
|
61
|
+
requirement: &2156331720 !ruby/object:Gem::Requirement
|
73
62
|
none: false
|
74
63
|
requirements:
|
75
64
|
- - ! '>='
|
@@ -77,10 +66,10 @@ dependencies:
|
|
77
66
|
version: '0'
|
78
67
|
type: :development
|
79
68
|
prerelease: false
|
80
|
-
version_requirements: *
|
69
|
+
version_requirements: *2156331720
|
81
70
|
- !ruby/object:Gem::Dependency
|
82
|
-
name:
|
83
|
-
requirement: &
|
71
|
+
name: sqlite3
|
72
|
+
requirement: &2156331300 !ruby/object:Gem::Requirement
|
84
73
|
none: false
|
85
74
|
requirements:
|
86
75
|
- - ! '>='
|
@@ -88,10 +77,10 @@ dependencies:
|
|
88
77
|
version: '0'
|
89
78
|
type: :development
|
90
79
|
prerelease: false
|
91
|
-
version_requirements: *
|
80
|
+
version_requirements: *2156331300
|
92
81
|
- !ruby/object:Gem::Dependency
|
93
|
-
name:
|
94
|
-
requirement: &
|
82
|
+
name: fakeweb
|
83
|
+
requirement: &2156330880 !ruby/object:Gem::Requirement
|
95
84
|
none: false
|
96
85
|
requirements:
|
97
86
|
- - ! '>='
|
@@ -99,13 +88,11 @@ dependencies:
|
|
99
88
|
version: '0'
|
100
89
|
type: :development
|
101
90
|
prerelease: false
|
102
|
-
version_requirements: *
|
103
|
-
description: ! "
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
logic, such questions cannot be solved by a robot.\n For more reasons on why logic
|
108
|
-
questions are useful, see here; http://textcaptcha.com/why"
|
91
|
+
version_requirements: *2156330880
|
92
|
+
description: ! "Simple question/answer based spam protection for your Rails models.\n
|
93
|
+
\ You can define your own logic questions and/or fetch questions from the textcaptcha.com
|
94
|
+
API.\n The questions involve human logic and are tough for spam bots to crack.\n
|
95
|
+
\ For more reasons on why logic questions are a good idea visit; http://textcaptcha.com/why"
|
109
96
|
email:
|
110
97
|
- matt@hiddenloop.com
|
111
98
|
executables: []
|
@@ -123,17 +110,17 @@ files:
|
|
123
110
|
- config/textcaptcha.yml
|
124
111
|
- init.rb
|
125
112
|
- lib/acts_as_textcaptcha.rb
|
113
|
+
- lib/acts_as_textcaptcha/framework/rails.rb
|
126
114
|
- lib/acts_as_textcaptcha/framework/rails2.rb
|
127
|
-
- lib/acts_as_textcaptcha/framework/rails3.rb
|
128
115
|
- lib/acts_as_textcaptcha/textcaptcha.rb
|
129
116
|
- lib/acts_as_textcaptcha/textcaptcha_helper.rb
|
130
117
|
- lib/acts_as_textcaptcha/version.rb
|
131
118
|
- lib/tasks/textcaptcha.rake
|
132
|
-
-
|
133
|
-
-
|
134
|
-
-
|
135
|
-
-
|
136
|
-
-
|
119
|
+
- test/schema.rb
|
120
|
+
- test/test_helper.rb
|
121
|
+
- test/test_models.rb
|
122
|
+
- test/textcaptcha_helper_test.rb
|
123
|
+
- test/textcaptcha_test.rb
|
137
124
|
has_rdoc: true
|
138
125
|
homepage: http://github.com/matthutchinson/acts_as_textcaptcha
|
139
126
|
licenses: []
|
@@ -158,11 +145,6 @@ rubyforge_project:
|
|
158
145
|
rubygems_version: 1.6.2
|
159
146
|
signing_key:
|
160
147
|
specification_version: 3
|
161
|
-
summary: Spam protection for your models via logic questions and the
|
162
|
-
|
163
|
-
test_files:
|
164
|
-
- spec/acts_as_textcaptcha_spec.rb
|
165
|
-
- spec/database.yml
|
166
|
-
- spec/schema.rb
|
167
|
-
- spec/spec.opts
|
168
|
-
- spec/spec_helper.rb
|
148
|
+
summary: Spam protection for your models via logic questions and the textcaptcha.com
|
149
|
+
API
|
150
|
+
test_files: []
|