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.
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
+ [![Build Status](https://travis-ci.org/muffinista/gopher2000.svg?branch=master)](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 1.9.2 or greater
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
- link 'current time', '/time'
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
File without changes
@@ -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
- link 'current time', '/time'
62
+ text_link 'current time', '/time'
61
63
  br
62
64
 
63
65
  # another link
64
- link 'about', '/about'
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 "eventmachine"
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
@@ -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
- # @param [Hash] a hash of options for the mount. Primarily this is a filter, which will restrict the list files outputted. example: :filter => '*.jpg'
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
- routes.each do |pattern, keys, block|
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] -- the name of the template. This is what
237
- # identifies the template when making a call to render
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
- # executed within an instance of Gopher::Rendering::Menu and will
240
- # have access to all of its methods.
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] -- the name of the template. This is what identifies the template when making a call to render
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] ||= ! debug_mode?
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
- # Sanitizes a gopher selector
436
- #
437
- def sanitize_selector(raw)
438
- "/#{raw}".dup.
439
- strip. # Strip whitespace
440
- sub(/\/$/, ''). # Strip last rslash
441
- sub(/^\/*/, '/'). # Strip extra lslashes
442
- gsub(/\.+/, '.') # Don't want consecutive dots!
443
-
444
- #raw = "/#{raw}" if ! raw.match(/^\//)
445
- end
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
- text "Sorry, #{@request.selector} was not found"
493
+ error "Sorry, #{@request.selector} was not found"
478
494
  end
479
495
 
480
496
  menu :'internal/error' do |details|
481
- text "Sorry, there was an error #{details}"
497
+ error "Sorry, there was an error #{details}"
482
498
  end
483
499
 
484
500
  menu :'internal/invalid_request' do
485
- text "invalid request"
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(selector)
29
- call! Request.new(selector, remote_ip)
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 [String] incoming selector
43
+ # @param [Request] request Request object to handle
35
44
  # @return Response object
36
45
  #
37
46
  def call!(request)
@@ -81,7 +81,7 @@ module Gopher
81
81
  end
82
82
 
83
83
  # watch the specified script for changes
84
- # @param [String] script to watch
84
+ # @param [String] f script to watch
85
85
  def watch(f)
86
86
  application.scripts << f
87
87
  end