gopher2000 0.4.0 → 0.5.3

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6c507fab7aaa1412d98ac4a18870fe786098aa9aa4e393bd1204cc476f111dfb
4
+ data.tar.gz: e767f8ea12042fc19a7c6f6fc25766b3d91838f053abf1abe97b17b8c99da4e5
5
+ SHA512:
6
+ metadata.gz: 3497713e468d3d6eefecafcb68cc7421c8af9db40bcd0e30c865f54e1a8a0e8ab0a8098fa8f5369a518cf4dfd391612ba3ada0d4a4ba51b17240934fa500275d
7
+ data.tar.gz: 89145000523a7d3d7c77ebad777a0421211b05edfe973c4bc3cbe45ea37d2541bfd773be0ed2c86cf114a0a6c47c1fa6a8e64b2ab76e9a8d278423d5b19cb80b
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.6.3
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.1
5
+ - 2.3.3
6
+ - 2.4.2
7
+ # uncomment this line if your project needs to run something other than `rake`:
8
+ # script: bundle exec rspec spec
data/Gemfile CHANGED
@@ -1,6 +1,4 @@
1
- source "http://rubygems.org"
2
-
3
- # Specify your gem's dependencies in gopher.gemspec
1
+ source 'https://rubygems.org'
4
2
  gemspec
5
3
 
6
4
  gem "rake"
@@ -8,19 +6,7 @@ gem "rake"
8
6
  # Add dependencies to develop your gem here.
9
7
  # Include everything needed to run rake, tests, features, etc.
10
8
  group :development do
11
- gem 'simplecov', :require => false, :group => :test
12
-
13
- gem "shoulda", ">= 0"
14
- gem "rspec"
15
-
16
- gem "bundler", "~> 1.0.0"
17
- gem "watchr"
18
9
 
19
10
  # There's a god example script stashed away in the repo
20
11
  gem "god"
21
-
22
- #
23
- # gems used in examples and for development.
24
- #
25
- gem "weather-underground"
26
12
  end
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.
File without changes
data/examples/figlet.rb CHANGED
File without changes
data/examples/nyan.rb CHANGED
File without changes
data/examples/simple.rb CHANGED
File without changes
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,11 +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
31
  s.add_runtime_dependency "artii", ">= 2.0.1"
34
- s.add_runtime_dependency "eventmachine"
32
+ s.add_runtime_dependency "eventmachine", "~> 1.2.5"
35
33
  s.add_runtime_dependency "logging"
34
+ s.add_runtime_dependency "mimemagic"
36
35
  end
@@ -385,7 +385,7 @@ module Gopher
385
385
  # Gopher servers in production)
386
386
  #
387
387
  def non_blocking?
388
- config[:non_blocking] ||= ! debug_mode?
388
+ config.key?(:non_blocking) ? config[:non_blocking] : ! debug_mode?
389
389
  end
390
390
 
391
391
 
@@ -25,10 +25,19 @@ module Gopher
25
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
43
  # @param [Request] request Request object to handle
@@ -96,7 +96,7 @@ module Gopher
96
96
  # iterate through the contents of this directory.
97
97
  # NOTE: we don't filter this, so we will ALWAYS list subdirectories of a mounted folder
98
98
  #
99
- Dir.glob("#{dir}/*.*").each do |x|
99
+ Dir.glob("#{dir}/*").each do |x|
100
100
  # if this is a directory, then generate a directory link for it
101
101
  if File.directory?(x)
102
102
  m.directory File.basename(x), to_selector(x), @application.host, @application.port
@@ -5,12 +5,14 @@ module Gopher
5
5
  #
6
6
  module Rendering
7
7
 
8
- require 'artii'
8
+ require 'artii'
9
9
 
10
10
  # "A CR LF denotes the end of the item." RFC 1436
11
11
  # @see http://www.faqs.org/rfcs/rfc1436.html
12
12
  LINE_ENDING = "\r\n"
13
13
 
14
+ DEFAULT_ENCODING = 'UTF-8'
15
+
14
16
  #
