sanford 0.10.1 → 0.11.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 (73) hide show
  1. data/Gemfile +1 -1
  2. data/README.md +41 -56
  3. data/Rakefile +0 -1
  4. data/bench/client.rb +8 -3
  5. data/bench/{services.rb → config.sanford} +11 -6
  6. data/bench/{runner.rb → report.rb} +2 -2
  7. data/bench/report.txt +32 -32
  8. data/lib/sanford/cli.rb +42 -28
  9. data/lib/sanford/config_file.rb +79 -0
  10. data/lib/sanford/{worker.rb → connection_handler.rb} +28 -20
  11. data/lib/sanford/error_handler.rb +7 -7
  12. data/lib/sanford/pid_file.rb +42 -0
  13. data/lib/sanford/process.rb +136 -0
  14. data/lib/sanford/process_signal.rb +20 -0
  15. data/lib/sanford/route.rb +48 -0
  16. data/lib/sanford/router.rb +36 -0
  17. data/lib/sanford/runner.rb +30 -58
  18. data/lib/sanford/sanford_runner.rb +19 -9
  19. data/lib/sanford/server.rb +211 -42
  20. data/lib/sanford/server_data.rb +47 -0
  21. data/lib/sanford/service_handler.rb +8 -46
  22. data/lib/sanford/template_source.rb +19 -2
  23. data/lib/sanford/test_runner.rb +27 -28
  24. data/lib/sanford/version.rb +1 -1
  25. data/lib/sanford.rb +1 -23
  26. data/sanford.gemspec +4 -5
  27. data/test/helper.rb +3 -20
  28. data/test/support/app_server.rb +142 -0
  29. data/test/support/config.sanford +7 -0
  30. data/test/support/config_invalid_run.sanford +3 -0
  31. data/test/support/config_no_run.sanford +0 -0
  32. data/test/support/fake_server_connection.rb +58 -0
  33. data/test/support/pid_file_spy.rb +19 -0
  34. data/test/support/template.erb +1 -0
  35. data/test/system/server_tests.rb +378 -0
  36. data/test/system/service_handler_tests.rb +224 -0
  37. data/test/unit/cli_tests.rb +187 -0
  38. data/test/unit/config_file_tests.rb +59 -0
  39. data/test/unit/connection_handler_tests.rb +254 -0
  40. data/test/unit/error_handler_tests.rb +30 -35
  41. data/test/unit/pid_file_tests.rb +70 -0
  42. data/test/unit/process_signal_tests.rb +61 -0
  43. data/test/unit/process_tests.rb +428 -0
  44. data/test/unit/route_tests.rb +92 -0
  45. data/test/unit/router_tests.rb +65 -0
  46. data/test/unit/runner_tests.rb +61 -15
  47. data/test/unit/sanford_runner_tests.rb +162 -28
  48. data/test/unit/sanford_tests.rb +0 -8
  49. data/test/unit/server_data_tests.rb +87 -0
  50. data/test/unit/server_tests.rb +502 -21
  51. data/test/unit/service_handler_tests.rb +114 -219
  52. data/test/unit/template_engine_tests.rb +1 -1
  53. data/test/unit/template_source_tests.rb +56 -16
  54. data/test/unit/test_runner_tests.rb +206 -0
  55. metadata +67 -67
  56. data/bench/tasks.rb +0 -41
  57. data/lib/sanford/config.rb +0 -28
  58. data/lib/sanford/host.rb +0 -129
  59. data/lib/sanford/host_data.rb +0 -65
  60. data/lib/sanford/hosts.rb +0 -38
  61. data/lib/sanford/manager.rb +0 -275
  62. data/test/support/fake_connection.rb +0 -36
  63. data/test/support/helpers.rb +0 -17
  64. data/test/support/service_handlers.rb +0 -154
  65. data/test/support/services.rb +0 -123
  66. data/test/support/simple_client.rb +0 -62
  67. data/test/system/request_handling_tests.rb +0 -306
  68. data/test/unit/config_tests.rb +0 -56
  69. data/test/unit/host_data_tests.rb +0 -71
  70. data/test/unit/host_tests.rb +0 -141
  71. data/test/unit/hosts_tests.rb +0 -50
  72. data/test/unit/manager_tests.rb +0 -195
  73. data/test/unit/worker_tests.rb +0 -24
