background_queue 0.2.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 (91) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +48 -0
  4. data/Gemfile +19 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.md +69 -0
  7. data/Rakefile +42 -0
  8. data/TODO +2 -0
  9. data/VERSION +1 -0
  10. data/background_queue.gemspec +158 -0
  11. data/bin/bg_queue +26 -0
  12. data/lib/background_queue.rb +8 -0
  13. data/lib/background_queue/client.rb +96 -0
  14. data/lib/background_queue/client_lib/command.rb +36 -0
  15. data/lib/background_queue/client_lib/config.rb +109 -0
  16. data/lib/background_queue/client_lib/connection.rb +105 -0
  17. data/lib/background_queue/client_lib/job_handle.rb +19 -0
  18. data/lib/background_queue/command.rb +49 -0
  19. data/lib/background_queue/config.rb +118 -0
  20. data/lib/background_queue/server_lib/balanced_queue.rb +108 -0
  21. data/lib/background_queue/server_lib/config.rb +339 -0
  22. data/lib/background_queue/server_lib/event_connection.rb +133 -0
  23. data/lib/background_queue/server_lib/event_server.rb +35 -0
  24. data/lib/background_queue/server_lib/job.rb +252 -0
  25. data/lib/background_queue/server_lib/job_registry.rb +30 -0
  26. data/lib/background_queue/server_lib/lru.rb +193 -0
  27. data/lib/background_queue/server_lib/owner.rb +54 -0
  28. data/lib/background_queue/server_lib/priority_queue.rb +156 -0
  29. data/lib/background_queue/server_lib/queue_registry.rb +123 -0
  30. data/lib/background_queue/server_lib/server.rb +314 -0
  31. data/lib/background_queue/server_lib/sorted_workers.rb +52 -0
  32. data/lib/background_queue/server_lib/task.rb +79 -0
  33. data/lib/background_queue/server_lib/task_registry.rb +51 -0
  34. data/lib/background_queue/server_lib/thread_manager.rb +121 -0
  35. data/lib/background_queue/server_lib/worker.rb +18 -0
  36. data/lib/background_queue/server_lib/worker_balancer.rb +97 -0
  37. data/lib/background_queue/server_lib/worker_client.rb +94 -0
  38. data/lib/background_queue/server_lib/worker_thread.rb +70 -0
  39. data/lib/background_queue/utils.rb +40 -0
  40. data/lib/background_queue/worker/base.rb +46 -0
  41. data/lib/background_queue/worker/calling.rb +59 -0
  42. data/lib/background_queue/worker/config.rb +35 -0
  43. data/lib/background_queue/worker/environment.rb +70 -0
  44. data/lib/background_queue/worker/worker_loader.rb +94 -0
  45. data/lib/background_queue_server.rb +21 -0
  46. data/lib/background_queue_worker.rb +5 -0
  47. data/spec/background_queue/client_lib/command_spec.rb +75 -0
  48. data/spec/background_queue/client_lib/config_spec.rb +156 -0
  49. data/spec/background_queue/client_lib/connection_spec.rb +170 -0
  50. data/spec/background_queue/client_spec.rb +95 -0
  51. data/spec/background_queue/command_spec.rb +34 -0
  52. data/spec/background_queue/config_spec.rb +134 -0
  53. data/spec/background_queue/server_lib/balanced_queue_spec.rb +122 -0
  54. data/spec/background_queue/server_lib/config_spec.rb +443 -0
  55. data/spec/background_queue/server_lib/event_connection_spec.rb +190 -0
  56. data/spec/background_queue/server_lib/event_server_spec.rb +48 -0
  57. data/spec/background_queue/server_lib/integration/full_test_spec.rb +247 -0
  58. data/spec/background_queue/server_lib/integration/queue_integration_spec.rb +98 -0
  59. data/spec/background_queue/server_lib/integration/serialize_spec.rb +127 -0
  60. data/spec/background_queue/server_lib/job_registry_spec.rb +65 -0
  61. data/spec/background_queue/server_lib/job_spec.rb +525 -0
  62. data/spec/background_queue/server_lib/owner_spec.rb +33 -0
  63. data/spec/background_queue/server_lib/priority_queue_spec.rb +182 -0
  64. data/spec/background_queue/server_lib/server_spec.rb +353 -0
  65. data/spec/background_queue/server_lib/sorted_workers_spec.rb +122 -0
  66. data/spec/background_queue/server_lib/task_registry_spec.rb +69 -0
  67. data/spec/background_queue/server_lib/task_spec.rb +20 -0
  68. data/spec/background_queue/server_lib/thread_manager_spec.rb +106 -0
  69. data/spec/background_queue/server_lib/worker_balancer_spec.rb +127 -0
  70. data/spec/background_queue/server_lib/worker_client_spec.rb +196 -0
  71. data/spec/background_queue/server_lib/worker_thread_spec.rb +125 -0
  72. data/spec/background_queue/utils_spec.rb +27 -0
  73. data/spec/background_queue/worker/base_spec.rb +35 -0
  74. data/spec/background_queue/worker/calling_spec.rb +103 -0
  75. data/spec/background_queue/worker/environment_spec.rb +67 -0
  76. data/spec/background_queue/worker/worker_loader_spec.rb +103 -0
  77. data/spec/background_queue_spec.rb +7 -0
  78. data/spec/resources/config-client.yml +7 -0
  79. data/spec/resources/config-serialize.yml +12 -0
  80. data/spec/resources/config.yml +12 -0
  81. data/spec/resources/example_worker.rb +4 -0
  82. data/spec/resources/example_worker_with_error.rb +4 -0
  83. data/spec/resources/test_worker.rb +8 -0
  84. data/spec/shared/queue_registry_shared.rb +216 -0
  85. data/spec/spec_helper.rb +15 -0
  86. data/spec/support/default_task.rb +9 -0
  87. data/spec/support/private.rb +23 -0
  88. data/spec/support/simple_server.rb +28 -0
  89. data/spec/support/simple_task.rb +58 -0
  90. data/spec/support/test_worker_server.rb +205 -0
  91. metadata +254 -0
