appsignal 2.9.16 → 2.9.17

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05276a7162dde5c58464ac4d6170837a4a71522f42024b77fb456f35773c74b3
4
- data.tar.gz: 8005bfc2837f546de89a5703ab0fdbd5a340f53f86871dc64de6550c3f728c26
3
+ metadata.gz: 2ea105e6dd4c3e9e9a6f8e5a5df0304bbc438932c820eadd4008548d02beadf9
4
+ data.tar.gz: 767b716c1860e4a8a6809f5765b8a6b5e97d3fe682f2d8cd8d797c1226b473bc
5
5
  SHA512:
6
- metadata.gz: 8906bffe72c3e8cceb40f1eb6e32827c4389acb19706fcd992901dc00074779ad2121e3d1c7f45ba9f876929c4bb2cb357808c5bd81d01bf3c6dd40695441b31
7
- data.tar.gz: d5582d45e830d06d291296136d8802bad1121227af7b6f82192112f991c48621b3fe55836343ff0d592d9770c7043fc4abce04e5626d0115ea491b94e9ab201c
6
+ metadata.gz: 7ff2cb1442967bc7e07a50c399d690c1a638a7ceff73e4c093f6157c4b84e3541333f3b169ed69e4574dd2904d07a38f3b737557f0d3c4d800ad7d074671b670
7
+ data.tar.gz: a06dcd00bb6644394795ce844ea9f1677e68e1b7c3c89ca2c289fc6c313fb5c8cecc6cd56453b3c7666a8c760261f1f3ec08ac74270ba17a20f1d30c690f51a3
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.9.17
4
+ - Handle missing file and load errors from `application.rb` in `appsignal
5
+ install` for Rails apps. PR #568
6
+ - Support minutely probes for Puma in clustered mode. PR #570
7
+ See the installation instructions for the Puma plugin:
8
+ https://docs.appsignal.com/ruby/integrations/puma.html
9
+
3
10
  ## 2.9.16
4
11
  - Check set_error arguments for Exceptions. PR #565
5
12
  - Bump agent to v-1d8917f - commit 737d6b1b8fc9cd2c0564050bb04246d9267dceb7
@@ -75,18 +75,39 @@ module Appsignal
75
75
  def install_for_rails(config)
76
76
  puts "Installing for Ruby on Rails"
77
77
 
78
- require File.expand_path(File.join(Dir.pwd, "config/application.rb"))
78
+ name_overwritten = configure_rails_app_name(config)
79
+ configure(config, rails_environments, name_overwritten)
80
+ done_notice
81
+ end
79
82
 
80
- config[:name] = Appsignal::Utils::RailsHelper.detected_rails_app_name
81
- name_overwritten = yes_or_no(" Your app's name is: '#{config[:name]}' \n Do you want to change how this is displayed in AppSignal? (y/n): ")
82
- puts
83
- if name_overwritten
84
- config[:name] = required_input(" Choose app's display name: ")
83
+ def configure_rails_app_name(config)
84
+ loaded =
85
+ begin
86
+ load Appsignal::Utils::RailsHelper.application_config_path
87
+ true
88
+ rescue LoadError, StandardError
89
+ false
90
+ end
91
+
92
+ name_overwritten = false
93
+ if loaded
94
+ config[:name] = Appsignal::Utils::RailsHelper.detected_rails_app_name
95
+ puts
96
+ name_overwritten = yes_or_no(
97
+ " Your app's name is: '#{config[:name]}' \n " \
98
+ "Do you want to change how this is displayed in AppSignal? " \
99
+ "(y/n): "
100
+ )
101
+ if name_overwritten
102
+ config[:name] = required_input(" Choose app's display name: ")
103
+ puts
104
+ end
105
+ else
106
+ puts " Unable to automatically detect your Rails app's name."
107
+ config[:name] = required_input(" Choose your app's display name for AppSignal.com: ")
85
108
  puts
86
109
  end
87
-
88
- configure(config, rails_environments, name_overwritten)
89
- done_notice
110
+ name_overwritten
90
111
  end
91
112
 
92
113
  def install_for_sinatra(config)
@@ -227,7 +248,10 @@ module Appsignal
227
248
 
228
249
  def installed_frameworks
229
250
  [].tap do |out|
