serverside 0.2.9 → 0.3.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 (45) hide show
  1. data/CHANGELOG +56 -0
  2. data/Rakefile +12 -52
  3. data/bin/serverside +1 -1
  4. data/lib/serverside/application.rb +2 -1
  5. data/lib/serverside/caching.rb +62 -50
  6. data/lib/serverside/controllers.rb +91 -0
  7. data/lib/serverside/core_ext.rb +6 -0
  8. data/lib/serverside/daemon.rb +25 -5
  9. data/lib/serverside/request.rb +17 -11
  10. data/lib/serverside/routing.rb +11 -10
  11. data/lib/serverside/server.rb +14 -6
  12. data/lib/serverside/static.rb +7 -18
  13. data/lib/serverside/template.rb +20 -12
  14. data/spec/caching_spec.rb +318 -0
  15. data/spec/cluster_spec.rb +140 -0
  16. data/{test/spec → spec}/connection_spec.rb +4 -4
  17. data/{test/spec/controller_spec.rb → spec/controllers_spec.rb} +15 -12
  18. data/{test/spec → spec}/core_ext_spec.rb +23 -18
  19. data/spec/daemon_spec.rb +99 -0
  20. data/{test/spec → spec}/request_spec.rb +45 -45
  21. data/spec/routing_spec.rb +240 -0
  22. data/spec/server_spec.rb +40 -0
  23. data/spec/static_spec.rb +279 -0
  24. data/spec/template_spec.rb +129 -0
  25. metadata +21 -35
  26. data/lib/serverside/controller.rb +0 -67
  27. data/test/functional/primitive_static_server_test.rb +0 -61
  28. data/test/functional/request_body_test.rb +0 -93
  29. data/test/functional/routing_server.rb +0 -14
  30. data/test/functional/routing_server_test.rb +0 -41
  31. data/test/functional/static_profile.rb +0 -17
  32. data/test/functional/static_rfuzz.rb +0 -31
  33. data/test/functional/static_server.rb +0 -7
  34. data/test/functional/static_server_test.rb +0 -31
  35. data/test/spec/caching_spec.rb +0 -139
  36. data/test/test_helper.rb +0 -2
  37. data/test/unit/cluster_test.rb +0 -129
  38. data/test/unit/connection_test.rb +0 -48
  39. data/test/unit/core_ext_test.rb +0 -46
  40. data/test/unit/daemon_test.rb +0 -75
  41. data/test/unit/request_test.rb +0 -278
  42. data/test/unit/routing_test.rb +0 -171
  43. data/test/unit/server_test.rb +0 -28
  44. data/test/unit/static_test.rb +0 -171
  45. data/test/unit/template_test.rb +0 -78
@@ -6,10 +6,11 @@ module ServerSide
6
6
  module Static
7
7
  include HTTP::Caching
8
8
 
9
- ETAG_FORMAT = '%x:%x:%x'.inspect.freeze
9
+ ETAG_FORMAT = '%x:%x:%x'.freeze
10
10
  TEXT_PLAIN = 'text/plain'.freeze
11
11
  TEXT_HTML = 'text/html'.freeze
12
12
  MAX_CACHE_FILE_SIZE = 100000.freeze # 100KB for the moment
13
+ MAX_AGE = 86400 # one day
13
14
 
14
15
  DIR_LISTING_START = '<html><head><title>Directory Listing for %s</title></head><body><h2>Directory listing for %s:</h2>'.freeze
15
16
  DIR_LISTING = '<a href="%s">%s</a><br/>'.freeze
@@ -29,8 +30,6 @@ module ServerSide
29
30
  '.png'.freeze => 'image/png'.freeze
30
31
  })
31
32
 
32
- @@static_files = {}
33
-
34
33
  # Serves a file over HTTP. The file is cached in memory for later retrieval.
35
34
  # If the If-None-Match header is included with an ETag, it is checked
36
35
  # against the file's current ETag. If there's a match, a 304 response is
