exceptional_synchrony 1.1.1 → 1.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.
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