flatulent 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/README +60 -16
  2. data/flatulent-0.0.2.gem +0 -0
  3. data/lib/flatulent/crypt/blowfish-tables.rb +190 -0
  4. data/lib/flatulent/crypt/blowfish.rb +109 -0
  5. data/lib/flatulent/crypt/cbc.rb +123 -0
  6. data/lib/flatulent/crypt/gost.rb +140 -0
  7. data/lib/flatulent/crypt/idea.rb +193 -0
  8. data/lib/flatulent/crypt/noise.rb +94 -0
  9. data/lib/flatulent/crypt/purerubystringio.rb +378 -0
  10. data/lib/flatulent/crypt/rijndael-tables.rb +117 -0
  11. data/lib/flatulent/crypt/rijndael.rb +269 -0
  12. data/lib/flatulent/crypt/stringxor.rb +27 -0
  13. data/lib/flatulent.rb +332 -121
  14. data/lib/flatulent.rb.bak +337 -0
  15. data/rails/app/controllers/flatulent_controller.rb +61 -6
  16. data/rails/lib/flatulent/attributes.rb +79 -0
  17. data/rails/lib/flatulent/crypt/blowfish-tables.rb +190 -0
  18. data/rails/lib/flatulent/crypt/blowfish.rb +109 -0
  19. data/rails/lib/flatulent/crypt/cbc.rb +123 -0
  20. data/rails/lib/flatulent/crypt/gost.rb +140 -0
  21. data/rails/lib/flatulent/crypt/idea.rb +193 -0
  22. data/rails/lib/flatulent/crypt/noise.rb +94 -0
  23. data/rails/lib/flatulent/crypt/purerubystringio.rb +378 -0
  24. data/rails/lib/flatulent/crypt/rijndael-tables.rb +117 -0
  25. data/rails/lib/flatulent/crypt/rijndael.rb +269 -0
  26. data/rails/lib/flatulent/fontfiles/banner.flf +2494 -0
  27. data/rails/lib/flatulent/fontfiles/big.flf +2204 -0
  28. data/rails/lib/flatulent/fontfiles/block.flf +1691 -0
  29. data/rails/lib/flatulent/fontfiles/bubble.flf +1630 -0
  30. data/rails/lib/flatulent/fontfiles/digital.flf +1286 -0
  31. data/rails/lib/flatulent/fontfiles/ivrit.flf +900 -0
  32. data/rails/lib/flatulent/fontfiles/lean.flf +1691 -0
  33. data/rails/lib/flatulent/fontfiles/mini.flf +899 -0
  34. data/rails/lib/flatulent/fontfiles/mnemonic.flf +3702 -0
  35. data/rails/lib/flatulent/fontfiles/script.flf +1493 -0
  36. data/rails/lib/flatulent/fontfiles/shadow.flf +1097 -0
  37. data/rails/lib/flatulent/fontfiles/slant.flf +1295 -0
  38. data/rails/lib/flatulent/fontfiles/small.flf +1097 -0
  39. data/rails/lib/flatulent/fontfiles/smscript.flf +1097 -0
  40. data/rails/lib/flatulent/fontfiles/smshadow.flf +899 -0
  41. data/rails/lib/flatulent/fontfiles/smslant.flf +1097 -0
  42. data/rails/lib/flatulent/fontfiles/standard.flf +2227 -0
  43. data/rails/lib/flatulent/fontfiles/term.flf +600 -0
  44. data/rails/lib/flatulent/pervasives.rb +32 -0
  45. data/rails/lib/flatulent/stringxor.rb +27 -0
  46. data/rails/lib/flatulent/text/double_metaphone.rb +356 -0
  47. data/rails/lib/flatulent/text/figlet/font.rb +117 -0
  48. data/rails/lib/flatulent/text/figlet/smusher.rb +64 -0
  49. data/rails/lib/flatulent/text/figlet/typesetter.rb +68 -0
  50. data/rails/lib/flatulent/text/figlet.rb +17 -0
  51. data/rails/lib/flatulent/text/levenshtein.rb +65 -0
  52. data/rails/lib/flatulent/text/metaphone.rb +97 -0
  53. data/rails/lib/flatulent/text/porter_stemming.rb +171 -0
  54. data/rails/lib/flatulent/text/soundex.rb +61 -0
  55. data/rails/lib/flatulent/text.rb +6 -0
  56. data/rails/lib/flatulent.rb +450 -0
  57. data/rails/log/development.log +14297 -0
  58. data/rails/log/fastcgi.crash.log +111 -0
  59. data/rails/log/lighttpd.access.log +3993 -0
  60. data/rails/log/lighttpd.error.log +111 -0
  61. data/rails/tmp/cache/javascripts/prototype.js-gzip-3275912-71260-1183440172 +0 -0
  62. data/rails/tmp/sessions/ruby_sess.32d68bc997054475 +0 -0
  63. data/rails/tmp/sessions/ruby_sess.4694a4b9bdf9bcf4 +0 -0
  64. data/rails/tmp/sessions/ruby_sess.99469fde69043a05 +0 -0
  65. data/rails/tmp/sessions/ruby_sess.a588c0a457345912 +0 -0
  66. data/rails/tmp/sessions/ruby_sess.b3344125a84a3efa +0 -0
  67. data/samples.rb +10 -0
  68. metadata +69 -3
  69. data/flatulent-0.0.0.gem +0 -0