@@ -38,17 +37,11 @@ module ServerSide
38
37
  def serve_file(fn)
39
38
  stat = File.stat(fn)
40
39
  etag = (ETAG_FORMAT % [stat.mtime.to_i, stat.size, stat.ino]).freeze
41
- validate_cache(etag, stat.mtime) do
42
- if @@static_files[fn] && (@@static_files[fn][0] == etag)
43
- content = @@static_files[fn][1]
44
- else
45
- content = IO.read(fn).freeze
46
- @@static_files[fn] = [etag.freeze, content]
47
- end
48
- send_response(200, @@mime_types[File.extname(fn)], content, stat.size)
40
+ validate_cache(stat.mtime, MAX_AGE, etag) do
41
+ send_response(200, @@mime_types[File.extname(fn)], IO.read(fn),
42
+ stat.size)
49
43
  end
50
44
  rescue => e
51
- puts e.message
52
45
  send_response(404, TEXT_PLAIN, 'Error reading file.')
53
46
  end
54
47
 
@@ -64,17 +57,13 @@ module ServerSide
64
57
  end
65
58
 
66
59
  def serve_template(fn, b = nil)
67
- if (fn =~ RHTML) || (File.file?(fn = fn + '.rhtml'))
68
- send_response(200, TEXT_HTML, Template.render(fn, b || binding))
69
- end
60
+ send_response(200, TEXT_HTML, Template.render(fn, b || binding))
70
61
  end
71
62
 
72
63
  # Serves static files and directory listings.
73
64
  def serve_static(path)
74
65
  if File.file?(path)
75
- serve_file(path)
76
- elsif serve_template(path)
77
- return
66
+ path =~ RHTML ? serve_template(path) : serve_file(path)
78
67
  elsif File.directory?(path)
79
68
  if File.file?(path/'index.html')
80
69
  serve_file(path/'index.html')
@@ -4,33 +4,41 @@ module ServerSide
4
4
  # The Template module implements an ERB template rendering system. Templates
5
5
  # are cached and automatically reloaded if the file changes.
6
6
  class Template
7
- # The @@templates variable is a hash keyed by template name. The values are
7
+ # The @@templates variable caches templates in use. The values are
8
8
  # arrays containing 2 objects: a file stamp (if the template comes from a
9
- # file,) and the template object itself.
9
+ # file,) and the template object itself.
10
10
  @@templates = {}
11
11
 
12
- # Stores a template for later use. The stamp parameter is used only when
12
+ # Caches a template for later use. The stamp parameter is used only when
13
13
  # the content of a template file is stored.
14
14
  def self.set(name, body, stamp = nil)
15
15
  @@templates[name] = [stamp, ERB.new(body)]
16
16
  end
17
17
 
18
- # Renders a template. If the template name is not found, attemps to load
19
- # the template from file. If the template has a non-nil stamp, the render
20
- # method compares it to the file stamp, and reloads the template content
21
- # if necessary.
22
- def self.render(name, binding)
18
+ # Validates the referenced template by checking its stamp. If the name
19
+ # refers to a file, its stamp is checked against the cache stamp, and it
20
+ # is reloaded if necessary. The function returns an ERB instance or nil if
21
+ # the template is not found.
22
+ def self.validate(name)
23
23
  t = @@templates[name]
24
- return t[1].result(binding) if t && t[0].nil?
25
-
24
+ return t[1] if t && t[0].nil?
26
25
  if File.file?(name)
27
26
  stamp = File.mtime(name)
28
27
  t = set(name, IO.read(name), stamp) if (!t || (stamp != t[0]))
29
- t[1].result(binding)
28
+ t[1]
30
29
  else
31
30
  @@templates[name] = nil
31
+ end
32
+ end
33
+
34
+ # Renders a template by first validating it, and by invoking it with the
35
+ # supplied binding.
36
+ def self.render(name, binding)
37
+ if template = validate(name)
38
+ template.result(binding)
39
+ else
32
40
  raise RuntimeError, 'Template not found.'
