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.
Files changed (47) hide show
  1. data/.gitignore +4 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +27 -0
  4. data/LICENSE.txt +14 -0
  5. data/README.markdown +344 -0
  6. data/Rakefile +38 -0
  7. data/bin/gopher2000 +51 -0
  8. data/examples/default_route.rb +22 -0
  9. data/examples/nyan.rb +62 -0
  10. data/examples/simple.rb +147 -0
  11. data/examples/twitter.rb +61 -0
  12. data/examples/weather.rb +69 -0
  13. data/gopher2000.gemspec +35 -0
  14. data/lib/gopher2000/base.rb +552 -0
  15. data/lib/gopher2000/dispatcher.rb +81 -0
  16. data/lib/gopher2000/dsl.rb +128 -0
  17. data/lib/gopher2000/errors.rb +14 -0
  18. data/lib/gopher2000/handlers/base_handler.rb +18 -0
  19. data/lib/gopher2000/handlers/directory_handler.rb +125 -0
  20. data/lib/gopher2000/rendering/abstract_renderer.rb +10 -0
  21. data/lib/gopher2000/rendering/base.rb +174 -0
  22. data/lib/gopher2000/rendering/menu.rb +129 -0
  23. data/lib/gopher2000/rendering/text.rb +10 -0
  24. data/lib/gopher2000/request.rb +21 -0
  25. data/lib/gopher2000/response.rb +25 -0
  26. data/lib/gopher2000/server.rb +85 -0
  27. data/lib/gopher2000/version.rb +4 -0
  28. data/lib/gopher2000.rb +33 -0
  29. data/scripts/god.rb +8 -0
  30. data/spec/application_spec.rb +54 -0
  31. data/spec/dispatching_spec.rb +144 -0
  32. data/spec/dsl_spec.rb +116 -0
  33. data/spec/gopher_spec.rb +1 -0
  34. data/spec/handlers/directory_handler_spec.rb +116 -0
  35. data/spec/helpers_spec.rb +16 -0
  36. data/spec/rendering/base_spec.rb +59 -0
  37. data/spec/rendering/menu_spec.rb +109 -0
  38. data/spec/rendering_spec.rb +84 -0
  39. data/spec/request_spec.rb +30 -0
  40. data/spec/response_spec.rb +33 -0
  41. data/spec/routing_spec.rb +92 -0
  42. data/spec/sandbox/old/socks.txt +0 -0
  43. data/spec/sandbox/socks.txt +0 -0
  44. data/spec/server_spec.rb +127 -0
  45. data/spec/spec_helper.rb +52 -0
  46. data/specs.watchr +60 -0
  47. metadata +211 -0
