require 'digest/md5' libdir = File.expand_path(__FILE__).gsub(%r/\.rb$/, '') + File::SEPARATOR begin require 'rubygems' rescue LoadError end begin require 'text/figlet' rescue LoadError require libdir + 'text/figlet' end begin require 'pervasives' rescue LoadError require libdir + 'pervasives' end begin require 'attributes' rescue LoadError require libdir + 'attributes' end class Flatulent singleton_class = class << self self end singleton_class.module_eval do attribute('libdir'){ File.expand_path(__FILE__).gsub(%r/\.rb$/, '') } attribute('fontdir'){ File.join libdir, 'fontfiles' } attribute('style'){ Hash[ 'white-space' => 'pre,nowrap', 'font-family' => 'monospace', 'font-weight' => 'bold', 'display' => 'table', 'padding' => '11px', 'background' => '#ffc', 'color' => 'blue', ] } attribute('noise_style'){ Hash[ 'color' => '#ccc', 'font-style' => 'oblique', ] } end singleton_class.attributes.each{|a| attribute(a){ self.class.send a}} attr 'string' def initialize keywords = {} opt = getopts keywords @size = opt[ 'size', 4 ] @string = opt[ 'string', generate_random_string ] @font = opt[ 'font', 'big' ] @noise = opt[ 'noise', 0.04 ] @no_table = opt[ 'no_table', false ] @id = opt[ 'id', 'flatulent' ] @action = opt[ 'action' ] @ttl = Integer opt[ 'ttl', 5 * 60 ] # seconds! figlet! element! form_tags! form! end def generate_random_string chars = ('A' .. 'Z').to_a + ('1' .. '9').to_a ### zero is too much like o/O Array.new(@size).map{ chars[rand(chars.size - 1)]}.join #.join(' ') end def getopts options self.class.getopts options end def figlet! spaced = @string.split(%r//).join(' ') fontfile = File.join fontdir, "#{ @font }.flf" font = Text::Figlet::Font.new fontfile typesetter = Text::Figlet::Typesetter.new font @figlet = typesetter[spaced] end attr 'figlet' def element! rows = [] rows << (row = []) chars = @figlet.split %r// size = chars.size last = size - 1 chars.each_with_index do |char, idx| content = case char when %r/\n/o "
" when %r/\s/o " " when %r/([^\s])/o $1 end row << content rows << (row = []) unless idx == last end (@noise * chars.size).ceil.times do y = rand(rows.size - 1) x = rand(rows.first.size - 1) next if rows[y][x] == '
' printable = rand(126 - 33) + 33 + 1 rows[y][x] = "#{ printable.chr }" end content = rows.join @element = "
#{ content }
" end attr 'element' def css style.map{|kv| kv.join ':'}.join ';' end def noise_css noise_style.map{|kv| kv.join ':'}.join ';' end def form_tags! n = @string.scan(%r/\w/).size @form_tags = <<-html #{ element }

Please enter the #{ n } large characters shown.

html end attr 'form_tags' def md5 Digest::MD5.hexdigest munge(@string) end def munge string self.class.munge string end def self.munge string string.downcase.strip.gsub(%r/[0Oo]/, '0') ### note that 0 (zero) looks like O and o end def valid_until Time.now.to_i + @ttl end def form! action = "action='#{ @action }'" @form = <<-html
#{ form_tags }
html end attr 'form' alias_method 'to_html', 'form' def to_html @element end def to_s form end class MD5Error < ::StandardError; end class TimeToLiveError < ::StandardError; end TTLError = TimeToLiveError def self.valid? keywords = {} begin validate! keywords true rescue MD5Error, TTLError false end end def self.validate! keywords = {} keywords = keywords['flatulent'] if keywords.has_key?('flatulent') keywords = keywords[:flatulent] if keywords.has_key?(:flatulent) opts = getopts keywords textarea = opts['t'] md5 = opts['m'] timestamp = Integer opts['v'] raise MD5Error unless Digest::MD5.hexdigest(munge(textarea)) == md5 raise TimeToLiveError unless Time.now.utc <= Time.at(timestamp).utc true end def self.getopts options lambda do |key, *default| default = default.first break options[key] if options.has_key?(key) key = key.to_s break options[key] if options.has_key?(key) key = key.to_sym break options[key] if options.has_key?(key) break default end end def self.form options = {} new(options).form end def self.element options = {} new(options).element end def self.form_tags options = {} new(options).form_tags end def self.figlet options = {} new(options).figlet end end def Flatulent(*a, &b) Flatulent.new(*a, &b) end