33
41
  end
34
42
  end
35
- end
43
+ end
36
44
  end
@@ -0,0 +1,318 @@
1
+ require File.join(File.dirname(__FILE__), '../lib/serverside')
2
+ require 'stringio'
3
+ include ServerSide::HTTP
4
+
5
+ class DummyRequest < Request
6
+ attr_accessor :socket, :persistent
7
+ include Caching
8
+
9
+ def initialize
10
+ super(StringIO.new)
11
+ @headers = {}
12
+ end
13
+ end
14
+
15
+ context "Caching#disable_caching" do
16
+ specify "should set the Cache-Control header to no-cache" do
17
+ r = DummyRequest.new
18
+ r.response_headers['Cache-Control'].should_be_nil
19
+ r.disable_caching
20
+ r.response_headers['Cache-Control'].should == 'no-cache'
21
+ end
22
+
23
+ specify "should remove all other cache-related headers" do
24
+ r = DummyRequest.new
25
+ r.response_headers['ETag'] = 'something'
26
+ r.response_headers['Vary'] = 'something'
27
+ r.response_headers['Expires'] = 'something'
28
+ r.response_headers['Last-Modified'] = 'something'
29
+ r.disable_caching
30
+ r.response_headers['ETag'].should_be_nil
31
+ r.response_headers['Vary'].should_be_nil
32
+ r.response_headers['Expires'].should_be_nil
33
+ r.response_headers['Last-Modified'].should_be_nil
34
+ end
35
+ end
36
+
37
+ context "Caching#etag_validators" do
38
+ specify "should return an empty array if no validators are present" do
39
+ r = DummyRequest.new
40
+ r.etag_validators.should == []
41
+ end
42
+
43
+ specify "should return an array containing all etag validators" do
44
+ r = DummyRequest.new
45
+ r.headers['If-None-Match'] = '"aaa-bbb"'
46
+ r.etag_validators.should == ['aaa-bbb']
47
+
48
+ r.headers['If-None-Match'] = '"aaa-bbb", "ccc-ddd"'
49
+ r.etag_validators.should == ['aaa-bbb', 'ccc-ddd']
50
+ end
51
+
52
+ specify "should handle etags with and without quotes" do
53
+ r = DummyRequest.new
54
+ r.headers['If-None-Match'] = 'aaa-bbb'
55
+ r.etag_validators.should == ['aaa-bbb']
56
+
57
+ r.headers['If-None-Match'] = 'aaa-bbb, "ccc-ddd"'
58
+ r.etag_validators.should == ['aaa-bbb', 'ccc-ddd']
59
+ end
60
+
61
+ specify "should handle a wildcard validator" do
62
+ r = DummyRequest.new
63
+ r.headers['If-None-Match'] = '*'
64
+ r.etag_validators.should == ['*']
65
+ end
66
+ end
67
+
68
+ context "Caching#valid_etag?" do
69
+ specify "should return nil if no validator matches the specified etag" do
70
+ r = DummyRequest.new
71
+ r.valid_etag?('xxx-yyy').should_be_nil
72
+
73
+ r.headers['If-None-Match'] = 'xx-yy, aaa-bbb'
74
+ r.valid_etag?('xxx-yyy').should_be_nil
75
+ end
76
+
77
+ specify "should return true if a validator matches the specifed etag" do
78
+ r = DummyRequest.new
79
+
80
+ r.headers['If-None-Match'] = 'xxx-yyy'
81
+ r.valid_etag?('xxx-yyy').should_be true
82
+
83
+ r.headers['If-None-Match'] = '"xxx-yyy"'
84
+ r.valid_etag?('xxx-yyy').should_be true
85
+
86
+ r.headers['If-None-Match'] = 'aaa-bbb, xxx-yyy'
87
+ r.valid_etag?('xxx-yyy').should_be true
88
+
89
+ r.headers['If-None-Match'] = 'xxx-yyy, aaa-bbb'
90
+ r.valid_etag?('xxx-yyy').should_be true
91
+ end
92
+
93
+ specify "should return true if a wildcard is included in If-None-Match" do
94
+ r = DummyRequest.new
95
+
96
+ r.headers['If-None-Match'] = '*'
97
+ r.valid_etag?('xxx-yyy').should_be true
98
+
99
+ r.headers['If-None-Match'] = 'aaa-bbb, *'
100
+ r.valid_etag?('xxx-yyy').should_be true
101
+ end
102
+ end
103
+
104
+ context "Caching#valid_etag? in expiry etag mode (no etag specified)" do
105
+ specify "should return nil if no etag validator is included" do
106
+ r = DummyRequest.new
107
+ r.valid_etag?.should_be_nil
108
+ end
109
+
110
+ specify "should return true if If-None-Match includes a wildcard" do
111
+ r = DummyRequest.new
112
+
113
+ r.headers['If-None-Match'] = '*'
114
+ r.valid_etag?.should_be true
115
+ end
116
+
117
+ specify "should ignore validators not formatted as expiry etags" do
118
+ r = DummyRequest.new
119
+
120
+ r.headers['If-None-Match'] = 'abcd'
121
+ r.valid_etag?.should_be_nil
122
+
123
+ r.headers['If-None-Match'] = 'xxx-yyy, zzz-zzz'
124
+ r.valid_etag?.should_be_nil
125
+ end
126
+
127
+ specify "should parse expiry etags and check the expiration stamp" do
128
+ r = DummyRequest.new
129
+ t = Time.now
130
+ fmt = Caching::EXPIRY_ETAG_FORMAT
131
+
132
+ r.headers['If-None-Match'] = fmt % [t.to_i, (t - 20).to_i]
133
+ r.valid_etag?.should_be_nil
134
+
135
+ r.headers['If-None-Match'] = fmt % [t.to_i, (t + 20).to_i]
136
+ r.valid_etag?.should_be true
137
+
138
+ r.headers['If-None-Match'] = "xxx-yyy, #{fmt % [t.to_i, (t + 20).to_i]}, #{fmt % [t.to_i, (t - 20).to_i]}"
139
+ r.valid_etag?.should_be true
140
+ end
141
+ end
142
+
143
+ context "Caching#expiry_etag" do
144
+ specify "should return an expiry etag with the stamp and expiration time" do
145
+ r = DummyRequest.new
146
+
147
+ t = Time.now
148
+ fmt = Caching::EXPIRY_ETAG_FORMAT
149
+ max_age = 54321
150
+
151
+ r.expiry_etag(t, max_age).should == (fmt % [t.to_i, (t + max_age).to_i])
152
+ end
153
+ end
154
+
155
+ context "Caching#valid_stamp?" do
156
+ specify "should return nil if no If-Modified-Since header is included" do
157
+ r = DummyRequest.new
158
+ r.valid_stamp?(Time.now).should_be_nil
159
+ end
160
+
161
+ specify "should return nil if the If-Modified-Since header is different than the specified stamp" do
162
+ t = Time.now
163
+ r = DummyRequest.new
164
+ r.headers['If-Modified-Since'] = t.httpdate
165
+ r.valid_stamp?(t + 1).should_be_nil
166
+ r.valid_stamp?(t - 1).should_be_nil
167
+ end
168
+
169
+ specify "should return true if the If-Modified-Since header matches the specified stamp" do
170
+ t = Time.now
171
+ r = DummyRequest.new
172
+ r.headers['If-Modified-Since'] = t.httpdate
173
+ r.valid_stamp?(t).should_be true
174
+ end
175
+ end
176
+
177
+ context "Caching#validate_cache" do
178
+ specify "should return nil if no validators are present" do
179
+ r = DummyRequest.new
180
+ r.validate_cache(Time.now, 360).should_be_nil
181
+ end
182
+
183
+ specify "should check for a stamp validator" do
184
+ r = DummyRequest.new
185
+ t = Time.now
186
+
187
+ r.headers['If-Modified-Since'] = t.httpdate
188
+ r.validate_cache(t + 1, 360).should_be_nil
189
+ r.validate_cache(t - 1, 360).should_be_nil
190
+ r.validate_cache(t, 360).should_be true
191
+ end
192
+
193
+ specify "should check for an etag validator" do
194
+ r = DummyRequest.new
195
+ t = Time.now
196
+ etag = 'abcdef'
197
+
198
+ r.validate_cache(t, 360, etag).should_be_nil
199
+ r.headers['If-None-Match'] = 'aaa-bbb'
200
+ r.validate_cache(t, 360, etag).should_be_nil
201
+ r.headers['If-None-Match'] = "aaa-bbb, #{etag}"
202
+ r.validate_cache(t, 360, etag).should_be true
203
+ r.headers['If-None-Match'] = '*'
204
+ r.validate_cache(t, 360, etag).should_be true
205
+ end
206
+
207
+ specify "should check for an expiry etag validator if etag is unspecified" do
208
+ r = DummyRequest.new
209
+ t = Time.now
210
+ fmt = Caching::EXPIRY_ETAG_FORMAT
211
+
212
+ r.headers['If-None-Match'] = 'aaa-bbb'
213
+ r.validate_cache(t, 360).should_be_nil
214
+ r.headers['If-None-Match'] = "aaa-bbb, #{fmt % [t.to_i, (t + 20).to_i]}"
215
+ r.validate_cache(t, 360).should_be true
216
+ r.headers['If-None-Match'] = '*'
217
+ r.validate_cache(t, 360).should_be true
218
+ end
219
+
220
+ specify "should set the response headers with caching info if request did not validate" do
221
+ r = DummyRequest.new
222
+ t = Time.now
223
+ r.validate_cache(t, 360, 'aaa-bbb', :public, 'Cookie')
224
+ r.response_headers['ETag'].should == '"aaa-bbb"'
225
+ r.response_headers['Last-Modified'].should == t.httpdate
226
+ r.response_headers['Expires'].should == ((t + 360).httpdate)
227
+ r.response_headers['Cache-Control'].should == :public
228
+ r.response_headers['Vary'].should == 'Cookie'
229
+ end
230
+
231
+ specify "should set an expiry etag if no etag is specified" do
232
+ r = DummyRequest.new
233
+ t = Time.now
234
+ fmt = Caching::EXPIRY_ETAG_FORMAT
235
+ r.validate_cache(t, 360)
236
+ r.response_headers['ETag'].should == (
237
+ "\"#{fmt % [t.to_i, (t + 360).to_i]}\"")
238
+ end
239
+
240
+ specify "should send a 304 response if the cache validates" do
241
+ r = DummyRequest.new
242
+ t = Time.now
243
+ fmt = Caching::EXPIRY_ETAG_FORMAT
244
+
245
+ r.headers['If-None-Match'] = "aaa-bbb, #{fmt % [t.to_i, (t + 20).to_i]}"
246
+ r.validate_cache(t, 360).should_be true
247
+ r.socket.rewind
248
+ resp = r.socket.read
249
+ resp.should_match /^HTTP\/1.1 304 Not Modified\r\n/
250
+
251
+ r = DummyRequest.new
252
+ t = Time.now
253
+
254
+ r.headers['If-Modified-Since'] = t.httpdate
255
+ r.validate_cache(t, 360).should_be true
256
+ r.socket.rewind
257
+ resp = r.socket.read
258
+ resp.should_match /^HTTP\/1.1 304 Not Modified\r\n/
259
+ end
260
+
261
+ specify "should not send anything if the cache doesn't validate" do
262
+ r = DummyRequest.new
263
+ t = Time.now
264
+
265
+ r.validate_cache(t, 360).should_be_nil
266
+ r.socket.rewind
267
+ resp = r.socket.read
268
+ resp.should_be_empty
269
+ end
270
+
271
+ specify "should not execute the given block if the cache validates" do
272
+ r = DummyRequest.new
273
+ t = Time.now
274
+ r.headers['If-Modified-Since'] = t.httpdate
275
+ proc {r.validate_cache(t, 360) {raise}}.should_not_raise
276
+ end
277
+
278
+ specify "should return the result of the given block if the cache doesn't validate" do
279
+ x = nil
280
+ l = proc {x = :executed}
281
+
282
+ r = DummyRequest.new
283
+ t = Time.now
284
+ r.validate_cache(t, 360, &l).should == :executed
285
+ x.should == :executed
286
+ end
287
+ end
288
+
289
+ context "Caching#send_not_modified_response" do
290
+ specify "should render a 304 response" do
291
+ r = DummyRequest.new
292
+ r.send_not_modified_response
293
+ r.socket.rewind
294
+ resp = r.socket.read
295
+ resp.should_match /^HTTP\/1.1 304 Not Modified\r\n/
296
+ resp.should_match /Content-Length: 0\r\n/
297
+ resp.should_match /\r\n\r\n$/ # empty response body
298
+ end
299
+
300
+ specify "should exclude Connection header if persistent" do
301
+ r = DummyRequest.new
302
+ r.persistent = true
303
+ r.send_not_modified_response
304
+ r.socket.rewind
305
+ resp = r.socket.read
306
+ resp.should_not_match /Connection: close\r\n/
307
+ end
308
+
309
+ specify "should include Connection header if persistent" do
310
+ r = DummyRequest.new
311
+ r.persistent = false
312
+ r.send_not_modified_response
313
+ r.socket.rewind
314
+ resp = r.socket.read
315
+ resp.should_match /Connection: close\r\n/
316
+ end
317
+ end
318
+
@@ -0,0 +1,140 @@
1
+ require File.join(File.dirname(__FILE__), '../lib/serverside')
2
+
3
+ __END__
4
+
5
+ context "Daemon::Cluster::PidFile" do
6
+ setup do
7
+ @fn = Daemon::Cluster::PidFile::FN
8
+ end
9
+
10
+ specify "::FN should be the cluster's pid file" do
11
+ Daemon::Cluster::PidFile::FN.should == 'serverside_cluster.pid'
12
+ end
13
+
14
+ specify "should delete the cluster's pid file" do
15
+ FileUtils.touch(@fn)
16
+ File.file?(@fn).should == true
17
+ Daemon::Cluster::PidFile.delete
18
+ File.file?(@fn).should == false
19
+ end
20
+
21
+ specify "should store multiple pids" do
22
+ Daemon::Cluster::PidFile.delete
23
+ Daemon::Cluster::PidFile.store_pid(1111)
24
+ IO.read(@fn).should == "1111\n"
25
+ Daemon::Cluster::PidFile.store_pid(2222)
26
+ IO.read(@fn).should == "1111\n2222\n"
27
+ end
28
+
29
+ def test_pid_recall_pids
30
+ Daemon::Cluster::PidFile.delete
31
+ proc {Daemon::Cluster::PidFile.recall_pids}.should_raise Errno::ENOENT
32
+ File.open(@fn, 'w') {|f| f.puts 3333; f.puts 4444}
33
+ Daemon::Cluster::PidFile.recall_pids.should == [3333, 4444]
34
+
35
+ FileUtils.rm(@fn)
36
+ Daemon::Cluster::PidFile.store_pid(6666)
37
+ Daemon::Cluster::PidFile.store_pid(7777)
38
+ Daemon::Cluster::PidFile.recall_pids.should == [6666, 7777]
39
+ end
40
+ end
41
+
42
+ class DummyCluster < Daemon::Cluster
43
+ FN = 'result'
44
+
45
+ def self.server_loop(port)
46
+ at_exit {File.open(FN, 'a') {|f| f.puts port}}
47
+ loop {sleep 10}
48
+ end
49
+
50
+ def self.ports
51
+ 5555..5556
52
+ end
53
+ end
54
+
55
+ context "Cluster.fork_server" do
56
+ specify "should fork a server on the specified port" do
57
+ FileUtils.rm(DummyCluster::FN) rescue nil
58
+ port = rand(5_000)
59
+ pid = DummyCluster.fork_server(port)
60
+ sleep 1
61
+ Process.kill('TERM', pid)
62
+ sleep 0.1
63
+ File.file?(DummyCluster::FN).should == true
64
+ File.open(DummyCluster::FN, 'r') do |f|
65
+ f.gets.to_i.should == port
66
+ f.eof?.should == true
67
+ end
68
+ FileUtils.rm(DummyCluster::FN) rescue nil
69
+ end
70
+ end
71
+
72
+ context "Cluster.start_servers" do
73
+ specify "should start a cluster of servers" do
74
+ FileUtils.rm(DummyCluster::FN) rescue nil
75
+ DummyCluster.start_servers
76
+ sleep 0.5
77
+ pids = Daemon::Cluster::PidFile.recall_pids
78
+ pids.length.should == 2
79
+ pids.each {|pid| Process.kill('TERM', pid)}
80
+ sleep 0.5
81
+ File.open(DummyCluster::FN, 'r') do |f|
82
+ p1, p2 = f.gets.to_i, f.gets.to_i
83
+ DummyCluster.ports.include?(p1).should == true
84
+ DummyCluster.ports.include?(p2).should == true
85
+ p1.should_not == p2
86
+ f.eof?.should == true
87
+ end
88
+ FileUtils.rm(DummyCluster::FN) rescue nil
89
+ end
90
+ end
91
+
92
+ context "Cluster.stop_servers" do
93
+ specify "should stop the cluster of servers" do
94
+ DummyCluster.start_servers
95
+ sleep 0.5
96
+ pids = Daemon::Cluster::PidFile.recall_pids
97
+ DummyCluster.stop_servers
98
+ sleep 0.5
99
+ File.file?(Daemon::Cluster::PidFile::FN).should == false
100
+ File.open(DummyCluster::FN, 'r') do |f|
101
+ p1, p2 = f.gets.to_i, f.gets.to_i
102
+ DummyCluster.ports.include?(p1).should == true
103
+ DummyCluster.ports.include?(p2).should == true
104
+ p1.should_not == p2
105
+ f.eof?.should == true
106
+ end
107
+ FileUtils.rm(DummyCluster::FN) rescue nil
108
+ end
109
+ end
110
+
111
+ class DummyCluster2 < Daemon::Cluster
112
+ def self.daemon_loop
113
+ @@a = true
114
+ end
115
+
116
+ def self.start_servers
117
+ @@b = true
118
+ end
119
+
120
+ def self.stop_servers
121
+ @@c = true
122
+ end
123
+
124
+ def self.a; @@a; end
125
+ def self.b; @@b; end
126
+ def self.c; @@c; end
127
+ end
128
+
129
+ context "Cluster.start and stop" do
130
+ specify "should start and stop the cluster daemon" do
131
+ DummyCluster2.start
132
+ DummyCluster2.a.should == true
133
+ DummyCluster2.b.should == true
134
+
135
+ proc {DummyCluster2.c}.should_raise
136
+ DummyCluster2.stop
137
+ DummyCluster2.c.should == true
138
+ end
139
+ end
140
+
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), '../../lib/serverside')
1
+ require File.join(File.dirname(__FILE__), '../lib/serverside')
2
2
 