@@ -1,48 +1,529 @@
1
1
  require 'assert'
2
2
  require 'sanford/server'
3
3
 
4
- class Sanford::Server
4
+ require 'dat-tcp/server_spy'
5
+ require 'ns-options/assert_macros'
6
+ require 'sanford/route'
7
+ require 'sanford-protocol/fake_connection'
8
+ require 'test/support/fake_server_connection'
9
+
10
+ module Sanford::Server
5
11
 
6
12
  class UnitTests < Assert::Context
7
13
  desc "Sanford::Server"
8
14
  setup do
9
- @server = Sanford::Server.new(TestHost, :keep_alive => true)
15
+ @server_class = Class.new do
16
+ include Sanford::Server
17
+ end
18
+ end
19
+ subject{ @server_class }
20
+
21
+ should have_imeths :configuration
22
+ should have_imeths :name, :ip, :port, :pid_file
23
+ should have_imeths :receives_keep_alive
24
+ should have_imeths :verbose_logging, :logger
25
+ should have_imeths :init, :error
26
+ should have_imeths :router
27
+ should have_imeths :template_source, :build_template_source
28
+
29
+ should "know its configuration" do
30
+ config = subject.configuration
31
+ assert_instance_of Configuration, config
32
+ assert_same config, subject.configuration
33
+ end
34
+
35
+ should "allow reading/writing its configuration name" do
36
+ new_name = Factory.string
37
+ subject.name(new_name)
38
+ assert_equal new_name, subject.configuration.name
39
+ assert_equal new_name, subject.name
40
+ end
41
+
42
+ should "allow reading/writing its configuration ip" do
43
+ new_ip = Factory.string
44
+ subject.ip(new_ip)
45
+ assert_equal new_ip, subject.configuration.ip
46
+ assert_equal new_ip, subject.ip
47
+ end
48
+
49
+ should "allow reading/writing its configuration port" do
50
+ new_port = Factory.integer
51
+ subject.port(new_port)
52
+ assert_equal new_port, subject.configuration.port
53
+ assert_equal new_port, subject.port
54
+ end
55
+
56
+ should "allow reading/writing its configuration pid file" do
57
+ new_pid_file = Factory.string
58
+ subject.pid_file(new_pid_file)
59
+ expected = Pathname.new(new_pid_file)
60
+ assert_equal expected, subject.configuration.pid_file
61
+ assert_equal expected, subject.pid_file
62
+ end
63
+
64
+ should "allow reading/writing its configuration receives keep alive" do
65
+ new_keep_alive = Factory.boolean
66
+ subject.receives_keep_alive(new_keep_alive)
67
+ assert_equal new_keep_alive, subject.configuration.receives_keep_alive
68
+ assert_equal new_keep_alive, subject.receives_keep_alive
69
+ end
70
+
71
+ should "allow reading/writing its configuration verbose logging" do
72
+ new_verbose = Factory.boolean
73
+ subject.verbose_logging(new_verbose)
74
+ assert_equal new_verbose, subject.configuration.verbose_logging
75
+ assert_equal new_verbose, subject.verbose_logging
76
+ end
77
+
78
+ should "allow reading/writing its configuration logger" do
79
+ new_logger = Factory.string
80
+ subject.logger(new_logger)
81
+ assert_equal new_logger, subject.configuration.logger
82
+ assert_equal new_logger, subject.logger
83
+ end
84
+
85
+ should "allow adding init procs to its configuration" do
86
+ new_init_proc = proc{ Factory.string }
87
+ subject.init(&new_init_proc)
88
+ assert_includes new_init_proc, subject.configuration.init_procs
89
+ end
90
+
91
+ should "allow adding error procs to its configuration" do
92
+ new_error_proc = proc{ Factory.string }
93
+ subject.error(&new_error_proc)
94
+ assert_includes new_error_proc, subject.configuration.error_procs
95
+ end
96
+
97
+ should "allow reading/writing its configuration router" do
98
+ new_router = Factory.string
99
+ subject.router(new_router)
100
+ assert_equal new_router, subject.configuration.router
101
+ assert_equal new_router, subject.router
102
+ end
103
+
104
+ should "allow configuring the router by passing a block to `router`" do
105
+ new_router = Factory.string
106
+
107
+ block_scope = nil
108
+ subject.router(new_router){ block_scope = self }
109
+ assert_equal new_router, subject.router
110
+ assert_equal new_router, block_scope
111
+ end
112
+
113
+ should "allow setting the configuration template source" do
114
+ new_template_source = Factory.string
115
+ subject.template_source(new_template_source)
116
+ assert_equal new_template_source, subject.configuration.template_source
117
+ assert_equal new_template_source, subject.template_source
118
+ end
119
+
120
+ should "allow setting its template source with a path and block" do
121
+ new_path = Factory.string
122
+ yielded = nil
123
+ subject.build_template_source(new_path){ |s| yielded = s }
124
+ assert_equal new_path, subject.configuration.template_source.path
125
+ assert_equal subject.configuration.template_source, yielded
126
+ end
127
+
128
+ should "allow setting its template source with only a path" do
129
+ new_path = Factory.string
130
+ subject.build_template_source(new_path)
131
+ assert_equal new_path, subject.configuration.template_source.path
132
+ end
133
+
134
+ end
135
+
136
+ class InitTests < UnitTests
137
+ desc "when init"
138
+ setup do
139
+ @server_class.name Factory.string
140
+ @server_class.ip Factory.string
141
+ @server_class.port Factory.integer
142
+ @server_class.error{ Factory.string }
143
+ @server_class.router do
144
+ service Factory.string, TestHandler.to_s
145
+ end
146
+
147
+ @dat_tcp_server_spy = DatTCP::ServerSpy.new
148
+ Assert.stub(DatTCP::Server, :new) do |&block|
149
+ @dat_tcp_server_spy.serve_proc = block
150
+ @dat_tcp_server_spy
151
+ end
152
+
153
+ @server = @server_class.new
10
154
  end
