appsignal 1.3.6 → 1.4.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.rspec +1 -3
  4. data/CHANGELOG.md +2 -28
  5. data/Rakefile +2 -2
  6. data/benchmark.rake +4 -0
  7. data/circle.yml +12 -0
  8. data/ext/agent.yml +11 -11
  9. data/ext/appsignal_extension.c +3 -3
  10. data/ext/extconf.rb +4 -4
  11. data/lib/appsignal.rb +10 -27
  12. data/lib/appsignal/cli/diagnose.rb +3 -4
  13. data/lib/appsignal/cli/install.rb +15 -16
  14. data/lib/appsignal/config.rb +7 -18
  15. data/lib/appsignal/event_formatter.rb +1 -1
  16. data/lib/appsignal/integrations/capistrano/appsignal.cap +1 -1
  17. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +2 -2
  18. data/lib/appsignal/integrations/object.rb +4 -4
  19. data/lib/appsignal/integrations/sinatra.rb +1 -1
  20. data/lib/appsignal/integrations/webmachine.rb +1 -1
  21. data/lib/appsignal/js_exception_transaction.rb +0 -1
  22. data/lib/appsignal/marker.rb +2 -3
  23. data/lib/appsignal/rack/sinatra_instrumentation.rb +5 -12
  24. data/lib/appsignal/rack/streaming_listener.rb +2 -4
  25. data/lib/appsignal/transaction.rb +3 -3
  26. data/lib/appsignal/transmitter.rb +7 -11
  27. data/lib/appsignal/utils.rb +0 -6
  28. data/lib/appsignal/version.rb +1 -1
  29. data/spec/lib/appsignal/auth_check_spec.rb +2 -0
  30. data/spec/lib/appsignal/capistrano2_spec.rb +78 -98
  31. data/spec/lib/appsignal/capistrano3_spec.rb +77 -56
  32. data/spec/lib/appsignal/cli/diagnose_spec.rb +13 -12
  33. data/spec/lib/appsignal/cli/install_spec.rb +15 -38
  34. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +5 -2
  35. data/spec/lib/appsignal/cli_spec.rb +5 -2
  36. data/spec/lib/appsignal/config_spec.rb +17 -131
  37. data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +2 -0
  38. data/spec/lib/appsignal/event_formatter/active_record/instantiation_formatter_spec.rb +2 -0
  39. data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +2 -0
  40. data/spec/lib/appsignal/event_formatter/elastic_search/search_formatter_spec.rb +2 -0
  41. data/spec/lib/appsignal/event_formatter/faraday/request_formatter_spec.rb +2 -0
  42. data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +2 -0
  43. data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +2 -0
  44. data/spec/lib/appsignal/event_formatter_spec.rb +2 -0
  45. data/spec/lib/appsignal/extension_spec.rb +1 -0
  46. data/spec/lib/appsignal/hooks/celluloid_spec.rb +2 -0
  47. data/spec/lib/appsignal/hooks/data_mapper_spec.rb +2 -0
  48. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -0
  49. data/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +2 -0
  50. data/spec/lib/appsignal/hooks/net_http_spec.rb +2 -0
  51. data/spec/lib/appsignal/hooks/passenger_spec.rb +2 -0
  52. data/spec/lib/appsignal/hooks/puma_spec.rb +2 -0
  53. data/spec/lib/appsignal/hooks/rake_spec.rb +1 -0
  54. data/spec/lib/appsignal/hooks/redis_spec.rb +2 -0
  55. data/spec/lib/appsignal/hooks/sequel_spec.rb +2 -0
  56. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +4 -1
  57. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +3 -2
  58. data/spec/lib/appsignal/hooks/unicorn_spec.rb +2 -0
  59. data/spec/lib/appsignal/hooks/webmachine_spec.rb +8 -2
  60. data/spec/lib/appsignal/hooks_spec.rb +2 -0
  61. data/spec/lib/appsignal/integrations/data_mapper_spec.rb +1 -0
  62. data/spec/lib/appsignal/integrations/grape_spec.rb +2 -0
  63. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +1 -0
  64. data/spec/lib/appsignal/integrations/object_spec.rb +1 -30
  65. data/spec/lib/appsignal/integrations/padrino_spec.rb +2 -0
  66. data/spec/lib/appsignal/integrations/railtie_spec.rb +2 -0
  67. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +2 -0
  68. data/spec/lib/appsignal/integrations/resque_spec.rb +3 -1
  69. data/spec/lib/appsignal/integrations/sinatra_spec.rb +7 -32
  70. data/spec/lib/appsignal/integrations/webmachine_spec.rb +11 -4
  71. data/spec/lib/appsignal/js_exception_transaction_spec.rb +2 -0
  72. data/spec/lib/appsignal/marker_spec.rb +48 -35
  73. data/spec/lib/appsignal/minutely_spec.rb +2 -0
  74. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +2 -0
  75. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +2 -0
  76. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +2 -0
  77. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +6 -46
  78. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +4 -3
  79. data/spec/lib/appsignal/subscriber_spec.rb +2 -0
  80. data/spec/lib/appsignal/transaction_spec.rb +3 -2
  81. data/spec/lib/appsignal/transmitter_spec.rb +20 -53
  82. data/spec/lib/appsignal/update_active_support_spec.rb +2 -0
  83. data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +2 -0
  84. data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +2 -0
  85. data/spec/lib/appsignal/utils_spec.rb +2 -0
  86. data/spec/lib/appsignal_spec.rb +37 -91
  87. data/spec/spec_helper.rb +28 -22
  88. metadata +5 -14
  89. data/spec/lib/appsignal/utils/gzip_spec.rb +0 -10
  90. data/spec/support/helpers/api_request_helper.rb +0 -19
  91. data/spec/support/helpers/directory_helper.rb +0 -25
  92. data/spec/support/helpers/std_streams_helper.rb +0 -35
  93. data/spec/support/helpers/very_specific_error.rb +0 -8
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  if rails_present?
2
4
  require 'action_view'
