exceptional_synchrony 1.1.1 → 1.2.0

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: 883532dc8502a497eabb0785806a621f034463c021e676526ff70a5c4119d5c1
4
- data.tar.gz: 927c40e91113f1c0fb6828f400253c18fa6abcb1fec772327913a966a21fdc9c
3
+ metadata.gz: 4f13da539398611f0cfabb6439072114ef3904d89028205ee79bd7c17cb439ca
4
+ data.tar.gz: 1089857cd4e512dd3717b403163a3f74d53bc2cf0902bd13890fb842c7a9644b
5
5
  SHA512:
6
- metadata.gz: eb19bc5fbcdd191ef8ea84acb72fae37c8b32a042fccfa15de3e6b0b41d9f6fc5a7d3a4aa488da6d337e60bf6f1ca93b9e1ff948c3ed2e4f948b92b576fc1a76
7
- data.tar.gz: 9ea68a7d671a07e59a09b4cf56c99562db38b83decd54b7399e45a54e8f7ac7fedca70c5e20ab319b90a054dda758eed701497683b9b5f39f0fd52ee79c3b590
6
+ metadata.gz: bb0ce260721a479fe42118cfe78c5e06a45e36801e7d3cc25eb823baeff7693c46071bb07e314e3f51aad2b5ed3d61373219c1015b2f9cc0ff28bd1a116f8bb7
7
+ data.tar.gz: 81ed89b82782b3f80494b999efeb3d629834c1a4ab1e9e31ab446e4e7a58da0d915252e6f3e401945e1f2be82c20bc3d1f59e34667805180199ee0c2758f91fd
data/.gitignore CHANGED
@@ -34,6 +34,7 @@ intermediate
34
34
  publish
35
35
  .bundle
36
36
  pkg/
37
+ test/reports
37
38
 
38
39
  # Vim
39
40
  *.s[a-w][a-z]
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/groovy
2
+ @Library('jenkins-pipeline@v0.4.5')
3
+ import com.invoca.docker.*;
4
+ pipeline {
5
+ agent {
6
+ kubernetes {
7
+ defaultContainer "ruby"
8
+ yamlFile ".jenkins/ruby_build_pod.yml"
9
+ }
10
+ }
11
+
12
+ environment {
13
+ GITHUB_TOKEN = credentials('github_token')
14
+ GITHUB_KEY = credentials('github_key')
15
+ BUNDLE_GEM__FURY__IO = credentials('gemfury_deploy_token')
16
+ }
17
+
18
+ stages {
19
+ stage('Setup') {
20
+ steps {
21
+ updateGitHubStatus('clean-build', 'pending', 'Unit tests.')
22
+ script {
23
+ sh '''
24
+ # get SSH setup inside the container
25
+ eval `ssh-agent -s`
26
+ echo "$GITHUB_KEY" | ssh-add -
27
+ mkdir -p /root/.ssh
28
+ ssh-keyscan -t rsa github.com > /root/.ssh/known_hosts
29
+ bundle install
30
+ ''' }
31
+ }
32
+ }
33
+ stage('Unit Test') {
34
+ steps {
35
+ script {
36
+ sh 'bundle exec rake'
37
+ }
38
+ }
39
+ post {
40
+ always { junit '*/reports/*.xml' }
41
+ success { updateGitHubStatus('clean-build', 'success', 'Unit tests.') }
42
+ failure { updateGitHubStatus('clean-build', 'failure', 'Unit tests.') }
43
+ }
44
+ }
45
+ }
46
+ }
47
+
48
+ void updateGitHubStatus(String context, String status, String description) {
49
+ gitHubStatus([
50
+ repoSlug: 'Invoca/exceptional_synchrony',
51
+ sha: env.GIT_COMMIT,
52
+ description: description,
53
+ context: context,
54
+ targetURL: env.RUN_DISPLAY_URL,
55
+ token: env.GITHUB_TOKEN,
56
+ status: status
57
+ ])
58
+ }
@@ -0,0 +1,19 @@
1
+ ---
2
+ apiVersion: v1
3
+ kind: Pod
4
+ metadata:
5
+ labels:
6
+ jenkins/exceptional-synchrony: 'true'
7
+ namespace: jenkins
8
+ name: exceptional-synchrony
9
+ spec:
10
+ containers:
11
+ - name: ruby
12
+ image: ruby:2.6.5
13
+ tty: true
14
+ resources:
15
+ requests:
16
+ memory: "100Mi"
17
+ command:
18
+ - cat
19
+
@@ -1,4 +1,4 @@
1
- # CHANGELOG for `exceptional_synchrony`
1
+ # CHANGELOG for `exception_synchrony`
2
2
 