11
155
  subject{ @server }
12
156
 
13
- should have_readers :sanford_host, :sanford_host_data, :sanford_host_options
14
- should have_imeths :on_run
157
+ should have_readers :server_data, :dat_tcp_server
158
+ should have_imeths :name, :ip, :port
159
+ should have_imeths :file_descriptor, :client_file_descriptors
160
+ should have_imeths :listen, :start, :pause, :stop, :halt
161
+ should have_imeths :paused?
162
+
163
+ should "have validated its configuration" do
164
+ assert_true subject.class.configuration.valid?
165
+ end
166
+
167
+ should "know its server data" do
168
+ configuration = subject.class.configuration
169
+ sd = subject.server_data
170
+
171
+ assert_instance_of Sanford::ServerData, sd
172
+ assert_equal configuration.name, sd.name
173
+ assert_equal configuration.ip, sd.ip
174
+ assert_equal configuration.port, sd.port
175
+ assert_equal configuration.verbose_logging, sd.verbose_logging
176
+ assert_equal configuration.receives_keep_alive, sd.receives_keep_alive
177
+ assert_equal configuration.error_procs, sd.error_procs
178
+ assert_equal configuration.routes, sd.routes.values
179
+ assert_instance_of configuration.logger.class, sd.logger
180
+ end
181
+
182
+ should "know its dat tcp server" do
183
+ assert_equal @dat_tcp_server_spy, subject.dat_tcp_server
184
+ assert_not_nil @dat_tcp_server_spy.serve_proc
185
+ end
186
+
187
+ should "know its name, pid file and logger" do
188
+ assert_equal subject.server_data.name, subject.name
189
+ assert_equal subject.server_data.pid_file, subject.pid_file
190
+ assert_equal subject.server_data.logger, subject.logger
191
+ end
192
+
193
+ should "call listen on its dat tcp server using `listen`" do
194
+ subject.listen
195
+ assert_true @dat_tcp_server_spy.listen_called
196
+ end
197
+
198
+ should "use its configured ip and port by default when listening" do
199
+ subject.listen
200
+ assert_equal subject.server_data.ip, @dat_tcp_server_spy.ip
201
+ assert_equal subject.server_data.port, @dat_tcp_server_spy.port
202
+ end
203
+
204
+ should "pass any args to its dat tcp server using `listen`" do
205
+ ip, port = Factory.string, Factory.integer
206
+ subject.listen(ip, port)
207
+ assert_equal ip, @dat_tcp_server_spy.ip
208
+ assert_equal port, @dat_tcp_server_spy.port
209
+
210
+ file_descriptor = Factory.integer
211
+ subject.listen(file_descriptor)
212
+ assert_equal file_descriptor, @dat_tcp_server_spy.file_descriptor
213
+ end
214
+
215
+ should "know its ip, port and file descriptor" do
216
+ assert_equal @dat_tcp_server_spy.ip, subject.ip
217
+ assert_equal @dat_tcp_server_spy.port, subject.port
218
+ subject.listen
219
+ assert_equal @dat_tcp_server_spy.ip, subject.ip
220
+ assert_equal @dat_tcp_server_spy.port, subject.port
221
+
222
+ assert_equal @dat_tcp_server_spy.file_descriptor, subject.file_descriptor
223
+ subject.listen(Factory.integer)
224
+ assert_equal @dat_tcp_server_spy.file_descriptor, subject.file_descriptor
225
+ end
226
+
227
+ should "call start on its dat tcp server using `start`" do
228
+ client_fds = [ Factory.integer ]
229
+ subject.start(client_fds)
230
+ assert_true @dat_tcp_server_spy.start_called
231
+ assert_equal client_fds, @dat_tcp_server_spy.client_file_descriptors
232
+ end
233
+
234
+ should "know its client file descriptors" do
235
+ expected = @dat_tcp_server_spy.client_file_descriptors
236
+ assert_equal expected, subject.client_file_descriptors
237
+ subject.start([ Factory.integer ])
238
+ expected = @dat_tcp_server_spy.client_file_descriptors
239
+ assert_equal expected, subject.client_file_descriptors
240
+ end
241
+
242
+ should "call pause on its dat tcp server using `pause`" do
243
+ wait = Factory.boolean
244
+ subject.pause(wait)
245
+ assert_true @dat_tcp_server_spy.pause_called
246
+ assert_equal wait, @dat_tcp_server_spy.waiting_for_pause
247
+ end
248
+
249
+ should "call stop on its dat tcp server using `stop`" do
250
+ wait = Factory.boolean
251
+ subject.stop(wait)
252
+ assert_true @dat_tcp_server_spy.stop_called
253
+ assert_equal wait, @dat_tcp_server_spy.waiting_for_stop
254
+ end
255
+
256
+ should "call halt on its dat tcp server using `halt`" do
257
+ wait = Factory.boolean
258
+ subject.halt(wait)
259
+ assert_true @dat_tcp_server_spy.halt_called
260
+ assert_equal wait, @dat_tcp_server_spy.waiting_for_halt
261
+ end
262
+
263
+ should "know if its been paused" do
264
+ assert_false subject.paused?
265
+ subject.listen
266
+ assert_true subject.paused?
267
+ subject.start
268
+ assert_false subject.paused?
269
+ subject.pause
270
+ assert_true subject.paused?
271
+ end
272
+
273
+ end
274
+
275
+ class ConfigureTCPServerTests < InitTests
276
+ desc "configuring its tcp server"
277
+ setup do
278
+ @tcp_server = TCPServerSpy.new
279
+ Assert.stub(@dat_tcp_server_spy, :listen) do |*args, &block|
280
+ @configure_tcp_server_proc = block
281
+ end
282
+ @server.listen
283
+ @configure_tcp_server_proc.call(@tcp_server)
284
+ end
285
+ subject{ @tcp_server }
15
286
 
