From: James Edward Gray II Date: 2005-09-26T12:04:25+09:00 Subject: Re: [SOLUTION] Math Captcha (#48) On Sep 25, 2005, at 2:55 PM, Gavin Kistner wrote: > My solution follows. My solution is below. Here are my random thoughts about it: 1. My code requires Glenn Parker's Ruby Quiz #25 solution. I did make one minor change to it, which was to wrap the quiz specific code in a: if __FILE__ == $0 # ... end That allowed me to use it as a library. 2. I did the first two extra credits, but couldn't think of good systems for the last two. 3. I'm in a "Write less code" kick this week, so I really tried to pack a lot of punch into minimal code. I'm happy with the results. Here's the code: #!/usr/local/bin/ruby -w require "erb" # Glenn Parker's code from Ruby Quiz 25... require "english_numerals" class Integer alias_method :to_en, :to_english end class Array def insert_at_nil( obj ) if i = index(nil) self[i] = obj i else self << obj size - 1 end end end module MathCaptcha @@captchas = Array.new @@answers = Array.new def self.add_captcha( template, &validator ) @@captchas << Array[template, validator] end def self.create_question raise "No captchas loaded." if @@captchas.empty? captcha = @@captchas[rand(@@captchas.size)] args = Array.new class << args def arg( value ) push(value) value end def resolve( template ) ERB.new(template).result(binding) end end question = args.resolve(captcha.first) index = @@answers.insert_at_nil(Array[captcha.first, *args]) Hash[:question => question, :answer_id => index] end def self.check_answer( answer ) raise "Answer id required." unless answer.include? :answer_id template, *args = @@answers[answer[:answer_id]] raise "Answer not found." if template.nil? validator = @@captchas.assoc(template).last raise "Unable to match captcha." if validator.nil? if validator[answer[:answer], *args] @@answers[answer[:answer_id]] = nil true else false end end def self.load_answers( file ) @@answers = File.open(file) { |answers| Marshal.load(answers) } end def self.load_captchas( file ) code = File.read(file) eval(code, binding) end def self.save_answers( file ) File.open(file, "w") { |answers| Marshal.dump(@@answers, answers) } end end if __FILE__ == $0 captchas = File.join(ENV["HOME"], ".math_captchas") unless File.exists? captchas File.open(captchas, "w") { |file| file << DATA.read } end MathCaptcha.load_captchas(captchas) answers = File.join(ENV["HOME"], ".math_captcha_answers") MathCaptcha.load_answers(answers) if File.exists? answers if ARGV.empty? question = MathCaptcha.create_question puts "#{question[:answer_id]} : #{question[:question]}" else args = Hash.new while ARGV.size >= 2 and ARGV.first =~ /^--\w+$/ key = ARGV.shift[2..-1].to_sym value = ARGV.first =~ /^\d+$/ ? ARGV.shift.to_i : ARGV.shift args[key] = value end answer = MathCaptcha.check_answer(args) puts answer end END { MathCaptcha.save_answers(answers) } end __END__ add_captcha( "<%= arg(rand(10)).to_en.capitalize %> plus <%= arg(2).to_en %>?" ) do |answer, *opers| if answer.is_a?(String) and answer =~ /^\d+$/ answer = answer.to_i.to_en elsif answer.is_a?(Integer) answer = answer.to_en end answer == opers.inject { |sum, var| sum + var }.to_en end __END__ James Edward Gray II