data/lib/flatulent.rb CHANGED
@@ -1,72 +1,261 @@
1
- require 'digest/md5'
1
+ # TODO - encrypt time
2
+ # TODO - noise is image chars
3
+ # TODO - newline in value??
4
+ # TODO - vertical offset in chars
2
5
 
3
- libdir = File.expand_path(__FILE__).gsub(%r/\.rb$/, '') + File::SEPARATOR
6
+ class Flatulent
7
+ Flatulent::VERSION = '0.0.2' unless defined? Flatulent::VERSION
8
+ def self.flatulent() Flatulent::VERSION end
9
+ def self.libdir() File.expand_path(__FILE__).gsub(%r/\.rb$/, '') end
4
10
 
5
- begin
6
- require 'rubygems'
7
- rescue LoadError
8
- end
11
+ require 'cgi'
12
+ require 'base64'
13
+ require 'digest/md5'
9
14
 
10
- begin
11
- require 'text/figlet'
12
- rescue LoadError
13
- require libdir + 'text/figlet'
14
- end
15
+ begin
16
+ require 'rubygems'
17
+ rescue LoadError
18
+ end
15
19
 
16
- begin
17
- require 'pervasives'
18
- rescue LoadError
19
- require libdir + 'pervasives'
20
- end
20
+ begin
21
+ $:.unshift libdir
22
+ require 'text/figlet'
23
+ require 'crypt/blowfish'
24
+ require 'pervasives'
25
+ require 'attributes'
26
+ ensure
27
+ $:.shift
28
+ end
29
+
30
+ class Error < ::StandardError; end
31
+ class EncryptionError < Error; end
32
+ class TimeBombError < Error; end
21
33
 
22
- begin
23
- require 'attributes'
24
- rescue LoadError
25
- require libdir + 'attributes'
26
- end
27
34
 
28
- class Flatulent
29
35
  singleton_class =
30
36
  class << self
31
37
  self
32
38
  end
33
39
 
34
40
  singleton_class.module_eval do
35
- attribute('libdir'){ File.expand_path(__FILE__).gsub(%r/\.rb$/, '') }
36
41
 
37
42
  attribute('fontdir'){ File.join libdir, 'fontfiles' }
38
43
 
39
44
  attribute('style'){ Hash[
40
- 'white-space' => 'pre,nowrap',
45
+ 'white-space' => 'pre',
41
46
  'font-family' => 'monospace',
42
47
  'font-weight' => 'bold',
43
- 'display' => 'table',
44
- 'padding' => '11px',
48
+ 'font-size' => 'medium',
45
49
  'background' => '#ffc',
46
- 'color' => 'blue',
50
+ 'color' => '#00f',
51
+ 'margin' => '2px',
52
+ 'padding' => '2px',
53
+ 'display' => 'table',
47
54
  ] }
48
55
 
49
56
  attribute('noise_style'){ Hash[
50
57
  'color' => '#ccc',
51
- 'font-style' => 'oblique',
52
58
  ] }
