winton-captcha 1.1.0 → 1.2.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/MIT-LICENSE +20 -0
- data/README.markdown +34 -31
- data/captcha.gemspec +7 -9
- data/lib/captcha/action.rb +15 -11
- data/lib/captcha/cipher.rb +40 -0
- data/lib/captcha/config.rb +31 -23
- data/lib/captcha/generator.rb +9 -12
- data/lib/captcha/image.rb +10 -9
- data/lib/captcha/model.rb +56 -0
- data/lib/captcha.rb +1 -0
- data/spec/lib/captcha_spec.rb +5 -37
- metadata +7 -9
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Winton Welsh
|
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.markdown
CHANGED
@@ -1,8 +1,19 @@
|
|
1
1
|
captcha
|
2
2
|
=======
|
3
3
|
|
4
|
-
|
4
|
+
A Google-style captcha for enterprise Rails apps
|
5
5
|
|
6
|
+
Goals
|
7
|
+
-----
|
8
|
+
|
9
|
+
* Batch generate captchas
|
10
|
+
* Use ciphered filenames (no need to store filename/captcha code pairs)
|
11
|
+
* Captchas refresh automatically
|
12
|
+
* Easy configuration
|
13
|
+
** Number of captchas
|
14
|
+
** Period for captcha refresh
|
15
|
+
** Colors, wave, implode
|
16
|
+
* Handle lots of users
|
6
17
|
|
7
18
|
Install
|
8
19
|
-------
|
@@ -12,7 +23,9 @@ Install
|
|
12
23
|
### Create lib/captcha_config.rb (optional)
|
13
24
|
|
14
25
|
<pre>
|
15
|
-
|
26
|
+
Captcha::Config.new(
|
27
|
+
# Used for filename cipher
|
28
|
+
:password => 'something-unique',
|
16
29
|
# Captcha colors
|
17
30
|
:colors => {
|
18
31
|
:background => '#FFFFFF',
|
@@ -23,8 +36,8 @@ Install
|
|
23
36
|
# Where to write captchas
|
24
37
|
:destination => "#{RAILS_ROOT}/public/images/captchas",
|
25
38
|
# Generate new batch every day
|
26
|
-
:generate_every =>
|
27
|
-
|
39
|
+
:generate_every => 24 * 60 * 60
|
40
|
+
)
|
28
41
|
</pre>
|
29
42
|
|
30
43
|
See <code>lib/captcha/config.rb</code> for more options.
|
@@ -35,50 +48,40 @@ See <code>lib/captcha/config.rb</code> for more options.
|
|
35
48
|
public/images/captchas/*
|
36
49
|
</pre>
|
37
50
|
|
38
|
-
###
|
39
|
-
|
40
|
-
script/generate controller Captchas
|
41
|
-
|
42
|
-
### Add the resource to your routes.rb
|
43
|
-
|
44
|
-
map.resource :captcha
|
45
|
-
|
46
|
-
### Add acts\_as\_captcha to captchas_controller.rb
|
51
|
+
### Add acts\_as\_captcha to application_controller.rb
|
47
52
|
|
48
53
|
<pre>
|
49
|
-
class
|
54
|
+
class ApplicationController < ActionController::Base
|
50
55
|
acts_as_captcha
|
51
56
|
end
|
52
|
-
</pre>
|
57
|
+
</pre>
|
58
|
+
|
59
|
+
You may now use the <code>reset_captcha</code> method in any controller.
|
53
60
|
|
54
61
|
### Add acts\_as\_captcha to your model
|
55
62
|
|
56
63
|
<pre>
|
57
|
-
|
64
|
+
class User < ActiveRecord::Base
|
65
|
+
acts_as_captcha :base => "base error when captcha fails", :field => "field error when captcha fails"
|
66
|
+
end
|
58
67
|
</pre>
|
59
68
|
|
60
|
-
|
69
|
+
No parameters behaves like <code>:field => true</code>, a default error is added to the "captcha" field.
|
70
|
+
|
71
|
+
Use <code>:base => true</code> to activate a default error for base.
|
61
72
|
|
62
73
|
### In your view
|
63
74
|
|
64
75
|
<pre>
|
65
|
-
|
76
|
+
<img src="/images/captchas/<%= session[:captcha] %>.jpg" />
|
66
77
|
<%= text_field_tag(:captcha) %>
|
67
78
|
</pre>
|
68
79
|
|
69
80
|
### In your controller
|
70
81
|
|
71
82
|
<pre>
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
Captchas will generate and re-generate automatically when a Rails instance starts. Captcha keeps the old batch of captchas around until the next re-generate so users do not get 404s.
|
79
|
-
|
80
|
-
We used the following URLs in our view:
|
81
|
-
* <code>/captcha</code> to retrieve the image
|
82
|
-
* <code>/captcha/new</code> to grab a new image
|
83
|
-
|
84
|
-
Use <code>session[:captcha]</code> to store, retrieve, and reset the captcha code.
|
83
|
+
user = User.new
|
84
|
+
user.known_captcha = session[:captcha]
|
85
|
+
user.captcha = params[:captcha]
|
86
|
+
user.save
|
87
|
+
</pre>
|
data/captcha.gemspec
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'captcha'
|
3
|
-
s.version = '1.
|
4
|
-
s.date = '2009-03-
|
3
|
+
s.version = '1.2.0'
|
4
|
+
s.date = '2009-03-27'
|
5
5
|
|
6
|
-
s.summary = "
|
7
|
-
s.description = "
|
6
|
+
s.summary = "A Google-style captcha for enterprise Rails apps"
|
7
|
+
s.description = "A Google-style captcha for enterprise Rails apps"
|
8
8
|
|
9
9
|
s.author = 'Winton Welsh'
|
10
10
|
s.email = 'mail@wintoni.us'
|
@@ -14,24 +14,22 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
# = MANIFEST =
|
16
16
|
s.files = %w[
|
17
|
+
MIT-LICENSE
|
17
18
|
README.markdown
|
18
19
|
Rakefile
|
19
20
|
captcha.gemspec
|
20
21
|
init.rb
|
21
22
|
lib/captcha.rb
|
22
23
|
lib/captcha/action.rb
|
23
|
-
lib/captcha/
|
24
|
-
lib/captcha/captcha.rb
|
24
|
+
lib/captcha/cipher.rb
|
25
25
|
lib/captcha/config.rb
|
26
26
|
lib/captcha/generator.rb
|
27
27
|
lib/captcha/image.rb
|
28
|
-
lib/captcha/
|
28
|
+
lib/captcha/model.rb
|
29
29
|
resources/captcha.ttf
|
30
|
-
resources/captchas.rb
|
31
30
|
spec/lib/captcha_spec.rb
|
32
31
|
spec/spec.opts
|
33
32
|
spec/spec_helper.rb
|
34
|
-
tasks/captcha.rake
|
35
33
|
]
|
36
34
|
# = MANIFEST =
|
37
35
|
end
|
data/lib/captcha/action.rb
CHANGED
@@ -7,22 +7,26 @@ module Captcha
|
|
7
7
|
|
8
8
|
module ClassMethods
|
9
9
|
def acts_as_captcha
|
10
|
-
include
|
10
|
+
unless included_modules.include? InstanceMethods
|
11
|
+
include InstanceMethods
|
12
|
+
end
|
13
|
+
before_filter :assign_captcha
|
11
14
|
end
|
12
15
|
end
|
13
16
|
|
14
17
|
module InstanceMethods
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
private
|
19
|
+
|
20
|
+
def assign_captcha
|
21
|
+
unless session[:captcha] && Captcha::Config.exists?(session[:captcha])
|
22
|
+
files = Captcha::Config.captchas
|
23
|
+
session[:captcha] = File.basename(files[rand(files.length)], '.jpg')
|
24
|
+
end
|
19
25
|
end
|
20
|
-
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
new and return unless File.exists?(file)
|
25
|
-
send_file(file, :disposition => 'inline', :type => 'image/jpeg')
|
26
|
+
|
27
|
+
def reset_captcha
|
28
|
+
session[:captcha] = nil
|
29
|
+
assign_captcha
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
module Captcha
|
5
|
+
class Cipher
|
6
|
+
@@key = Digest::SHA1.hexdigest(Config.options[:password])
|
7
|
+
@@iv = 'captchas'*2
|
8
|
+
|
9
|
+
def self.encrypt(text)
|
10
|
+
# Encrypt
|
11
|
+
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
|
12
|
+
cipher.encrypt
|
13
|
+
cipher.key = @@key
|
14
|
+
cipher.iv = @@iv
|
15
|
+
encrypted = cipher.update(text)
|
16
|
+
encrypted << cipher.final
|
17
|
+
# Turn into chr codes separated by underscores
|
18
|
+
# 135_14_163_53_43_135_172_31_1_23_169_81_49_110_49_230
|
19
|
+
encrypted = (0..encrypted.length-1).collect do |x|
|
20
|
+
encrypted[x]
|
21
|
+
end
|
22
|
+
encrypted.join('_')
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.decrypt(text)
|
26
|
+
# Decode chr coded string
|
27
|
+
encrypted = text.split('_').collect do |x|
|
28
|
+
x.to_i.chr
|
29
|
+
end
|
30
|
+
encrypted = encrypted.join('')
|
31
|
+
# Decrypt
|
32
|
+
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
|
33
|
+
cipher.decrypt
|
34
|
+
cipher.key = @@key
|
35
|
+
cipher.iv = @@iv
|
36
|
+
decrypted = cipher.update(encrypted)
|
37
|
+
decrypted << cipher.final
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/captcha/config.rb
CHANGED
@@ -1,11 +1,19 @@
|
|
1
1
|
module Captcha
|
2
2
|
class Config
|
3
3
|
|
4
|
-
|
5
|
-
RAILS_ENV == 'production' || RAILS_ENV == 'staging'
|
6
|
-
|
4
|
+
if defined?(RAILS_ENV)
|
5
|
+
PRODUCTION = RAILS_ENV == 'production' || RAILS_ENV == 'staging'
|
6
|
+
ROOT = "#{RAILS_ROOT}/"
|
7
|
+
else
|
8
|
+
PRODUCTION = false
|
9
|
+
ROOT = ""
|
10
|
+
end
|
11
|
+
ONE_DAY = 24 * 60 * 60
|
7
12
|
|
13
|
+
@@exists = {}
|
14
|
+
@@exists_since = Time.now
|
8
15
|
@@options = {
|
16
|
+
:password => 'captcha',
|
9
17
|
:colors => {
|
10
18
|
:background => '#FFFFFF',
|
11
19
|
:font => '#080288'
|
@@ -19,7 +27,7 @@ module Captcha
|
|
19
27
|
# canvas width (px)
|
20
28
|
:width => 110
|
21
29
|
},
|
22
|
-
:generate_every => PRODUCTION ?
|
30
|
+
:generate_every => PRODUCTION ? ONE_DAY * 1 : ONE_DAY * 10000,
|
23
31
|
# http://www.imagemagick.org/RMagick/doc/image2.html#implode
|
24
32
|
:implode => 0.2,
|
25
33
|
:letters => {
|
@@ -27,7 +35,7 @@ module Captcha
|
|
27
35
|
:baseline => 25,
|
28
36
|
# number of letters in captcha
|
29
37
|
:count => 6,
|
30
|
-
:ignore => ['a','e','i','o','u','l','j','q'],
|
38
|
+
:ignore => ['a','e','i','o','u','l','j','q','v'],
|
31
39
|
# font size (pts)
|
32
40
|
:points => 38,
|
33
41
|
# width of a character (used to decrease or increase space between characters) (px)
|
@@ -47,29 +55,25 @@ module Captcha
|
|
47
55
|
@@options.merge!(options)
|
48
56
|
end
|
49
57
|
|
50
|
-
def self.
|
51
|
-
|
52
|
-
files_modified = Dir["#{@@options[:destination]}/*.jpg"].collect do |file|
|
53
|
-
[ file, File.mtime(file) ]
|
54
|
-
end
|
55
|
-
# Youngest to oldest
|
56
|
-
files_modified.sort! { |a, b| b[1] <=> a[1] }
|
57
|
-
files_modified.collect { |f| f[0] }
|
58
|
+
def self.captchas
|
59
|
+
Dir["#{@@options[:destination]}/*.jpg"]
|
58
60
|
end
|
59
|
-
|
61
|
+
|
60
62
|
def self.codes
|
61
|
-
self.
|
63
|
+
self.captchas.collect do |f|
|
62
64
|
File.basename f, '.jpg'
|
63
65
|
end
|
64
66
|
end
|
65
67
|
|
66
|
-
def self.
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
68
|
+
def self.exists?(code)
|
69
|
+
if Time.now - @@exists_since > 60 * 60
|
70
|
+
@@exists = {}
|
71
|
+
@@exists_since = Time.now
|
72
|
+
end
|
73
|
+
unless @@exists[code]
|
74
|
+
@@exists[code] = File.exists?("#{@@options[:destination]}/#{code}.jpg")
|
72
75
|
end
|
76
|
+
@@exists[code]
|
73
77
|
end
|
74
78
|
|
75
79
|
def self.options
|
@@ -77,8 +81,12 @@ module Captcha
|
|
77
81
|
end
|
78
82
|
|
79
83
|
def self.last_modified
|
80
|
-
|
81
|
-
|
84
|
+
file = self.captchas.first
|
85
|
+
if file
|
86
|
+
File.mtime(file)
|
87
|
+
else
|
88
|
+
nil
|
89
|
+
end
|
82
90
|
end
|
83
91
|
end
|
84
92
|
end
|
data/lib/captcha/generator.rb
CHANGED
@@ -7,23 +7,20 @@ module Captcha
|
|
7
7
|
def generate
|
8
8
|
return unless Config.options
|
9
9
|
return if Config.last_modified && Config.last_modified > Time.now - Config.options[:generate_every]
|
10
|
+
path = Config.options[:destination]
|
11
|
+
if File.exists?(path)
|
12
|
+
FileUtils.rm_rf Config.options[:destination]
|
13
|
+
end
|
10
14
|
FileUtils.mkdir_p Config.options[:destination]
|
11
15
|
(1..Config.options[:count]).each do |x|
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
image = Image.new Config.options
|
17
|
+
path = "#{Config.options[:destination]}/#{Cipher.encrypt(image.code)}.jpg"
|
18
|
+
next if File.exists?(path)
|
19
|
+
File.open(path, 'w') do |f|
|
20
|
+
f << image.data
|
15
21
|
end
|
16
22
|
end
|
17
|
-
destroy_if_over_limit
|
18
23
|
GC.start
|
19
24
|
end
|
20
|
-
|
21
|
-
def destroy_if_over_limit
|
22
|
-
if Config.codes.length >= Config.options[:count] * 3
|
23
|
-
Config.captchas_ordered_by_modified[(Config.options[:count] * 2)..-1].each do |file|
|
24
|
-
FileUtils.rm_f(file)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
25
|
end
|
29
26
|
end
|
data/lib/captcha/image.rb
CHANGED
@@ -4,13 +4,12 @@ module Captcha
|
|
4
4
|
class Image
|
5
5
|
|
6
6
|
include Magick
|
7
|
-
attr_reader :code, :
|
7
|
+
attr_reader :code, :data
|
8
8
|
|
9
9
|
def initialize(o)
|
10
|
-
|
10
|
+
generate_code o
|
11
11
|
|
12
|
-
canvas = Magick::
|
13
|
-
canvas.new_image(o[:dimensions][:width], o[:dimensions][:height]) {
|
12
|
+
canvas = Magick::Image.new(o[:dimensions][:width], o[:dimensions][:height]) {
|
14
13
|
self.background_color = o[:colors][:background]
|
15
14
|
}
|
16
15
|
|
@@ -30,17 +29,19 @@ module Captcha
|
|
30
29
|
canvas = canvas.wave(o[:wave][:amplitude], rand(w.last - w.first) + w.first)
|
31
30
|
canvas = canvas.implode(o[:implode])
|
32
31
|
|
33
|
-
@code
|
34
|
-
@
|
32
|
+
@code = @code.to_s
|
33
|
+
@data = canvas.to_blob { self.format = "JPG" }
|
34
|
+
canvas.destroy!
|
35
35
|
end
|
36
36
|
|
37
37
|
private
|
38
38
|
|
39
39
|
def generate_code(o)
|
40
40
|
chars = ('a'..'z').to_a - o[:letters][:ignore]
|
41
|
-
|
42
|
-
1.upto(o[:letters][:count])
|
43
|
-
|
41
|
+
@code = []
|
42
|
+
1.upto(o[:letters][:count]) do
|
43
|
+
@code << chars[rand(chars.length)]
|
44
|
+
end
|
44
45
|
end
|
45
46
|
end
|
46
47
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Captcha
|
2
|
+
module Model
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ActMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ActMethods
|
8
|
+
def acts_as_captcha(options={})
|
9
|
+
extend ClassMethods
|
10
|
+
include InstanceMethods
|
11
|
+
attr_reader :captcha, :known_captcha
|
12
|
+
cattr_accessor :captcha_options
|
13
|
+
self.captcha_options = options
|
14
|
+
validate :captcha_must_match_known_captcha
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
end
|
20
|
+
|
21
|
+
module InstanceMethods
|
22
|
+
def captcha=(c)
|
23
|
+
@captcha = c || ''
|
24
|
+
end
|
25
|
+
|
26
|
+
def known_captcha=(c)
|
27
|
+
@known_captcha = c || ''
|
28
|
+
end
|
29
|
+
|
30
|
+
def captcha_must_match_known_captcha
|
31
|
+
return true if self.captcha.nil? || self.known_captcha.nil?
|
32
|
+
if self.captcha.strip.downcase != Captcha::Cipher.decrypt(self.known_captcha)
|
33
|
+
if self.captcha_options[:base]
|
34
|
+
self.errors.add_to_base(
|
35
|
+
case self.captcha_options[:base]
|
36
|
+
when true
|
37
|
+
"Enter the correct text in the image (6 characters)"
|
38
|
+
else
|
39
|
+
self.captcha_options[:base]
|
40
|
+
end
|
41
|
+
)
|
42
|
+
else
|
43
|
+
self.errors.add(:captcha,
|
44
|
+
case self.captcha_options[:field]
|
45
|
+
when true, nil
|
46
|
+
"text does not match the text in the image."
|
47
|
+
else
|
48
|
+
self.captcha_options[:field]
|
49
|
+
end
|
50
|
+
)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/captcha.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require File.dirname(__FILE__) + "/captcha/action.rb"
|
2
2
|
require File.dirname(__FILE__) + "/captcha/image.rb"
|
3
3
|
require File.dirname(__FILE__) + "/captcha/config.rb"
|
4
|
+
require File.dirname(__FILE__) + "/captcha/cipher.rb"
|
4
5
|
require File.dirname(__FILE__) + "/captcha/generator.rb"
|
data/spec/lib/captcha_spec.rb
CHANGED
@@ -17,47 +17,15 @@ describe :captcha do
|
|
17
17
|
it "should generate captchas" do
|
18
18
|
Captcha::Config.codes.length.should == 10
|
19
19
|
end
|
20
|
-
it "should
|
21
|
-
@generator.generate
|
22
|
-
Captcha::Config.codes.length.should == 10
|
23
|
-
sleep @delay
|
24
|
-
@generator.generate
|
25
|
-
Captcha::Config.codes.length.should == 20
|
26
|
-
end
|
27
|
-
it "should not allow more than twice the captcha limit to exist" do
|
28
|
-
sleep @delay
|
29
|
-
@generator.generate
|
30
|
-
Captcha::Config.codes.length.should == 20
|
31
|
-
sleep @delay
|
32
|
-
@generator.generate
|
33
|
-
Captcha::Config.codes.length.should == 20
|
34
|
-
end
|
35
|
-
it "should only destroy the oldest captchas" do
|
20
|
+
it "should generate fresh captchas if the files are older than the generate_every option" do
|
36
21
|
codes = Captcha::Config.codes
|
37
22
|
sleep @delay
|
38
23
|
@generator.generate
|
39
|
-
|
40
|
-
|
41
|
-
|
24
|
+
codes.should_not == Captcha::Config.codes
|
25
|
+
end
|
26
|
+
it "should not allow more than the captcha limit to exist" do
|
42
27
|
sleep @delay
|
43
28
|
@generator.generate
|
44
|
-
Captcha::Config.codes.length.should ==
|
45
|
-
Captcha::Config.codes[9..-1].should_include_all_from(codes2)
|
46
|
-
Captcha::Config.codes.should_not_include_any_from(codes)
|
47
|
-
@generator.generate
|
48
|
-
Captcha::Config.codes.length.should == 20
|
49
|
-
Captcha::Config.codes.should_not_include_any_from(codes2)
|
50
|
-
end
|
51
|
-
class Array
|
52
|
-
def should_include_all_from(array)
|
53
|
-
included_all = true
|
54
|
-
array.each { |a| included_all = false unless self.include?(a) }
|
55
|
-
included_all
|
56
|
-
end
|
57
|
-
def should_not_include_any_from(array)
|
58
|
-
included_any = false
|
59
|
-
array.each { |a| included_any = true if self.include?(a) }
|
60
|
-
included_any
|
61
|
-
end
|
29
|
+
Captcha::Config.codes.length.should == 10
|
62
30
|
end
|
63
31
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: winton-captcha
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Winton Welsh
|
@@ -9,11 +9,11 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-03-
|
12
|
+
date: 2009-03-27 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description:
|
16
|
+
description: A Google-style captcha for enterprise Rails apps
|
17
17
|
email: mail@wintoni.us
|
18
18
|
executables: []
|
19
19
|
|
@@ -22,24 +22,22 @@ extensions: []
|
|
22
22
|
extra_rdoc_files: []
|
23
23
|
|
24
24
|
files:
|
25
|
+
- MIT-LICENSE
|
25
26
|
- README.markdown
|
26
27
|
- Rakefile
|
27
28
|
- captcha.gemspec
|
28
29
|
- init.rb
|
29
30
|
- lib/captcha.rb
|
30
31
|
- lib/captcha/action.rb
|
31
|
-
- lib/captcha/
|
32
|
-
- lib/captcha/captcha.rb
|
32
|
+
- lib/captcha/cipher.rb
|
33
33
|
- lib/captcha/config.rb
|
34
34
|
- lib/captcha/generator.rb
|
35
35
|
- lib/captcha/image.rb
|
36
|
-
- lib/captcha/
|
36
|
+
- lib/captcha/model.rb
|
37
37
|
- resources/captcha.ttf
|
38
|
-
- resources/captchas.rb
|
39
38
|
- spec/lib/captcha_spec.rb
|
40
39
|
- spec/spec.opts
|
41
40
|
- spec/spec_helper.rb
|
42
|
-
- tasks/captcha.rake
|
43
41
|
has_rdoc: false
|
44
42
|
homepage: http://github.com/winton/captcha
|
45
43
|
post_install_message:
|
@@ -65,6 +63,6 @@ rubyforge_project:
|
|
65
63
|
rubygems_version: 1.2.0
|
66
64
|
signing_key:
|
67
65
|
specification_version: 2
|
68
|
-
summary:
|
66
|
+
summary: A Google-style captcha for enterprise Rails apps
|
69
67
|
test_files: []
|
70
68
|
|