3
5
 
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::EventFormatter::ActiveRecord::InstantiationFormatter do
2
4
  let(:klass) { Appsignal::EventFormatter::ActiveRecord::InstantiationFormatter }
3
5
  let(:formatter) { klass.new }
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::EventFormatter::ActiveRecord::InstantiationFormatter do
2
4
  let(:klass) { Appsignal::EventFormatter::ActiveRecord::SqlFormatter }
3
5
  let(:formatter) { klass.new }
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::EventFormatter::ElasticSearch::SearchFormatter do
2
4
  let(:klass) { Appsignal::EventFormatter::ElasticSearch::SearchFormatter }
3
5
  let(:formatter) { klass.new }
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::EventFormatter::Faraday::RequestFormatter do
2
4
  let(:klass) { Appsignal::EventFormatter::Faraday::RequestFormatter }
3
5
  let(:formatter) { klass.new }
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::EventFormatter::MongoRubyDriver::QueryFormatter do
2
4
  let(:formatter) { Appsignal::EventFormatter::MongoRubyDriver::QueryFormatter }
3
5
 
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::EventFormatter::Moped::QueryFormatter do
2
4
  let(:klass) { Appsignal::EventFormatter::Moped::QueryFormatter }
3
5
  let(:formatter) { klass.new }
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  class MockFormatter < Appsignal::EventFormatter
2
4
  register 'mock'
3
5
 
@@ -1,3 +1,4 @@
1
+ require 'spec_helper'
1
2
  require 'fileutils'
2
3
 
3
4
  describe "extension loading and operation" do
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::Hooks::CelluloidHook do
2
4
  context "with celluloid" do
3
5
  before :all do
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::Hooks::DataMapperHook do
2
4
  context "with datamapper" do
3
5
  before :all do
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::Hooks::DelayedJobHook do
2
4
  context "with delayed job" do
3
5
  before(:all) do
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::Hooks::MongoRubyDriverHook do
2
4
  require 'appsignal/integrations/mongo_ruby_driver'
3
5
 
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::Hooks::NetHttpHook do
2
4
  before :all do
3
5
  start_agent
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::Hooks::PassengerHook do
2
4
  context "with passenger" do
3
5
  before(:all) do
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::Hooks::PumaHook do
2
4
  context "with puma" do
3
5
  before(:all) do
@@ -1,3 +1,4 @@
1
+ require 'spec_helper'
1
2
  require 'rake'
2
3
 
