Oct
17
Hello Everyone !!
I have released a captcha plugin Simple Captcha. It is really simple to implement, and provides a cool feature of multiple styles of images. Visit here for more explanation.
Previous post on improving image for validates_captcha
———————————————————
Validates captcha is a good plugin to implement captcha in your rails application.
However i found that there is repetition of the string of the image and the quality of image is not that good. To get a good quality image and random string
something like this

replace the code of the file /vendor/plugins/validates_captcha/lib/captcha_challenge.rb with the following code…
require 'digest/sha1'
module AngryMidgetPluginsInc #:nodoc:
# CaptchaChallenge
class CaptchaChallenge
include CaptchaConfig
extend CaptchaConfig
DEFAULT_TTL = 1200#Lifetime in seconds. Default is 20 minutes.
attr_reader :id, :created_at
attr_accessor :ttl
def initialize(options = {}) #:nodoc:
generate_id
options = {
:ttl => config['default_ttl'] || DEFAULT_TTL
}.update(options)
self.ttl = options[:ttl]
@created_at = Time.now
self.class.prune
end
# Implement in subclasses.
def correct? #:nodoc:
raise NotImplementedError
end
private
def generate_id #:nodoc:
self.id = Digest::SHA1.hexdigest(Time.now.to_s+rand.to_s)
end
def id=(i) #:nodoc:
@id = i
end
def write_to_store #:nodoc:
store.transaction{
store[:captchas] = Array.new unless store.root?(:captchas)
store[:captchas] << self
}
end
class << self
# Removes old instances from PStore
def prune
store.transaction{
if store.root?(:captchas)
store[:captchas].each_with_index{|c,i|
if Time.now > c.created_at+c.ttl
store[:captchas].delete_at(i)
end
}
end
}
end#prune
end#class << self
end
# A CAPTCHA challenge where an image with text is
# generated. A human can read the text with relative
# ease, while most robots can not. There are accessibility
# problems with this challenge, though, as people
# with reduced or no vision are unlikely to pass the test.
class CaptchaImageChallenge < CaptchaChallenge
WORDS = ‘gorilla costume, superman, banana bender, chuck norris, xray vision, ahoy me hearties,
chunky bacon, latex, rupert murdoch, clap your hands, year 2000,
sugar coated, coca cola, rastafarian, airbus a380′.split(/,s+/)
DEFAULT_DIR = ‘captcha’#public/images/captcha
WRITE_DIR = File.join(RAILS_ROOT, ‘public’, ‘images’)
DEFAULT_FILETYPE = ‘jpg’
attr_reader :image
attr_accessor :string, :dir, :filename, :filetype
# Creates an image challenge.
def initialize(options = {})
super
options = {
:string => config['words'] ? config['words'][rand(config['words'].size)] : WORDS[rand(WORDS.size)],
:dir => config['default_dir'] || DEFAULT_DIR,
:filetype => config['default_filetype'] || DEFAULT_FILETYPE
}.update(options)
self.string = Digest::SHA1.hexdigest(Time.now.to_s)[0..4].upcase #options[:string]
self.dir = options[:dir]
self.filetype = options[:filetype]
self.filename = options[:filename] || generate_filename
write_to_store
end
# Generates the image.
def generate(options = {})
options = {
:fontsize => 50,
:padding => 20,
:color => ‘#000′,
:background => ‘#fff’,
:fontweight => ‘bolw’,
:rotate => true
}.update(options)
options[:fontweight] = case options[:fontweight]
when ‘bold’ then 700
else 400
end
#added
text = Array.new
0.upto(4) do |i|
text[i] = Magick::Draw.new
text[i].pointsize = options[:fontsize]
text[i].font_weight = 600 #options[:fontweight]
text[i].fill = ‘black’ #options[:color]
text[i].gravity = Magick::CenterGravity
text[i].rotation = (rand(2)==1 ? rand(30) : -rand(30)) if options[:rotate]
end
metric = text[2].get_type_metrics(self.string)
#add bg
canvas = Magick::ImageList.new
fill = Magick::HatchFill.new(’white’,'black’)
x = metric.width+options[:padding]
y = metric.height+options[:padding]
#ADDING NOISE
img1 = Magick::Image.new(x,y,fill)
gc = Magick::Draw.new
gc.stroke_linejoin(’round’)
gc.stroke(’black’)
gc.stroke_width(1)
100.times do |i|
x1 = rand(x)
y1 = rand(y)
gc.circle(x1,y1,x1.next,y1)
end
gc.draw(img1)
canvas << img1
0.upto(4) do |i|
canvas << Magick::Image.new(metric.width+options[:padding], metric.height+options[:padding]){
self.background_color = ‘#000F’
y_loc = rand(y)
}.annotate(text[i], 0, 0, (i-2)*30, rand(y/4), self.string[i,1]) #.wave(5, 20)
end
canvas << Magick::Image.new(metric.width+options[:padding], metric.height+options[:padding]){
#changed
p = Magick::Pixel.from_color(options[:background])
p.opacity = Magick::MaxRGB
self.background_color = p
} #.add_noise(Magick::LaplacianNoise)
self.image = canvas.flatten_images.blur_image(1)
end
# Writes image to file.
def write(dir = self.dir, filename = self.filename)
self.image.write(File.join(WRITE_DIR, dir, filename))
end
# Determine if the supplied +string+ matches
# that used when generating the image.
def correct?(string)
string.downcase == self.string.downcase
end
# The full path to the image file, relative
# to <tt>public/images</tt>.
def file_path
File.join(dir,filename)
end
class << self
# Deletes old image files. Also calls CaptchaChallenge.prune
def prune
store.transaction{
if store.root?(:captchas)
store[:captchas].each_with_index{|c,i|
if Time.now > c.created_at+c.ttl
if File.exists?(File.join(WRITE_DIR, c.file_path))
begin
File.unlink(File.join(WRITE_DIR, c.file_path))
rescue Exception
end
end
end
}
end
}
super
end#prune
end#class << self
private
def generate_filename #:nodoc:
self.id+’.'+self.filetype
end
def image=(i) #:nodoc:
@image = i
end
end
end
visit Here to view the customized use of the plugin validates_captcha in RoR.


Hello,Sur
Can you send a mail about this correct source code to me?
When i put above code to my ruby editor,it show some error.
I think it has some syntax error.Maybe it be bring by html editor.
So,if you correct source code please send a copy to me.
Thank you so much!!!
wangchangxiao@hotmail.com
Hi Jerry !!
Sorry if there is any mistake residing in the above code. I have also done some more modifications in the source code of this file to make the images more practically implementable. I will upload the new code here today itself, side by i will mail it to you also.
——————————————————————————
Hello everybody !!
Everyone who is reading this post, as jerry suggested… if you are getting any problem with the code provided here then let me know via comment(or else) and provide your email… so that i can mail you that modified file itself.
hi ,
it is a great thing and thnx a lot
can u help me for doing some changes in yr code.
1) how can i change the size of the image ie height and width?
2)how can i remove the black dots from the image that u r generating for the word?
Hi Chintan !!
Although i would recommend to use the dots coz there are some smart bots available which are based on pixel recognition and can even read the string from images if images are clean(without noise) and easy to read.
However remove this code from the code above given to remove the dots
100.times do |i|
x1 = rand(x)
y1 = rand(y)
gc.circle(x1,y1,x1.next,y1)
end
Do always remember to restart the server whenever you change the code of any plugin.
I havent explored the resizing yet…
hopefully i will tell you soon… and will upload the code also.
thnx Sur
Hi,
I still have problems with the image (i have a lot of noise and no characters) even with the modified code of the class from this page.
Could you please send me the source code at laurent.bois@gmail.com
I would like to use this too.
Could you please send me the source code at
diddek@gmail.com
Thanks in advance
Dirk
Hi Dirk !!
The modified code for the file capcha_challenge.rb is right there in the post itself. If you wanna see the usage of this plugin then see this post to implement captcha and then add the code given above, to improve the image quality !!
Dear ‘Sur Max’
I am sorry bu thte code provided above does not run ( `const_missing’: uninitialized constant CaptchaConfig (NameError))
The posts here mention the same, so I wondered if you could provide me with the working version
Can you either update the source in the post or mail me?
Dirk
Hi Dirk !
I have mailed the captcha plugin which i am using with all the modified code. Try it by just putting it into vendor/plugin directory and restarting the server… Assuming u have written all the code in model and controller correctly it would work fine.
Hi Sur Max
I am sorry if I dont get it, but what’s the difference with the official version?
The word list is the same in your version and the ‘official’ one:
WORDS = ‘gorilla costume, superman, chuck norris, xray vision, ahoy me hearties,
chunky bacon, latex, rupert murdoch, clap your hands, year 2000, disco rocks,
sugar coated, staple my ears, rastafarian, airbus a380, good old days’.split(/,\s+/)
I dont see a random text or the image like you display on top of this page.
Differences I do see is that the code is arranged differently and ‘namespaces’ have been changed (AngryMidget … instead of FleksPlugin) - what am I missing here?
Dirk
Yes u didnt get it !! lol …
The words written now are only text in the modified code and not being used anywhere, i am wandering if you are still getting the repeated words and not random string… probably u havnt restarted the server or u have not implemented it and just looking at code only …
Hi Sur,
i m in big problem, u have to help me at any how? check yr captcha code with the mongrel? it creates big problems so pls reply me soon i m waiting for reply
it gives me an error like “pid…….” and when i tried to run the method which contains the captcha code it just displays as blank page and when i go for the page source it showm me blank there also, and it also clear the clusters of the mongrel, from there mongrel will be stoped so when i tried to go some where else it gives me an error of 503…. so pls help me as soon as possible.
bye and thnx
Could you send me the code?
Got the same error as Dirk from above ( `const_missing’: uninitialized constant CaptchaConfig (NameError))
Any ideas? Seems to be some namespace problem
Regards
Stabilo