15
17
  # base class for rendering output. this class provides methods
16
18
  # that can be used when rendering both text and gopher menus
@@ -42,7 +44,7 @@ module Gopher
42
44
  # then adds any required spacing
43
45
  #
44
46
  def text(text)
45
- self << text
47
+ self << text.force_encoding(DEFAULT_ENCODING)
46
48
  add_spacing
47
49
  end
48
50
 
@@ -96,31 +98,31 @@ module Gopher
96
98
  self.to_s
97
99
  end
98
100
 
99
- #
100
- # output a figlet, which is a big ASCII art header like this:
101
- # _ _ _ _ _
102
- # | | | | | | | | |
103
- # | |__| | ___| | | ___ | |
104
- # | __ |/ _ \ | |/ _ \| |
105
- # | | | | __/ | | (_) |_|
106
- # |_| |_|\___|_|_|\___/(_)
107
- #
108
- # This method doesn't do any width checks, so you should be
109
- # careful with it.
110
- # You can get a list of fonts from the artii source code or
111
- # http://www.figlet.org/examples.html
112
- # https://github.com/miketierney/artii/tree/master/lib/figlet/fonts
113
-
114
- # @param [String] str the text you want to use for your figlet
115
- # @param [String] font name of the font. Defaults to 'big'.
116
- #
117
- def figlet(str, font = 'big')
118
- a = Artii::Base.new(:font => font)
119
- a.asciify(str).split("\n").each do |l|
120
- text l
121
- end
122
- self.to_s
123
- end
101
+ #
102
+ # output a figlet, which is a big ASCII art header like this:
103
+ # _ _ _ _ _
104
+ # | | | | | | | | |
105
+ # | |__| | ___| | | ___ | |
106
+ # | __ |/ _ \ | |/ _ \| |
107
+ # | | | | __/ | | (_) |_|
108
+ # |_| |_|\___|_|_|\___/(_)
109
+ #
110
+ # This method doesn't do any width checks, so you should be
111
+ # careful with it.
112
+ # You can get a list of fonts from the artii source code or
113
+ # http://www.figlet.org/examples.html
114
+ # https://github.com/miketierney/artii/tree/master/lib/figlet/fonts
115
+
116
+ # @param [String] str the text you want to use for your figlet
117
+ # @param [String] font name of the font. Defaults to 'big'.
118
+ #
119
+ def figlet(str, font = 'big')
120
+ a = Artii::Base.new(:font => font)
121
+ a.asciify(str).split("\n").each do |l|
122
+ text l
123
+ end
124
+ self.to_s
125
+ end
124
126
 
125
127
  #
126
128
  # output a centered string with a nice underline below it,
@@ -147,16 +149,16 @@ module Gopher
147
149
  underline(@width, under)
148
150
  end
149
151
 
150
- #
151
- # output a 'small' header, just the text with an underline
152
+ #
153
+ # output a 'small' header, just the text with an underline
152
154
  # @param [String] str - the string to output
153
155
  # @param [String] under - the desired underline character
154
- #
155
- def small_header(str, under = '=')
156
- str = " " + str + " "
157
- text(str)
158
- underline(str.length, under)
159
- end
156
+ #
157
+ def small_header(str, under = '=')
158
+ str = " " + str + " "
159
+ text(str)
160
+ underline(str.length, under)
161
+ end
160
162
 
161
163
  #
162
164
  # output a centered string in a box
@@ -1,5 +1,7 @@
1
1
  module Gopher
2
2
  module Rendering
3
+ require 'mimemagic'
4
+
3
5
  #
4
6
  # The MenuContext is for rendering gopher menus in the "pseudo
5
7
  # file-system hierarchy" defined by RFC1436
@@ -7,7 +9,6 @@ module Gopher
7
9
  # @see http://www.ietf.org/rfc/rfc1436.txt
8
10
  #
9
11
  class Menu < Base
10
-
11
12
  # default host value when rendering a line with no selector
12
13
  NO_HOST = '(FALSE)'