230
- out << :rails if framework_available? "rails"
251
+ if framework_available?("rails") &&
252
+ File.exist?(Appsignal::Utils::RailsHelper.application_config_path)
253
+ out << :rails
254
+ end
231
255
  out << :sinatra if framework_available? "sinatra"
232
256
  out << :padrino if framework_available? "padrino"
233
257
  out << :grape if framework_available? "grape"
@@ -11,27 +11,22 @@ module Appsignal
11
11
  end
12
12
 
13
13
  def install
14
- if ::Puma.respond_to?(:stats)
14
+ if ::Puma.respond_to?(:stats) && !defined?(APPSIGNAL_PUMA_PLUGIN_LOADED)
15
+ # Only install the minutely probe if a user isn't using our Puma
16
+ # plugin, which lives in `lib/puma/appsignal.rb`. This plugin defines
17
+ # the {APPSIGNAL_PUMA_PLUGIN_LOADED} constant.
18
+ #
19
+ # We prefer people use the AppSignal Puma plugin. This fallback is
20
+ # only there when users relied on our *magic* integration.
21
+ #
22
+ # Using the Puma plugin, the minutely probe thread will still run in
23
+ # Puma workers, for other non-Puma probes, but the Puma probe only
24
+ # runs in the Puma main process.
25
+ # For more information:
26
+ # https://docs.appsignal.com/ruby/integrations/puma.html
15
27
  Appsignal::Minutely.probes.register :puma, PumaProbe
16
28
  end
17
29
 
18
- if ::Puma.respond_to?(:cli_config) && ::Puma.cli_config
19
- ::Puma.cli_config.options[:before_fork] ||= []
20
- ::Puma.cli_config.options[:before_fork] << proc do |_id|
21
- Appsignal::Minutely.start
22
- end
23
-
24
- ::Puma.cli_config.options[:before_worker_boot] ||= []
25
- ::Puma.cli_config.options[:before_worker_boot] << proc do |_id|
26
- Appsignal.forked
27
- end
28
-
29
- ::Puma.cli_config.options[:before_worker_shutdown] ||= []
30
- ::Puma.cli_config.options[:before_worker_shutdown] << proc do |_id|
31
- Appsignal.stop("puma before_worker_shutdown")
32
- end
33
- end
34
-
35
30
  return unless defined?(::Puma::Cluster)
36
31
  # For clustered mode with multiple workers
37
32
  ::Puma::Cluster.class_eval do
@@ -11,6 +11,10 @@ module Appsignal
11
11
  rails_class.parent_name
12
12
  end
13
13
  end
14
+
15
+ def self.application_config_path
16
+ File.expand_path(File.join(Dir.pwd, "config/application.rb"))
17
+ end
14
18
  end
15
19
  end
16
20
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "2.9.16".freeze
4
+ VERSION = "2.9.17".freeze
5
5
  end
@@ -0,0 +1,26 @@
1
+ APPSIGNAL_PUMA_PLUGIN_LOADED = true
2
+
3
+ # AppSignal Puma plugin
4
+ #
5
+ # This plugin ensures the minutely probe thread is started with the Puma
6
+ # minutely probe in the Puma master process.
7
+ #
8
+ # The constant {APPSIGNAL_PUMA_PLUGIN_LOADED} is here to mark the Plugin as
9
+ # loaded by the rest of the AppSignal gem. This ensures that the Puma minutely
10
+ # probe is not also started in every Puma workers, which was the old behavior.
11
+ # See {Appsignal::Hooks::PumaHook#install} for more information.
12
+ #
13
+ # For even more information:
14
+ # https://docs.appsignal.com/ruby/integrations/puma.html
15
+ Puma::Plugin.create do
16
+ def start(launcher = nil)
17
+ launcher.events.on_booted do
18
+ require "appsignal"
19
+ if ::Puma.respond_to?(:stats)
20
+ Appsignal::Minutely.probes.register :puma, Appsignal::Hooks::PumaProbe
21
+ end
22
+ Appsignal.start
23
+ Appsignal.start_logger
24
+ end
25
+ end
26
+ end
@@ -16,6 +16,10 @@ describe Appsignal::CLI::Install do
16
16
  allow(described_class).to receive(:press_any_key)
17
17
  allow(Appsignal::Demo).to receive(:transmit).and_return(true)
18
18
  end