3
3
  Inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4
4
 
@@ -9,3 +9,16 @@ All notable changes to this project will be documented in this file.
9
9
  - Replace hobo_support with invoca_utils
10
10
 
11
11
  [1.1.1]: https://github.com/Invoca/exceptional_synchrony/compare/v1.1.0...v1.1.1
12
+
13
+ ## [1.2.0] - 2020-06-02
14
+ ### Changed
15
+ - If `EMP.run` rescues an exception, previous versions would simply log the exception and continue.
16
+ Instead this version has an `on_error` option with possible values `:log` and `:raise`.
17
+ It defaults to `:log` and in that case, as before, logs any rescued `StandardError` exception and continues.
18
+ When `on_error` is set to `:raise`, the method raises a `FatalRunError` wrapper around the rescued exception.
19
+ This `FatalRunError` exception does not derive from `StandardError`, so it will not be erroneously rescued by any
20
+ registered `EMP.error_handler`. Instead it should be rescued at the outer edge of the process.
21
+ We expect that outer edge handler to log the exception chain (the wrapper plus nested `cause` exception(s))
22
+ and exit the process with a non-0 status code.
23
+
24
+ [1.2.0]: https://github.com/Invoca/exceptional_synchrony/compare/v1.1.1...v1.2.0
data/Gemfile CHANGED
@@ -7,6 +7,7 @@ gemspec
7
7
 
8
8
  group :development do
9
9
  gem 'minitest'
10
+ gem 'minitest-reporters'
10
11
  gem 'pry'
11
12
  gem 'rake'
12
13
  gem 'rr', '~> 1.2'
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- exceptional_synchrony (1.1.1)
4
+ exceptional_synchrony (1.2.0)
5
5
  em-http-request
6
6
  em-synchrony
7
7
  eventmachine
@@ -11,39 +11,41 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- actionmailer (4.2.11.1)
15
- actionpack (= 4.2.11.1)
16
- actionview (= 4.2.11.1)
17
- activejob (= 4.2.11.1)
14
+ actionmailer (6.0.3.1)
15
+ actionpack (= 6.0.3.1)
16
+ actionview (= 6.0.3.1)
17
+ activejob (= 6.0.3.1)
18
18
  mail (~> 2.5, >= 2.5.4)
19
- rails-dom-testing (~> 1.0, >= 1.0.5)
20
- actionpack (4.2.11.1)
21
- actionview (= 4.2.11.1)
22
- activesupport (= 4.2.11.1)
23
- rack (~> 1.6)
24
- rack-test (~> 0.6.2)
25
- rails-dom-testing (~> 1.0, >= 1.0.5)
26
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
27
- actionview (4.2.11.1)
28
- activesupport (= 4.2.11.1)
19
+ rails-dom-testing (~> 2.0)
20
+ actionpack (6.0.3.1)
21
+ actionview (= 6.0.3.1)
22
+ activesupport (= 6.0.3.1)
23
+ rack (~> 2.0, >= 2.0.8)
24
+ rack-test (>= 0.6.3)
25
+ rails-dom-testing (~> 2.0)
26
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
27
+ actionview (6.0.3.1)
28
+ activesupport (= 6.0.3.1)
29
29
  builder (~> 3.1)
30
- erubis (~> 2.7.0)
31
- rails-dom-testing (~> 1.0, >= 1.0.5)
32
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
33
- activejob (4.2.11.1)
34
- activesupport (= 4.2.11.1)
35
- globalid (>= 0.3.0)
36
- activesupport (4.2.11.1)
37
- i18n (~> 0.7)
30
+ erubi (~> 1.4)
31
+ rails-dom-testing (~> 2.0)
32
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
33
+ activejob (6.0.3.1)
34
+ activesupport (= 6.0.3.1)
35
+ globalid (>= 0.3.6)
36
+ activesupport (6.0.3.1)
37
+ concurrent-ruby (~> 1.0, >= 1.0.2)
38
+ i18n (>= 0.7, < 2)
38
39
  minitest (~> 5.1)
