gopher2000 0.1.0
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.
- data/.gitignore +4 -0
- data/.rvmrc +1 -0
- data/Gemfile +27 -0
- data/LICENSE.txt +14 -0
- data/README.markdown +344 -0
- data/Rakefile +38 -0
- data/bin/gopher2000 +51 -0
- data/examples/default_route.rb +22 -0
- data/examples/nyan.rb +62 -0
- data/examples/simple.rb +147 -0
- data/examples/twitter.rb +61 -0
- data/examples/weather.rb +69 -0
- data/gopher2000.gemspec +35 -0
- data/lib/gopher2000/base.rb +552 -0
- data/lib/gopher2000/dispatcher.rb +81 -0
- data/lib/gopher2000/dsl.rb +128 -0
- data/lib/gopher2000/errors.rb +14 -0
- data/lib/gopher2000/handlers/base_handler.rb +18 -0
- data/lib/gopher2000/handlers/directory_handler.rb +125 -0
- data/lib/gopher2000/rendering/abstract_renderer.rb +10 -0
- data/lib/gopher2000/rendering/base.rb +174 -0
- data/lib/gopher2000/rendering/menu.rb +129 -0
- data/lib/gopher2000/rendering/text.rb +10 -0
- data/lib/gopher2000/request.rb +21 -0
- data/lib/gopher2000/response.rb +25 -0
- data/lib/gopher2000/server.rb +85 -0
- data/lib/gopher2000/version.rb +4 -0
- data/lib/gopher2000.rb +33 -0
- data/scripts/god.rb +8 -0
- data/spec/application_spec.rb +54 -0
- data/spec/dispatching_spec.rb +144 -0
- data/spec/dsl_spec.rb +116 -0
- data/spec/gopher_spec.rb +1 -0
- data/spec/handlers/directory_handler_spec.rb +116 -0
- data/spec/helpers_spec.rb +16 -0
- data/spec/rendering/base_spec.rb +59 -0
- data/spec/rendering/menu_spec.rb +109 -0
- data/spec/rendering_spec.rb +84 -0
- data/spec/request_spec.rb +30 -0
- data/spec/response_spec.rb +33 -0
- data/spec/routing_spec.rb +92 -0
- data/spec/sandbox/old/socks.txt +0 -0
- data/spec/sandbox/socks.txt +0 -0
- data/spec/server_spec.rb +127 -0
- data/spec/spec_helper.rb +52 -0
- data/specs.watchr +60 -0
- metadata +211 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.2-head@gopherpedia
|
data/Gemfile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in gopher.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem "rake"
|
7
|
+
gem "logging"
|
8
|
+
|
9
|
+
# Add dependencies to develop your gem here.
|
10
|
+
# Include everything needed to run rake, tests, features, etc.
|
11
|
+
group :development do
|
12
|
+
gem 'simplecov', :require => false, :group => :test
|
13
|
+
|
14
|
+
gem "shoulda", ">= 0"
|
15
|
+
gem "rspec"
|
16
|
+
|
17
|
+
gem "bundler", "~> 1.0.0"
|
18
|
+
gem "watchr"
|
19
|
+
|
20
|
+
# There's a god example script stashed away in the repo
|
21
|
+
gem "god"
|
22
|
+
|
23
|
+
#
|
24
|
+
# gems used in examples and for development.
|
25
|
+
#
|
26
|
+
gem "weather-underground"
|
27
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
2
|
+
Version 2, December 2004
|
3
|
+
|
4
|
+
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
5
|
+
|
6
|
+
Everyone is permitted to copy and distribute verbatim or modified
|
7
|
+
copies of this license document, and changing it is allowed as long
|
8
|
+
as the name is changed.
|
9
|
+
|
10
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
11
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
12
|
+
|
13
|
+
0. You just DO WHAT THE FUCK YOU WANT TO.
|
14
|
+
|
data/README.markdown
ADDED
@@ -0,0 +1,344 @@
|
|
1
|
+
It's...
|
2
|
+
|
3
|
+
_____ _ _____ _____ _____ _____
|
4
|
+
| __ \ | | / __ \| _ || _ || _ |
|
5
|
+
| | \/ ___ _ __ | |__ ___ _ __ `' / /'| |/' || |/' || |/' |
|
6
|
+
| | __ / _ \| '_ \| '_ \ / _ \ '__| / / | /| || /| || /| |
|
7
|
+
| |_\ \ (_) | |_) | | | | __/ | ./ /___\ |_/ /\ |_/ /\ |_/ /
|
8
|
+
\____/\___/| .__/|_| |_|\___|_| \_____/ \___/ \___/ \___/
|
9
|
+
| |
|
10
|
+
|_|
|
11
|
+
|
12
|
+
|
13
|
+
Gopher2000 - A Gopher server for the next millenium
|
14
|
+
===================================================
|
15
|
+
|
16
|
+
Gopher2000 is a ruby-based Gopher server. It is built for speedy, enjoyable development of
|
17
|
+
all sorts of gopher sites.
|
18
|
+
|
19
|
+
Features
|
20
|
+
--------
|
21
|
+
* Simple, Sintra-inspired routing DSL.
|
22
|
+
* Dynamic requests via named parameters on request paths.
|
23
|
+
* built on Event Machine.
|
24
|
+
* Easy to mount directories and serve up files.
|
25
|
+
* built in logging and stats.
|
26
|
+
* Runs on Ruby 1.9.2 with all the modern conveniences.
|
27
|
+
|
28
|
+
Requirements
|
29
|
+
------------
|
30
|
+
* Ruby 1.9.2 or greater
|
31
|
+
* Nerves of steel
|
32
|
+
|
33
|
+
Examples
|
34
|
+
--------
|
35
|
+
|
36
|
+
Writing a functional Gopher app is as simple as:
|
37
|
+
|
38
|
+
```rb
|
39
|
+
route '/simple' do
|
40
|
+
"hi" # You can output any text you want here
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
|
45
|
+
Or, if you want to provide more interactivity, you can do something like:
|
46
|
+
|
47
|
+
```rb
|
48
|
+
route '/' do
|
49
|
+
render :index
|
50
|
+
end
|
51
|
+
|
52
|
+
menu :index do
|
53
|
+
# output a text entry in the menu
|
54
|
+
text 'simple gopher example'
|
55
|
+
|
56
|
+
# use br(x) to add x space between lines
|
57
|
+
br(2)
|
58
|
+
|
59
|
+
# link somewhere
|
60
|
+
link 'current time', '/time'
|
61
|
+
br
|
62
|
+
end
|
63
|
+
|
64
|
+
route '/time' do
|
65
|
+
"It is currently #{Time.now}"
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
You can see more working examples in the examples/ folder
|
70
|
+
|
71
|
+
Running a script
|
72
|
+
----------------
|
73
|
+
|
74
|
+
You can use the supplied wrapper script
|
75
|
+
|
76
|
+
```
|
77
|
+
gopher2000 -d examples/simple.rb
|
78
|
+
|
79
|
+
==> *start server at 0.0.0.0 7070*
|
80
|
+
```
|
81
|
+
|
82
|
+
Or, if you include gopher in your file, you can just run the script itself:
|
83
|
+
|
84
|
+
```rb
|
85
|
+
# scriptname.rb
|
86
|
+
require 'gopher2000'
|
87
|
+
|
88
|
+
# ...
|
89
|
+
# write some code here
|
90
|
+
# ...
|
91
|
+
|
92
|
+
# Then, run 'ruby scriptname.rb'
|
93
|
+
|
94
|
+
==> *start server at 0.0.0.0 7070*
|
95
|
+
```
|
96
|
+
There are several command-line options:
|
97
|
+
|
98
|
+
* -d -- run in debug mode
|
99
|
+
* -p [port] -- which port to listen on
|
100
|
+
* -o [addr] -- what IP/host to listen on
|
101
|
+
* -e [env] -- what 'environment' to use -- this isn't really used by
|
102
|
+
Gopher2000, but you could use it when writing your app to determine
|
103
|
+
how you behave in production vs development, etc.
|
104
|
+
|
105
|
+
Command line options will override defaults specified in your script
|
106
|
+
-- so you can try out things on a different port/address if needed.
|
107
|
+
|
108
|
+
|
109
|
+
Developing Gopher Sites
|
110
|
+
-----------------------
|
111
|
+
|
112
|
+
Gopher2000 makes developing sites easy! Any time you change your
|
113
|
+
script, Gopher2000 will reload it. This way, you can make tweaks and
|
114
|
+
your site will be refreshed immediately. NOTE -- this is an
|
115
|
+
experimental feature, and might need some work.
|
116
|
+
|
117
|
+
Serving Files and Directories
|
118
|
+
-----------------------------
|
119
|
+
|
120
|
+
If you just want to serve up some files, there's a command for that:
|
121
|
+
|
122
|
+
```rb
|
123
|
+
mount '/files' => '/home/username/files', :filter => '*.jpg'
|
124
|
+
```
|
125
|
+
|
126
|
+
This will display a list of all the JPGs in the files directory.
|
127
|
+
|
128
|
+
Outputting Gopher Menus
|
129
|
+
-----------------------
|
130
|
+
|
131
|
+
There are a collection of commands to output Gopher menus (see
|
132
|
+
rendering/menu.rb for the code). The commands are:
|
133
|
+
|
134
|
+
**line(type, text, selector)** - output a line of type 'type' -- see
|
135
|
+
the RFC for the different types of links you can have.
|
136
|
+
|
137
|
+
**text** - output a line of text with no action on it.
|
138
|
+
|
139
|
+
**br(x)** - output x blank lines.
|
140
|
+
|
141
|
+
**error** - output an error message.
|
142
|
+
|
143
|
+
**directory** - (aliased as menu) output a link to a 'directory' -- this could be an
|
144
|
+
actual directory if you're building some sort of filesystem tree, or
|
145
|
+
a sub-menu for other actions in your app.
|
146
|
+
|
147
|
+
**link(text, selector)** - output a menu link to to the /selector path.
|
148
|
+
|
149
|
+
**search(text, selector)** -- output a link to a search action at
|
150
|
+
/selector.
|
151
|
+
|
152
|
+
Outputting Text
|
153
|
+
---------------
|
154
|
+
|
155
|
+
If you would like to output text, but have the ability to format it
|
156
|
+
nicely, you can use a 'text' block like this:
|
157
|
+
|
158
|
+
```rb
|
159
|
+
route '/prettytext' do
|
160
|
+
render :prettytext
|
161
|
+
end
|
162
|
+
|
163
|
+
#
|
164
|
+
# special text output rendering
|
165
|
+
#
|
166
|
+
text :prettytext do
|
167
|
+
@text = "A really long chunk of text. Lorem ipsum dolor sit amet ... nec massa."
|
168
|
+
|
169
|
+
# nicely wrapped text
|
170
|
+
block @text
|
171
|
+
|
172
|
+
# spacing
|
173
|
+
br(2)
|
174
|
+
|
175
|
+
# smaller line-width
|
176
|
+
block @text, 30
|
177
|
+
end
|
178
|
+
|
179
|
+
```
|
180
|
+
|
181
|
+
A call to:
|
182
|
+
|
183
|
+
```
|
184
|
+
echo "/prettytext" | ncat -C localhost 7070
|
185
|
+
```
|
186
|
+
|
187
|
+
Will return your text, but with nice wrapping, etc.
|
188
|
+
|
189
|
+
@todo headers, etc.
|
190
|
+
|
191
|
+
|
192
|
+
Making It Pretty
|
193
|
+
----------------
|
194
|
+
|
195
|
+
There are several helpers which you can call within render blocks to
|
196
|
+
help make your output a little shinier:
|
197
|
+
|
198
|
+
**width(x)** will set the width of your output. The default is 80
|
199
|
+
characters. You can change this to make your output wider or
|
200
|
+
thinner. This setting is used by **block** and also by the methods
|
201
|
+
described below.
|
202
|
+
|
203
|
+
**header(text, style='=')** will generate a very simple 'header', which is
|
204
|
+
basically the text you specify with an underline of the character
|
205
|
+
you specify. It will be centered in your output width, and will look
|
206
|
+
something like this:
|
207
|
+
|
208
|
+
Hello There!
|
209
|
+
=====================
|
210
|
+
|
211
|
+
**big_header** is the same as header, except it is bigger and better!
|
212
|
+
|
213
|
+
=====================
|
214
|
+
= Hello There! =
|
215
|
+
=====================
|
216
|
+
|
217
|
+
**underline** can be used to just output plain old lines, if you're
|
218
|
+
into that sort of thing.
|
219
|
+
|
220
|
+
|
221
|
+
Testing
|
222
|
+
-------
|
223
|
+
|
224
|
+
Here's some simple ways to test your server. First, you can always
|
225
|
+
just install a
|
226
|
+
[gopher client](http://lmgtfy.com/?q=gopher+clients). Or, if you like
|
227
|
+
to live on the edge, there's a few commands worth learning. First, you
|
228
|
+
can use [netcat](http://netcat.sourceforge.net/) to achieve
|
229
|
+
awesomeness. Here's some examples, assuming you're running the example
|
230
|
+
script on port 7070:
|
231
|
+
|
232
|
+
|
233
|
+
```
|
234
|
+
#
|
235
|
+
# getting a menu listing
|
236
|
+
#
|
237
|
+
|
238
|
+
~/Projects/gopher2000: echo "/" | nc localhost 7070
|
239
|
+
isimple gopher example null (FALSE) 0
|
240
|
+
i null (FALSE) 0
|
241
|
+
i null (FALSE) 0
|
242
|
+
0current time /time 0.0.0.0 7070
|
243
|
+
i null (FALSE) 0
|
244
|
+
0about /about 0.0.0.0 7070
|
245
|
+
i null (FALSE) 0
|
246
|
+
7Hey, what is your name? /hello 0.0.0.0 7070
|
247
|
+
i null (FALSE) 0
|
248
|
+
7echo test /echo_test 0.0.0.0 7070
|
249
|
+
i null (FALSE) 0
|
250
|
+
1filez /files 0.0.0.0 7070
|
251
|
+
|
252
|
+
#
|
253
|
+
# getting a simple text response
|
254
|
+
#
|
255
|
+
~/Projects/gopher2000: echo "/about" | nc localhost 7070
|
256
|
+
Gopher 2000 -- World Domination via Text Protocols
|
257
|
+
.
|
258
|
+
```
|
259
|
+
|
260
|
+
|
261
|
+
Or, you can use the equally awesome [ncat](http://nmap.org/ncat/),
|
262
|
+
which is basically the successor to netcat. In general, I find that
|
263
|
+
ncat works better, particularly if you're using non-blocking
|
264
|
+
operations. Here's an example of it in operation:
|
265
|
+
|
266
|
+
|
267
|
+
```
|
268
|
+
#
|
269
|
+
# Testing text output
|
270
|
+
#
|
271
|
+
|
272
|
+
~/Projects/gopher2000: echo "/about" | ncat -C localhost 7070
|
273
|
+
Gopher 2000 -- World Domination via Text Protocols
|
274
|
+
.
|
275
|
+
|
276
|
+
#
|
277
|
+
# testing a route with some input
|
278
|
+
#
|
279
|
+
|
280
|
+
~/Projects/gopher2000: echo "/hello\tcolin" | ncat -C localhost 7070
|
281
|
+
iHello, colin! null (FALSE) 0
|
282
|
+
|
283
|
+
.
|
284
|
+
|
285
|
+
```
|
286
|
+
|
287
|
+
|
288
|
+
|
289
|
+
Logging
|
290
|
+
-------
|
291
|
+
|
292
|
+
Logging is pretty basic at the moment. Right now debug messages are
|
293
|
+
dumped to stderr. There's also an apache-esque access log, which can
|
294
|
+
be written to a file specified like this:
|
295
|
+
|
296
|
+
```rb
|
297
|
+
set :access_log, "/tmp/access.log"
|
298
|
+
```
|
299
|
+
|
300
|
+
The log will rollover daily, so your million hits per day won't
|
301
|
+
accumulate into an unmanageable file.
|
302
|
+
|
303
|
+
The format is a pretty basic tab-delimited file:
|
304
|
+
|
305
|
+
timestamp ip_address request_url result_code response_size
|
306
|
+
2012-04-05 19:14:01 127.0.0.1 /lookup success 46
|
307
|
+
|
308
|
+
Non-Blocking Requests
|
309
|
+
---------------------
|
310
|
+
|
311
|
+
When not running in debug mode, Gopher2000 will handle requests
|
312
|
+
without blocking -- this way, if you have an app that handles slow
|
313
|
+
requests, your users aren't held up waiting for other requests to
|
314
|
+
finish. However, this is somewhat experimental, so you can turn it off
|
315
|
+
by setting :non_blocking to be false in your script:
|
316
|
+
|
317
|
+
```rb
|
318
|
+
set :non_blocking, false
|
319
|
+
```
|
320
|
+
|
321
|
+
Also, non-blocking is always off in debug mode.
|
322
|
+
|
323
|
+
You probably need to be wary of this feature if you're actually
|
324
|
+
running a Gopher server that needs to be non-blocking. Read up on
|
325
|
+
EventMachine's defer feature if you need to learn more.
|
326
|
+
|
327
|
+
|
328
|
+
TODO
|
329
|
+
----
|
330
|
+
* More examples
|
331
|
+
* Work on putting routing/rendering/etc into same context, and making
|
332
|
+
instance variables/methods generally available.
|
333
|
+
* Documentation
|
334
|
+
* clean up/improve EventMachine usage
|
335
|
+
* stats generation
|
336
|
+
|
337
|
+
References
|
338
|
+
----------
|
339
|
+
|
340
|
+
* http://www.ietf.org/rfc/rfc1436.txt -- the original RFC for the
|
341
|
+
Gopher Protocol
|
342
|
+
* https://github.com/sinatra/sinatra -- almost everything good in this
|
343
|
+
library was taken or influenced by something in Sinatra. RUN don't
|
344
|
+
walk to the code and take a look.
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.setup :default, :test, :development
|
3
|
+
|
4
|
+
require "bundler/gem_tasks"
|
5
|
+
require 'rdoc/task'
|
6
|
+
|
7
|
+
require "gopher2000/version"
|
8
|
+
|
9
|
+
require 'rspec/core'
|
10
|
+
require 'rspec/core/rake_task'
|
11
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
12
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
16
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
17
|
+
spec.rcov_opts = %w{--exclude .bundler,.rvm}
|
18
|
+
spec.rcov = true
|
19
|
+
end
|
20
|
+
|
21
|
+
task :default => :spec
|
22
|
+
|
23
|
+
Bundler::GemHelper.install_tasks
|
24
|
+
|
25
|
+
begin
|
26
|
+
require 'yard'
|
27
|
+
YARD_OPTS = ['-m', 'markdown', '-M', 'redcarpet']
|
28
|
+
DOC_FILES = ['lib/**/*.rb', 'README.markdown']
|
29
|
+
|
30
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
31
|
+
t.files = DOC_FILES
|
32
|
+
#t.options = YARD_OPTS
|
33
|
+
|
34
|
+
puts t.inspect
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
puts "You need to install YARD."
|
38
|
+
end
|
data/bin/gopher2000
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
# get rid of errors when running via bundle
|
5
|
+
# @see https://github.com/ddollar/foreman/issues/94
|
6
|
+
#
|
7
|
+
$stdout.sync = true
|
8
|
+
|
9
|
+
require 'optparse'
|
10
|
+
require 'gopher2000'
|
11
|
+
|
12
|
+
#
|
13
|
+
# pull in any arguments and set them as env variables
|
14
|
+
#
|
15
|
+
opts = OptionParser.new
|
16
|
+
opts.banner = <<-EOS
|
17
|
+
|
18
|
+
Run a gopher server!
|
19
|
+
|
20
|
+
Usage: #{File.basename($0)} [options] [scriptname]
|
21
|
+
|
22
|
+
EOS
|
23
|
+
|
24
|
+
opts.separator ""
|
25
|
+
opts.separator "Options:"
|
26
|
+
|
27
|
+
params = {
|
28
|
+
:debug => false,
|
29
|
+
:env => :production
|
30
|
+
}
|
31
|
+
|
32
|
+
opts.on('-d', '--debug', "run in debug mode") { params[:debug] = true }
|
33
|
+
opts.on('-p port', 'set the port (default is 70)') { |val| params[:port] = Integer(val) }
|
34
|
+
opts.on('-o addr', 'set the host (default is 0.0.0.0)') { |val| params[host] = val }
|
35
|
+
opts.on('-e env', 'set the environment (default is development)') { |val| params[:env] = val.to_sym }
|
36
|
+
|
37
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
38
|
+
puts opts
|
39
|
+
exit!
|
40
|
+
end
|
41
|
+
|
42
|
+
extra = opts.parse!(ARGV)
|
43
|
+
|
44
|
+
script = extra.shift
|
45
|
+
|
46
|
+
if script.nil?
|
47
|
+
puts "Sorry, you need to specify a script to run"
|
48
|
+
exit!
|
49
|
+
end
|
50
|
+
|
51
|
+
run script, params
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
#
|
5
|
+
# Simple gopher example
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'gopher2000'
|
9
|
+
|
10
|
+
set :host, '0.0.0.0'
|
11
|
+
set :port, 7070
|
12
|
+
|
13
|
+
# you can specify a destination for access log, for stats/etc
|
14
|
+
set :access_log, "/tmp/access.log"
|
15
|
+
|
16
|
+
route '/gopher' do
|
17
|
+
"Greetings from Gopher 2000!" # You can output any text you want here
|
18
|
+
end
|
19
|
+
|
20
|
+
default_route do
|
21
|
+
"I AM HERE"
|
22
|
+
end
|
data/examples/nyan.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
#
|
5
|
+
# NYAN cat in gopherspace
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'gopher2000'
|
9
|
+
|
10
|
+
set :host, '0.0.0.0'
|
11
|
+
set :port, 7070
|
12
|
+
|
13
|
+
route '/nyan' do
|
14
|
+
|
15
|
+
@cats = [
|
16
|
+
" + o + o
|
17
|
+
+ o + +
|
18
|
+
o +
|
19
|
+
o + + +
|
20
|
+
+ o o + o
|
21
|
+
-_-_-_-_-_-_-_,------, o
|
22
|
+
_-_-_-_-_-_-_-| /\\_/\\
|
23
|
+
-_-_-_-_-_-_-~|__( ^ .^) + +
|
24
|
+
_-_-_-_-_-_-_-\"\" \"\"
|
25
|
+
+ o o + o
|
26
|
+
+ +
|
27
|
+
o o o o +
|
28
|
+
o +
|
29
|
+
+ + o o + ",
|
30
|
+
|
31
|
+
" o + + +
|
32
|
+
o + o
|
33
|
+
o +
|
34
|
+
+ o + o
|
35
|
+
o + o +
|
36
|
+
_-_-_-_-_-_-_-,------, o +
|
37
|
+
-_-_-_-_-_-_-_| /\\_/\\ +
|
38
|
+
_-_-_-_-_-_-_~|__( ^ .^) o
|
39
|
+
-_-_-_-_-_-_-_ \"\" \"\"
|
40
|
+
+ + o o +
|
41
|
+
o + o +
|
42
|
+
+ o + + o
|
43
|
+
+ +
|
44
|
+
+ o + ",
|
45
|
+
|
46
|
+
"░░▓▓░░░░░░░░▓▓░░
|
47
|
+
░▓▒▒▓░░░░░░▓▒▒▓░
|
48
|
+
░▓▒▒▒▓░░░░▓▒▒▒▓░
|
49
|
+
░▓▒▒▒▒▓▓▓▓▒▒▒▒▓░
|
50
|
+
░▓▒▒▒▒▒▒▒▒▒▒▒▒▒▓
|
51
|
+
▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓
|
52
|
+
▓▒▒▒░▓▒▒▒▒▒░▓▒▒▓
|
53
|
+
▓▒▒▒▓▓▒▒▒▓▒▓▓▒▒▓
|
54
|
+
▓▒░░▒▒▒▒▒▒▒▒▒░░▓
|
55
|
+
▓▒░░▒▓▒▒▓▒▒▓▒░░▓
|
56
|
+
░▓▒▒▒▓▓▓▓▓▓▓▒▒▓░
|
57
|
+
░░▓▒▒▒▒▒▒▒▒▒▒▓░░
|
58
|
+
░░░▓▓▓▓▓▓▓▓▓▓░░░"
|
59
|
+
]
|
60
|
+
|
61
|
+
@cats.sample
|
62
|
+
end
|