@@ -0,0 +1,109 @@
1
+ require File.join(File.dirname(__FILE__), '/../spec_helper')
2
+
3
+ describe Gopher::Rendering::Menu do
4
+ before(:each) do
5
+ @app = Gopher::Application.new
6
+ @app.reset!
7
+ @app.config[:host] = "host"
8
+ @app.config[:port] = 1234
9
+
10
+ @ctx = Gopher::Rendering::Menu.new(@app)
11
+ end
12
+
13
+ it 'should add text as a gopher line' do
14
+ @ctx.text("gopher forever")
15
+ @ctx.result.should == "igopher forever\tnull\t(FALSE)\t0\r\n"
16
+ end
17
+
18
+ describe "sanitize_text" do
19
+ it "should remove extra whitespace from end of line" do
20
+ @ctx.sanitize_text("x ").should == "x"
21
+ end
22
+
23
+ it "should convert tabs to spaces" do
24
+ @ctx.sanitize_text("x\tx").should == "x x"
25
+ end
26
+
27
+ it "should remove newlines" do
28
+ @ctx.sanitize_text("x\nx").should == "xx"
29
+ end
30
+ end
31
+
32
+ describe "line" do
33
+ it "should work" do
34
+ @ctx.line("type", "text", "selector", "host", "port").should == "typetext\tselector\thost\tport\r\n"
35
+ end
36
+
37
+ it "should use application host/port as defaults" do
38
+ @ctx.line("type", "text", "selector").should == "typetext\tselector\thost\t1234\r\n"
39
+ end
40
+ end
41
+
42
+ describe "error" do
43
+ it "should call text with right selector" do
44
+ @ctx.should_receive(:text).with("foo", '3')
45
+ @ctx.error("foo")
46
+ end
47
+ end
48
+
49
+ describe "directory" do
50
+ it "should call link with right selector" do
51
+ @ctx.should_receive(:line).with("1", "foo", "/bar/foo", nil, nil)
52
+ @ctx.directory("foo", "/bar/foo")
53
+ end
54
+ end
55
+
56
+ describe "link" do
57
+ it "should get type with determine_type" do
58
+ @ctx.should_receive(:determine_type).with("foo.txt").and_return("A")
59
+ @ctx.link("FILE", "foo.txt").should == "AFILE\tfoo.txt\thost\t1234\r\n"
60
+ end
61
+ end
62
+
63
+ describe "search" do
64
+ it "should output link type/text" do
65
+ @ctx.search("FIND", "search").should == "7FIND\tsearch\thost\t1234\r\n"
66
+ end
67
+ end
68
+
69
+ describe "menu" do
70
+ it "should output link type/text" do
71
+ @ctx.menu("MENU ITEM", "item").should == "1MENU ITEM\titem\thost\t1234\r\n"
72
+ end
73
+ end
74
+
75
+ describe "br" do
76
+ it "should generate an empty text line" do
77
+ @ctx.should_receive(:text).with("i", "")
78
+ @ctx.br
79
+ end
80
+
81
+ it "should call #text multiple times" do
82
+ @ctx.should_receive(:text).twice.with("i", "")
83
+ @ctx.br(2)
84
+ end
85
+
86
+ it "should output an empty menu item" do
87
+ @ctx.br
88
+ @ctx.result.should == "i\tnull\t(FALSE)\t0\r\n"
89
+ end
90
+ end
91
+
92
+ context "determine_type" do
93
+ {
94
+ "foo.zip" => '5',
95
+ "foo.gz" => '5',
96
+ "foo.bz2" => '5',
97
+ "foo.gif" => 'g',
98
+ "foo.jpg" => 'I',
99
+ "foo.png" => 'I',
100
+ "foo.mp3" => 's',
101
+ "foo.wav" => 's',
102
+ "foo.random-file" => "0"
103
+ }.each do |file, expected|
104
+ it "should have right selector for #{file}" do
105
+ @ctx.determine_type(file).should == expected
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,84 @@
1
+ require File.join(File.dirname(__FILE__), '/spec_helper')
2
+
3
+ class MockServer < Gopher::Application
4
+ attr_accessor :menus, :text_templates, :params, :request
5
+
6
+ def initialize
7
+ @menus = {}
8
+ @text_templates = {}
9
+ end
10
+ end
11
+
12
+
13
+ describe Gopher::Application do
14
+ before(:each) do
15
+ @s = MockServer.new
16
+ end
17
+
18
+ describe "find_template" do
19
+ it "should check in menus" do
20
+ @s.menus['foo'] = "bar"
21
+ @s.find_template('foo').should == ["bar", Gopher::Rendering::Menu]
22
+ end
23
+ it "should check in text_templates" do
24
+ @s.text_templates['foo'] = "bar"
25
+ @s.find_template('foo').should == ["bar", Gopher::Rendering::Text]
26
+ end
27
+ end
28
+
29
+ describe "render" do
30
+ it "should raise error if no such template" do
31
+ expect{@s.render('xyzzy')}.to raise_error(Gopher::TemplateNotFound)
32
+ end
33
+
34
+ it "has access to params obj" do
35
+ @s.params = "xyz"
36
+ @s.menu :foo do
37
+ @params
38
+ end
39
+
40
+ @s.render(:foo).should == "xyz"
41
+ end
42
+
43
+ it "has access to request obj" do
44
+ @s.request = "abc"
45
+ @s.menu :foo do
46
+ @request
47
+ end
48
+
49
+ @s.render(:foo).should == "abc"
50
+ end
51
+
52
+ it "rendering text access to request obj" do
53
+ @s.request = "abc"
54
+ @s.text :foo do
55
+ @request
56
+ end
57
+
58
+ @s.render(:foo).should == "abc"
59
+ end
60
+ end
61
+
62
+ describe "not_found_template" do
63
+ before(:each) do
64
+ @s.reset!
65
+ end
66
+
67
+ it "should use custom template if provided" do
68
+ @s.not_found do ; end
69
+ @s.not_found_template.should == :not_found
70
+ end
71
+
72
+ it "should use default otherwise" do
73
+ @s.not_found_template.should == :'internal/not_found'
74
+ end
75
+ end
76
+
77
+ # describe "register_defaults" do
78
+ # it "should add to internal" do
79
+ # @s.find_template(:'internal/not_found').should be_nil
80
+ # @s.register_defaults
81
+ # @s.find_template(:'internal/not_found').should_not be_nil
82
+ # end
83
+ # end
84
+ end
@@ -0,0 +1,30 @@
1
+ require File.join(File.dirname(__FILE__), '/spec_helper')
2
+
3
+ describe Gopher::Request do
4
+ it 'should split raw request' do
5
+ request = Gopher::Request.new("foo\tbar")
6
+ request.selector.should == "foo"
7
+ request.input.should == "bar"
8
+ end
9
+
10
+ it "should be ok with just selector" do
11
+ request = Gopher::Request.new("foo")
12
+ request.selector.should == "foo"
13
+ request.input.should == nil
14
+ end
15
+
16
+ it "should accept ip_address" do
17
+ request = Gopher::Request.new("foo", "bar")
18
+ request.ip_address.should == "bar"
19
+ end
20
+
21
+ it "valid? == true for valid selectors" do
22
+ request = Gopher::Request.new("x" * 255, "bar")
23
+ request.valid?.should == true
24
+ end
25
+
26
+ it "valid? == false for invalid selectors" do
27
+ request = Gopher::Request.new("x" * 256, "bar")
28
+ request.valid?.should == false
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ require File.join(File.dirname(__FILE__), '/spec_helper')
2
+ require 'tempfile'
3
+
4
+ describe Gopher::Response do
5
+ before(:each) do
6
+ @response = Gopher::Response.new
7
+ end
8
+
9
+ it "gets size for string results" do
10
+ @response.body = "hi"
11
+ @response.size.should == 2
12
+ end
13
+
14
+ it "gets size for stringio results" do
15
+ @response.body = StringIO.new("12345")
16
+ @response.size.should == 5
17
+ end
18
+
19
+ it "defaults to 0 size for weird objects" do
20
+ @response.body = mock(Object)
21
+ @response.size.should == 0
22
+ end
23
+
24
+
25
+ it "gets size for file results" do
26
+ temp_file = Tempfile.new('result')
27
+ temp_file.write("1234567890")
28
+ temp_file.flush
29
+
30
+ @response.body = File.new(temp_file.path)
31
+ @response.size.should == 10
32
+ end
33
+ end
@@ -0,0 +1,92 @@
1
+ require File.join(File.dirname(__FILE__), '/spec_helper')
2
+
3
+ class MockServer < Gopher::Application
4
+ # attr_accessor :routes
5
+ # include Gopher::Routing
6
+ end
7
+
8
+ describe Gopher::Application do
9
+ before(:each) do
10
+ @router = MockServer.new
11
+ end
12
+
13
+ describe "default_route" do
14
+ it "should set route as default" do
15
+ @router.default_route do
16
+ "hi"
17
+ end
18
+ junk, block = @router.lookup("sfssfdfsfsd")
19
+ block.class.should eql(UnboundMethod)
20
+ end
21
+ end
22
+
23
+ describe "globify" do
24
+ it "should add glob if none yet" do
25
+ @router.globify("/foo").should == "/foo/?*"
26
+ end
27
+
28
+ it "should be ok with trailing slashes" do
29
+ @router.globify("/foo/").should == "/foo/?*"
30
+ end
31
+
32
+ it "shouldn't add glob if there is one already" do
33
+ @router.globify("/foo/*").should == "/foo/*"
34
+ end
35
+ end
36
+
37
+ describe "mount" do
38
+ before(:each) do
39
+ @h = mock(Gopher::Handlers::DirectoryHandler)
40
+ @h.should_receive(:application=).with(@router)
41
+
42
+ Gopher::Handlers::DirectoryHandler.should_receive(:new).with({:bar => :baz, :mount_point => "/foo"}).and_return(@h)
43
+ end
44
+
45
+ it "should work" do
46
+ @router.mount("/foo", :bar => :baz)
47
+ end
48
+ end
49
+
50
+ describe "compile" do
51
+ it "should generate a basic string for routes without keys" do
52
+ lookup, keys, block = @router.compile! "/foo" do; end
53
+ lookup.to_s.should == /^\/foo$/.to_s
54
+ keys.should == []
55
+ end
56
+
57
+ context "with keys" do
58
+ it "should generate a lookup and keys for routes with keys" do
59
+ lookup, keys, block = @router.compile! "/foo/:bar" do; end
60
+ lookup.to_s.should == "(?-mix:^\\/foo\\/([^\\/?#]+)$)"
61
+
62
+ keys.should == ["bar"]
63
+ end
64
+
65
+ it "should match correctly" do
66
+ lookup, keys, block = @router.compile! "/foo/:bar" do; end
67
+ lookup.to_s.should == "(?-mix:^\\/foo\\/([^\\/?#]+)$)"
68
+
69
+ lookup.match("/foo/baz").should_not be_nil
70
+ lookup.match("/foo2/baz").should be_nil
71
+ lookup.match("/baz/foo/baz").should be_nil
72
+ lookup.match("/foo/baz/bar").should be_nil
73
+ end
74
+ end
75
+
76
+ context "with splat" do
77
+ it "should work with splats" do
78
+ lookup, keys, block = @router.compile! "/foo/*" do; end
79
+ lookup.to_s.should == "(?-mix:^\\/foo\\/(.*?)$)"
80
+ keys.should == ["splat"]
81
+ end
82
+
83
+ it "should match correctly" do
84
+ lookup, keys, block = @router.compile! "/foo/*" do; end
85
+
86
+ lookup.match("/foo/baz/bar/bam").should_not be_nil
87
+ lookup.match("/foo2/baz").should be_nil
88
+ lookup.match("/baz/foo/baz").should be_nil
89
+ end
90
+ end
91
+ end
92
+ end
File without changes
File without changes
@@ -0,0 +1,127 @@
1
+ require File.join(File.dirname(__FILE__), '/spec_helper')
2
+ require 'tempfile'
3
+
4
+ if ENV["WITH_SERVER_SPECS"].to_i == 1
5
+
6
+ class FakeApp < Gopher::Application
7
+ attr_accessor :fake_response
8
+ def dispatch(x)
9
+ @fake_response
10
+ end
11
+ end
12
+
13
+ describe Gopher::Server do
14
+ before(:each) do
15
+ @application = FakeApp.new
16
+ @application.scripts = []
17
+ @application.reset!
18
+
19
+ @host = "0.0.0.0"
20
+ @port = 12345
21
+
22
+ @application.config[:host] = @host
23
+ @application.config[:port] = @port
24
+
25
+ @request = Gopher::Request.new("foo", "bar")
26
+
27
+ @response = Gopher::Response.new(@request)
28
+ @response.code = :success
29
+ @response.body = "hi"
30
+ end
31
+
32
+ it "should work in non-blocking mode" do
33
+ @application.fake_response = @response
34
+ @application.stub!(:non_blocking?).and_return(false)
35
+
36
+ ::EM.run {
37
+ server = Gopher::Server.new(@application)
38
+ server.run!
39
+
40
+ # opens the socket client connection
41
+ socket = ::EM.connect(@host, @port, FakeSocketClient)
42
+ socket.send_data("123\n")
43
+
44
+ socket.onopen = lambda {
45
+ socket.data.last.chomp.should == "hi\r\n."
46
+ EM.stop
47
+ }
48
+ }
49
+ end
50
+
51
+ it "should handle Gopher::Response results" do
52
+ @application.fake_response = @response
53
+
54
+ ::EM.run {
55
+ server = Gopher::Server.new(@application)
56
+ server.run!
57
+
58
+ # opens the socket client connection
59
+ socket = ::EM.connect(@host, @port, FakeSocketClient)
60
+ socket.send_data("123\n")
61
+
62
+ socket.onopen = lambda {
63
+ socket.data.last.chomp.should == "hi\r\n."
64
+ EM.stop
65
+ }
66
+ }
67
+ end
68
+
69
+ it "should handle string results" do
70
+ @application.fake_response = @response
71
+
72
+ ::EM.run {
73
+ server = Gopher::Server.new(@application)
74
+ server.run!
75
+
76
+ # opens the socket client connection
77
+ socket = ::EM.connect(@host, @port, FakeSocketClient)
78
+ socket.send_data("123\n")
79
+
80
+ socket.onopen = lambda {
81
+ socket.data.last.chomp.should == "hi\r\n."
82
+ EM.stop
83
+ }
84
+ }
85
+ end
86
+
87
+ it "should handle File results" do
88
+ file = Tempfile.new('foo')
89
+ file.write("hi")
90
+ file.close
91
+
92
+ @application.fake_response = File.new(file)
93
+
94
+ ::EM.run {
95
+ server = Gopher::Server.new(@application)
96
+ server.run!
97
+
98
+ # opens the socket client connection
99
+ socket = ::EM.connect(@host, @port, FakeSocketClient)
100
+ socket.send_data("123\n")
101
+
102
+ socket.onopen = lambda {
103
+ socket.data.last.chomp.should == "hi"
104
+ EM.stop
105
+ }
106
+ }
107
+ end
108
+
109
+ it "should handle StringIO results" do
110
+ @application.fake_response = StringIO.new("hi")
111
+
112
+ ::EM.run {
113
+ server = Gopher::Server.new(@application)
114
+ server.run!
115
+
116
+ # opens the socket client connection
117
+ socket = ::EM.connect(@host, @port, FakeSocketClient)
118
+ socket.send_data("123\n")
119
+
120
+ socket.onopen = lambda {
121
+ socket.data.last.chomp.should == "hi\r\n."
122
+ EM.stop
123
+ }
124
+ }
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,52 @@
1
+ ENV['gopher_test'] = "1"
2
+
3
+ require 'simplecov'
4
+ SimpleCov.start do
5
+ add_filter "/spec/"
6
+ end
7
+
8
+ require 'bundler/setup'
9
+ Bundler.require
10
+
11
+ # Requires supporting files with custom matchers and macros, etc,
12
+ # in ./support/ and its subdirectories.
13
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
14
+
15
+
16
+ require 'eventmachine'
17
+
18
+ #
19
+ # http://www.rosskaff.com/2010/12/behavior-driven-event-driven-eventmachine-rspec/
20
+ #
21
+ class FakeSocketClient < EventMachine::Connection
22
+
23
+ attr_writer :onopen, :onclose, :onmessage
24
+ attr_reader :data
25
+
26
+ def initialize
27
+ super
28
+
29
+ @state = :new
30
+ @data = []
31
+ end
32
+
33
+ def receive_data(data)
34
+ #puts "RECV: #{data}"
35
+ @data << data
36
+ if @state == :new
37
+ @onopen.call if @onopen
38
+ @state = :open
39
+ else
40
+ @onmessage.call(data) if @onmessage
41
+ end
42
+ end
43
+
44
+ def unbind
45
+ @onclose.call if @onclose
46
+ end
47
+ end
48
+
49
+
50
+ class FakeSocketServer < FakeSocketClient
51
+ attr_accessor :application
52
+ end
data/specs.watchr ADDED
@@ -0,0 +1,60 @@
1
+ # Run me with:
2
+ #
3
+ # $ watchr specs.watchr
4
+
5
+ # --------------------------------------------------
6
+ # Convenience Methods
7
+ # --------------------------------------------------
8
+ def all_spec_files
9
+ Dir['spec/**/*_spec.rb']
10
+ end
11
+
12
+ def run_spec_matching(thing_to_match)
13
+ puts "run #{thing_to_match}"
14
+ matches = all_spec_files.grep(/#{thing_to_match}/i)
15
+ if matches.empty?
16
+ puts "Sorry, thanks for playing, but there were no matches for #{thing_to_match}"
17
+ else
18
+ run matches.join(' ')
19
+ end
20
+ end
21
+
22
+ def run(files_to_run)
23
+ puts("Running: #{files_to_run}")
24
+ system("clear;rspec -cfs #{files_to_run}")
25
+ no_int_for_you
26
+ end
27
+
28
+ def run_all_specs
29
+ run(all_spec_files.join(' '))
30
+ end
31
+
32
+ # --------------------------------------------------
33
+ # Watchr Rules
34
+ # --------------------------------------------------
35
+ watch('^spec/(.*)_spec\.rb') { |m| run_spec_matching(m[1]) }
36
+ watch("^lib/gopher2000/(.*)\.rb") { |m| run_all_specs }
37
+
38
+ watch('^spec/spec_helper\.rb') { run_all_spec }
39
+
40
+ # --------------------------------------------------
41
+ # Signal Handling
42
+ # --------------------------------------------------
43
+
44
+ def no_int_for_you
45
+ @sent_an_int = nil
46
+ end
47
+
48
+ Signal.trap 'INT' do
49
+ if @sent_an_int then
50
+ puts " A second INT? Ok, I get the message. Shutting down now."
51
+ exit
52
+ else
53
+ puts " Did you just send me an INT? Ugh. I'll quit for real if you do it again."
54
+ @sent_an_int = true
55
+ Kernel.sleep 1.5
56
+ run_all_specs
57
+ end
58
+ end
59
+
60
+ run_all_specs