39
- thread_safe (~> 0.3, >= 0.3.4)
40
40
  tzinfo (~> 1.1)
41
+ zeitwerk (~> 2.2, >= 2.2.2)
41
42
  addressable (2.7.0)
42
43
  public_suffix (>= 2.0.2, < 5.0)
44
+ ansi (1.5.0)
43
45
  builder (3.2.4)
44
46
  coderay (1.1.2)
45
47
  concurrent-ruby (1.1.6)
46
- contextual_logger (0.6.1)
48
+ contextual_logger (0.8.0)
47
49
  activesupport
48
50
  json
49
51
  cookiejar (0.3.3)
@@ -60,20 +62,20 @@ GEM
60
62
  eventmachine (>= 1.0.0.beta.4)
61
63
  em-synchrony (1.0.6)
62
64
  eventmachine (>= 1.0.0.beta.1)
63
- erubis (2.7.0)
65
+ erubi (1.9.0)
64
66
  eventmachine (1.2.7)
65
- exception_handling (2.4.1)
66
- actionmailer (~> 4.2)
67
- actionpack (~> 4.2)
68
- activesupport (~> 4.2)
69
- contextual_logger
67
+ exception_handling (2.4.3)
68
+ actionmailer (>= 4.2, < 7.0)
69
+ actionpack (>= 4.2, < 7.0)
70
+ activesupport (>= 4.2, < 7.0)
71
+ contextual_logger (~> 0.7)
70
72
  eventmachine (~> 1.0)
71
73
  invoca-utils (~> 0.3)
72
74
  globalid (0.4.2)
73
75
  activesupport (>= 4.2.0)
74
76
  hashdiff (1.0.1)
75
77
  http_parser.rb (0.6.0)
76
- i18n (0.9.5)
78
+ i18n (1.8.2)
77
79
  concurrent-ruby (~> 1.0)
78
80
  invoca-utils (0.3.0)
79
81
  json (2.3.0)
@@ -86,25 +88,28 @@ GEM
86
88
  mini_mime (1.0.2)
87
89
  mini_portile2 (2.4.0)
88
90
  minitest (5.14.0)
91
+ minitest-reporters (1.4.2)
92
+ ansi
93
+ builder
94
+ minitest (>= 5.0)
95
+ ruby-progressbar
89
96
  nokogiri (1.10.9)
90
97
  mini_portile2 (~> 2.4.0)
91
98
  pry (0.13.1)
92
99
  coderay (~> 1.1)
93
100
  method_source (~> 1.0)
94
101
  public_suffix (4.0.4)
95
- rack (1.6.13)
96
- rack-test (0.6.3)
97
- rack (>= 1.0)
98
- rails-deprecated_sanitizer (1.0.3)
99
- activesupport (>= 4.2.0.alpha)
100
- rails-dom-testing (1.0.9)
101
- activesupport (>= 4.2.0, < 5.0)
102
- nokogiri (~> 1.6)
103
- rails-deprecated_sanitizer (>= 1.0.1)
102
+ rack (2.2.2)
103
+ rack-test (1.1.0)
104
+ rack (>= 1.0, < 3)
105
+ rails-dom-testing (2.0.3)
106
+ activesupport (>= 4.2.0)
107
+ nokogiri (>= 1.6)
104
108
  rails-html-sanitizer (1.3.0)
105
109
  loofah (~> 2.3)
106
110
  rake (13.0.1)
107
111
  rr (1.2.1)
112
+ ruby-progressbar (1.10.1)
108
113
  safe_yaml (1.0.5)
109
114
  thor (1.0.1)
110
115
  thread_safe (0.3.6)
@@ -114,6 +119,7 @@ GEM
114
119
  addressable (>= 2.3.6)
115
120
  crack (>= 0.3.2)
116
121
  hashdiff
122
+ zeitwerk (2.3.0)
117
123
 
118
124
  PLATFORMS
119
125
  ruby
@@ -121,6 +127,7 @@ PLATFORMS
121
127
  DEPENDENCIES
122
128
  exceptional_synchrony!
123
129
  minitest
130
+ minitest-reporters
124
131
  pry