59
+
60
+ attribute('key'){ default_key }
61
+
62
+ def valid? keywords = {}
63
+ begin
64
+ validate! keywords
65
+ true
66
+ rescue EncryptionError, TimeBombError
67
+ false
68
+ end
69
+ end
70
+
71
+ def validate! keywords = {}
72
+ keywords = keywords['flatulent'] if keywords.has_key?('flatulent')
73
+ keywords = keywords[:flatulent] if keywords.has_key?(:flatulent)
74
+
75
+ opts = getopts keywords
76
+
77
+ captcha = opts['c'] or raise 'no captcha'
78
+ string = opts['s'] or raise 'no string'
79
+ time = opts['t'] or raise 'no time'
80
+
81
+ expected = zeroh(decrypt(string))
82
+ actual = zeroh(captcha)
83
+ raise EncryptionError, "expected #{ expected } got #{ actual }" unless
84
+ expected == actual
85
+
86
+ timebomb = Time.at(Integer(decrypt(time))).utc rescue(raise("bad time #{ time }"))
87
+ raise TimeBombError unless Time.now.utc <= timebomb
88
+
89
+ return actual
90
+ end
91
+
92
+ def zeroh string
93
+ string.gsub(%r/[0Oo]/, '0') # ignore diffs between 0/o/O
94
+ end
95
+
96
+ def blowfish
97
+ @blowfish ||= Hash.new{|h,k| h[k] = Crypt::Blowfish.new(key)}
98
+ end
99
+
100
+ def munge string
101
+ string.strip.downcase
102
+ end
103
+
104
+ def encrypt string
105
+ Base64.encode64(blowfish[key].encrypt_string(string.to_s)).chop # kill "\n"
106
+ end
107
+
108
+ def decrypt string
109
+ munge(blowfish[key].decrypt_string(Base64.decode64("#{ string }\n")))
110
+ end
111
+
112
+ def getopts options
113
+ lambda do |key, *default|
114
+ default = default.first
115
+ break options[key] if options.has_key?(key)
116
+ key = key.to_s
117
+ break options[key] if options.has_key?(key)
118
+ key = key.to_sym
119
+ break options[key] if options.has_key?(key)
120
+ break default
121
+ end
122
+ end
123
+
124
+ def default_key
125
+ #attribute('default_key') do
126
+ return @default_key if defined? @default_key
127
+ require 'socket'
128
+ hostname = Socket.gethostname
129
+ maddr = mac_address rescue nil
130
+ warn "could not determine mac addresss!" unless maddr
131
+ #puts(( Digest::MD5.hexdigest "--#{ hostname }--#{ maddr }--" ))
132
+ #Digest::MD5.hexdigest "--#{ hostname }--#{ maddr }--"
133
+ #@default_key = "--#{ hostname }--#{ maddr }--"
134
+ @default_key = "--#{ hostname }--#{ maddr }--"
135
+ end
136
+
137
+ def mac_address
138
+ return @mac_address if defined? @mac_address
139
+ re = %r/[^:\-](?:[0-9A-F][0-9A-F][:\-]){5}[0-9A-F][0-9A-F][^:\-]/io
140
+ cmds = '/sbin/ifconfig', '/bin/ifconfig', 'ifconfig', 'ipconfig /all'
141
+
142
+ null = test(?e, '/dev/null') ? '/dev/null' : 'NUL'
143
+
144
+ lines = nil
145
+ cmds.each do |cmd|
146
+ stdout = IO.popen("#{ cmd } 2> #{ null }"){|fd| fd.readlines} rescue next
147
+ next unless stdout and stdout.size > 0
148
+ lines = stdout and break
149
+ end
150
+ raise "all of #{ cmds.join ' ' } failed" unless lines
151
+
152
+ candidates = lines.select{|line| line =~ re}
153
+ raise 'no mac address candidates' unless candidates.first
154
+ candidates.map!{|c| c[re]}
155
+
156
+ maddr = candidates.first
157
+ raise 'no mac address found' unless maddr
158
+
159
+ maddr.strip!
160
+ maddr.instance_eval{ @list = candidates; def list() @list end }
161
+
162
+ @mac_address = maddr
163
+ end
164
+
165
+ def figlet options = {}
166
+ new(options).figlet
167
+ end
168
+ def figlets options = {}
169
+ new(options).figlets
170
+ end
171
+ def element options = {}
172
+ new(options).element
173
+ end
174
+ def form_tags options = {}
175
+ new(options).form_tags
176
+ end
177
+ def form options = {}
178
+ new(options).form
179
+ end
180
+
181
+ def latest_prototype_lib
182
+ 'http://www.prototypejs.org/assets/2007/6/20/prototype.js'
183
+ end
184
+ def require_prototype
185
+ %Q`
186
+ <script type="text/javascript">
187
+ var prototype = true;
188
+ try{ Prototype; } catch(e) { prototype = false };
189
+ if(!prototype){
190
+ var js = document.createElement('script');
191
+ js.type='text/javascript';
192
+ js.src='/javascripts/prototype.js';
193
+ document.getElementsByTagName('head')[0].appendChild(js);
194
+ }
195
+ </script>
196
+ <script type="text/javascript">
197
+ var prototype = true;
198
+ try{ Prototype; } catch(e) { prototype = false };
199
+ if(!prototype){
200
+ var js = document.createElement('script');
201
+ js.type='text/javascript';
202
+ js.src='#{ latest_prototype_lib }';
203
+ document.getElementsByTagName('head')[0].appendChild(js);
204
+ }
205
+ </script>
206
+ `
207
+ end
208
+ def javascript options = {}
209
+ id = options[:id] || options['id'] || 'flatulent'
210
+ url = options[:url] || options['url'] || '/flatulent/captcha'
211
+ %Q`
212
+ #{ require_prototype }
213
+ <script type="text/javascript">
214
+ new Ajax.Updater('#{ id }', '#{ url }', { method: 'get' });
215
+ </script>
216
+ `
217
+ end
218
+ def ajax options = {}
219
+ id = options[:id] || options['id'] || 'flatulent'
220
+ %Q`
221
+ <div id="#{ id }"></div>
222
+ #{ Flatulent.javascript }
223
+ `
224
+ end
53
225
  end