19
+ after do
20
+ FileUtils.rm_rf(tmp_dir)
21
+ FileUtils.mkdir_p(tmp_dir)
22
+ end
19
23
  around do |example|
20
24
  original_stdin = $stdin
21
25
  $stdin = StringIO.new
@@ -157,16 +161,10 @@ describe Appsignal::CLI::Install do
157
161
  shared_examples "capistrano install" do
158
162
  let(:capfile) { File.join(tmp_dir, "Capfile") }
159
163
  before do
160
- FileUtils.mkdir_p(tmp_dir)
161
-
162
164
  enter_app_name "foo"
163
165
  add_cli_input "n"
164
166
  choose_environment_config
165
167
  end
166
- after do
167
- FileUtils.rm_rf(tmp_dir)
168
- FileUtils.mkdir_p(tmp_dir)
169
- end
170
168
 
171
169
  context "without Capfile" do
172
170
  it "does nothing" do
@@ -260,7 +258,6 @@ describe Appsignal::CLI::Install do
260
258
  FileUtils.touch(File.join(environments_dir, "development.rb"))
261
259
  FileUtils.touch(File.join(environments_dir, "staging.rb"))
262
260
  FileUtils.touch(File.join(environments_dir, "production.rb"))
263
- enter_app_name app_name
264
261
  end
265
262
 
266
263
  describe "environments" do
@@ -410,6 +407,53 @@ describe Appsignal::CLI::Install do
410
407
  end
411
408
  end
412
409
  end
410
+
411
+ context "when there is no Rails application.rb file" do
412
+ before do
413
+ # Do not detect it as another framework for testing
414
+ allow(described_class).to receive(:framework_available?).and_call_original
415
+ allow(described_class).to receive(:framework_available?).with("sinatra").and_return(false)
416
+
417
+ File.delete(File.join(config_dir, "application.rb"))
418
+ expect(File.exist?(File.join(config_dir, "application.rb"))).to eql(false)
419
+ end
420
+
421
+ it "fails the installation" do
422
+ run
423
+
424
+ expect(output).to include("We could not detect which framework you are using.")
425
+ expect(output).to_not include("Installing for Ruby on Rails")
426
+ expect(output).to include_complete_install
427
+
428
+ expect(File.exist?(config_file_path)).to be(false)
429
+ end
430
+ end
431
+
432
+ context "when failed to load the Rails application.rb file" do
433
+ before do
434
+ File.open(File.join(config_dir, "application.rb"), "w") do |file|
435
+ file.write("I am invalid code")
436
+ end
437
+ end
438
+
439
+ it "prompts the user to fill in an app name" do
440
+ enter_app_name app_name
441
+ choose_config_file
442
+ run
443
+
444
+ expect(output).to include("Installing for Ruby on Rails")
445
+ expect(output).to include("Unable to automatically detect your Rails app's name.")
446
+ expect(output).to include("Choose your app's display name for AppSignal.com:")
447
+ expect(output).to include_file_config
448
+ expect(output).to include_complete_install
449
+
450
+ expect(config_file).to configure_app_name(app_name)
451
+ expect(config_file).to configure_push_api_key(push_api_key)
452
+ expect(config_file).to configure_environment("development")
453
+ expect(config_file).to configure_environment("staging")
454
+ expect(config_file).to configure_environment("production")
455
+ end
456
+ end
413
457
  end
414
458
  end
415
459
 
@@ -27,6 +27,8 @@ describe Appsignal::Hooks::PumaHook do
27
27
  end
28
28
 
29
29
  describe "installation" do
30
+ before { Appsignal::Minutely.probes.clear }
31
+
30
32
  context "when not clustered mode" do
31
33
  it "does not add AppSignal stop behavior Puma::Cluster" do
32
34
  expect(defined?(::Puma::Cluster)).to be_falsy
@@ -34,9 +36,27 @@ describe Appsignal::Hooks::PumaHook do
34
36
  Appsignal::Hooks::PumaHook.new.install
35
37
  end
36
38
 