125
132
  rake
126
133
  rr (~> 1.2)
@@ -5,6 +5,10 @@ require 'em-http'
5
5
  require 'em-synchrony/em-http'
6
6
 
7
7
  module ExceptionalSynchrony
8
+ # It is important for this exception to be inherited from Exception so that
9
+ # when thrown it does not get caught by the EventMachine.error_handler.
10
+ class FatalRunError < Exception; end
11
+
8
12
  class EventMachineProxy
9
13
 
10
14
  attr_reader :connection
@@ -64,13 +68,14 @@ module ExceptionalSynchrony
64
68
  @proxy_class.connect(server, port, handler, *args, &block)
65
69
  end
66
70
 
67
- def run(&block)
68
- ensure_completely_safe("run") do
69
- if @proxy_class.respond_to?(:synchrony)
70
- @proxy_class.synchrony(&block)
71
- else
72
- @proxy_class.run(&block)
73
- end
71
+ # The on_error option has these possible values:
72
+ # :log - log any rescued StandardError exceptions and continue
73
+ # :raise - raise FatalRunError for any rescued StandardError exceptions
74
+ def run(on_error: :log, &block)
75
+ case on_error
76
+ when :log then run_with_error_logging(&block)
77
+ when :raise then run_with_error_raising(&block)
78
+ else raise ArgumentError, "Invalid on_error: #{on_error.inspect}, must be :log or :raise"
74
79
  end
75
80
  end
76
81
 
@@ -108,6 +113,38 @@ module ExceptionalSynchrony
108
113
  yield
109
114
  end
110
115
  end
116
+
117
+ def rescue_exceptions_and_ensure_exit(context)
118
+ yield
119
+ rescue StandardError => ex
120
+ # Raise a non-StandardError so that not caught by EM.error_handler.
121
+ # Expecting rescued exception to be stored in this new exception's cause.
122
+ raise FatalRunError, "Fatal EventMachine #{context} error\n#{ex.class.name}: #{ex.message}"
123
+ end
124
+
125
+ private
126
+
127
+ def run_with_error_logging(&block)
128
+ ensure_completely_safe("run_with_error_logging") do
129
+ if @proxy_class.respond_to?(:synchrony)
130
+ @proxy_class.synchrony(&block)
131
+ else
132
+ @proxy_class.run(&block)
133
+ end
134
+ end
135
+ end
136
+
137
+ def run_with_error_raising(&block)
138
+ run_block = -> { rescue_exceptions_and_ensure_exit("run_with_error_raising", &block) }
139
+
140
+ rescue_exceptions_and_ensure_exit("run_with_error_raising") do
141
+ if @proxy_class.respond_to?(:synchrony)
142
+ @proxy_class.synchrony(&run_block)
143
+ else
144
+ @proxy_class.run(&run_block)
145
+ end
146
+ end
147
+ end
111
148
  end
112
149
 
113
150
  EMP = EventMachineProxy.new(EventMachine, EventMachine::HttpRequest)
@@ -1,3 +1,3 @@
1
1
  module ExceptionalSynchrony
2
- VERSION = '1.1.1'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -7,6 +7,11 @@ require_relative '../lib/exceptional_synchrony.rb'
7
7
 
8
8
  require 'minitest/autorun' or raise "Already loaded minitest?"
9
9
  require 'minitest/pride'
10
+ require 'minitest/reporters'
11
+ Minitest::Reporters.use! [
12
+ Minitest::Reporters::DefaultReporter.new,
13
+ Minitest::Reporters::JUnitReporter.new
14
+ ]
10
15
  require 'webmock'
11
16
  require 'webmock/minitest'
12
17
  require 'rr'
@@ -3,10 +3,31 @@ require_relative '../test_helper'
3
3
  describe ExceptionalSynchrony::EventMachineProxy do
4
4
  include TestHelper
5
5
 
6
+ class RunProxyMock
7
+ class << self
8
+ def run(&block)
9
+ block.call
10
+ :run
11
+ end
12
+
13
+ def error_handler
14
+ end
15
+ end
16
+ end
17
+
18
+ class SynchronyProxyMock < RunProxyMock
19
+ class << self
20
+ def synchrony(&block)
21
+ block.call
22
+ :synchrony
23
+ end
24
+ end
25
+ end
26
+
6
27
  before do