54
226
 
55
227
  singleton_class.attributes.each{|a| attribute(a){ self.class.send a}}
56
228
 
57
- attr 'string'
58
-
59
- def initialize keywords = {}
60
- opt = getopts keywords
229
+ attribute 'string'
230
+ attribute 'size'
231
+ attribute 'font'
232
+ attribute 'noise'
233
+ attribute 'id'
234
+ attribute 'action'
235
+ attribute 'ttl'
236
+
237
+ attribute 'figlet'
238
+ attribute 'figlets'
239
+ attribute 'element'
240
+ attribute 'form_tags'
241
+ attribute 'form'
242
+
243
+ def initialize arg = {}
244
+ if Hash === arg
245
+ opt = getopts arg
246
+ @size = Integer opt[ 'size', 4 ]
247
+ @string = String opt[ 'string', generate_random_string ]
248
+ else
249
+ opt = getopts Hash.new
250
+ @string = String arg
251
+ @size = @string.size
252
+ end
61
253
 
62
- @size = opt[ 'size', 4 ]
63
- @string = opt[ 'string', generate_random_string ]
64
- @font = opt[ 'font', 'big' ]
65
- @noise = opt[ 'noise', 0.04 ]
66
- @no_table = opt[ 'no_table', false ]
67
- @id = opt[ 'id', 'flatulent' ]
68
- @action = opt[ 'action' ]
69
- @ttl = Integer opt[ 'ttl', 5 * 60 ] # seconds!
254
+ @font = String opt[ 'font', 'big' ]
255
+ @noise = Float opt[ 'noise', 0.04 ]
256
+ @id = String opt[ 'id', 'flatulent' ]
257
+ @action = String opt[ 'action' ]
258
+ @ttl = Integer opt[ 'ttl', 256 ]
70
259
 
71
260
  figlet!
72
261
  element!
@@ -76,21 +265,19 @@ class Flatulent
76
265
 
77
266
  def generate_random_string
78
267
  chars = ('A' .. 'Z').to_a + ('1' .. '9').to_a ### zero is too much like o/O
79
- Array.new(@size).map{ chars[rand(chars.size - 1)]}.join #.join(' ')
80
- end
81
-
82
- def getopts options
83
- self.class.getopts options
268
+ Array.new(@size).map{ chars[rand(chars.size - 1)]}.join
84
269
  end
85
270
 
86
271
  def figlet!