@@ -0,0 +1,196 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'background_queue_server'
3
+
4
+
5
+ describe BackgroundQueue::ServerLib::WorkerClient do
6
+
7
+ subject { BackgroundQueue::ServerLib::WorkerClient.new(SimpleServer.new({:config=>double("cfg", {:address=>double("addr", {:port=>123})} )})) }
8
+
9
+
10
+ context "#build_request" do
11
+ it "builds a request payload from a task" do
12
+ task = SimpleTask.new(:owner_id, :job_id, :task_id, 3, { :domain=>"www.example.com" })
13
+ task.should_receive(:to_json).and_return(:the_json)
14
+ Net::HTTP::Post.any_instance.should_receive(:set_form_data).with({:task=>:the_json, :auth=>"auth", :server_port=>123})
15
+ Net::HTTP::Post.any_instance.should_receive("[]=".intern).with("host", "www.example.com")
16
+ Net::HTTP::Post.any_instance.should_receive("[]=".intern).with(any_args).at_least(:once)
17
+ request = subject.__prv__build_request(URI("http://127.0.0.1:3000/worker/run"), task, "auth")
18
+ end
19
+ end
20
+
21
+ context "#send_request" do
22
+ it "uses http client to call the worker" do
23
+ uri = URI("http://127.0.0.1:3000/worker/run")
24
+ worker_config = BackgroundQueue::ServerLib::Worker.new(uri)
25
+ task = SimpleTask.new(:owner_id, :job_id, :task_id, 3, { :domain=>"www.example.com" })
26
+ subject.should_receive(:build_request).with(uri, task, "auth").and_return(:post_request)
27
+
28
+ http_instance = double("http")
29
+ http_instance.should_receive(:request).with(:post_request).and_yield(:http_response)
30
+ subject.should_receive(:read_response).with(worker_config, :http_response, task).and_return(true)
31
+
32
+ Net::HTTP.should_receive(:start).with("127.0.0.1", 3000).and_yield(http_instance)
33
+ subject.send_request(worker_config, task, "auth").should be_true
34
+
35
+ end
36
+
37
+ it "will fail if the connection fails" do
38
+ uri = URI("http://127.0.0.1:3000/worker/run")
39
+ worker_config = BackgroundQueue::ServerLib::Worker.new(uri)
40
+ task = SimpleTask.new(:owner_id, :job_id, :task_id, 3, { :domain=>"www.example.com" })
41
+ subject.should_receive(:build_request).with(uri, task, "auth").and_return(:post_request)
42
+ Net::HTTP.should_receive(:start).with("127.0.0.1", 3000).and_raise("connection error")
43
+ subject.send_request(worker_config, task, "auth").should be_false
44
+ end
45
+ end
46
+
47
+ context "#read_response" do
48
+ it "will read the streamed response" do
49
+ http_response = double("response", :code=>"200")
50
+ http_response.should_receive(:read_body).and_yield("data")
51
+ subject.should_receive(:process_chunk).with("data", :task)
52
+ subject.__prv__read_response(:worker_config, http_response, :task)
53
+ end
54
+
55
+ it "will raise an error if response is not 200" do
56
+ http_response = double("response", :code=>"400")
57
+ worker_config = double("wc", :url=>"uri")
58
+ expect { subject.__prv__read_response(worker_config, http_response, :task) }.to raise_exception
59
+ end
60
+ end
61
+
62
+ context "#process_response_chunk" do
63
+ it "will handle a reponse in 1 chunk" do
64
+ subject.should_receive(:process_line).with("data", :task)
65
+ subject.__prv__process_chunk("data\n", :task)
66
+ end
67
+
68
+ it "will handle a partial response" do
69
+ subject.should_not_receive(:process_line)
70
+ subject.__prv__process_chunk("data", :task)
71
+ end
72
+
73
+ it "will handle multiple responses in 1 chunk" do
74
+ subject.should_receive(:process_line).with("data", :task).once
75
+ subject.should_receive(:process_line).with("data2", :task).once
76
+ subject.__prv__process_chunk("data\ndata2\n", :task)
77
+ end
78
+
79
+ it "will handle multiple partial responses" do
80
+ subject.should_receive(:process_line).with("data", :task).once
81
+ subject.should_receive(:process_line).with("data2", :task).once
82
+ subject.should_receive(:process_line).with("data3", :task).once
83
+ subject.should_receive(:process_line).with("data4", :task).once
84
+ subject.__prv__process_chunk("da", :task)
85
+ subject.__prv__process_chunk("ta\ndata2\nda", :task)
86
+ subject.__prv__process_chunk("ta3\nda", :task)
87
+ subject.__prv__process_chunk("ta4\n", :task)
88
+ end
89
+ end
90
+
91
+ context "#process_line" do
92
+ it "loads from json" do
93
+ subject.should_receive(:set_worker_status).with({'a'=>'b'}, :task)
94
+ subject.__prv__process_line('{ "a":"b"}', :task).should be_true
95
+ end
96
+
97
+ it "fails gracefully on invalid json" do
98
+ subject.__prv__process_line('{ "a":"b"', DefaultTask.new).should be_false
99
+ end
100
+ end
101
+
102
+ context "#set_worker_status" do
103
+ it "calls set_worker_status on the task" do
104
+ task = DefaultTask.new
105
+
106
+ BackgroundQueue::Utils::AnyKeyHash.should_receive(:new).with(:status).and_return(:kstatus)
107
+ task.should_receive(:set_worker_status).with(:kstatus)
108
+ subject.__prv__set_worker_status(:status, task)
109
+ end
110
+ end
111
+
112
+
113
+ context "can handle thread cancelling" do
114
+ #this can cause issues with other tests....
115
+ xit "will return false if exception raised" do
116
+
117
+ mutex = Mutex.new
118
+ resource = ConditionVariable.new
119
+
120
+ mutex2 = Mutex.new
121
+ resource2 = ConditionVariable.new
122
+
123
+ run_request = false
124
+ ss = TestWorkerServer.new(8001)
125
+ ss.start(Proc.new { |controller|
126
+ #puts "in proc"
127
+
128
+ mutex2.synchronize {
129
+ resource2.signal
130
+ }
131
+ #puts "in proc: waiting"
132
+ mutex.synchronize {
133
+ resource.wait(mutex)
134
+ }
135
+ #puts "waited"
136
+ run_request = true
137
+ controller.render :text =>{:percent=>100, :caption=>"Done"}.to_json, :type=>"text/text"
138
+ })
139
+
140
+
141
+ uri = URI("http://127.0.0.1:8001/background_queue")
142
+ worker_config = BackgroundQueue::ServerLib::Worker.new(uri)
143
+ task = SimpleTask.new(:owner_id, :job_id, :task_id, 3, { :domain=>"www.example.com" })
144
+
145
+ call_result = nil
146
+ t1 = Thread.new {
147
+ #puts "calling"
148
+ begin
149
+ status = Timeout::timeout(2) {
150
+ call_result = subject.send_request(worker_config, task, "abcd")
151
+ # puts "called"
152
+ }
153
+ rescue Timeout::Error=>te
154
+ #puts "timeout"
155
+ call_result = :timeout
156
+ end
157
+ mutex2.synchronize {
158
+ resource2.signal
159
+ }
160
+ }
161
+
162
+ #wait until we know the request has been sent and is processing
163
+ mutex2.synchronize {
164
+ resource2.wait(mutex2)
165
+ }
166
+ run_request.should be_false
167
+ #puts "cancelling"
168
+ t1.raise "Cancelling From Thread"
169
+
170
+ #puts "canceled"
171
+ #wait until we know the request has been cancelled
172
+ mutex2.synchronize {
173
+ resource2.wait(mutex2)
174
+ }
175
+ call_result.should be_false
176
+ run_request.should be_false
177
+
178
+ mutex.synchronize {
179
+ resource.signal
180
+ }
181
+
182
+
183
+ t1.join
184
+ ss.stop
185
+
186
+ end
187
+
188
+ it "will do nothing if not started" do
189
+
190
+ end
191
+
192
+ end
193
+
194
+
195
+
196
+ end
@@ -0,0 +1,125 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'background_queue_server'
3
+
4
+
5
+ describe BackgroundQueue::ServerLib::WorkerThread do
6
+
7
+ let(:balanced_queue) {
8
+ double("balanced_queue")
9
+ }
10
+
11
+ let(:workers) {
12
+ double("workers")
13
+ }
14
+
15
+ let(:server) {
16
+ SimpleServer.new(:task_queue=>balanced_queue, :workers=>workers, :config=>double("conf", :secret=>:secret))
17
+ }
18
+
19
+ subject { BackgroundQueue::ServerLib::WorkerThread.new(server) }
20
+
21
+ context "#get_next_task" do
22
+ it "will get the next task from the servers balanced queue" do
23
+ server.task_queue.should_receive(:next_task).and_return(:task)
24
+ server.stub('running?'=>true)
25
+ subject.get_next_task.should eq(:task)
26
+ end
27
+
28
+ it "will try again if the balanced queue returns nil" do
29
+ count = 0
30
+ server.task_queue.should_receive(:next_task).twice {
31
+ count += 1
32
+ count == 2 ? :task : nil
33
+ }
34
+ server.stub('running?'=>true)
35
+ subject.get_next_task.should eq(:task)
36
+ end
37
+
38
+ it "will stop if the server has stopped" do
39
+ server.should_receive(:running?).and_return(false)
40
+ subject.get_next_task.should be_nil
41
+ end
42
+
43
+ end
44
+
45
+
46
+ context "#call_worker" do
47
+ it "will get a worker and call it" do
48
+ server.stub('running?'=>true)
49
+ worker = double("worker")
50
+ task = DefaultTask.new
51
+ BackgroundQueue::ServerLib::WorkerClient.any_instance.should_receive(:send_request).with(worker, task, :secret).and_return(true)
52
+ server.workers.should_receive(:get_next_worker).and_return(worker)
53
+ server.workers.should_receive(:finish_using_worker).with(worker, true)
54
+ server.task_queue.should_receive(:finish_task).with(task)
55
+ subject.call_worker(task).should be_true
56
+ end
57
+
58
+ it "will keep trying if the worker fails" do
59
+ server.stub('running?'=>true)
60
+ worker = double("worker")
61
+ task = DefaultTask.new
62
+ count = 0
63
+ worker_client1 = double("w1")
64
+ worker_client1.should_receive(:send_request).with(worker, task, :secret).and_return(false)
65
+ worker_client2 = double("w2")
66
+ worker_client2.should_receive(:send_request).with(worker, task, :secret).and_return(true)
67
+
68
+ subject.should_receive(:build_client).twice {
69
+ count += 1
70
+ count == 1 ? worker_client1 : worker_client2
71
+ }
72
+ server.workers.should_receive(:get_next_worker).twice.and_return(worker)
73
+ server.workers.should_receive(:finish_using_worker).with(worker, false)
74
+ server.workers.should_receive(:finish_using_worker).with(worker, true)
75
+ server.task_queue.should_receive(:finish_task).with(task)
76
+ subject.call_worker(task).should be_true
77
+ end
78
+
79
+ it "will sleep and try again if there are no workers" do
80
+ server.stub('running?'=>true)
81
+ count = 0
82
+ task = DefaultTask.new
83
+ worker = double("worker")
84
+ server.workers.should_receive(:get_next_worker).twice {
85
+ count += 1
86
+ count == 1 ? nil : worker
87
+ }
88
+ Kernel.should_receive(:sleep).with(1)
89
+ BackgroundQueue::ServerLib::WorkerClient.any_instance.should_receive(:send_request).with(worker, task, :secret).and_return(true)
90
+ server.workers.should_receive(:finish_using_worker).with(worker, true)
91
+ server.task_queue.should_receive(:finish_task).with(task)
92
+ subject.call_worker(task).should be_true
93
+ end
94
+
95
+ it "will stop trying if the server is not running" do
96
+ server.stub('running?'=>false)
97
+ task = DefaultTask.new
98
+ server.task_queue.should_receive(:finish_task).with(task)
99
+ server.task_queue.should_receive(:add_task).with(task)
100
+ subject.call_worker(task).should be_false
101
+ end
102
+ end
103
+
104
+ context "#run" do
105
+ before do
106
+ count = 0
107
+ server.should_receive('running?').twice {
108
+ count += 1
109
+ count < 2
110
+ }
111
+ end
112
+
113
+ it "will keep running while the server is running" do
114
+ subject.should_receive(:get_next_task).and_return(:task)
115
+ subject.should_receive(:call_worker).with(:task)
116
+ subject.run
117
+ end
118
+
119
+ it "will not call_worker if no task" do
120
+ subject.should_receive(:get_next_task).and_return(nil)
121
+ subject.run
122
+ end
123
+ end
124
+
125
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'background_queue'
3
+
4
+ describe "Utils" do
5
+
6
+ context "hash" do
7
+ it "gets an entry by string" do
8
+ BackgroundQueue::Utils.get_hash_entry({:key=>:value}, "key").should eq(:value)
9
+ BackgroundQueue::Utils.get_hash_entry({'key'=>:value}, "key").should eq(:value)
10
+ end
11
+
12
+ it "gets an entry by symbol" do
13
+ BackgroundQueue::Utils.get_hash_entry({:key=>:value}, :key).should eq(:value)
14
+ BackgroundQueue::Utils.get_hash_entry({'key'=>:value}, :key).should eq(:value)
15
+ end
16
+ end
17
+
18
+ context "any key hash" do
19
+ it "wraps hash with any key accessor" do
20
+ hash = {:a=>:b}
21
+ any_hash = BackgroundQueue::Utils::AnyKeyHash.new(hash)
22
+ any_hash[:a].should eq(:b)
23
+ any_hash["a"].should eq(:b)
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,35 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'background_queue_worker'
3
+
4
+
5
+ describe BackgroundQueue::Worker::Base do
6
+
7
+ let(:logger) {
8
+ l = double("logger")
9
+ l.stub(:debug)
10
+ l.stub(:info)
11
+ l.stub(:error)
12
+ l.stub(:warn)
13
+ l
14
+ }
15
+
16
+ let(:env) {
17
+ double("env", :logger=>logger)
18
+ }
19
+
20
+ context "#set_progress" do
21
+ it "will serialize the progress" do
22
+ subject.set_environment(env)
23
+ env.should_receive(:send_data).with({:caption=>"TEST", :percent=>15.1}.to_json)
24
+ subject.set_progress("TEST", 15.1)
25
+ end
26
+ end
27
+
28
+ context "#add_progress_meta" do
29
+ it "will serialize the meta" do
30
+ subject.set_environment(env)
31
+ env.should_receive(:send_data).with({:meta=>{:a=>"b"}}.to_json)
32
+ subject.add_progress_meta(:a, 'b')
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,103 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'background_queue_worker'
3
+
4
+ class DummyClass
5
+ include BackgroundQueue::Worker::Calling
6
+ end
7
+
8
+ describe "Calling" do
9
+
10
+ let(:logger) {
11
+ l = double("logger")
12
+ l.stub(:debug)
13
+ l.stub(:info)
14
+ l.stub(:error)
15
+ l.stub(:warn)
16
+ l
17
+ }
18
+
19
+ let(:params) {
20
+ {
21
+ :auth=>"ABCD"
22
+ }
23
+ }
24
+
25
+ let(:response) {
26
+ double("resp")
27
+ }
28
+
29
+ let(:headers) { {} }
30
+
31
+
32
+ let(:subject) {
33
+ s = DummyClass.new
34
+ s.stub(:params).and_return(params)
35
+ s.stub(:logger).and_return(logger)
36
+ s.stub(:response).and_return(response)
37
+ s.stub(:headers).and_return(headers)
38
+ s
39
+ }
40
+
41
+ before do
42
+ BackgroundQueue::Worker::Config.secret = "ABCD"
43
+ end
44
+
45
+ context "#check_secret" do
46
+ it "will pass if the secret matches" do
47
+ subject.check_secret.should be_true
48
+ end
49
+
50
+ it "will fail and return a 401 auth error if secret does not match" do
51
+ BackgroundQueue::Worker::Config.secret = "XYZ"
52
+ subject.should_receive(:render).with(:text=>"Invalid auth (ABCD)", :status=>401)
53
+ subject.check_secret.should be_false
54
+ end
55
+ end
56
+
57
+ context "#init_environment" do
58
+ it "will instanciate an environment and init it" do
59
+ BackgroundQueue::Worker::Environment.any_instance.should_receive(:init_from_controller).with(subject)
60
+ subject.init_environment
61
+ end
62
+ end
63
+
64
+ context "#run_worker" do
65
+ it "should return if check_secret fails" do
66
+ subject.should_receive(:check_secret).and_return(false)
67
+ subject.run_worker()
68
+ end
69
+
70
+ it "will call worker if all is ok" do
71
+ env = double("env", :worker=>:worker_name)
72
+ worker = double("worker")
73
+ worker.should_receive(:set_environment).with(env)
74
+
75
+ subject.should_receive(:call_worker).with(worker, env)
76
+ subject.should_receive(:init_environment).and_return(env)
77
+ BackgroundQueue::Worker::WorkerLoader.should_receive(:get_worker).with(:worker_name).and_return(worker)
78
+ subject.run_worker
79
+ end
80
+
81
+ it "will render the error and raise it if worker does not initialize correctly" do
82
+ subject.should_receive(:init_environment).and_raise "ERROR"
83
+ subject.should_receive(:render).with(:text=>"Error initializing worker: ERROR", :status=>500)
84
+ expect { subject.run_worker }.to raise_exception("ERROR")
85
+ end
86
+ end
87
+
88
+ context "#call_worker" do
89
+ it "will call worker.run within a render block" do
90
+ env = double("env")
91
+ worker = double("worker")
92
+ worker.should_receive(:set_environment).with(nil)
93
+ env.should_receive(:set_output).with(:output)
94
+ worker.should_receive(:run)
95
+ subject.should_receive(:render) { |opts|
96
+ opts[:text].call(:response, :output)
97
+ }
98
+ subject.call_worker(worker, env)
99
+ subject.headers['X-Accel-Buffering'].should eq('no')
100
+ end
101
+ end
102
+
103
+ end