37
- it "adds the Puma minutely probe" do
38
- probe = Appsignal::Minutely.probes[:puma]
39
- expect(probe).to eql(Appsignal::Hooks::PumaProbe)
39
+ context "with APPSIGNAL_PUMA_PLUGIN_LOADED defined" do
40
+ before do
41
+ # Set in lib/puma/appsignal.rb
42
+ APPSIGNAL_PUMA_PLUGIN_LOADED = true
43
+ end
44
+ after { Object.send :remove_const, :APPSIGNAL_PUMA_PLUGIN_LOADED }
45
+
46
+ it "does not add the Puma minutely probe" do
47
+ Appsignal::Hooks::PumaHook.new.install
48
+ expect(Appsignal::Minutely.probes[:puma]).to be_nil
49
+ end
50
+ end
51
+
52
+ context "without APPSIGNAL_PUMA_PLUGIN_LOADED defined" do
53
+ it "adds the Puma minutely probe" do
54
+ expect(defined?(APPSIGNAL_PUMA_PLUGIN_LOADED)).to be_nil
55
+
56
+ Appsignal::Hooks::PumaHook.new.install
57
+ probe = Appsignal::Minutely.probes[:puma]
58
+ expect(probe).to eql(Appsignal::Hooks::PumaProbe)
59
+ end
40
60
  end
41
61
  end
42
62
 
@@ -49,11 +69,11 @@ describe Appsignal::Hooks::PumaHook do
49
69
  end
50
70
  end
51
71
  end
52
- Appsignal::Hooks::PumaHook.new.install
53
72
  end
54
73
  after { Puma.send(:remove_const, :Cluster) }
55
74
 
56
75
  it "adds behavior to Puma::Cluster.stop_workers" do
76
+ Appsignal::Hooks::PumaHook.new.install
57
77
  cluster = Puma::Cluster.new
58
78
 
59
79
  expect(cluster.instance_variable_defined?(:@called)).to be_falsy
@@ -62,40 +82,28 @@ describe Appsignal::Hooks::PumaHook do
62
82
  expect(cluster.instance_variable_get(:@called)).to be(true)
63
83
  end
64
84
 
65
- it "adds the Puma minutely probe" do
66
- probe = Appsignal::Minutely.probes[:puma]
67
- expect(probe).to eql(Appsignal::Hooks::PumaProbe)
68
- end
69
- end
70
- end
71
-
72
- context "with nil hooks" do
73
- before do
74
- Puma.cli_config.options.delete(:before_fork)
75
- Puma.cli_config.options.delete(:before_worker_boot)
76
- Puma.cli_config.options.delete(:before_worker_shutdown)
77
- Appsignal::Hooks::PumaHook.new.install
78
- end
85
+ context "with APPSIGNAL_PUMA_PLUGIN_LOADED defined" do
86
+ before do
87
+ # Set in lib/puma/appsignal.rb
88
+ APPSIGNAL_PUMA_PLUGIN_LOADED = true
89
+ end
90
+ after { Object.send :remove_const, :APPSIGNAL_PUMA_PLUGIN_LOADED }
79
91
 
80
- it "should add a before shutdown worker callback" do
81
- expect(Puma.cli_config.options[:before_fork].first).to be_a(Proc)
82
- expect(Puma.cli_config.options[:before_worker_boot].first).to be_a(Proc)
83
- expect(Puma.cli_config.options[:before_worker_shutdown].first).to be_a(Proc)
84
- end
85
- end
92
+ it "does not add the Puma minutely probe" do
93
+ Appsignal::Hooks::PumaHook.new.install
94
+ expect(Appsignal::Minutely.probes[:puma]).to be_nil
95
+ end
96
+ end
86
97
 
87
- context "with existing hooks" do
88
- before do
89
- Puma.cli_config.options[:before_fork] = []
90
- Puma.cli_config.options[:before_worker_boot] = []
91
- Puma.cli_config.options[:before_worker_shutdown] = []
92
- Appsignal::Hooks::PumaHook.new.install
93
- end
98
+ context "without APPSIGNAL_PUMA_PLUGIN_LOADED defined" do
99
+ it "adds the Puma minutely probe" do
100
+ expect(defined?(APPSIGNAL_PUMA_PLUGIN_LOADED)).to be_nil
94
101
 
95
- it "should add a before shutdown worker callback" do
96
- expect(Puma.cli_config.options[:before_fork].first).to be_a(Proc)
97
- expect(Puma.cli_config.options[:before_worker_boot].first).to be_a(Proc)
98
- expect(Puma.cli_config.options[:before_worker_shutdown].first).to be_a(Proc)
102
+ Appsignal::Hooks::PumaHook.new.install
103
+ probe = Appsignal::Minutely.probes[:puma]
104
+ expect(probe).to eql(Appsignal::Hooks::PumaProbe)
105
+ end
106
+ end
99
107
  end
