acts_as_textcaptcha 2.2.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|