3
3
  class ServerSide::HTTP::Connection
4
4
  attr_reader :socket, :request_class, :thread
@@ -49,11 +49,11 @@ context "Connection.initialize" do
49
49
  $pause_request = true
50
50
  c = Connection.new(DummySocket.new, DummyRequest1)
51
51
  c.thread.should_be_an_instance_of Thread
52
- c.thread.alive?.should_equal true
53
- DummyRequest1.instance_count.should_equal 1
52
+ c.thread.alive?.should == true
53
+ DummyRequest1.instance_count.should == 1
54
54
  $pause_request = false
55
55
  sleep 0.1 while c.thread.alive?
56
- DummyRequest1.instance_count.should_equal 1000
56
+ DummyRequest1.instance_count.should == 1000
57
57
  end
58
58
  end
59
59
 
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), '../../lib/serverside')
1
+ require File.join(File.dirname(__FILE__), '../lib/serverside')
2
2
  require 'stringio'
3
3
 
4
4
  class ServerSide::Router
@@ -28,14 +28,15 @@ context "ServerSide::Controller.mount" do
28
28
  ServerSide::Router.reset_rules
29
29
  rule = {:path => '/test'}
30
30
  c = ServerSide::Controller.mount(rule)
31
+ sub_class = Class.new(c)
31
32
  r = ServerSide::Router.rules.first