100
108
  end
101
109
  end
@@ -1,4 +1,6 @@
1
1
  describe Appsignal::Minutely do
2
+ include WaitForHelper
3
+
2
4
  before { Appsignal::Minutely.probes.clear }
3
5
 
4
6
  it "returns a ProbeCollection" do
@@ -7,38 +9,26 @@ describe Appsignal::Minutely do
7
9
  end
8
10
 
9
11
  describe ".start" do
10
- class Probe
11
- attr_reader :calls
12
-
13
- def initialize
14
- @calls = 0
15
- end
16
-
17
- def call
18
- @calls += 1
19
- end
20
- end
21
-
22
- class ProbeWithoutDependency < Probe
12
+ class ProbeWithoutDependency < MockProbe
23
13
  def self.dependencies_present?
24
14
  true
25
15
  end
26
16
  end
27
17
 
28
- class ProbeWithMissingDependency < Probe
18
+ class ProbeWithMissingDependency < MockProbe
29
19
  def self.dependencies_present?
30
20
  false
31
21
  end
32
22
  end
33
23
 
34
- class BrokenProbe < Probe
24
+ class BrokenProbe < MockProbe
35
25
  def call
36
26
  super
37
27
  raise "oh no!"
38
28
  end
39
29
  end
40
30
 
41
- class BrokenProbeOnInitialize < Probe
31
+ class BrokenProbeOnInitialize < MockProbe
42
32
  def initialize
43
33
  super
44
34
  raise "oh no initialize!"
@@ -60,7 +50,7 @@ describe Appsignal::Minutely do
60
50
 
61
51
  context "with an instance of a class" do
62
52
  it "calls the probe every <wait_time>" do
63
- probe = Probe.new
53
+ probe = MockProbe.new
64
54
  Appsignal::Minutely.probes.register :my_probe, probe
65
55
  Appsignal::Minutely.start
66
56
 
@@ -90,8 +80,8 @@ describe Appsignal::Minutely do
90
80
 
91
81
  context "with probe class" do
92
82
  it "creates an instance of the class and call that every <wait time>" do
93
- probe = Probe
94
- probe_instance = Probe.new
83
+ probe = MockProbe
84
+ probe_instance = MockProbe.new
95
85
  expect(probe).to receive(:new).and_return(probe_instance)
96
86
  Appsignal::Minutely.probes.register :my_probe, probe
97
87
  Appsignal::Minutely.start
@@ -163,7 +153,7 @@ describe Appsignal::Minutely do
163
153
 
164
154
  context "with a broken probe" do
165
155
  it "logs the error and continues calling the probes every <wait_time>" do
166
- probe = Probe.new
156
+ probe = MockProbe.new
167
157
  broken_probe = BrokenProbe.new
168
158
  Appsignal::Minutely.probes.register :my_probe, probe
169
159
  Appsignal::Minutely.probes.register :broken_probe, broken_probe
@@ -183,7 +173,7 @@ describe Appsignal::Minutely do
183
173
 
184
174
  it "ensures only one minutely probes thread is active at a time" do
185
175
  alive_thread_counter = proc { Thread.list.reject { |t| t.status == "dead" }.length }
186
- probe = Probe.new
176
+ probe = MockProbe.new
187
177
  Appsignal::Minutely.probes.register :my_probe, probe
188
178
  expect do
189
179
  Appsignal::Minutely.start
@@ -349,31 +339,4 @@ describe Appsignal::Minutely do
349
339
  end
350
340
  end
351
341
  end
352
-
353
- # Wait for a condition to be met
354
- #
355
- # @example
356
- # # Perform threaded operation
357
- # wait_for("enough probe calls") { probe.calls >= 2 }
358
- # # Assert on result
359
- #
360
- # @param name [String] The name of the condition to check. Used in the
361
- # error when it fails.
362
- # @yield Assertion to check.
363
- # @yieldreturn [Boolean] True/False value that indicates if the condition
364
- # is met.
365
- # @raise [StandardError] Raises error if the condition is not met after 5
366
- # seconds, 5_000 tries.
367
- def wait_for(name)
368
- max_wait = 5_000
369
- i = 0
370
- while i <= max_wait
371
- break if yield
372
- i += 1
373
- sleep 0.001
374
- end
375
-
376
- return unless i == max_wait
377
- raise "Waited 5 seconds for #{name} condition, but was not met."
378
- end
379
342
  end
