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 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