16
- should "include DatTCP::Server" do
17
- assert_includes DatTCP::Server, subject.class
287
+ should "set the TCP_NODELAY option" do
288
+ expected = [ ::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true ]
289
+ assert_includes expected, @tcp_server.set_socket_option_calls
18
290
  end
19
291
 
20
- should "save its host and host options but not initialize a host data yet" do
21
- assert_equal TestHost, subject.sanford_host
22
- assert_equal true, subject.sanford_host_options[:receives_keep_alive]
23
- assert_nil subject.sanford_host_data
292
+ end
293
+
294
+ class ServeTests < InitTests
295
+ desc "serve"
296
+ setup do
297
+ @socket = Factory.binary
298
+
299
+ @connection = FakeServerConnection.new
300
+ Assert.stub(Connection, :new).with(@socket){ @connection }
301
+
302
+ @connection_handler_spy = ConnectionHandlerSpy.new
303
+ Assert.stub(Sanford::ConnectionHandler, :new).tap do |s|
304
+ s.with(@server.server_data, @connection){ @connection_handler_spy }
305
+ end
306
+
307
+ @serve_proc = @dat_tcp_server_spy.serve_proc
308
+ end
309
+ subject{ @serve_proc }
310
+
311
+ should "run a connection_handler when called with a socket" do
312
+ Assert.stub(@server.server_data, :receives_keep_alive){ false }
313
+ @connection.read_data = Factory.boolean
314
+ assert_false @connection_handler_spy.run_called
315
+ subject.call(@socket)
316
+ assert_true @connection_handler_spy.run_called
317
+ end
318
+
319
+ should "not run a keep-alive connection when configured to receive them" do
320
+ Assert.stub(@server.server_data, :receives_keep_alive){ true }
321
+ @connection.read_data = nil # nothing to read makes it a keep-alive
322
+ assert_false @connection_handler_spy.run_called
323
+ subject.call(@socket)
324
+ assert_false @connection_handler_spy.run_called
325
+ end
326
+
327
+ should "run a keep-alive connection when configured to receive them" do
328
+ Assert.stub(@server.server_data, :receives_keep_alive){ false }
329
+ @connection.read_data = nil # nothing to read makes it a keep-alive
330
+ assert_false @connection_handler_spy.run_called
331
+ subject.call(@socket)
332
+ assert_true @connection_handler_spy.run_called
24
333
  end