7
28
  @em = ExceptionalSynchrony::EventMachineProxy.new(EventMachine, nil)
8
29
  @yielded_value = nil
9
- @block = lambda { |value| @yielded_value = value }
30
+ @block = -> (value) { @yielded_value = value }
10
31
  end
11
32
 
12
33
  it "should proxy add_timer" do
@@ -106,27 +127,50 @@ describe ExceptionalSynchrony::EventMachineProxy do
106
127
  end
107
128
  end
108
129
 
109
- [false, true].each do |synchrony|
110
- describe "synchrony = #{synchrony}" do
111
- it "should dispatch to the proxy's synchrony method instead of run iff synchrony" do
112
- proxy_mock = Struct.new(:proxy, :class_connection) do
113
- if synchrony
114
- def self.synchrony(&block)
115
- block.(:synchrony)
130
+ { synchrony: SynchronyProxyMock, run: RunProxyMock }.each do |method, proxy_mock|
131
+ describe "run" do
132
+ before do
133
+ @proxy = ExceptionalSynchrony::EventMachineProxy.new(proxy_mock, nil)
134
+ end
135
+
136
+ it "should raise ArgumentError if on_error has invalid value" do
137
+ assert_raises(ArgumentError, "Invalid on_error: :ignore, must be :log or :raise") do
138
+ @proxy.run(on_error: :ignore)
139
+ end
140
+ end
141
+
142
+ describe "without error" do
143
+ [:log, :raise].each do |on_error|
144
+ describe "when using #{method} and on_error = #{on_error}" do
145
+ it "should dispatch to the proxy's synchrony method instead of run iff synchrony" do
146
+ dispatched = false
147
+ assert_equal method, (@proxy.run(on_error: on_error) { dispatched = true })
148
+ assert_equal true, dispatched
116
149
  end
117
150
  end
151
+ end
152
+ end
118
153
 
119
- def self.run(&block)
120
- block.(:run)
121
- end
154
+ describe "with error" do
155
+ before do
156
+ set_test_const('ExceptionalSynchrony::EventMachineProxy::WRAP_WITH_ENSURE_COMPLETELY_SAFE', true)
122
157
  end
123
158
 
124
- mock(proxy_mock).error_handler
159
+ describe "when using #{method} and on_error = :log" do
160
+ it "should rescue any exceptions and log them" do
161
+ mock(ExceptionHandling).log_error(EXCEPTION, "run_with_error_logging", {})
125
162
 
126
- proxy = ExceptionalSynchrony::EventMachineProxy.new(proxy_mock, nil)
163
+ @proxy.run(on_error: :log) { raise EXCEPTION }
164
+ end
165
+ end
127
166
 
128
- proxy.run(&@block)
129
- expect(@yielded_value).must_equal(synchrony ? :synchrony : :run)
167
+ describe "when using #{method} and on_error = :raise" do
168
+ it "should rescue any exceptions and raise FatalRunError" do
169
+ assert_raises(ExceptionalSynchrony::FatalRunError, "Fatal EventMachine run error") do
170
+ @proxy.run(on_error: :raise) { raise EXCEPTION }
171
+ end
172
+ end
173
+ end
130
174
  end
131
175
  end
132
176
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exceptional_synchrony
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Invoca
@@ -88,6 +88,8 @@ extra_rdoc_files: []
88
88
  files:
89
89
  - ".dependabot/config.yml"
90
90
  - ".gitignore"
91
+ - ".jenkins/Jenkinsfile"
92
+ - ".jenkins/ruby_build_pod.yml"
91
93
  - ".ruby-gemset"
92
94
  - ".ruby-version"
93
95
  - CHANGELOG.md
@@ -102,7 +104,6 @@ files:
102
104
  - lib/exceptional_synchrony/limited_work_queue.rb
103
105
  - lib/exceptional_synchrony/parallel_sync.rb
104
106
  - lib/exceptional_synchrony/version.rb
105
- - semaphore_ci/setup.sh
106
107
  - test/test_helper.rb
107
108
  - test/unit/callback_exceptions_test.rb
108
109
  - test/unit/event_machine_proxy_test.rb
@@ -1,3 +0,0 @@
1
- #!/bin/sh -x
2
-
3
- bundle install --path vendor/bundle