32
- r.first.should_equal rule
33
+ r.first.should == rule
33
34
  r.last.should_be_a_kind_of Proc
34
35
  c.module_eval do
35
36
  define_method(:initialize) {|req| $req = req}
36
37
  end
37
38
  res = r.last.call
38
- res.should_be_a_kind_of c
39
+ res.should_be_a_kind_of sub_class
39
40
 
40
41
  r = ServerSide::Router.new(StringIO.new)
41
42
  r.path = '/test'
@@ -46,13 +47,13 @@ context "ServerSide::Controller.mount" do
46
47
  specify "should accept either an argument or block as the rule" do
47
48
  ServerSide::Router.reset_rules
48
49
  rule = {:path => '/test'}
49
- c = ServerSide::Controller.mount(rule)
50
+ c = Class.new(ServerSide::Controller.mount(rule))
50
51
  r = ServerSide::Router.rules.first
51
52
  r.first.should_be rule
52
53
 
53
54
  ServerSide::Router.reset_rules
54
55
  rule = proc {true}
55
- c = ServerSide::Controller.mount(&rule)
56
+ c = Class.new(ServerSide::Controller.mount(&rule))
56
57
  r = ServerSide::Router.rules.first
57
58
  r.first.should_be rule
58
59
  end
@@ -69,10 +70,10 @@ end
69
70
  require 'metaid'