@@ -0,0 +1,91 @@
1
+ RSpec.describe "Puma plugin" do
2
+ include WaitForHelper
3
+
4
+ class MockPumaLauncher
5
+ def events
6
+ return @events if defined?(@events)
7
+
8
+ @events = MockPumaEvents.new
9
+ end
10
+ end
11
+
12
+ class MockPumaEvents
13
+ def on_booted(&block)
14
+ @on_booted = block if block_given?
15
+ @on_booted if defined?(@on_booted)
16
+ end
17
+ end
18
+
19
+ let(:probe) { MockProbe.new }
20
+ let(:launcher) { MockPumaLauncher.new }
21
+ before do
22
+ module Puma
23
+ def self.stats
24
+ end
25
+
26
+ class Plugin
27
+ class << self
28
+ attr_reader :plugin
29
+
30
+ def create(&block)
31
+ @plugin = Class.new(::Puma::Plugin)
32
+ @plugin.class_eval(&block)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ Appsignal::Minutely.probes.clear
39
+ ENV["APPSIGNAL_ENABLE_MINUTELY_PROBES"] = "true"
40
+ Appsignal.config = project_fixture_config
41
+ # Speed up test time
42
+ allow(Appsignal::Minutely).to receive(:initial_wait_time).and_return(0.001)
43
+ allow(Appsignal::Minutely).to receive(:wait_time).and_return(0.001)
44
+
45
+ Appsignal::Minutely.probes.register :my_probe, probe
46
+ load File.expand_path("../lib/puma/plugin/appsignal.rb", APPSIGNAL_SPEC_DIR)
47
+ end
48
+ after do
49
+ Appsignal.config = nil
50
+ Object.send :remove_const, :Puma
51
+ Object.send :remove_const, :APPSIGNAL_PUMA_PLUGIN_LOADED
52
+ end
53
+
54
+ it "registers the PumaProbe" do
55
+ expect(Appsignal::Minutely.probes[:my_probe]).to eql(probe)
56
+ expect(Appsignal::Minutely.probes[:puma]).to be_nil
57
+ plugin = Puma::Plugin.plugin.new
58
+ expect(launcher.events.on_booted).to be_nil
59
+
60
+ plugin.start(launcher)
61
+ expect(Appsignal::Minutely.probes[:puma]).to be_nil
62
+ expect(launcher.events.on_booted).to_not be_nil
63
+
64
+ launcher.events.on_booted.call
65
+ expect(Appsignal::Minutely.probes[:puma]).to eql(Appsignal::Hooks::PumaProbe)
66
+
67
+ # Minutely probes started and called
68
+ wait_for("enough probe calls") { probe.calls >= 2 }
69
+ end
70
+
71
+ context "without Puma.stats" do
72
+ before { Puma.singleton_class.send(:remove_method, :stats) }
73
+
74
+ it "does not register the PumaProbe" do
75
+ expect(Appsignal::Minutely.probes[:my_probe]).to eql(probe)
76
+ expect(Appsignal::Minutely.probes[:puma]).to be_nil
77
+ plugin = Puma::Plugin.plugin.new
78
+ expect(launcher.events.on_booted).to be_nil
79
+
80
+ plugin.start(launcher)
81
+ expect(Appsignal::Minutely.probes[:puma]).to be_nil
82
+ expect(launcher.events.on_booted).to_not be_nil
83
+
84
+ launcher.events.on_booted.call
85
+ expect(Appsignal::Minutely.probes[:puma]).to be_nil
86
+
87
+ # Minutely probes started and called
88
+ wait_for("enough probe calls") { probe.calls >= 2 }
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,28 @@
1
+ module WaitForHelper
2
+ # Wait for a condition to be met
3
+ #
4
+ # @example
5
+ # # Perform threaded operation
6
+ # wait_for("enough probe calls") { probe.calls >= 2 }
7
+ # # Assert on result
8
+ #
9
+ # @param name [String] The name of the condition to check. Used in the
10
+ # error when it fails.
11
+ # @yield Assertion to check.
12
+ # @yieldreturn [Boolean] True/False value that indicates if the condition
13
+ # is met.
14
+ # @raise [StandardError] Raises error if the condition is not met after 5
15
+ # seconds, 5_000 tries.
16
+ def wait_for(name)
17
+ max_wait = 5_000
18
+ i = 0
19
+ while i <= max_wait
20
+ break if yield
21
+ i += 1
22
+ sleep 0.001
23
+ end
24
+
25
+ return unless i == max_wait
26
+ raise "Waited 5 seconds for #{name} condition, but was not met."
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ class MockProbe
2
+ attr_reader :calls
3
+
4
+ def initialize
5
+ @calls = 0
6
+ end
7
+
8
+ def call
9
+ @calls += 1
10
+ end
11
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appsignal
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.16
4
+ version: 2.9.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Beekman
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2019-09-30 00:00:00.000000000 Z
13
+ date: 2019-11-07 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -255,6 +255,7 @@ files:
255
255
  - lib/appsignal/utils/query_params_sanitizer.rb