13
14
 
@@ -92,11 +93,51 @@ module Gopher
92
93
  # method instead
93
94
  # @param [String] host for link, defaults to current host
94
95
  # @param [String] port for link, defaults to current port
95
- def link(text, selector, host=nil, port=nil)
96
- type = determine_type(selector)
96
+ # @param [String] real filepath of the link
97
+ # @param [String] selector type. if not specified, we will guess
98
+ def link(text, selector, host=nil, port=nil, filepath=nil, type=nil)
99
+ if !type
100
+ if filepath
101
+ type = determine_type(filepath)
102
+ else
103
+ type = determine_type(selector)
104
+ end
105
+ end
97
106
  line type, text, selector, host, port
98
107
  end
99
108
 
109
+ #
110
+ # output a link to text output
111
+ #
112
+ # @param [String] text the text of the link
113
+ # @param [String] selector the path of the link. the extension of this path will be used to
114
+ # detemine the type of link -- image, archive, etc. If you want
115
+ # to specify a specific link-type, you should use the text
116
+ # method instead
117
+ # @param [String] host for link, defaults to current host
118
+ # @param [String] port for link, defaults to current port
119
+ # @param [String] real filepath of the link
120
+ def text_link(text, selector, host=nil, port=nil, filepath=nil)
121
+ link(text, selector, host, port, filepath, '0')
122
+ end
123
+
124
+ # Create an HTTP link entry. This is how this works (via wikipedia)
125
+ #
126
+ # For example, to create a link to http://gopher.quux.org/, the
127
+ # item type is "h", the display string is the title of the link,
128
+ # the item selector is "URL:http://gopher.quux.org/", and the
129
+ # domain and port are that of the originating Gopher server (so
130
+ # that clients that do not support URL links will query the
131
+ # server and receive an HTML redirection page).
132
+ #
133
+ # @param [String] text the text of the link
134
+ # @param [String] URL of the link
135
+ # @param [String] host for link, defaults to current host
136
+ # @param [String] port for link, defaults to current port
137
+ def http(text, url, host=nil, port=nil)
138
+ line "h", text, "URL:#{url}", host, port
139
+ end
140
+
100
141
  #
101
142
  # output a search entry
102
143
  # @param [String] text the text of the link
@@ -109,19 +150,67 @@ module Gopher
109
150
 
110
151
  #
111
152
  # Determines the gopher type for +selector+ based on the
112
- # extension. This is a pretty simple check based on the entities
113
- # list in http://www.ietf.org/rfc/rfc1436.txt
114
- # @param [String] selector presumably a link to a file name with an extension
153
+ # information stored in the shared mime database.
154
+ # @param [String] filepath The full path to the file (should also exist, if possible)
115
155
  # @return gopher selector type
116
156
  #