70
71
 
71
72
  class DummyController < ServerSide::Controller
72
- attr_reader :process_called
73
+ attr_reader :response_called
73
74
 
74
- def process
75
- @process_called = true
75
+ def response
76
+ @response_called = true
76
77
  end
77
78
 
78
79
  def render_default
@@ -91,10 +92,10 @@ context "ServerSide::Controller new instance" do
91
92
  c.parameters.should_be req.parameters
92
93
  end
93
94
 
94
- specify "should invoke the process method" do
95
+ specify "should invoke the response method" do
95
96
  req = ServerSide::HTTP::Request.new(StringIO.new)
96
97
  c = DummyController.new(req)
97
- c.process_called.should_be true
98
+ c.response_called.should_be true
98
99
  end
99
100
 
100
101
  specify "should invoke render_default unless @rendered" do
@@ -103,7 +104,7 @@ context "ServerSide::Controller new instance" do
103
104
  c.rendered.should_be :default
104
105
 
105
106
  c_class = Class.new(DummyController) do
106
- define_method(:process) {@rendered = true}
107
+ define_method(:response) {@rendered = true}
107
108
  end
108
109
  c = c_class.new(req)
109
110
  c.rendered.should_be true
@@ -134,6 +135,8 @@ context "ServerSide::Controller.render" do
134
135
  req = ServerSide::HTTP::Request.new(StringIO.new)
135
136
  c = ServerSide::Controller.new(req)
136
137
  c.render('hello world', 'text/plain')
137
- c.rendered.should_equal true
138
+ c.rendered.should == true
138
139
  end
139
140
  end
141
+
142
+