gopher2000 0.3.0 → 0.6.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.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +19 -0
- data/.gitignore +3 -0
- data/.ruby-version +1 -0
- data/Dockerfile +17 -0
- data/Gemfile +1 -15
- data/LICENSE.txt +669 -9
- data/README.markdown +32 -3
- data/bin/gopher2000 +1 -1
- data/examples/default_route.rb +0 -0
- data/examples/figlet.rb +28 -0
- data/examples/nyan.rb +0 -0
- data/examples/simple.rb +4 -2
- data/examples/twitter.rb +0 -0
- data/examples/weather.rb +0 -0
- data/gopher2000.gemspec +4 -4
- data/lib/gopher2000/base.rb +81 -39
- data/lib/gopher2000/dispatcher.rb +13 -4
- data/lib/gopher2000/dsl.rb +1 -1
- data/lib/gopher2000/handlers/directory_handler.rb +5 -7
- data/lib/gopher2000/rendering/base.rb +58 -8
- data/lib/gopher2000/rendering/menu.rb +104 -15
- data/lib/gopher2000/request.rb +12 -3
- data/lib/gopher2000/server.rb +11 -4
- data/lib/gopher2000/version.rb +1 -1
- data/spec/application_spec.rb +12 -12
- data/spec/dispatching_spec.rb +38 -17
- data/spec/dsl_spec.rb +76 -67
- data/spec/handlers/directory_handler_spec.rb +35 -35
- data/spec/helpers_spec.rb +1 -1
- data/spec/rendering/base_spec.rb +30 -11
- data/spec/rendering/menu_spec.rb +40 -18
- data/spec/rendering_spec.rb +7 -7
- data/spec/request_spec.rb +14 -8
- data/spec/response_spec.rb +5 -5
- data/spec/routing_spec.rb +21 -21
- data/spec/server_spec.rb +23 -7
- metadata +121 -68
- data/.rvmrc +0 -1
data/README.markdown
CHANGED
@@ -16,6 +16,8 @@ Gopher2000 - A Gopher server for the next millenium
|
|
16
16
|
Gopher2000 is a ruby-based Gopher server. It is built for speedy, enjoyable development of
|
17
17
|
all sorts of gopher sites.
|
18
18
|
|
19
|
+
[](https://travis-ci.org/muffinista/gopher2000)
|
20
|
+
|
19
21
|
Features
|
20
22
|
--------
|
21
23
|
* Simple, Sintra-inspired routing DSL.
|
@@ -23,11 +25,10 @@ Features
|
|
23
25
|
* built on Event Machine.
|
24
26
|
* Easy to mount directories and serve up files.
|
25
27
|
* built in logging and stats.
|
26
|
-
* Runs on Ruby 1.9.2 with all the modern conveniences.
|
27
28
|
|
28
29
|
Requirements
|
29
30
|
------------
|
30
|
-
* Ruby
|
31
|
+
* Ruby 2 or greater
|
31
32
|
* Nerves of steel
|
32
33
|
|
33
34
|
Examples
|
@@ -57,7 +58,7 @@ menu :index do
|
|
57
58
|
br(2)
|
58
59
|
|
59
60
|
# link somewhere
|
60
|
-
|
61
|
+
text_link 'current time', '/time'
|
61
62
|
br
|
62
63
|
end
|
63
64
|
|
@@ -106,6 +107,34 @@ Command line options will override defaults specified in your script
|
|
106
107
|
-- so you can try out things on a different port/address if needed.
|
107
108
|
|
108
109
|
|
110
|
+
Docker
|
111
|
+
------
|
112
|
+
|
113
|
+
There's a pretty simple docker script which you can use to run an
|
114
|
+
app. To run one of the included examples, you could do something like:
|
115
|
+
|
116
|
+
|
117
|
+
```
|
118
|
+
docker build -t gopher2000 .
|
119
|
+
docker run -p 7070:7070 --rm -it gopher2000 examples/simple.rb
|
120
|
+
```
|
121
|
+
|
122
|
+
This will run the `simple` example on port 7070. You can view it
|
123
|
+
locally by running something like:
|
124
|
+
|
125
|
+
```
|
126
|
+
lynx gopher://0.0.0.0:7070
|
127
|
+
```
|
128
|
+
|
129
|
+
The Dockerfile is also published to Docker Hub, so you could run
|
130
|
+
something like this:
|
131
|
+
|
132
|
+
```
|
133
|
+
docker run -p 7070:7070 --rm -v $PWD:/opt muffinista/gopher2000 /opt/gopher-script.rb
|
134
|
+
```
|
135
|
+
|
136
|
+
|
137
|
+
|
109
138
|
Developing Gopher Sites
|
110
139
|
-----------------------
|
111
140
|
|
data/bin/gopher2000
CHANGED
@@ -31,7 +31,7 @@ params = {
|
|
31
31
|
|
32
32
|
opts.on('-d', '--debug', "run in debug mode") { params[:debug] = true }
|
33
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 }
|
34
|
+
opts.on('-o addr', 'set the host (default is 0.0.0.0)') { |val| params[:host] = val }
|
35
35
|
opts.on('-e env', 'set the environment (default is development)') { |val| params[:env] = val.to_sym }
|
36
36
|
|
37
37
|
opts.on_tail("-h", "--help", "Show this message") do
|
data/examples/default_route.rb
CHANGED
File without changes
|
data/examples/figlet.rb
ADDED
@@ -0,0 +1,28 @@
|
|
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
|
+
route '/figlet' do
|
14
|
+
render :figlet
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# special text output rendering
|
19
|
+
#
|
20
|
+
text :figlet do
|
21
|
+
@text = "Hello!"
|
22
|
+
|
23
|
+
# nicely wrapped text
|
24
|
+
figlet @text
|
25
|
+
|
26
|
+
# spacing
|
27
|
+
br(2)
|
28
|
+
end
|
data/examples/nyan.rb
CHANGED
File without changes
|
data/examples/simple.rb
CHANGED
@@ -5,6 +5,8 @@
|
|
5
5
|
# Simple gopher example
|
6
6
|
#
|
7
7
|
|
8
|
+
require "rubygems"
|
9
|
+
require "bundler/setup"
|
8
10
|
require 'gopher2000'
|
9
11
|
|
10
12
|
set :host, '0.0.0.0'
|
@@ -57,11 +59,11 @@ menu :index do
|
|
57
59
|
br(2)
|
58
60
|
|
59
61
|
# link somewhere
|
60
|
-
|
62
|
+
text_link 'current time', '/time'
|
61
63
|
br
|
62
64
|
|
63
65
|
# another link
|
64
|
-
|
66
|
+
text_link 'about', '/about'
|
65
67
|
br
|
66
68
|
|
67
69
|
# ask for some input
|
data/examples/twitter.rb
CHANGED
File without changes
|
data/examples/weather.rb
CHANGED
File without changes
|
data/gopher2000.gemspec
CHANGED
@@ -12,7 +12,6 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.summary = %q{Gopher2000 - A Gopher server for the next millenium}
|
13
13
|
s.description = %q{Gopher2000 is a ruby-based Gopher server. It is built for speedy, enjoyable development of all sorts of gopher sites.}
|
14
14
|
|
15
|
-
s.rubyforge_project = "gopher2000"
|
16
15
|
s.licenses = ["WTFPL"]
|
17
16
|
|
18
17
|
s.files = `git ls-files`.split("\n")
|
@@ -26,10 +25,11 @@ Gem::Specification.new do |s|
|
|
26
25
|
s.add_development_dependency "yard"
|
27
26
|
s.add_development_dependency "shoulda"
|
28
27
|
s.add_development_dependency "rdoc"
|
29
|
-
s.add_development_dependency "simplecov"
|
28
|
+
s.add_development_dependency "simplecov", "~> 0.16.1"
|
30
29
|
s.add_development_dependency "watchr"
|
31
|
-
s.add_development_dependency "eventmachine"
|
32
30
|
|
33
|
-
s.add_runtime_dependency "
|
31
|
+
s.add_runtime_dependency "artii", ">= 2.0.1"
|
32
|
+
s.add_runtime_dependency "eventmachine", "~> 1.2.5"
|
34
33
|
s.add_runtime_dependency "logging"
|
34
|
+
s.add_runtime_dependency "mimemagic"
|
35
35
|
end
|
data/lib/gopher2000/base.rb
CHANGED
@@ -54,6 +54,13 @@ module Gopher
|
|
54
54
|
config[:port] ||= 70
|
55
55
|
end
|
56
56
|
|
57
|
+
#
|
58
|
+
# return the application environment
|
59
|
+
#
|
60
|
+
def env
|
61
|
+
config[:env] ||= 'development'
|
62
|
+
end
|
63
|
+
|
57
64
|
#
|
58
65
|
# are we in debugging mode?
|
59
66
|
#
|
@@ -91,9 +98,9 @@ module Gopher
|
|
91
98
|
#
|
92
99
|
# mount a directory for browsing via gopher
|
93
100
|
#
|
94
|
-
# @param [Hash] A hash specifying the path your route will answer to, and the filesystem path to use '/route' => '/home/path/etc'
|
95
|
-
#
|
96
|
-
|
101
|
+
# @param [Hash] path A hash specifying the path your route will answer to, and the filesystem path to use '/route' => '/home/path/etc'
|
102
|
+
# @param [Hash] opts a hash of options for the mount. Primarily this is a filter, which will restrict the list files outputted. example: :filter => '*.jpg'
|
103
|
+
# @param [Class] klass The class that should be used to handle this mount. You could write and use a custom handler if desired
|
97
104
|
#
|
98
105
|
# @example mount the directory '/home/user/foo' at the gopher path '/files', and only show JPG files:
|
99
106
|
# mount '/files' => '/home/user/foo', :filter => '*.jpg'
|
@@ -118,8 +125,7 @@ module Gopher
|
|
118
125
|
|
119
126
|
#
|
120
127
|
# define a route.
|
121
|
-
# @param [String] the path your route will answer to. This is
|
122
|
-
# basically a URI path
|
128
|
+
# @param [String] path the path your route will answer to. This is basically a URI path
|
123
129
|
# @yield a block that handles your route
|
124
130
|
#
|
125
131
|
# @example respond with a simple string
|
@@ -145,6 +151,7 @@ module Gopher
|
|
145
151
|
|
146
152
|
#
|
147
153
|
# specify a default route to handle requests if no other route exists
|
154
|
+
# @yield a block to handle the default route
|
148
155
|
#
|
149
156
|
# @example render a template
|
150
157
|
# default_route do
|
@@ -158,16 +165,15 @@ module Gopher
|
|
158
165
|
#
|
159
166
|
# lookup an incoming path
|
160
167
|
#
|
161
|
-
# @param [String] the selector path of the incoming request
|
168
|
+
# @param [String] selector the selector path of the incoming request
|
162
169
|
#
|
163
170
|
def lookup(selector)
|
164
171
|
unless routes.nil?
|
165
|
-
|
166
|
-
|
172
|
+
routes.each do |pattern, keys, block|
|
167
173
|
if match = pattern.match(selector)
|
168
174
|
match = match.to_a
|
169
175
|
url = match.shift
|
170
|
-
|
176
|
+
|
171
177
|
params = to_params_hash(keys, match)
|
172
178
|
|
173
179
|
#
|
@@ -190,7 +196,7 @@ module Gopher
|
|
190
196
|
|
191
197
|
#
|
192
198
|
# find and run the first route which matches the incoming request
|
193
|
-
# @param [Request] Gopher::Request object
|
199
|
+
# @param [Request] req Gopher::Request object
|
194
200
|
#
|
195
201
|
def dispatch(req)
|
196
202
|
debug_log(req)
|
@@ -201,6 +207,9 @@ module Gopher
|
|
201
207
|
if ! @request.valid?
|
202
208
|
response.body = handle_invalid_request
|
203
209
|
response.code = :error
|
210
|
+
elsif @request.url?
|
211
|
+
response.body = handle_url(@request)
|
212
|
+
response.code = :success
|
204
213
|
else
|
205
214
|
begin
|
206
215
|
debug_log("do lookup for #{@request.selector}")
|
@@ -233,11 +242,11 @@ module Gopher
|
|
233
242
|
# define a template which will be used to render a gopher-style
|
234
243
|
# menu.
|
235
244
|
#
|
236
|
-
# @param [String/Symbol]
|
237
|
-
#
|
245
|
+
# @param [String/Symbol] name the name of the template. This is what
|
246
|
+
# identifies the template when making a call to render
|
238
247
|
# @yield a block which will output the menu. This block is
|
239
|
-
#
|
240
|
-
#
|
248
|
+
# executed within an instance of Gopher::Rendering::Menu and will
|
249
|
+
# have access to all of its methods.
|
241
250
|
#
|
242
251
|
# @example a simple menu:
|
243
252
|
# menu :index do
|
@@ -273,7 +282,7 @@ module Gopher
|
|
273
282
|
# access to the methods defined in Gopher::Rendering::Text for
|
274
283
|
# wrapping strings, adding simple headers, etc.
|
275
284
|
#
|
276
|
-
# @param [String/Symbol]
|
285
|
+
# @param [String/Symbol] name the name of the template. This is what identifies the template when making a call to render
|
277
286
|
#
|
278
287
|
# @yield a block which will output the menu. This block is executed within an instance of Gopher::Rendering::Text and will have access to all of its methods.
|
279
288
|
# @example simple example
|
@@ -295,7 +304,7 @@ module Gopher
|
|
295
304
|
|
296
305
|
#
|
297
306
|
# find a template
|
298
|
-
# @param [String/Symbol] name of the template
|
307
|
+
# @param [String/Symbol] t name of the template
|
299
308
|
# @return template block and the class context it should use
|
300
309
|
#
|
301
310
|
def find_template(t)
|
@@ -311,8 +320,8 @@ module Gopher
|
|
311
320
|
|
312
321
|
#
|
313
322
|
# Find the desired template and call it within the proper context
|
314
|
-
# @param [String/Symbol] name of the template to render
|
315
|
-
# @param [Array] optional arguments to be passed to template
|
323
|
+
# @param [String/Symbol] template name of the template to render
|
324
|
+
# @param [Array] arguments optional arguments to be passed to template
|
316
325
|
# @return result of rendering
|
317
326
|
#
|
318
327
|
def render(template, *arguments)
|
@@ -347,6 +356,15 @@ module Gopher
|
|
347
356
|
menus.include?(:error) ? :error : :'internal/error'
|
348
357
|
end
|
349
358
|
|
359
|
+
|
360
|
+
#
|
361
|
+
# get the id of the template that will be used when rendering an html page
|
362
|
+
# @return name of error template
|
363
|
+
#
|
364
|
+
def url_template
|
365
|
+
menus.include?(:html) ? :html : :'internal/url'
|
366
|
+
end
|
367
|
+
|
350
368
|
#
|
351
369
|
# get the id of the template that will be used when rendering an
|
352
370
|
# invalid request
|
@@ -385,7 +403,7 @@ module Gopher
|
|
385
403
|
# Gopher servers in production)
|
386
404
|
#
|
387
405
|
def non_blocking?
|
388
|
-
config[:non_blocking]
|
406
|
+
config.key?(:non_blocking) ? config[:non_blocking] : ! debug_mode?
|
389
407
|
end
|
390
408
|
|
391
409
|
|
@@ -411,7 +429,7 @@ module Gopher
|
|
411
429
|
# turn a path string with optional keys (/foo/:bar/:boo) into a
|
412
430
|
# regexp which will be used when searching for a route
|
413
431
|
#
|
414
|
-
# @param [String] the path to compile
|
432
|
+
# @param [String] path the path to compile
|
415
433
|
#
|
416
434
|
def compile(path)
|
417
435
|
keys = []
|
@@ -431,24 +449,22 @@ module Gopher
|
|
431
449
|
|
432
450
|
class << self
|
433
451
|
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
#
|
452
|
+
#
|
453
|
+
# Sanitizes a gopher selector
|
454
|
+
#
|
455
|
+
def sanitize_selector(raw)
|
456
|
+
"/#{raw}".dup.
|
457
|
+
strip. # Strip whitespace
|
458
|
+
sub(/\/$/, ''). # Strip last rslash
|
459
|
+
sub(/^\/*/, '/'). # Strip extra lslashes
|
460
|
+
gsub(/\.+/, '.') # Don't want consecutive dots!
|
461
|
+
end
|
462
|
+
|
463
|
+
#
|
448
464
|
# generate a method which we will use to run routes. this is
|
449
465
|
# based on #generate_method as used by sinatra.
|
450
466
|
# @see https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb
|
451
|
-
# @param [String] name to use for the method
|
467
|
+
# @param [String] method_name name to use for the method
|
452
468
|
# @yield block to use for the method
|
453
469
|
def generate_method(method_name, &block)
|
454
470
|
define_method(method_name, &block)
|
@@ -457,7 +473,7 @@ module Gopher
|
|
457
473
|
method
|
458
474
|
end
|
459
475
|
end
|
460
|
-
|
476
|
+
|
461
477
|
#
|
462
478
|
# output a debugging message
|
463
479
|
#
|
@@ -474,15 +490,37 @@ module Gopher
|
|
474
490
|
#
|
475
491
|
def register_defaults
|
476
492
|
menu :'internal/not_found' do
|
477
|
-
|
493
|
+
error "Sorry, #{@request.selector} was not found"
|
478
494
|
end
|
479
495
|
|
480
496
|
menu :'internal/error' do |details|
|
481
|
-
|
497
|
+
error "Sorry, there was an error #{details}"
|
482
498
|
end
|
483
499
|
|
484
500
|
menu :'internal/invalid_request' do
|
485
|
-
|
501
|
+
error "invalid request"
|
502
|
+
end
|
503
|
+
|
504
|
+
menu :'internal/url' do
|
505
|
+
output = <<-EOHTML
|
506
|
+
<html>
|
507
|
+
<head>
|
508
|
+
<meta http-equiv="refresh" content="5;URL=#{@request.url}">
|
509
|
+
</head>
|
510
|
+
<body>
|
511
|
+
<p>
|
512
|
+
You are following a link from gopher to a website. If your browser supports it, you will be
|
513
|
+
automatically taken to the web site shortly. If you do not get
|
514
|
+
sent there, please click <a href="#{@request.url}">here</a>.
|
515
|
+
</p>
|
516
|
+
<p>
|
517
|
+
The URL linked is: <a href="#{@request.url}">#{@request.url}</a>.
|
518
|
+
</p>
|
519
|
+
<p>Have a nice day!</p>
|
520
|
+
</body>
|
521
|
+
</html>
|
522
|
+
EOHTML
|
523
|
+
output
|
486
524
|
end
|
487
525
|
end
|
488
526
|
|
@@ -490,6 +528,10 @@ module Gopher
|
|
490
528
|
render not_found_template
|
491
529
|
end
|
492
530
|
|
531
|
+
def handle_url(request)
|
532
|
+
render url_template, request
|
533
|
+
end
|
534
|
+
|
493
535
|
def handle_error(e)
|
494
536
|
render error_template, e
|
495
537
|
end
|
@@ -22,16 +22,25 @@ module Gopher
|
|
22
22
|
#
|
23
23
|
# called by EventMachine when there's an incoming request
|
24
24
|
#
|
25
|
-
# @param [String] incoming selector
|
25
|
+
# @param [String] selector incoming selector
|
26
26
|
# @return Response object
|
27
27
|
#
|
28
|
-
def receive_data
|
29
|
-
|
28
|
+
def receive_data data
|
29
|
+
(@buf ||= '') << data
|
30
|
+
|
31
|
+
while line = @buf.slice!(/(.*)\r?\n/)
|
32
|
+
receive_line(line)
|
33
|
+
end
|
30
34
|
end
|
31
35
|
|
36
|
+
# Invoked with lines received over the network
|
37
|
+
def receive_line(line)
|
38
|
+
call! Request.new(line, remote_ip)
|
39
|
+
end
|
40
|
+
|
32
41
|
#
|
33
42
|
# generate a request object from an incoming selector, and dispatch it to the app
|
34
|
-
# @param [
|
43
|
+
# @param [Request] request Request object to handle
|
35
44
|
# @return Response object
|
36
45
|
#
|
37
46
|
def call!(request)
|