ankh 0.1.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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +39 -0
- data/Rakefile +87 -0
- data/VERSION +1 -0
- data/ankh.gemspec +84 -0
- data/features/ankh.feature +9 -0
- data/features/step_definitions/ankh_steps.rb +0 -0
- data/features/support/env.rb +4 -0
- data/lib/ankh.rb +25 -0
- data/lib/ankh/model.rb +17 -0
- data/lib/ankh/question.rb +23 -0
- data/lib/ankh/rails/legacy.rb +41 -0
- data/lib/ankh/validations/human.rb +33 -0
- data/spec/ankh/model_spec.rb +49 -0
- data/spec/ankh/question_spec.rb +40 -0
- data/spec/ankh_spec.rb +5 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/active_record_spec_helper.rb +44 -0
- metadata +185 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2009 Dan Pickett
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
= ankh
|
|
2
|
+
|
|
3
|
+
Ankh is the ancient Egyptian sign of life.
|
|
4
|
+
|
|
5
|
+
Use Ankh in your Rails (2.3.x or 3.0) projects to protect against bots and other spam producers. It asks a simple
|
|
6
|
+
arithmatic question to verify that the poster is human.
|
|
7
|
+
|
|
8
|
+
In your model:
|
|
9
|
+
|
|
10
|
+
validates_human :on => :create
|
|
11
|
+
|
|
12
|
+
In your view (Rails 2 syntax shown below):
|
|
13
|
+
|
|
14
|
+
<%- obj.generate_human_question %>
|
|
15
|
+
<%- form_for obj do |f| -%>
|
|
16
|
+
<%= f.label :human_answer, obj.human_question %>
|
|
17
|
+
<%= f.text_field :human_answer %>
|
|
18
|
+
<%= f.hidden_field :salted_human_answer %>
|
|
19
|
+
<%- end -%>
|
|
20
|
+
|
|
21
|
+
== TODO
|
|
22
|
+
|
|
23
|
+
* Nice form helpers
|
|
24
|
+
* I18n support
|
|
25
|
+
* automated testing of Rails 2.3.x vs. 3.0
|
|
26
|
+
|
|
27
|
+
== Note on Patches/Pull Requests
|
|
28
|
+
|
|
29
|
+
* Fork the project.
|
|
30
|
+
* Make your feature addition or bug fix.
|
|
31
|
+
* Add tests for it. This is important so I don't break it in a
|
|
32
|
+
future version unintentionally.
|
|
33
|
+
* Commit, do not mess with rakefile, version, or history.
|
|
34
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
|
35
|
+
* Send me a pull request. Bonus points for topic branches.
|
|
36
|
+
|
|
37
|
+
== Copyright
|
|
38
|
+
|
|
39
|
+
Copyright (c) 2010 Dan Pickett. See LICENSE for details.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'jeweler'
|
|
6
|
+
Jeweler::Tasks.new do |gem|
|
|
7
|
+
gem.name = "ankh"
|
|
8
|
+
gem.summary = %Q{Protect against bots and other spam producers with simple mathematical questions}
|
|
9
|
+
gem.description = %Q{Use Ankh in your Rails (2.3.x or 3.0) projects to protect against bots and other spam producers. It asks a simple
|
|
10
|
+
arithmatic question to verify that the poster is human.}
|
|
11
|
+
gem.email = "dpickett@enlightsolutions.com"
|
|
12
|
+
gem.homepage = "http://github.com/dpickett/ankh"
|
|
13
|
+
gem.authors = ["Dan Pickett"]
|
|
14
|
+
gem.add_dependency "activerecord", ">= 2.3.8"
|
|
15
|
+
gem.add_dependency "activesupport", ">= 2.3.8"
|
|
16
|
+
gem.add_dependency "configatron", "2.6.3"
|
|
17
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
|
18
|
+
# gem.add_development_dependency "yard", ">= 0"
|
|
19
|
+
gem.add_development_dependency "cucumber", ">= 0"
|
|
20
|
+
gem.add_development_dependency "mocha"
|
|
21
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
|
22
|
+
end
|
|
23
|
+
Jeweler::GemcutterTasks.new
|
|
24
|
+
rescue LoadError
|
|
25
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
require 'spec/rake/spectask'
|
|
29
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
|
30
|
+
spec.libs << 'lib' << 'spec'
|
|
31
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
|
35
|
+
spec.libs << 'lib' << 'spec'
|
|
36
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
|
37
|
+
spec.rcov = true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
task :spec => :check_dependencies
|
|
41
|
+
|
|
42
|
+
begin
|
|
43
|
+
require 'cucumber/rake/task'
|
|
44
|
+
Cucumber::Rake::Task.new(:features)
|
|
45
|
+
|
|
46
|
+
task :features => :check_dependencies
|
|
47
|
+
rescue LoadError
|
|
48
|
+
task :features do
|
|
49
|
+
abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
begin
|
|
54
|
+
require 'reek/adapters/rake_task'
|
|
55
|
+
Reek::RakeTask.new do |t|
|
|
56
|
+
t.fail_on_error = true
|
|
57
|
+
t.verbose = false
|
|
58
|
+
t.source_files = 'lib/**/*.rb'
|
|
59
|
+
end
|
|
60
|
+
rescue LoadError
|
|
61
|
+
task :reek do
|
|
62
|
+
abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
begin
|
|
67
|
+
require 'roodi'
|
|
68
|
+
require 'roodi_task'
|
|
69
|
+
RoodiTask.new do |t|
|
|
70
|
+
t.verbose = false
|
|
71
|
+
end
|
|
72
|
+
rescue LoadError
|
|
73
|
+
task :roodi do
|
|
74
|
+
abort "Roodi is not available. In order to run roodi, you must: sudo gem install roodi"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
task :default => :spec
|
|
79
|
+
|
|
80
|
+
begin
|
|
81
|
+
require 'yard'
|
|
82
|
+
YARD::Rake::YardocTask.new
|
|
83
|
+
rescue LoadError
|
|
84
|
+
task :yardoc do
|
|
85
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
|
86
|
+
end
|
|
87
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.0
|
data/ankh.gemspec
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
|
4
|
+
# -*- encoding: utf-8 -*-
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |s|
|
|
7
|
+
s.name = %q{ankh}
|
|
8
|
+
s.version = "0.1.0"
|
|
9
|
+
|
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
|
+
s.authors = ["Dan Pickett"]
|
|
12
|
+
s.date = %q{2010-10-13}
|
|
13
|
+
s.description = %q{Use Ankh in your Rails (2.3.x or 3.0) projects to protect against bots and other spam producers. It asks a simple
|
|
14
|
+
arithmatic question to verify that the poster is human.}
|
|
15
|
+
s.email = %q{dpickett@enlightsolutions.com}
|
|
16
|
+
s.extra_rdoc_files = [
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"README.rdoc"
|
|
19
|
+
]
|
|
20
|
+
s.files = [
|
|
21
|
+
".document",
|
|
22
|
+
".gitignore",
|
|
23
|
+
"LICENSE",
|
|
24
|
+
"README.rdoc",
|
|
25
|
+
"Rakefile",
|
|
26
|
+
"VERSION",
|
|
27
|
+
"ankh.gemspec",
|
|
28
|
+
"features/ankh.feature",
|
|
29
|
+
"features/step_definitions/ankh_steps.rb",
|
|
30
|
+
"features/support/env.rb",
|
|
31
|
+
"lib/ankh.rb",
|
|
32
|
+
"lib/ankh/model.rb",
|
|
33
|
+
"lib/ankh/question.rb",
|
|
34
|
+
"lib/ankh/rails/legacy.rb",
|
|
35
|
+
"lib/ankh/validations/human.rb",
|
|
36
|
+
"spec/ankh/model_spec.rb",
|
|
37
|
+
"spec/ankh/question_spec.rb",
|
|
38
|
+
"spec/ankh_spec.rb",
|
|
39
|
+
"spec/spec.opts",
|
|
40
|
+
"spec/spec_helper.rb",
|
|
41
|
+
"spec/support/active_record_spec_helper.rb"
|
|
42
|
+
]
|
|
43
|
+
s.homepage = %q{http://github.com/dpickett/ankh}
|
|
44
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
|
45
|
+
s.require_paths = ["lib"]
|
|
46
|
+
s.rubygems_version = %q{1.3.7}
|
|
47
|
+
s.summary = %q{Protect against bots and other spam producers with simple mathematical questions}
|
|
48
|
+
s.test_files = [
|
|
49
|
+
"spec/ankh/model_spec.rb",
|
|
50
|
+
"spec/ankh/question_spec.rb",
|
|
51
|
+
"spec/ankh_spec.rb",
|
|
52
|
+
"spec/spec_helper.rb",
|
|
53
|
+
"spec/support/active_record_spec_helper.rb"
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
if s.respond_to? :specification_version then
|
|
57
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
|
58
|
+
s.specification_version = 3
|
|
59
|
+
|
|
60
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
61
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 2.3.8"])
|
|
62
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 2.3.8"])
|
|
63
|
+
s.add_runtime_dependency(%q<configatron>, ["= 2.6.3"])
|
|
64
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
|
65
|
+
s.add_development_dependency(%q<cucumber>, [">= 0"])
|
|
66
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
|
67
|
+
else
|
|
68
|
+
s.add_dependency(%q<activerecord>, [">= 2.3.8"])
|
|
69
|
+
s.add_dependency(%q<activesupport>, [">= 2.3.8"])
|
|
70
|
+
s.add_dependency(%q<configatron>, ["= 2.6.3"])
|
|
71
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
|
72
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
|
73
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
|
74
|
+
end
|
|
75
|
+
else
|
|
76
|
+
s.add_dependency(%q<activerecord>, [">= 2.3.8"])
|
|
77
|
+
s.add_dependency(%q<activesupport>, [">= 2.3.8"])
|
|
78
|
+
s.add_dependency(%q<configatron>, ["= 2.6.3"])
|
|
79
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
|
80
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
|
81
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
File without changes
|
data/lib/ankh.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require "rubygems"
|
|
2
|
+
require "active_record"
|
|
3
|
+
require "digest/sha1"
|
|
4
|
+
require "configatron"
|
|
5
|
+
|
|
6
|
+
require "ankh/rails/legacy"
|
|
7
|
+
|
|
8
|
+
require "ankh/question"
|
|
9
|
+
require "ankh/validations/human"
|
|
10
|
+
|
|
11
|
+
require "ankh/model"
|
|
12
|
+
|
|
13
|
+
module Ankh
|
|
14
|
+
def self.salt=(salt)
|
|
15
|
+
configatron.ankh.salt = salt
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.salt
|
|
19
|
+
configatron.ankh.salt ||= 'fqphweuyir8y34295yu234y5219pthfsdlhgfsaf'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.encrypt(item_to_encrypt)
|
|
23
|
+
Digest::SHA1.hexdigest("--#{salt}--#{item_to_encrypt}")
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/ankh/model.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Ankh
|
|
2
|
+
module Model
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.send(:attr_accessor, :human_answer)
|
|
5
|
+
base.send(:attr_reader, :human_question)
|
|
6
|
+
base.send(:attr_accessor, :salted_human_answer)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def generate_human_question
|
|
10
|
+
question = Ankh::Question.generate
|
|
11
|
+
@human_question = question.question
|
|
12
|
+
self.salted_human_answer = Ankh.encrypt(question.answer)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
ActiveRecord::Base.extend(Ankh::Validations::HelperMethods)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Ankh
|
|
2
|
+
class Question
|
|
3
|
+
OPERATIONS = [
|
|
4
|
+
"+",
|
|
5
|
+
"-"
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
attr_reader :first_number, :second_number, :operation, :question, :answer
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@second_number = rand(9)
|
|
12
|
+
@first_number = @second_number + rand(9)
|
|
13
|
+
@operation = OPERATIONS[rand(2)]
|
|
14
|
+
|
|
15
|
+
@question = "What is #{first_number} #{operation} #{second_number}?"
|
|
16
|
+
@answer = @first_number.send(@operation, @second_number)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.generate
|
|
20
|
+
new
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Ankh
|
|
2
|
+
module Legacy
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
def extended(base)
|
|
5
|
+
base.class_attribute :_validators
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def validates_with(*args, &block)
|
|
9
|
+
_validators ||= {}
|
|
10
|
+
|
|
11
|
+
options = args.extract_options!
|
|
12
|
+
args.each do |klass|
|
|
13
|
+
validator = klass.new(options, &block)
|
|
14
|
+
validator.setup(self) if validator.respond_to?(:setup)
|
|
15
|
+
|
|
16
|
+
if validator.respond_to?(:attributes) && !validator.attributes.empty?
|
|
17
|
+
validator.attributes.each do |attribute|
|
|
18
|
+
_validators[nil] ||= []
|
|
19
|
+
_validators[attribute.to_sym] << validator
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
_validators[nil] ||= []
|
|
23
|
+
_validators[nil] << validator
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
validate(validator, options)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
def _merge_attributes(attr_names)
|
|
32
|
+
options = attr_names.extract_options!
|
|
33
|
+
options.merge(:attributes => attr_names.flatten)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
unless ActiveRecord::Base.respond_to?(:validates_with)
|
|
40
|
+
ActiveRecord::Base.extend Ankh::Legacy::ActiveRecord
|
|
41
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Ankh
|
|
2
|
+
module Validations
|
|
3
|
+
class HumanValidator
|
|
4
|
+
def initialize(options = {})
|
|
5
|
+
@options ||= options
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def validate(record)
|
|
9
|
+
if has_valid_answer?(record)
|
|
10
|
+
record.errors.add(:human_answer, @options[:message] || "is not valid")
|
|
11
|
+
record.human_answer = ""
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
def has_valid_answer?(record)
|
|
17
|
+
record.salted_human_answer.present? && Ankh.encrypt(record.human_answer) != record.salted_human_answer
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module HelperMethods
|
|
22
|
+
def validates_human(options = {})
|
|
23
|
+
self.send(:include, Ankh::Model) unless probably_included_already?
|
|
24
|
+
validates_with HumanValidator, options
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
def probably_included_already?
|
|
29
|
+
self.respond_to?(:human_question) && self.respond_to?(:human_answer)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Ankh::Model do
|
|
4
|
+
INVALID_ANSWER = "3241235" #this answer will never be valid due to constraints
|
|
5
|
+
class Post < ActiveRecord::Base
|
|
6
|
+
validates_human
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
subject do
|
|
10
|
+
p = Post.new
|
|
11
|
+
p.generate_human_question
|
|
12
|
+
p
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe "generation" do
|
|
16
|
+
its(:human_question){ should_not be_nil }
|
|
17
|
+
its(:human_answer){ should be_blank }
|
|
18
|
+
|
|
19
|
+
it "should have a salted human answer" do
|
|
20
|
+
subject.salted_human_answer.should_not be_blank
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should not save if I do not specify a human answer" do
|
|
25
|
+
subject.human_answer = ""
|
|
26
|
+
subject.save.should be_false
|
|
27
|
+
subject.errors[:human_answer].should_not be_nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should clear out the human answer if validation fails" do
|
|
31
|
+
subject.human_answer = INVALID_ANSWER
|
|
32
|
+
subject.save.should be_false
|
|
33
|
+
subject.human_answer.should be_blank
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "should not save if I do not match the intended answer" do
|
|
37
|
+
subject.human_answer = INVALID_ANSWER
|
|
38
|
+
subject.save.should be_false
|
|
39
|
+
subject.errors[:human_answer].should_not be_nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should save if I match the intended answer" do
|
|
43
|
+
question = Ankh::Question.new
|
|
44
|
+
Ankh::Question.expects(:generate).returns(question)
|
|
45
|
+
|
|
46
|
+
subject.human_answer = question.answer
|
|
47
|
+
subject.save.should be_true
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Ankh::Question do
|
|
4
|
+
subject { Ankh::Question.generate }
|
|
5
|
+
|
|
6
|
+
its(:question){ should_not be_nil }
|
|
7
|
+
its(:answer){ should_not be_nil }
|
|
8
|
+
|
|
9
|
+
it "should have a valid operation" do
|
|
10
|
+
Ankh::Question::OPERATIONS.should include(subject.operation)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should have a first number less than 20" do
|
|
14
|
+
subject.first_number.should < 20
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should have a second number less than 10" do
|
|
18
|
+
subject.second_number.should < 10
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe "the question" do
|
|
22
|
+
it "should contain the first number" do
|
|
23
|
+
subject.question.should =~ /#{subject.first_number}/
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should contain the second number" do
|
|
27
|
+
subject.question.should =~ /#{subject.second_number}/
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should contain the operation" do
|
|
31
|
+
subject.question.should =~ /#{Regexp.escape(subject.operation)}/
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "the answer" do
|
|
36
|
+
it "should perform the operation on the two numbers" do
|
|
37
|
+
subject.answer.should eql(subject.first_number.send(subject.operation, subject.second_number))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/spec/ankh_spec.rb
ADDED
data/spec/spec.opts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
3
|
+
require 'ankh'
|
|
4
|
+
require 'spec'
|
|
5
|
+
require 'spec/autorun'
|
|
6
|
+
require 'mocha'
|
|
7
|
+
|
|
8
|
+
require 'support/active_record_spec_helper'
|
|
9
|
+
Spec::Runner.configure do |config|
|
|
10
|
+
include ActiveRecordSpecHelper
|
|
11
|
+
|
|
12
|
+
config.before(:all) do
|
|
13
|
+
create_tables
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
config.after(:all) do
|
|
17
|
+
drop_tables
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
config.mock_with :mocha
|
|
21
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# taken from the state_machine gem
|
|
2
|
+
# Load TestCase helpers
|
|
3
|
+
require 'active_support/test_case'
|
|
4
|
+
require 'active_record/fixtures'
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
require 'active_record/test_case'
|
|
8
|
+
rescue LoadError
|
|
9
|
+
class ActiveRecord::TestCase < ActiveSupport::TestCase
|
|
10
|
+
# self.fixture_path = FIXTURES_ROOT
|
|
11
|
+
self.use_instantiated_fixtures = false
|
|
12
|
+
self.use_transactional_fixtures = true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Establish database connection
|
|
17
|
+
ActiveRecord::Base.establish_connection({'adapter' => 'sqlite3', 'database' => ':memory:'})
|
|
18
|
+
# ActiveRecord::Base.logger = Logger.new("#{File.dirname(__FILE__)}/../../active_record.log")
|
|
19
|
+
|
|
20
|
+
module ActiveRecordSpecHelper
|
|
21
|
+
TABLES = [
|
|
22
|
+
"posts"
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
def create_tables
|
|
26
|
+
each_table do |table_name|
|
|
27
|
+
ActiveRecord::Base.connection.create_table table_name unless table_exists?(table_name)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def drop_tables
|
|
32
|
+
each_table do |table_name|
|
|
33
|
+
ActiveRecord::Base.connection.drop_table table_name if table_exists?(table_name)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def table_exists?(table_name)
|
|
38
|
+
ActiveRecord::Base.connection.table_exists?(table_name)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def each_table(&block)
|
|
42
|
+
TABLES.each { |table_name| block.call(table_name) }
|
|
43
|
+
end
|
|
44
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ankh
|
|
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
|
+
- Dan Pickett
|
|
14
|
+
autorequire:
|
|
15
|
+
bindir: bin
|
|
16
|
+
cert_chain: []
|
|
17
|
+
|
|
18
|
+
date: 2010-10-13 00:00:00 -04:00
|
|
19
|
+
default_executable:
|
|
20
|
+
dependencies:
|
|
21
|
+
- !ruby/object:Gem::Dependency
|
|
22
|
+
name: activerecord
|
|
23
|
+
prerelease: false
|
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
hash: 19
|
|
30
|
+
segments:
|
|
31
|
+
- 2
|
|
32
|
+
- 3
|
|
33
|
+
- 8
|
|
34
|
+
version: 2.3.8
|
|
35
|
+
type: :runtime
|
|
36
|
+
version_requirements: *id001
|
|
37
|
+
- !ruby/object:Gem::Dependency
|
|
38
|
+
name: activesupport
|
|
39
|
+
prerelease: false
|
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
hash: 19
|
|
46
|
+
segments:
|
|
47
|
+
- 2
|
|
48
|
+
- 3
|
|
49
|
+
- 8
|
|
50
|
+
version: 2.3.8
|
|
51
|
+
type: :runtime
|
|
52
|
+
version_requirements: *id002
|
|
53
|
+
- !ruby/object:Gem::Dependency
|
|
54
|
+
name: configatron
|
|
55
|
+
prerelease: false
|
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - "="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
hash: 17
|
|
62
|
+
segments:
|
|
63
|
+
- 2
|
|
64
|
+
- 6
|
|
65
|
+
- 3
|
|
66
|
+
version: 2.6.3
|
|
67
|
+
type: :runtime
|
|
68
|
+
version_requirements: *id003
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
prerelease: false
|
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
|
73
|
+
none: false
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
hash: 13
|
|
78
|
+
segments:
|
|
79
|
+
- 1
|
|
80
|
+
- 2
|
|
81
|
+
- 9
|
|
82
|
+
version: 1.2.9
|
|
83
|
+
type: :development
|
|
84
|
+
version_requirements: *id004
|
|
85
|
+
- !ruby/object:Gem::Dependency
|
|
86
|
+
name: cucumber
|
|
87
|
+
prerelease: false
|
|
88
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
|
89
|
+
none: false
|
|
90
|
+
requirements:
|
|
91
|
+
- - ">="
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
hash: 3
|
|
94
|
+
segments:
|
|
95
|
+
- 0
|
|
96
|
+
version: "0"
|
|
97
|
+
type: :development
|
|
98
|
+
version_requirements: *id005
|
|
99
|
+
- !ruby/object:Gem::Dependency
|
|
100
|
+
name: mocha
|
|
101
|
+
prerelease: false
|
|
102
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
|
103
|
+
none: false
|
|
104
|
+
requirements:
|
|
105
|
+
- - ">="
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
hash: 3
|
|
108
|
+
segments:
|
|
109
|
+
- 0
|
|
110
|
+
version: "0"
|
|
111
|
+
type: :development
|
|
112
|
+
version_requirements: *id006
|
|
113
|
+
description: |-
|
|
114
|
+
Use Ankh in your Rails (2.3.x or 3.0) projects to protect against bots and other spam producers. It asks a simple
|
|
115
|
+
arithmatic question to verify that the poster is human.
|
|
116
|
+
email: dpickett@enlightsolutions.com
|
|
117
|
+
executables: []
|
|
118
|
+
|
|
119
|
+
extensions: []
|
|
120
|
+
|
|
121
|
+
extra_rdoc_files:
|
|
122
|
+
- LICENSE
|
|
123
|
+
- README.rdoc
|
|
124
|
+
files:
|
|
125
|
+
- .document
|
|
126
|
+
- .gitignore
|
|
127
|
+
- LICENSE
|
|
128
|
+
- README.rdoc
|
|
129
|
+
- Rakefile
|
|
130
|
+
- VERSION
|
|
131
|
+
- ankh.gemspec
|
|
132
|
+
- features/ankh.feature
|
|
133
|
+
- features/step_definitions/ankh_steps.rb
|
|
134
|
+
- features/support/env.rb
|
|
135
|
+
- lib/ankh.rb
|
|
136
|
+
- lib/ankh/model.rb
|
|
137
|
+
- lib/ankh/question.rb
|
|
138
|
+
- lib/ankh/rails/legacy.rb
|
|
139
|
+
- lib/ankh/validations/human.rb
|
|
140
|
+
- spec/ankh/model_spec.rb
|
|
141
|
+
- spec/ankh/question_spec.rb
|
|
142
|
+
- spec/ankh_spec.rb
|
|
143
|
+
- spec/spec.opts
|
|
144
|
+
- spec/spec_helper.rb
|
|
145
|
+
- spec/support/active_record_spec_helper.rb
|
|
146
|
+
has_rdoc: true
|
|
147
|
+
homepage: http://github.com/dpickett/ankh
|
|
148
|
+
licenses: []
|
|
149
|
+
|
|
150
|
+
post_install_message:
|
|
151
|
+
rdoc_options:
|
|
152
|
+
- --charset=UTF-8
|
|
153
|
+
require_paths:
|
|
154
|
+
- lib
|
|
155
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
156
|
+
none: false
|
|
157
|
+
requirements:
|
|
158
|
+
- - ">="
|
|
159
|
+
- !ruby/object:Gem::Version
|
|
160
|
+
hash: 3
|
|
161
|
+
segments:
|
|
162
|
+
- 0
|
|
163
|
+
version: "0"
|
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
|
+
none: false
|
|
166
|
+
requirements:
|
|
167
|
+
- - ">="
|
|
168
|
+
- !ruby/object:Gem::Version
|
|
169
|
+
hash: 3
|
|
170
|
+
segments:
|
|
171
|
+
- 0
|
|
172
|
+
version: "0"
|
|
173
|
+
requirements: []
|
|
174
|
+
|
|
175
|
+
rubyforge_project:
|
|
176
|
+
rubygems_version: 1.3.7
|
|
177
|
+
signing_key:
|
|
178
|
+
specification_version: 3
|
|
179
|
+
summary: Protect against bots and other spam producers with simple mathematical questions
|
|
180
|
+
test_files:
|
|
181
|
+
- spec/ankh/model_spec.rb
|
|
182
|
+
- spec/ankh/question_spec.rb
|
|
183
|
+
- spec/ankh_spec.rb
|
|
184
|
+
- spec/spec_helper.rb
|
|
185
|
+
- spec/support/active_record_spec_helper.rb
|