3
4
  describe Appsignal::Hooks::RakeHook do
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::Hooks::RedisHook do
2
4
  before :all do
3
5
  start_agent
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::Hooks::SequelHook, if: sequel_present? do
2
4
  let(:db) { Sequel.sqlite }
3
5
 
@@ -1,3 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+
1
4
  describe Appsignal::Hooks::ShoryukenMiddleware do
2
5
  let(:current_transaction) { background_job_transaction }
3
6
 
@@ -48,7 +51,7 @@ describe Appsignal::Hooks::ShoryukenMiddleware do
48
51
  end
49
52
 
50
53
  context "with an erroring call" do
51
- let(:error) { VerySpecificError.new }
54
+ let(:error) { VerySpecificError.new('on fire') }
52
55
 
53
56
  it "should add the exception to appsignal" do
54
57
  Appsignal::Transaction.any_instance.should_receive(:set_error).with(error)
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::Hooks::SidekiqPlugin do
2
4
  let(:worker) { double }
3
5
  let(:queue) { double }
@@ -77,8 +79,7 @@ describe Appsignal::Hooks::SidekiqPlugin do
77
79
  end
78
80
 
79
81
  context "with an erroring call" do
80
- let(:error) { VerySpecificError.new }
81
-
82
+ let(:error) { VerySpecificError.new('the roof') }
82
83
  it "should add the exception to appsignal" do
83
84
  Appsignal::Transaction.any_instance.should_receive(:set_error).with(error)
84
85
  end
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::Hooks::UnicornHook do
2
4
  context "with unicorn" do
3
5
  before :all do
@@ -1,11 +1,17 @@
1
+ require 'spec_helper'
2
+
1
3
  if webmachine_present?
4
+
2
5
  describe Appsignal::Hooks::WebmachineHook do
3
6
  context "with webmachine" do
4
- let(:fsm) { Webmachine::Decision::FSM.new(double(:trace? => false), double, double) }
5
- before(:all) { start_agent }
7
+ before(:all) do
8
+ Appsignal::Hooks::WebmachineHook.new.install
9
+ end
6
10
 
7
11
  its(:dependencies_present?) { should be_true }
8
12
 
13
+ let(:fsm) { Webmachine::Decision::FSM.new(double(:trace? => false), double, double) }
14
+
9
15
  it "should include the run alias methods" do
10
16
  expect( fsm ).to respond_to(:run_with_appsignal)
11
17
  expect( fsm ).to respond_to(:run_without_appsignal)
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  class MockPresentHook < Appsignal::Hooks::Hook
2
4
  def dependencies_present?
3
5
  true
@@ -1,3 +1,4 @@
1
+ require 'spec_helper'
1
2
  require 'appsignal/integrations/data_mapper'
2
3
 
3
4
  describe Appsignal::Hooks::DataMapperLogListener do
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  if grape_present?
2
4
  require 'appsignal/integrations/grape'
3
5
 
@@ -1,3 +1,4 @@
1
+ require 'spec_helper'
1
2
  require 'appsignal/integrations/mongo_ruby_driver'
2
3
  describe Appsignal::Hooks::MongoMonitorSubscriber do
3
4
  let(:subscriber) { Appsignal::Hooks::MongoMonitorSubscriber.new }
@@ -1,3 +1,4 @@
1
+ require 'spec_helper'
1
2
  require 'appsignal/integrations/object'
2
3
 
3
4
  describe Object do
@@ -92,21 +93,6 @@ describe Object do
92
93
  expect(instance.foo).to eq(1)
93
94
  end
94
95
  end
95
-
96
- context "with a method given a block" do
97
- let(:klass) do
98
- Class.new do
99
- def foo
100
- yield
101
- end
102
- appsignal_instrument_method :foo
103
- end
104
- end
105
-
106
- it "should yield the block" do
107
- expect(instance.foo { 42 }).to eq(42)
108
- end
109
- end
110
96
  end
111
97
 
112
98
  context "when not active" do
@@ -209,21 +195,6 @@ describe Object do
209
195
  expect(klass.bar).to eq(2)
210
196
  end
211
197
  end