25
334
 
26
335
  end
27
336
 
28
- class RunTests < UnitTests
29
- desc "run"
337
+ class ConnectionTests < UnitTests
338
+ desc "Connection"
30
339
  setup do
31
- @server.listen(TestHost.ip, TestHost.port)
32
- @server.run
340
+ fake_socket = Factory.string
341
+ @protocol_conn = Sanford::Protocol::FakeConnection.new(Factory.binary)
342
+ Assert.stub(Sanford::Protocol::Connection, :new).with(fake_socket) do
343
+ @protocol_conn
344
+ end
345
+ @connection = Connection.new(fake_socket)
33
346
  end
34
- teardown do
35
- @server.stop
347
+ subject{ @connection }
348
+
349
+ should have_imeths :read_data, :write_data, :peek_data
350
+ should have_imeths :close_write
351
+
352
+ should "default its timeout" do
353
+ assert_equal 1.0, subject.timeout
354
+ end
355
+
356
+ should "allowing reading from the protocol connection" do
357
+ result = subject.read_data
358
+ assert_equal @protocol_conn.read_data, result
359
+ assert_equal @protocol_conn.read_timeout, subject.timeout
36
360
  end
37
361
 
38
- should "have initialized a host data instance" do
39
- assert_instance_of Sanford::HostData, subject.sanford_host_data
362
+ should "allowing writing to the protocol connection" do
363
+ data = Factory.binary
364
+ subject.write_data(data)
365
+ assert_equal @protocol_conn.write_data, data
366
+ end
367
+
368
+ should "allowing peeking from the protocol connection" do
369
+ result = subject.peek_data
370
+ assert_equal @protocol_conn.peek_data, result
371
+ assert_equal @protocol_conn.peek_timeout, subject.timeout
372
+ end
373
+
374
+ should "allow closing the write stream on the protocol connection" do
375
+ assert_false @protocol_conn.closed_write
376
+ subject.close_write
377
+ assert_true @protocol_conn.closed_write
40
378
  end
41
379
 
42
380
  end
43
381
 
