custom_captcha 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/CHANGELOG +4 -0
- data/Gemfile +4 -0
- data/Guardfile +22 -0
- data/README.markdown +107 -0
- data/Rakefile +2 -0
- data/app/controllers/custom_captcha/captcha_images_controller.rb +28 -0
- data/app/views/custom_captcha/captcha_images/change.html.erb +2 -0
- data/app/views/custom_captcha/captcha_images/change.js.erb +3 -0
- data/app/views/custom_captcha/captcha_images/show.html.erb +2 -0
- data/app/views/custom_captcha/captcha_styles/_default.html.erb +10 -0
- data/config/locales/custom_captcha.en.yml +6 -0
- data/config/routes.rb +9 -0
- data/custom_captcha.gemspec +31 -0
- data/lib/custom_captcha.rb +39 -0
- data/lib/custom_captcha/action_controller_extension.rb +42 -0
- data/lib/custom_captcha/action_view_extension.rb +56 -0
- data/lib/custom_captcha/active_record_extension.rb +51 -0
- data/lib/custom_captcha/common_methods.rb +13 -0
- data/lib/custom_captcha/configuration.rb +107 -0
- data/lib/custom_captcha/engine.rb +5 -0
- data/lib/custom_captcha/errors.rb +5 -0
- data/lib/custom_captcha/image.rb +86 -0
- data/lib/custom_captcha/railtie.rb +25 -0
- data/lib/custom_captcha/utils.rb +94 -0
- data/lib/custom_captcha/version.rb +4 -0
- data/lib/generators/custom_captcha/install/USAGE +8 -0
- data/lib/generators/custom_captcha/install/install_generator.rb +49 -0
- data/lib/generators/custom_captcha/install/templates/README +18 -0
- data/lib/generators/custom_captcha/uninstall/USAGE +8 -0
- data/lib/generators/custom_captcha/uninstall/templates/README +2 -0
- data/lib/generators/custom_captcha/uninstall/uninstall_generator.rb +38 -0
- data/lib/generators/custom_captcha/utils.rb +12 -0
- data/lib/tasks/.gitkeep +0 -0
- data/lib/tasks/custom_captcha_utils.rake +26 -0
- data/spec/configuration_spec.rb +19 -0
- data/spec/image_spec.rb +10 -0
- data/spec/spec_helper.rb +6 -0
- metadata +165 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/CHANGELOG
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'rspec', :version => 2 do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
|
9
|
+
# Rails example
|
10
|
+
watch(%r{^spec/.+_spec\.rb$})
|
11
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
12
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
13
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
14
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
15
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
16
|
+
watch('spec/spec_helper.rb') { "spec" }
|
17
|
+
watch('config/routes.rb') { "spec/routing" }
|
18
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
19
|
+
# Capybara request specs
|
20
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
|
21
|
+
end
|
22
|
+
|
data/README.markdown
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
== CustomCaptcha
|
2
|
+
|
3
|
+
CustomCaptcha is a simple and custom captcha plugin for Rails3.
|
4
|
+
|
5
|
+
|
6
|
+
== Supported versions
|
7
|
+
|
8
|
+
* Ruby 1.8.7, 1.9.2, 1.9.3
|
9
|
+
|
10
|
+
* Rails 3.0.x, 3.1
|
11
|
+
|
12
|
+
* ImageMagick should be installed on your machine to use this plugin.
|
13
|
+
|
14
|
+
|
15
|
+
== Installation
|
16
|
+
|
17
|
+
In your app's `Gemfile`, add:
|
18
|
+
|
19
|
+
gem "custom_captcha", :git => "git://github.com/vkill/custom_captcha.git"
|
20
|
+
|
21
|
+
Then run:
|
22
|
+
|
23
|
+
bundle
|
24
|
+
rails generate custom_captcha:install
|
25
|
+
|
26
|
+
If you want to modify default captcha images storage location, edit your `config/initializers/custom_captcha.rb`
|
27
|
+
|
28
|
+
Then run:
|
29
|
+
|
30
|
+
bundle exec rake custom_captcha:generate_images[100]
|
31
|
+
|
32
|
+
|
33
|
+
== Base Usage
|
34
|
+
|
35
|
+
=== View helper
|
36
|
+
|
37
|
+
In the view file within the form tags add this code
|
38
|
+
|
39
|
+
<%= display_custom_captcha %>
|
40
|
+
|
41
|
+
and in the controller's action authenticate it as
|
42
|
+
|
43
|
+
if custom_captcha_valid?(params[:custom_captcha])
|
44
|
+
flash[:notice] = "captcha did match."
|
45
|
+
else
|
46
|
+
flash[:notice] = "captcha did not match."
|
47
|
+
end
|
48
|
+
|
49
|
+
=== FormBuilder helper
|
50
|
+
|
51
|
+
In the model class add this code
|
52
|
+
|
53
|
+
class User < ActiveRecord::Basse
|
54
|
+
apply_custom_captcha
|
55
|
+
end
|
56
|
+
|
57
|
+
and in the view file within the form tags write this code
|
58
|
+
|
59
|
+
<%= form_for @user do |form| -%>
|
60
|
+
...
|
61
|
+
<%= form.custom_captcha %>
|
62
|
+
...
|
63
|
+
<% end -%>
|
64
|
+
|
65
|
+
|
66
|
+
== Usage your custom captcha area template
|
67
|
+
|
68
|
+
first, copy and modify your custom captcha area template
|
69
|
+
|
70
|
+
cd app/views/custom_captcha/captcha_styles/
|
71
|
+
cp _default.html.erb _mycaptcha.html.erb
|
72
|
+
|
73
|
+
and then in the view file your the use of
|
74
|
+
|
75
|
+
<%= display_custom_captcha(:template => :mycaptcha) %>
|
76
|
+
|
77
|
+
|
78
|
+
== Options & Examples
|
79
|
+
|
80
|
+
=== Helper Options, View helper and FormBuilder helper
|
81
|
+
|
82
|
+
* *template* - use your custom template
|
83
|
+
|
84
|
+
* *label_text* - custom label text
|
85
|
+
|
86
|
+
* *key_id* - custom captcha hidden input key id
|
87
|
+
|
88
|
+
* *key_name* - custom captcha hidden input key name
|
89
|
+
|
90
|
+
* *field_name* - custom captcha input field name
|
91
|
+
|
92
|
+
* *img_id* - custom captcha img id
|
93
|
+
|
94
|
+
* *change_text* - custom change text
|
95
|
+
|
96
|
+
=== Examples
|
97
|
+
|
98
|
+
<%= display_custom_captcha(:img_id => :captcha, :label_text => "captcha", :field_name => :captcha %>
|
99
|
+
|
100
|
+
|
101
|
+
== I18n supported
|
102
|
+
|
103
|
+
|
104
|
+
== Copyright
|
105
|
+
|
106
|
+
Copyright (c) 2011 vkill.net .
|
107
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class CustomCaptcha::CaptchaImagesController < ApplicationController
|
2
|
+
|
3
|
+
respond_to :html, :js, :json
|
4
|
+
|
5
|
+
def show
|
6
|
+
@image_file_path = read_image_file_path(params[:key])
|
7
|
+
respond_to do |format|
|
8
|
+
format.html {
|
9
|
+
send_file(@image_file_path, :type => 'image/jpeg', :disposition => 'inline', :filename => 'custom_captcha.jpg')
|
10
|
+
}
|
11
|
+
format.json { render :json => {:image_file_path => @image_file_path} }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def change
|
16
|
+
@img_id = params[:img_id] || CustomCaptcha::DEFAULTVALUE[:img_id]
|
17
|
+
@key_id = params[:key_id] || CustomCaptcha::DEFAULTVALUE[:key_id]
|
18
|
+
@key = choice_image_file_path_and_write_session_cache()
|
19
|
+
@img_src = show_captcha_image_path(@key)
|
20
|
+
|
21
|
+
respond_to do |format|
|
22
|
+
format.js
|
23
|
+
format.json { render :json => {@img_id => @img_src, @key_id => @key} }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<br/>
|
2
|
+
|
3
|
+
<%= label_tag captcha[:label_text] %>
|
4
|
+
<%= text_field_tag captcha[:field_name] %>
|
5
|
+
<%= hidden_field_tag captcha[:key_name], captcha[:key], :id => captcha[:key_id] %>
|
6
|
+
<%= image_tag captcha[:img_src], :id => captcha[:img_id], :alt => captcha[:label_text] %>
|
7
|
+
<%= link_to captcha[:change_text], captcha[:change_url], :remote => true %>
|
8
|
+
|
9
|
+
<br/>
|
10
|
+
|
data/config/routes.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Rails.application.routes.draw do
|
2
|
+
match "captcha_images/change" => "custom_captcha/captcha_images#change",
|
3
|
+
:as => :change_captcha_images
|
4
|
+
|
5
|
+
match "captcha_images/:key" => "custom_captcha/captcha_images#show",
|
6
|
+
:as => :show_captcha_image,
|
7
|
+
:constraints => { :key => /[A-Za-z0-9]{32,40}/ }
|
8
|
+
end
|
9
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "custom_captcha/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "custom_captcha"
|
7
|
+
s.version = CustomCaptcha::VERSION
|
8
|
+
s.date = Date.today
|
9
|
+
s.authors = ["vkill"]
|
10
|
+
s.email = ["vkill.net@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/vkill/custom_captcha"
|
12
|
+
s.summary = "A simple and custom captcha for Rails3."
|
13
|
+
s.description = "A simple and custom captcha for Rails3."
|
14
|
+
|
15
|
+
s.rubyforge_project = "custom_captcha"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_development_dependency 'bundler'
|
23
|
+
s.add_development_dependency "rake"
|
24
|
+
s.add_development_dependency "rspec"
|
25
|
+
s.add_development_dependency "guard-rspec"
|
26
|
+
s.add_dependency "rails", "~> 3.0"
|
27
|
+
s.add_dependency "rmagick", "~> 2.13.1"
|
28
|
+
s.add_dependency 'jquery-rails', "~> 1.0.16"
|
29
|
+
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
require "active_support/core_ext/object"
|
3
|
+
require "active_support/core_ext/class/attribute_accessors"
|
4
|
+
require "active_support/core_ext/module/attribute_accessors"
|
5
|
+
require "securerandom"
|
6
|
+
require "RMagick"
|
7
|
+
include Magick
|
8
|
+
require "tmpdir"
|
9
|
+
require "fileutils"
|
10
|
+
require "digest/md5"
|
11
|
+
require "find"
|
12
|
+
|
13
|
+
require 'custom_captcha/errors'
|
14
|
+
require 'custom_captcha/version'
|
15
|
+
require 'custom_captcha/configuration'
|
16
|
+
require 'custom_captcha/utils'
|
17
|
+
require 'custom_captcha/image'
|
18
|
+
|
19
|
+
require "rails"
|
20
|
+
require "custom_captcha/engine"
|
21
|
+
require "custom_captcha/railtie"
|
22
|
+
|
23
|
+
module CustomCaptcha
|
24
|
+
DEFAULTVALUE = {
|
25
|
+
:field_name => :custom_captcha,
|
26
|
+
:key_name => :custom_captcha_key,
|
27
|
+
:key_id => :custom_captcha_key,
|
28
|
+
:img_id => :custom_captcha_img,
|
29
|
+
:template => 'default'
|
30
|
+
}
|
31
|
+
class << self
|
32
|
+
def configure(&block)
|
33
|
+
CustomCaptcha::Configuration.configure(&block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
# init configuration
|
37
|
+
CustomCaptcha::Configuration.init_config()
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module CustomCaptcha
|
2
|
+
module ActionControllerExtension
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
included do
|
5
|
+
include CustomCaptcha::CommonMethods
|
6
|
+
helper_method :choice_image_file_path_and_write_session_cache,
|
7
|
+
:read_captcha_digested, :read_image_file_path
|
8
|
+
end
|
9
|
+
module ClassMethods
|
10
|
+
end
|
11
|
+
module InstanceMethods
|
12
|
+
def custom_captcha_valid?(captcha_value=nil)
|
13
|
+
if CustomCaptcha::Configuration.disable? or Rails.env.test?
|
14
|
+
return true
|
15
|
+
end
|
16
|
+
captcha_value ||= params[CustomCaptcha::DEFAULTVALUE[:field_name]]
|
17
|
+
if captcha_value
|
18
|
+
CustomCaptcha::Utils.captcha_value_valid?(captcha_value, read_captcha_digested())
|
19
|
+
else
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def choice_image_file_path_and_write_session_cache
|
25
|
+
image_file_path, captcha_digested, key =
|
26
|
+
CustomCaptcha::Utils.choice_image_file_path_and_generate_captcha_digested_key
|
27
|
+
session[:captcha_digested] = captcha_digested
|
28
|
+
Rails.cache.write(key, [image_file_path, captcha_digested])
|
29
|
+
key
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_captcha_digested
|
33
|
+
session[:captcha_digested]
|
34
|
+
end
|
35
|
+
|
36
|
+
def read_image_file_path(key)
|
37
|
+
Rails.cache.read(key)[0] rescue nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module CustomCaptcha
|
2
|
+
module ActionViewExtension
|
3
|
+
module Base
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
included do
|
6
|
+
include CustomCaptcha::CommonMethods
|
7
|
+
end
|
8
|
+
module ClassMethods
|
9
|
+
end
|
10
|
+
module InstanceMethods
|
11
|
+
def display_custom_captcha(options={})
|
12
|
+
return "" if CustomCaptcha::Configuration.disable?
|
13
|
+
options[:key] = choice_image_file_path_and_write_session_cache
|
14
|
+
|
15
|
+
options[:field_name] ||= CustomCaptcha::DEFAULTVALUE[:field_name]
|
16
|
+
options[:key_name] ||= CustomCaptcha::DEFAULTVALUE[:key_name]
|
17
|
+
options[:key_id] ||= CustomCaptcha::DEFAULTVALUE[:key_id]
|
18
|
+
options[:img_id] ||= CustomCaptcha::DEFAULTVALUE[:img_id]
|
19
|
+
|
20
|
+
options[:img_src] = show_captcha_image_path(options[:key])
|
21
|
+
options[:template] ||= CustomCaptcha::DEFAULTVALUE[:template]
|
22
|
+
options[:label_text] ||= I18n.t('custom_captcha.label')
|
23
|
+
options[:change_text] ||= I18n.t('custom_captcha.change')
|
24
|
+
options[:change_url] = change_captcha_images_path(:img_id => options[:img_id], :key_id => options[:key_id])
|
25
|
+
render "custom_captcha/captcha_styles/#{options[:template].to_s}", :captcha => options
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Helpers
|
31
|
+
module FormBuilder
|
32
|
+
extend ActiveSupport::Concern
|
33
|
+
included do
|
34
|
+
include CustomCaptcha::CommonMethods
|
35
|
+
end
|
36
|
+
module ClassMethods
|
37
|
+
end
|
38
|
+
module InstanceMethods
|
39
|
+
def custom_captcha(options={})
|
40
|
+
return "" unless (@object.custom_captcha_key rescue nil)
|
41
|
+
options[:object_name] = @object_name.to_s
|
42
|
+
options[:key] = @object.custom_captcha_key
|
43
|
+
|
44
|
+
options[:field_name] = object_field_name options[:object_name], CustomCaptcha::DEFAULTVALUE[:field_name]
|
45
|
+
options[:key_name] = object_field_name options[:object_name], CustomCaptcha::DEFAULTVALUE[:key_name]
|
46
|
+
options[:key_id] = object_field_id options[:object_name], CustomCaptcha::DEFAULTVALUE[:key_id]
|
47
|
+
options[:img_id] = object_field_id options[:object_name], CustomCaptcha::DEFAULTVALUE[:img_id]
|
48
|
+
|
49
|
+
@template.display_custom_captcha(options)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module CustomCaptcha
|
2
|
+
module ActiveRecordExtension
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
included do
|
5
|
+
include CustomCaptcha::CommonMethods
|
6
|
+
extend ClassMethods
|
7
|
+
end
|
8
|
+
module ClassMethods
|
9
|
+
def apply_custom_captcha
|
10
|
+
attr_accessor :custom_captcha, :custom_captcha_key
|
11
|
+
|
12
|
+
after_initialize do
|
13
|
+
after_initialize if custom_captcha_need_valid?
|
14
|
+
end
|
15
|
+
|
16
|
+
validate :valid_custom_captcha, :if => :custom_captcha_need_valid?
|
17
|
+
|
18
|
+
include CustomCaptcha::ActiveRecordExtension::InstanceMethods
|
19
|
+
end
|
20
|
+
end
|
21
|
+
module InstanceMethods
|
22
|
+
def after_initialize
|
23
|
+
self.custom_captcha_key ||= choice_image_file_path_and_write_cache
|
24
|
+
end
|
25
|
+
|
26
|
+
def valid_custom_captcha
|
27
|
+
unless CustomCaptcha::Utils.captcha_value_valid?(custom_captcha, read_captcha_digested())
|
28
|
+
errors.add(:custom_captcha, I18n.t('custom_captcha.message'))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def custom_captcha_need_valid?
|
33
|
+
!CustomCaptcha::Configuration.disable? and !Rails.env.test?
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def choice_image_file_path_and_write_cache
|
38
|
+
image_file_path, captcha_digested, key =
|
39
|
+
CustomCaptcha::Utils.choice_image_file_path_and_generate_captcha_digested_key
|
40
|
+
Rails.cache.write(key, [image_file_path, captcha_digested])
|
41
|
+
key
|
42
|
+
end
|
43
|
+
|
44
|
+
def read_captcha_digested
|
45
|
+
Rails.cache.read(self.custom_captcha_key)[1] rescue ""
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module CustomCaptcha
|
2
|
+
module CommonMethods
|
3
|
+
private
|
4
|
+
def object_field_name(object_name, field_name)
|
5
|
+
object_name.to_s + "[" + field_name.to_s + "]"
|
6
|
+
end
|
7
|
+
|
8
|
+
def object_field_id(object_name, field_name)
|
9
|
+
object_name.to_s + "_" + field_name.to_s
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module CustomCaptcha
|
2
|
+
module Configuration
|
3
|
+
|
4
|
+
mattr_accessor :image_columns,
|
5
|
+
:image_rows,
|
6
|
+
:image_style,
|
7
|
+
:text_range,
|
8
|
+
:text_length,
|
9
|
+
:images_path,
|
10
|
+
:enable,
|
11
|
+
:salt
|
12
|
+
mattr_reader :images_path_definite
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
def configure
|
17
|
+
yield self
|
18
|
+
# make inage files path and defined @@images_path_definite
|
19
|
+
make_and_define_images_path()
|
20
|
+
# reload all image files key
|
21
|
+
CustomCaptcha::Utils.reload_image_files_paths()
|
22
|
+
end
|
23
|
+
|
24
|
+
def reset_config
|
25
|
+
configure do |config|
|
26
|
+
config.image_columns = default_image_columns()
|
27
|
+
config.image_rows = default_image_rows()
|
28
|
+
config.image_style = CustomCaptcha::Image.image_style.keys.first
|
29
|
+
config.text_range = default_text_range()
|
30
|
+
config.text_length = default_text_length()
|
31
|
+
config.images_path = default_images_path()
|
32
|
+
config.enable = true
|
33
|
+
config.salt = SecureRandom.hex(10)
|
34
|
+
end
|
35
|
+
make_and_define_images_path()
|
36
|
+
end
|
37
|
+
alias_method :init_config, :reset_config
|
38
|
+
|
39
|
+
def disable?
|
40
|
+
!@@enable
|
41
|
+
end
|
42
|
+
|
43
|
+
#image columns setting
|
44
|
+
def default_image_columns
|
45
|
+
120
|
46
|
+
end
|
47
|
+
def image_columns
|
48
|
+
image_columns = @@image_columns.to_i.blank? ? default_image_columns() : @@image_columns
|
49
|
+
end
|
50
|
+
|
51
|
+
#image rows setting
|
52
|
+
def default_image_rows
|
53
|
+
40
|
54
|
+
end
|
55
|
+
def image_rows
|
56
|
+
image_rows = @@image_rows.to_i.blank? ? default_image_rows() : @@image_rows
|
57
|
+
end
|
58
|
+
|
59
|
+
#image style setting
|
60
|
+
def default_image_style
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
#image text range setting
|
65
|
+
def default_text_range
|
66
|
+
('a'..'z').to_a + (0..9).to_a
|
67
|
+
end
|
68
|
+
def text_range
|
69
|
+
text_range = @@text_range.to_a.blank? ? default_text_range() : @@text_range
|
70
|
+
end
|
71
|
+
|
72
|
+
#image text length setting
|
73
|
+
def default_text_length
|
74
|
+
5
|
75
|
+
end
|
76
|
+
def text_length
|
77
|
+
text_length = @@text_length.to_i.blank? ? default_text_length() : @@text_length
|
78
|
+
if 4 < text_length or text_length < 10
|
79
|
+
text_length
|
80
|
+
else
|
81
|
+
default_text_length()
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
#images path setting
|
86
|
+
def default_images_path
|
87
|
+
File.join(Dir::tmpdir, 'captcha_images')
|
88
|
+
end
|
89
|
+
def images_path
|
90
|
+
images_path = @@images_path.to_s.blank? ? default_images_path() : @@images_path
|
91
|
+
end
|
92
|
+
|
93
|
+
#images path make and define
|
94
|
+
def make_and_define_images_path
|
95
|
+
images_path = images_path()
|
96
|
+
begin
|
97
|
+
FileUtils.mkdir_p images_path
|
98
|
+
rescue
|
99
|
+
raise CustomCaptcha::MakeImagePathError
|
100
|
+
else
|
101
|
+
@@images_path_definite = images_path
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module CustomCaptcha
|
2
|
+
module Image
|
3
|
+
mattr_reader :image_style
|
4
|
+
@@image_style = {
|
5
|
+
:simply_blue => {:background_color => :white, :text_fill => :darkblue, :text_font_family => :helvetica}
|
6
|
+
}
|
7
|
+
#image text length and image text font_size proportion
|
8
|
+
@@font_size = {
|
9
|
+
4 => 46,
|
10
|
+
5 => 41,
|
11
|
+
6 => 36,
|
12
|
+
7 => 31,
|
13
|
+
8 => 26,
|
14
|
+
9 => 21,
|
15
|
+
10 => 16
|
16
|
+
}
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def create(number=1, &block)
|
20
|
+
image_style_simply_blue = @@image_style[:simply_blue]
|
21
|
+
image_style = @@image_style[CustomCaptcha::Configuration.image_style] || image_style_simply_blue
|
22
|
+
options = {}
|
23
|
+
options[:background_color] = image_style[:background_color] || image_style_simply_blue[:background_color]
|
24
|
+
options[:text_fill] = image_style[:text_fill] || image_style_simply_blue[:text_fill]
|
25
|
+
options[:text_font_family] = image_style[:text_font_family] || image_style_simply_blue[:text_font_family]
|
26
|
+
image_files = create_image_files(number, options, &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
class << self
|
32
|
+
def generate_image_text
|
33
|
+
text_range = CustomCaptcha::Configuration.text_range
|
34
|
+
text_length = CustomCaptcha::Configuration.text_length
|
35
|
+
text_range.to_a.sort_by!{rand()}.take(text_length).join()
|
36
|
+
end
|
37
|
+
|
38
|
+
def generate_image_file_path(text)
|
39
|
+
File.join(
|
40
|
+
CustomCaptcha::Utils.image_file_dirname(),
|
41
|
+
[
|
42
|
+
CustomCaptcha::Utils.image_file_basename(text),
|
43
|
+
CustomCaptcha::Utils.image_file_extname()
|
44
|
+
].join()
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_image_files(number, options={}, &block)
|
49
|
+
columns = CustomCaptcha::Configuration.image_columns
|
50
|
+
rows = CustomCaptcha::Configuration.image_rows
|
51
|
+
|
52
|
+
image_files = []
|
53
|
+
number.to_i.times do |n|
|
54
|
+
canvas = Magick::ImageList.new
|
55
|
+
canvas.new_image(columns, rows) {
|
56
|
+
self.background_color = options[:background_color].to_s
|
57
|
+
}
|
58
|
+
text = Magick::Draw.new
|
59
|
+
text.font_family = options[:text_font_family].to_s
|
60
|
+
text.fill = options[:text_fill].to_s
|
61
|
+
text.gravity = Magick::CenterGravity
|
62
|
+
|
63
|
+
image_text = generate_image_text()
|
64
|
+
text.annotate(canvas, 0,0,0,0, image_text) {
|
65
|
+
self.pointsize = @@font_size[image_text.length]
|
66
|
+
}
|
67
|
+
image_file = generate_image_file_path(image_text)
|
68
|
+
begin
|
69
|
+
canvas.write(image_file)
|
70
|
+
rescue
|
71
|
+
yield(number, n+1, "failed", image_file) if block
|
72
|
+
next
|
73
|
+
else
|
74
|
+
yield(number, n+1, "succeed", image_file) if block
|
75
|
+
image_files << image_file
|
76
|
+
ensure
|
77
|
+
canvas.clear
|
78
|
+
end
|
79
|
+
end
|
80
|
+
image_files
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'custom_captcha/common_methods'
|
2
|
+
require 'custom_captcha/action_controller_extension'
|
3
|
+
require 'custom_captcha/action_view_extension'
|
4
|
+
require 'custom_captcha/active_record_extension'
|
5
|
+
|
6
|
+
module CustomCaptcha
|
7
|
+
class Railtie < ::Rails::Railtie
|
8
|
+
initializer 'custom_captcha' do |app|
|
9
|
+
ActiveSupport.on_load :action_controller do
|
10
|
+
ActionController::Base.send :include, CustomCaptcha::ActionControllerExtension
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveSupport.on_load :action_view do
|
14
|
+
ActionView::Base.send :include, CustomCaptcha::ActionViewExtension::Base
|
15
|
+
ActionView::Helpers::FormBuilder.send :include,
|
16
|
+
CustomCaptcha::ActionViewExtension::Helpers::FormBuilder
|
17
|
+
end
|
18
|
+
|
19
|
+
ActiveSupport.on_load :active_record do
|
20
|
+
include CustomCaptcha::ActiveRecordExtension
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module CustomCaptcha
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
mattr_reader :image_files_paths, :image_files_paths_choice_size
|
5
|
+
@@image_files_paths = []
|
6
|
+
@@image_files_paths_choice_size = @@image_files_paths.size
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def generate_image_files(number=1, &block)
|
11
|
+
CustomCaptcha::Image.create(number, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear_image_files
|
15
|
+
FileUtils.rm_r Dir[File.join(image_file_dirname(), "**", "*")]
|
16
|
+
end
|
17
|
+
|
18
|
+
def reload_image_files_paths
|
19
|
+
@@image_files_paths.clear
|
20
|
+
reset_image_files_paths()
|
21
|
+
end
|
22
|
+
alias_method :init_image_files_paths, :reload_image_files_paths
|
23
|
+
|
24
|
+
|
25
|
+
def choice_image_file_path_and_generate_captcha_digested_key
|
26
|
+
image_file_path = random_choice_image_file_path
|
27
|
+
captcha_digested = get_captcha_digested(image_file_path)
|
28
|
+
key = hexdigest_text([Time.now.to_i, SecureRandom.hex(10)].join())
|
29
|
+
[image_file_path, captcha_digested, key]
|
30
|
+
end
|
31
|
+
|
32
|
+
def captcha_value_valid?(captcha_value, captcha_digested)
|
33
|
+
image_file_basename_suffix(captcha_value) == captcha_digested
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
class << self
|
40
|
+
def hexdigest_text(text)
|
41
|
+
Digest::MD5.hexdigest([text.to_s.downcase, CustomCaptcha::Configuration.salt].join())
|
42
|
+
end
|
43
|
+
|
44
|
+
def image_file_dirname
|
45
|
+
CustomCaptcha::Configuration.images_path_definite
|
46
|
+
end
|
47
|
+
|
48
|
+
def image_file_basename_prefix
|
49
|
+
# ("a".."z").to_a.sort_by!{rand()}.take(8).join()
|
50
|
+
SecureRandom.hex(4)
|
51
|
+
end
|
52
|
+
|
53
|
+
def image_file_basename_suffix(text)
|
54
|
+
hexdigest_text(text)
|
55
|
+
end
|
56
|
+
|
57
|
+
def image_file_basename(text)
|
58
|
+
[image_file_basename_prefix, image_file_basename_suffix(text)].join()
|
59
|
+
end
|
60
|
+
|
61
|
+
def image_file_extname
|
62
|
+
".gif"
|
63
|
+
end
|
64
|
+
|
65
|
+
# if image file path empty, then auto generating 3 image file.
|
66
|
+
def reset_image_files_paths
|
67
|
+
if @@image_files_paths.blank? or @@image_files_paths_choice_size == 0
|
68
|
+
n = 0
|
69
|
+
while true
|
70
|
+
@@image_files_paths = Dir[File.join(image_file_dirname(), ["[a-z0-9]*", image_file_extname()].join())]
|
71
|
+
break unless self.image_files_paths.blank?
|
72
|
+
generate_image_files(3)
|
73
|
+
raise InitImageFilesKeyError if n >= 3
|
74
|
+
n += 1
|
75
|
+
end
|
76
|
+
@@image_files_paths.sort_by!{rand()}
|
77
|
+
@@image_files_paths_choice_size = @@image_files_paths.size
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def random_choice_image_file_path
|
82
|
+
reset_image_files_paths()
|
83
|
+
@@image_files_paths_choice_size -= 1
|
84
|
+
@@image_files_paths[@@image_files_paths_choice_size]
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_captcha_digested(image_file_path)
|
88
|
+
image_file_basename = File.basename(image_file_path, image_file_extname())
|
89
|
+
image_file_basename[-32..-1]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.expand_path('../../utils', __FILE__)
|
2
|
+
|
3
|
+
class CustomCaptcha::InstallGenerator < Rails::Generators::Base
|
4
|
+
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
include CustomCaptcha::Generators::Utils::InstanceMethods
|
8
|
+
|
9
|
+
desc "Creates CustomCaptcha initializer and copy locale files to your application."
|
10
|
+
|
11
|
+
def create_initializer
|
12
|
+
data_custom_captcha_rb = %Q/
|
13
|
+
CustomCaptcha.configure do |config|
|
14
|
+
config.image_columns = 120
|
15
|
+
config.image_rows = 40
|
16
|
+
config.image_style = "simply_blue"
|
17
|
+
config.text_range = ("a".."z").to_a
|
18
|
+
config.text_length = 5
|
19
|
+
config.images_path = Rails.root.join("public", "system", "captcha_images").to_s
|
20
|
+
config.enable = true
|
21
|
+
config.salt = "#{ SecureRandom.hex(20) }"
|
22
|
+
end
|
23
|
+
/
|
24
|
+
initializer "custom_captcha.rb", data_custom_captcha_rb
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_locale
|
28
|
+
copy_file "../../../../../config/locales/custom_captcha.en.yml", "config/locales/custom_captcha.en.yml"
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_views
|
32
|
+
filename_pattern = File.expand_path("../../../../../app/views/custom_captcha/captcha_styles/*", __FILE__)
|
33
|
+
Dir[filename_pattern].each do |f|
|
34
|
+
copy_file f, "app/views/custom_captcha/captcha_styles/#{File.basename f}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_rakes
|
39
|
+
display "test generate and clear images..."
|
40
|
+
rake("custom_captcha:generate_images[1]")
|
41
|
+
rake("custom_captcha:clear_images")
|
42
|
+
end
|
43
|
+
|
44
|
+
def show_readme
|
45
|
+
display "Succeed! custom_captcha has been installed."
|
46
|
+
readme("README")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
===============================================================================
|
2
|
+
|
3
|
+
Some setup you must do manually:
|
4
|
+
|
5
|
+
1. Modify captcha images directory in config/initializers/custom_captcha.rb.
|
6
|
+
For example:
|
7
|
+
|
8
|
+
config.images_path = Rails.root.join("public", "captcha_images").to_s
|
9
|
+
|
10
|
+
2. Generate captcha images.
|
11
|
+
|
12
|
+
# bundle exec rake custom_captcha:generate_images[100]
|
13
|
+
|
14
|
+
|
15
|
+
Homepage: https://github.com/vkill/custom_captcha
|
16
|
+
by: vkill
|
17
|
+
===============================================================================
|
18
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path('../../utils', __FILE__)
|
2
|
+
|
3
|
+
class CustomCaptcha::UninstallGenerator < Rails::Generators::Base
|
4
|
+
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
include CustomCaptcha::Generators::Utils::InstanceMethods
|
8
|
+
|
9
|
+
desc "Remove CustomCaptcha initializer and remove locale files from your application."
|
10
|
+
|
11
|
+
def clear_images
|
12
|
+
rake("custom_captcha:clear_images")
|
13
|
+
end
|
14
|
+
|
15
|
+
def destroy_initializer
|
16
|
+
remove_file "config/initializers/custom_captcha.rb"
|
17
|
+
end
|
18
|
+
|
19
|
+
def destroy_locale
|
20
|
+
remove_file "config/locales/custom_captcha.en.yml"
|
21
|
+
end
|
22
|
+
|
23
|
+
def destroy_views
|
24
|
+
# filename_pattern = File.expand_path("../../../../../app/views/custom_captcha/captcha_styles/*", __FILE__)
|
25
|
+
# Dir[filename_pattern].each do |f|
|
26
|
+
# copy_file f, "app/views/custom_captcha/captcha_styles/#{File.basename f}"
|
27
|
+
# end
|
28
|
+
Dir["app/views/custom_captcha/captcha_styles/*"].each do |f|
|
29
|
+
remove_file f
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def show_readme
|
34
|
+
display "Done! custom_captcha has been uninstalled."
|
35
|
+
readme("README")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
data/lib/tasks/.gitkeep
ADDED
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "bundler/setup"
|
5
|
+
|
6
|
+
require "custom_captcha"
|
7
|
+
|
8
|
+
namespace :custom_captcha do
|
9
|
+
desc "Generate {count} captcha images."
|
10
|
+
task :generate_images, [:count] => :environment do |t, args|
|
11
|
+
args.with_defaults(:count => 3)
|
12
|
+
count = args[:count].to_i
|
13
|
+
CustomCaptcha::Utils.generate_image_files(count) do |number, n, state, image_file|
|
14
|
+
puts " - [%s/%s] %s Generating %s" % [ n, number, state.capitalize!, image_file ]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Clear captcha images."
|
19
|
+
task :clear_images => :environment do |t|
|
20
|
+
print "\r - Clearing captcha images..."
|
21
|
+
CustomCaptcha::Utils.clear_image_files()
|
22
|
+
print "done\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe CustomCaptcha::Configuration do
|
5
|
+
|
6
|
+
it "should set a configuration" do
|
7
|
+
CustomCaptcha::Configuration.text_length.should == 5
|
8
|
+
CustomCaptcha.configure do |config|
|
9
|
+
config.text_length = 6
|
10
|
+
end
|
11
|
+
CustomCaptcha::Configuration.text_length.should == 6
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should define images path and create images path" do
|
15
|
+
CustomCaptcha::Configuration.make_and_define_images_path()
|
16
|
+
File.exist?(CustomCaptcha::Configuration.images_path_definite).should be_true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
data/spec/image_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: custom_captcha
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- vkill
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: &70249600 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70249600
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &70249330 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70249330
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &70249040 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70249040
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: guard-rspec
|
49
|
+
requirement: &70248730 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70248730
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: rails
|
60
|
+
requirement: &70248350 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ~>
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '3.0'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70248350
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rmagick
|
71
|
+
requirement: &70248000 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 2.13.1
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70248000
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: jquery-rails
|
82
|
+
requirement: &70247670 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ~>
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: 1.0.16
|
88
|
+
type: :runtime
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *70247670
|
91
|
+
description: A simple and custom captcha for Rails3.
|
92
|
+
email:
|
93
|
+
- vkill.net@gmail.com
|
94
|
+
executables: []
|
95
|
+
extensions: []
|
96
|
+
extra_rdoc_files: []
|
97
|
+
files:
|
98
|
+
- .gitignore
|
99
|
+
- .rspec
|
100
|
+
- CHANGELOG
|
101
|
+
- Gemfile
|
102
|
+
- Guardfile
|
103
|
+
- README.markdown
|
104
|
+
- Rakefile
|
105
|
+
- app/controllers/custom_captcha/captcha_images_controller.rb
|
106
|
+
- app/views/custom_captcha/captcha_images/change.html.erb
|
107
|
+
- app/views/custom_captcha/captcha_images/change.js.erb
|
108
|
+
- app/views/custom_captcha/captcha_images/show.html.erb
|
109
|
+
- app/views/custom_captcha/captcha_styles/_default.html.erb
|
110
|
+
- config/locales/custom_captcha.en.yml
|
111
|
+
- config/routes.rb
|
112
|
+
- custom_captcha.gemspec
|
113
|
+
- lib/custom_captcha.rb
|
114
|
+
- lib/custom_captcha/action_controller_extension.rb
|
115
|
+
- lib/custom_captcha/action_view_extension.rb
|
116
|
+
- lib/custom_captcha/active_record_extension.rb
|
117
|
+
- lib/custom_captcha/common_methods.rb
|
118
|
+
- lib/custom_captcha/configuration.rb
|
119
|
+
- lib/custom_captcha/engine.rb
|
120
|
+
- lib/custom_captcha/errors.rb
|
121
|
+
- lib/custom_captcha/image.rb
|
122
|
+
- lib/custom_captcha/railtie.rb
|
123
|
+
- lib/custom_captcha/utils.rb
|
124
|
+
- lib/custom_captcha/version.rb
|
125
|
+
- lib/generators/custom_captcha/install/USAGE
|
126
|
+
- lib/generators/custom_captcha/install/install_generator.rb
|
127
|
+
- lib/generators/custom_captcha/install/templates/README
|
128
|
+
- lib/generators/custom_captcha/uninstall/USAGE
|
129
|
+
- lib/generators/custom_captcha/uninstall/templates/README
|
130
|
+
- lib/generators/custom_captcha/uninstall/uninstall_generator.rb
|
131
|
+
- lib/generators/custom_captcha/utils.rb
|
132
|
+
- lib/tasks/.gitkeep
|
133
|
+
- lib/tasks/custom_captcha_utils.rake
|
134
|
+
- spec/configuration_spec.rb
|
135
|
+
- spec/image_spec.rb
|
136
|
+
- spec/spec_helper.rb
|
137
|
+
homepage: https://github.com/vkill/custom_captcha
|
138
|
+
licenses: []
|
139
|
+
post_install_message:
|
140
|
+
rdoc_options: []
|
141
|
+
require_paths:
|
142
|
+
- lib
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
none: false
|
145
|
+
requirements:
|
146
|
+
- - ! '>='
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
|
+
none: false
|
151
|
+
requirements:
|
152
|
+
- - ! '>='
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
requirements: []
|
156
|
+
rubyforge_project: custom_captcha
|
157
|
+
rubygems_version: 1.8.10
|
158
|
+
signing_key:
|
159
|
+
specification_version: 3
|
160
|
+
summary: A simple and custom captcha for Rails3.
|
161
|
+
test_files:
|
162
|
+
- spec/configuration_spec.rb
|
163
|
+
- spec/image_spec.rb
|
164
|
+
- spec/spec_helper.rb
|
165
|
+
has_rdoc:
|