flatulent 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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