117
- def determine_type(selector)
118
- ext = File.extname(selector).downcase
119
- case ext
120
- when '.zip', '.gz', '.bz2' then '5'
121
- when '.gif' then 'g'
122
- when '.jpg', '.png' then 'I'
123
- when '.mp3', '.wav' then 's'
124
- else '0'
157
+ def determine_type(filepath)
158
+ # Determine MIME type by path
159
+ mimetype = MimeMagic.by_path(filepath)
160
+
161
+ # Determine MIME type by contents
162
+ if !mimetype
163
+ begin
164
+ # Open file
165
+ file = File.open(filepath)
166
+
167
+ # Try to detect MIME type using by recognition of typical characters
168
+ mimetype = MimeMagic.by_magic(file)
169
+
170
+ if !mimetype
171
+ file.rewind
172
+
173
+ # Read up to 1k of file data and look for a "\0\0" sequence (typical for binary files)
174
+ if file.read(1000).include?("\0\0")
175
+ mimetype = MimeMagic.new("application/octet-stream")
176
+ else
177
+ mimetype = MimeMagic.new("text/plain")
178
+ end
179
+
180
+ file.close
181
+ end
182
+ rescue SystemCallError,IOError
183
+ nil
184
+ end
185
+ end
186
+
187
+ if !mimetype
188
+ ext = File.extname(filepath).split(".").last
189
+ mimetype = MimeMagic.by_extension(ext)
190
+ end
191
+
192
+ if !mimetype
193
+ return '9' # Binary file
194
+ elsif mimetype.child_of?('application/gzip') || mimetype.child_of?('application/x-bzip') || mimetype.child_of?('application/zip')
195
+ return '5' # archive
196
+ elsif mimetype.child_of?('image/gif')
197
+ return 'g' # GIF image
198
+ elsif mimetype.child_of?('text/x-uuencode')
199
+ return '6' # UUEncode encoded file
200
+ elsif mimetype.child_of?('application/mac-binhex40')
201
+ return '4' # BinHex encoded file
202
+ elsif mimetype.child_of?('text/html') || mimetype.child_of?('application/xhtml+xml')
203
+ return 'h' # HTML file
204
+ elsif mimetype.mediatype == 'text' || mimetype.child_of?('text/plain')
205
+ return '0' # General text file
206
+ elsif mimetype.mediatype == 'image'
207
+ return 'I' # General image file
208
+ elsif mimetype.mediatype == 'audio'
209
+ return 's' # General audio file
210
+ elsif mimetype.mediatype == 'video'
211
+ return 'v' # General video file
212
+ else
213
+ return '9' # Binary file
125
214
  end
126
215
  end
127
216
  end
@@ -1,4 +1,4 @@
1
1
  module Gopher
2
2
  # current version of the app
3
- VERSION = "0.4.0"
3
+ VERSION = "0.5.3"
4
4
  end
@@ -9,46 +9,46 @@ describe Gopher::Application do
9
9
  end
10
10
 
11
11
  it 'should have default host/port' do
12
- @app.host.should == "0.0.0.0"
13
- @app.port.should == 70
12
+ expect(@app.host).to eq("0.0.0.0")
13
+ expect(@app.port).to eq(70)
14
14
  end
15
15
 
16
16
  describe "should_reload?" do
17
17
  it "is false if no scripts" do
18
- @app.should_reload?.should == false
18
+ expect(@app.should_reload?).to eq(false)
19
19
  end
20
20
 
21
21
  it "shouldn't do anything if last_reload not set" do
22
- @app.last_reload.should be_nil
22
+ expect(@app.last_reload).to be_nil
23
23
  @app.scripts << "foo.rb"
24
- @app.should_reload?.should == false
24
+ expect(@app.should_reload?).to eq(false)
25
25
  end
26
26
 
27
27
  it "should check script date" do
28
28
  now = Time.now
29
- Time.stub!(:now).and_return(now)
29
+ allow(Time).to receive(:now).and_return(now)
30
30
 
31
31
  @app.last_reload = Time.now - 1000
32
32
  @app.scripts << "foo.rb"
33
- File.should_receive(:mtime).with("foo.rb").and_return(now)
33
+ expect(File).to receive(:mtime).with("foo.rb").and_return(now)
34
34
 
35
- @app.should_reload?.should == true
35
+ expect(@app.should_reload?).to eq(true)
36
36
  end
37
37
  end
38
38
 
39
39
  describe "reload_stale" do
40
40
  it "should load script and update last_reload" do
41
41
  now = Time.now
42
- Time.stub!(:now).and_return(now)
42
+ allow(Time).to receive(:now).and_return(now)
43
43
 
44
- @app.should_receive(:should_reload?).and_return(true)
44
+ expect(@app).to receive(:should_reload?).and_return(true)
45
45
 
46
46
  @app.last_reload = Time.now - 1000
47
47
  @app.scripts << "foo.rb"
48
- @app.should_receive(:load).with("foo.rb")
48
+ expect(@app).to receive(:load).with("foo.rb")
49
49
  @app.reload_stale
50
50
 
51
- @app.last_reload.should == now
51
+ expect(@app.last_reload).to eq(now)
52
52
  end
53
53
  end
54
54
  end