87
- spaced = @string.split(%r//).join(' ')
272
+ spaced = @string.split(%r//).join #.join(' ')
88
273
  fontfile = File.join fontdir, "#{ @font }.flf"
89
274
  font = Text::Figlet::Font.new fontfile
90
275
  typesetter = Text::Figlet::Typesetter.new font
276
+ @figlets = []
277
+ chars = spaced.split %r//
278
+ chars.each{|char| @figlets << typesetter[char]}
91
279
  @figlet = typesetter[spaced]
92
280
  end
93
- attr 'figlet'
94
281
 
95
282
  def element!
96
283
  rows = []
@@ -105,61 +292,125 @@ class Flatulent
105
292
  when %r/\n/o
106
293
  "<br>"
107
294
  when %r/\s/o
295
+ #rand > 0.42 ? "&nbsp;" : " "
108
296
  "&nbsp;"
109
297
  when %r/([^\s])/o
110
- $1
298
+ CGI.escapeHTML $1
111
299
  end
300
+ Array.new(rand(42)){ content = "<span>#{ content }</span>"}
112
301
  row << content
113
302
  rows << (row = []) unless idx == last
114
303
  end
115
304
 
305
+ noisy = %W` | / - _ ( ) \ `
116
306
  (@noise * chars.size).ceil.times do
117
307
  y = rand(rows.size - 1)
118
308
  x = rand(rows.first.size - 1)
119
- next if rows[y][x] == '<br>'
120
- printable = rand(126 - 33) + 33 + 1
121
- rows[y][x] = "<span 'style=#{ noise_css }'>#{ printable.chr }</span>"
309
+ next if rows[y][x] =~ %r"br"
310
+ char = noisy[ rand(noisy.size) ]
311
+ rows[y][x] = char
122
312
  end
123
313
 
124
314
  content = rows.join
125
315
  @element = "<pre id='#{ @id }_element' style='#{ css }'>#{ content }</pre>"
126
316
  end
127
- attr 'element'
317
+
318
+ =begin
319
+
320
+ def element!
321
+ cells = []
322
+
323
+ @figlets.each do |figlet|
324
+ rows = []
325
+ rows << (row = [])
326
+
327
+ offset_t = Array.new(rand(4)).map{ "\n"}
328
+ offset_b = Array.new(rand(4)).map{ "\n"}
329
+
330
+ offset_l = Array.new(rand(4)).map{ " "}
331
+ offset_r = Array.new(rand(4)).map{ " "}
332
+
333
+ chars = offset_t + figlet.split(%r//) + offset_b
334
+ size = chars.size
335
+ last = size - 1
336
+ #drawn = chars.select{|char| char !~ %r/\s/}
337
+ drawn = %w` | / \ &lt; &gt; v ^ - _ ( ) `
338
+
339
+ chars.each_with_index do |char, idx|
340
+ content =
341
+ case char
342
+ when %r/\n/o
343
+ "<br>"
344
+ when %r/\s/o
345
+ #rand > 0.42 ? "&nbsp;" : " "
346
+ "&nbsp;"
347
+ when %r/([^\s])/o
348
+ CGI.escapeHTML $1
349
+ end
350
+ #rand(10).times{ content = "<span>#{ content }</span>" }
351
+ row << content
352
+ rows << (row = []) unless idx == last
353
+ end
354
+
355
+ noisy = %w` | / \ - _ ( ) `
356
+ (@noise * chars.size).ceil.times do
357
+ y = rand(rows.size - 1)
358
+ x = rand(rows.first.size - 1)
359
+ next if rows[y][x] == "<br>"
360
+ char = noisy[ rand(noisy.size) ]
361
+ rows[y][x] = char
362
+ end
363
+
364
+ content = rows.join
365
+ cells << content
366
+ end
367
+
368
+ formatted = lambda{|x| "<pre class='#{ @id }_figlet' style='#{ css }'>#{ x }</pre>"}
369
+
370
+ @element =
371
+ "<table id='#{ @id }_element' border='0' cellpadding='0' cellspacing='0' bgcolor='#{ style["background"] }'><tr>" <<
372
+ cells.map{|cell| "<td>#{ formatted[cell] }</td>"}.join <<
373
+ "</tr></table>"
374
+ end
375
+ =end
128
376
 
129
377
  def css
130
- style.map{|kv| kv.join ':'}.join ';'
378
+ css_for style
131
379
  end
132
380
  def noise_css
133
- noise_style.map{|kv| kv.join ':'}.join ';'
381
+ css_for noise_style
382
+ end
383
+
384
+ def css_for hash
385
+ hash.map{|kv| kv.join ':' }.join ';'
134
386
  end
135
387
 
136
388
  def form_tags!
137
389
  n = @string.scan(%r/\w/).size
390
+ string = @string
391
+ timebomb = Time.now.utc.to_i + @ttl
138
392
  @form_tags = <<-html
139
393
  #{ element }
140
394
  <p id='#{ @id }_instructions'>
141
- Please enter the #{ n } large characters shown.
395
+ Please enter the #{ n } large characters (A-Z, 1-9) shown.
142
396
  </p>
143
- <input type='textarea' name='#{ @id }[t]' id='#{ @id }_textarea' />
144
- <input type='hidden' name='#{ @id }[m]' id='#{ @id }_m' value='#{ md5 }' />
145
- <input type='hidden' name='#{ @id }[v]' id='#{ @id }_v' value='#{ valid_until }' />
397
+ <input type='textarea' name='#{ @id }[c]' id='#{ @id }_textarea' />
398
+ <input type='hidden' name='#{ @id }[s]' id='#{ @id }_e' value='#{ encrypt string }' />
399
+ <input type='hidden' name='#{ @id }[t]' id='#{ @id }_v' value='#{ encrypt timebomb }' />
146
400
  html
147
401
  end
148
- attr 'form_tags'
402
+ alias_method 'to_html', 'form_tags'
149
403
 
150
- def md5
151
- Digest::MD5.hexdigest munge(@string)
404
+ def encrypt string
405
+ self.class.encrypt string.to_s
152
406
  end
153
407
 
154
- def munge string
155
- self.class.munge string
156
- end
157
- def self.munge string
158
- string.downcase.strip.gsub(%r/[0Oo]/, '0') ### note that 0 (zero) looks like O and o
408
+ def encrypted
409
+ self.class.encrypt @string
159
410
  end
160
411
 
161
- def valid_until
162
- Time.now.to_i + @ttl
412
+ def munge string
413
+ self.class.munge string
163
414
  end
164
415
 
165
416
  def form!
@@ -171,69 +422,29 @@ class Flatulent
171
422
  </form>
172
423
  html
173
424
  end
174
- attr 'form'
175
- alias_method 'to_html', 'form'
176
425
 
177
426
  def to_html
178
- @element
427
+ element
179
428
  end
180
429
 
181
430
  def to_s
182
431
  form
183
432
  end
184
433
 
185
- class MD5Error < ::StandardError; end
186
- class TimeToLiveError < ::StandardError; end
187
- TTLError = TimeToLiveError
188
-
189
- def self.valid? keywords = {}
190
- begin
191
- validate! keywords
192
- true
193
- rescue MD5Error, TTLError
194
- false
195
- end
434
+ def getopts options
435
+ self.class.getopts options
196
436
  end
437
+ end
197
438
 
198
- def self.validate! keywords = {}
199
- keywords = keywords['flatulent'] if keywords.has_key?('flatulent')
200
- keywords = keywords[:flatulent] if keywords.has_key?(:flatulent)
201
-
202
- opts = getopts keywords
203
-
204
- textarea = opts['t']
205
- md5 = opts['m']
206
- timestamp = Integer opts['v']
207
-
208
- raise MD5Error unless Digest::MD5.hexdigest(munge(textarea)) == md5
209
- raise TimeToLiveError unless Time.now.utc <= Time.at(timestamp).utc
210
- true
211
- end
439
+ def Flatulent(*a, &b) Flatulent.new(*a, &b) end
212
440
 
213
- def self.getopts options
214
- lambda do |key, *default|
215
- default = default.first
216
- break options[key] if options.has_key?(key)
217
- key = key.to_s
218
- break options[key] if options.has_key?(key)
219
- key = key.to_sym
220
- break options[key] if options.has_key?(key)
221
- break default
222
- end
223
- end
441
+ if $0 == __FILE__
442
+ #string = rand.to_s
443
+ #puts Flatulent(string)
444
+ #Flatulent.validate! :t => string, :e => Flatulent.encrypt(string), :v => (Time.now.utc.to_i + 60)
445
+ #e = Flatulent.encrypt('foobar')
446
+ #p e
447
+ #p Flatulent.decrypt(e)
224
448
 
225
- def self.form options = {}
226
- new(options).form
227
- end
228
- def self.element options = {}
229
- new(options).element
230
- end
231
- def self.form_tags options = {}
232
- new(options).form_tags
233
- end
234
- def self.figlet options = {}
235
- new(options).figlet
236
- end
449
+ puts Flatulent.figlet('foobar')
237
450
  end
238
-
239
- def Flatulent(*a, &b) Flatulent.new(*a, &b) end