212
-
213
- context "with a method given a block" do
214
- let(:klass) do
215
- Class.new do
216
- def self.bar
217
- yield
218
- end
219
- appsignal_instrument_class_method :bar
220
- end
221
- end
222
-
223
- it "should yield the block" do
224
- expect(klass.bar { 42 }).to eq(42)
225
- end
226
- end
227
198
  end
228
199
 
229
200
  context "when not active" do
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  begin
2
4
  require 'padrino'
3
5
  rescue LoadError
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  if rails_present?
2
4
  describe Appsignal::Integrations::Railtie do
3
5
  context "after initializing the app" do
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  if resque_present? && active_job_present?
2
4
  describe "Resque ActiveJob integration" do
3
5
  let(:file) { File.expand_path('lib/appsignal/integrations/resque_active_job.rb') }
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  if resque_present?
2
4
  describe "Resque integration" do
3
5
  let(:file) { File.expand_path('lib/appsignal/integrations/resque.rb') }
@@ -18,7 +20,7 @@ if resque_present?
18
20
  extend Appsignal::Integrations::ResquePlugin
19
21
 
20
22
  def self.perform
21
- raise VerySpecificError.new
23
+ raise VerySpecificError.new('broken')
22
24
  end
23
25
  end
24
26
  end
@@ -1,45 +1,20 @@
1
+ require 'spec_helper'
2
+
1
3
  if sinatra_present? && !padrino_present?
2
4
  ENV['APPSIGNAL_PUSH_API_KEY'] = 'key'
3
5
  require 'appsignal/integrations/sinatra'
4
6
 
5
7
  describe "Sinatra integration" do
6
- context "Appsignal.logger" do
8
+ context "logger" do
7
9
  subject { Appsignal.logger }
8
10
 
9
11
  it { should be_a Logger }
10
12
  end
11
13
 