256
256
  - lib/appsignal/utils/rails_helper.rb
257
257
  - lib/appsignal/version.rb
258
+ - lib/puma/plugin/appsignal.rb
258
259
  - lib/sequel/extensions/appsignal_integration.rb
259
260
  - resources/appsignal.yml.erb
260
261
  - resources/cacert.pem
@@ -328,6 +329,7 @@ files:
328
329
  - spec/lib/appsignal/utils/json_spec.rb
329
330
  - spec/lib/appsignal/utils/query_params_sanitizer_spec.rb
330
331
  - spec/lib/appsignal_spec.rb
332
+ - spec/lib/puma/appsignal_spec.rb
331
333
  - spec/spec_helper.rb
332
334
  - spec/support/fixtures/generated_config.yml
333
335
  - spec/support/fixtures/projects/valid/config/application.rb
@@ -350,9 +352,11 @@ files:
350
352
  - spec/support/helpers/system_helpers.rb
351
353
  - spec/support/helpers/time_helpers.rb
352
354
  - spec/support/helpers/transaction_helpers.rb
355
+ - spec/support/helpers/wait_for_helper.rb
353
356
  - spec/support/matchers/contains_log.rb
354
357
  - spec/support/mocks/fake_gc_profiler.rb
355
358
  - spec/support/mocks/mock_extension.rb
359
+ - spec/support/mocks/mock_probe.rb
356
360
  - spec/support/rails/my_app.rb
357
361
  - spec/support/shared_examples/instrument.rb
358
362
  - spec/support/stubs/delayed_job.rb
@@ -384,7 +388,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
384
388
  - !ruby/object:Gem::Version
385
389
  version: '0'
386
390
  requirements: []
387
- rubygems_version: 3.0.2
391
+ rubygems_version: 3.0.6
388
392
  signing_key:
389
393
  specification_version: 4
390
394
  summary: Logs performance and exception data from your app to appsignal.com
@@ -459,6 +463,7 @@ test_files:
459
463
  - spec/lib/appsignal/utils/json_spec.rb
460
464
  - spec/lib/appsignal/utils/query_params_sanitizer_spec.rb
461
465
  - spec/lib/appsignal_spec.rb
466
+ - spec/lib/puma/appsignal_spec.rb
462
467
  - spec/spec_helper.rb
463
468
  - spec/support/fixtures/generated_config.yml
464
469
  - spec/support/fixtures/projects/valid/config/application.rb
@@ -481,9 +486,11 @@ test_files:
481
486
  - spec/support/helpers/system_helpers.rb
482
487
  - spec/support/helpers/time_helpers.rb
483
488
  - spec/support/helpers/transaction_helpers.rb
489
+ - spec/support/helpers/wait_for_helper.rb
484
490
  - spec/support/matchers/contains_log.rb
485
491
  - spec/support/mocks/fake_gc_profiler.rb
486
492
  - spec/support/mocks/mock_extension.rb
493
+ - spec/support/mocks/mock_probe.rb
487
494
  - spec/support/rails/my_app.rb
488
495
  - spec/support/shared_examples/instrument.rb
489
496
  - spec/support/stubs/delayed_job.rb