44
- # Sanford::Server#serve is tested in test/system/request_handling_test.rb,
45
- # it requires multiple parts of Sanford and basically tests a large portion of
46
- # the entire system
382
+ class TCPCorkTests < UnitTests
383
+ desc "TCPCork"
384
+ subject{ TCPCork }
385
+
386
+ should have_imeths :apply, :remove
387
+
388
+ end
389
+
390
+ class ConfigurationTests < UnitTests
391
+ include NsOptions::AssertMacros
392
+
393
+ desc "Configuration"
394
+ setup do
395
+ @configuration = Configuration.new.tap do |c|
396
+ c.name Factory.string
397
+ c.ip Factory.string
398
+ c.port Factory.integer
399
+ end
400
+ end
401
+ subject{ @configuration }
402
+
403
+ should have_options :name, :ip, :port, :pid_file
404
+ should have_options :receives_keep_alive
405
+ should have_options :verbose_logging, :logger
406
+ should have_options :template_source
407
+ should have_accessors :init_procs, :error_procs
408
+ should have_accessors :router
409
+ should have_imeths :routes
410
+ should have_imeths :to_hash
411
+ should have_imeths :valid?, :validate!
412
+
413
+ should "be an ns-options proxy" do
414
+ assert_includes NsOptions::Proxy, subject.class
415
+ end
416
+
417
+ should "default its options" do
418
+ config = Configuration.new
419
+ assert_nil config.name
420
+ assert_equal '0.0.0.0', config.ip
421
+ assert_nil config.port
422
+ assert_nil config.pid_file
423
+
424
+ assert_false config.receives_keep_alive
425
+
426
+ assert_true config.verbose_logging
427
+ assert_instance_of Sanford::NullLogger, config.logger
428
+ assert_instance_of Sanford::NullTemplateSource, config.template_source
429
+
430
+ assert_equal [], config.init_procs
431
+ assert_equal [], config.error_procs
432
+
433
+ assert_instance_of Sanford::Router, config.router
434
+ assert_empty config.router.routes
435
+ end
436
+
437
+ should "not be valid by default" do
438
+ assert_false subject.valid?
439
+ end
440
+
441
+ should "know its routes" do
442
+ assert_equal subject.router.routes, subject.routes
443
+ subject.router.service(Factory.string, TestHandler.to_s)
444
+ assert_equal subject.router.routes, subject.routes
445
+ end
446
+
447
+ should "include its routes, error procs and template source in its hash" do
448
+ config_hash = subject.to_hash
449
+ assert_equal subject.error_procs, config_hash[:error_procs]
450
+ assert_equal subject.routes, config_hash[:routes]
451
+ template_source = subject.template_source
452
+ assert_instance_of template_source.class, config_hash[:template_source]
453
+ assert_equal template_source.path, config_hash[:template_source].path
454
+ end
455
+
456
+ should "call its init procs when validated" do
457
+ called = false
458
+ subject.init_procs << proc{ called = true }
459
+ subject.validate!
460
+ assert_true called
461
+ end
462
+
463
+ should "ensure its required options have been set when validated" do
464
+ subject.name = nil
465
+ assert_raises(InvalidError){ subject.validate! }
466
+ subject.name = Factory.string
467
+
468
+ subject.ip = nil
469
+ assert_raises(InvalidError){ subject.validate! }
470
+ subject.ip = Factory.string
471
+
472
+ subject.port = nil
473
+ assert_raises(InvalidError){ subject.validate! }
474
+ subject.port = Factory.integer
475
+
476
+ assert_nothing_raised{ subject.validate! }
477
+ end
478
+
479
+ should "validate its routes when validated" do
480
+ subject.router.service(Factory.string, TestHandler.to_s)
481
+ subject.routes.each{ |route| assert_nil route.handler_class }
482
+ subject.validate!
483
+ subject.routes.each{ |route| assert_not_nil route.handler_class }
484
+ end
485
+
486
+ should "be valid after being validated" do
487
+ assert_false subject.valid?
488
+ subject.validate!
489
+ assert_true subject.valid?
490
+ end
491
+
492
+ should "only be able to be validated once" do
493
+ called = 0
494
+ subject.init_procs << proc{ called += 1 }
495
+ subject.validate!
496
+ assert_equal 1, called
497
+ subject.validate!
498
+ assert_equal 1, called
499
+ end
500
+
501
+ end
502
+
503
+ TestHandler = Class.new
504
+
505
+ class TCPServerSpy
506
+ attr_reader :set_socket_option_calls
507
+
508
+ def initialize
509
+ @set_socket_option_calls = []
510
+ end
511
+
512
+ def setsockopt(*args)
513
+ @set_socket_option_calls << args
514
+ end
515
+ end
516
+
517
+ class ConnectionHandlerSpy
518
+ attr_reader :run_called
519
+
520
+ def initialize
521
+ @run_called = false
522
+ end
523
+
524
+ def run
525
+ @run_called = true
526
+ end
527
+ end
47
528
 
48
529
  end