12
- describe "middleware" do
13
- it "adds the instrumentation middleware to Sinatra::Base" do
14
- Sinatra::Base.middleware.to_a.should include(
15
- [Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
16
- )
17
- end
18
- end
19
-
20
- describe "environment" do
21
- subject { Appsignal.config.env }
22
-
23
- context "without APPSIGNAL_APP_ENV" do
24
- before do
25
- load File.expand_path('lib/appsignal/integrations/sinatra.rb', project_dir)
26
- end
27
-
28
- it "uses the app environment" do
29
- expect(subject).to eq('test')
30
- end
31
- end
32
-
33
- context "with APPSIGNAL_APP_ENV" do
34
- before do
35
- ENV['APPSIGNAL_APP_ENV'] = 'env-staging'
36
- load File.expand_path('lib/appsignal/integrations/sinatra.rb', project_dir)
37
- end
38
-
39
- it "uses the environment variable" do
40
- expect(subject).to eq('env-staging')
41
- end
42
- end
14
+ it "should have added the instrumentation middleware" do
15
+ Sinatra::Base.middleware.to_a.should include(
16
+ [Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
17
+ )
43
18
  end
44
19
  end
45
20
  end
@@ -1,16 +1,20 @@
1
+ require 'spec_helper'
1
2
  if webmachine_present?
2
3
 
3
4
  require 'appsignal/integrations/webmachine'
4
5
 
5
6
  describe Appsignal::Integrations::WebmachinePlugin::FSM do
7
+ before(:all) do
8
+ Appsignal::Hooks::WebmachineHook.new.install
9
+ end
6
10
  let(:request) do
7
11
  Webmachine::Request.new('GET', 'http://google.com:80/foo', {}, nil)
8
12
  end
9
13
  let(:resource) { double(:trace? => false, :handle_exception => true) }
10
14
  let(:response) { double }
11
15
  let(:transaction) { double(:set_action => true) }
16
+
12
17
  let(:fsm) { Webmachine::Decision::FSM.new(resource, request, response) }
13
- before(:all) { start_agent }
14
18
 
15
19
  # Make sure the request responds to the method we need to get query params.
16
20
  describe "request" do
@@ -55,15 +59,18 @@ if webmachine_present?
55
59
  after { fsm.run }
56
60
  end
57
61
 
58
- describe "#handle_exceptions_with_appsignal" do
59
- let(:error) { VerySpecificError.new }
62
+ describe "handle_exceptions_with_appsignal" do
63
+ let(:error) { VerySpecificError.new('error') }
60
64
 
61
65
  it "should catch the error and send it to AppSignal" do
62
66
  expect( Appsignal ).to receive(:set_error).with(error)
63
67
  end
64
68
 
65
69
  after do
66
- fsm.send(:handle_exceptions) { raise error }
70
+ begin
71
+ fsm.send(:handle_exceptions) { raise error };
72
+ rescue VerySpecificError
73
+ end
67
74
  end
68
75
  end
69
76
 
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::JSExceptionTransaction do
2
4
  before { SecureRandom.stub(:uuid => '123abc') }
3
5
 
@@ -1,7 +1,9 @@
1
+ require 'spec_helper'
2
+
1
3
  describe Appsignal::Marker do
2
4
  let(:config) { project_fixture_config }
3
- let(:marker) do
4
- described_class.new(
5
+ let(:marker) {
6
+ Appsignal::Marker.new(
5
7
  {
6
8
  :revision => '503ce0923ed177a3ce000005',
7
9
  :repository => 'master',
@@ -10,46 +12,57 @@ describe Appsignal::Marker do
10
12
  },
11
13
  config
12
14
  )
13
- end
15
+ }
14
16
  let(:out_stream) { StringIO.new }
15
- around do |example|
16
- capture_stdout(out_stream) { example.run }
17
+ before do
18
+ @original_stdout = $stdout
19
+ $stdout = out_stream
17
20
  end
21
+ after do
22
+ $stdout = @original_stdout
23
+ end
24
+
25
+ context "transmit" do
26
+ let(:transmitter) { double }
27
+ before do
28
+ Appsignal::Transmitter.should_receive(:new).with(
29
+ 'markers', config
30
+ ).and_return(transmitter)
31
+ end
32
+
33
+ it "should transmit data" do
34
+ transmitter.should_receive(:transmit).with(
35
+ :revision => '503ce0923ed177a3ce000005',
36
+ :repository => 'master',
37
+ :user => 'batman',
38
+ :rails_env => 'production'
39
+ )
18
40
 
19
- describe "#transmit" do
20
- def stub_marker_request
21
- stub_api_request config, "markers", marker.marker_data
41
+ marker.transmit
22
42
  end
23
43
 
24
- context "when request is valid" do
25
- before do
26
- stub_marker_request.to_return(:status => 200)
27
- marker.transmit
28
- end
29
-
30
- it "outputs success" do
31
- output = out_stream.string
32
- expect(output).to include \
33
- 'Notifying Appsignal of deploy with: revision: 503ce0923ed177a3ce000005, user: batman',
34
- 'Appsignal has been notified of this deploy!'
35
- end
44
+ it "should log status 200" do
45
+ transmitter.should_receive(:transmit).and_return('200')
46
+
47
+ marker.transmit
48
+
49
+ out_stream.string.should include('Notifying Appsignal of deploy with: revision: 503ce0923ed177a3ce000005, user: batman')
50
+ out_stream.string.should include('Appsignal has been notified of this deploy!')
36
51
  end
37
52
 
38
- context "when request is invalid" do
39
- before do
40
- stub_marker_request.to_return(:status => 500)
41
- marker.transmit
42
- end
43
-
44
- it "outputs failure" do
45
- output = out_stream.string
46
- expect(output).to include \
47
- 'Notifying Appsignal of deploy with: revision: 503ce0923ed177a3ce000005, user: batman',
48
- "Something went wrong while trying to notify Appsignal: 500 at "\
49
- "#{config[:endpoint]}/1/markers"
50
- expect(output).to_not include \
51
- 'Appsignal has been notified of this deploy!'
52
- end
53
+ it "should log other status" do
54
+ transmitter.should_receive(:transmit).and_return('500')
55
+ transmitter.should_receive(:uri).and_return('http://localhost:3000/1/markers')
56
+
57
+ marker.transmit
58
+
59
+ out_stream.string.should include('Notifying Appsignal of deploy with: revision: 503ce0923ed177a3ce000005, user: batman')
60
+ out_stream.string.should include(
61
+ 'Something went wrong while trying to notify Appsignal: 500 at http://localhost:3000/1/markers'
62
+ )
63
+ out_stream.string.should_not include(
64
+ 'Appsignal has been notified of this deploy!'
65
+ )
53
66
  end
54
67
  end
55
68
  end