bigcommerce-multitrap 0.1.1
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 +7 -0
- data/CHANGELOG.md +15 -0
- data/README.md +160 -0
- data/bigcommerce-multitrap.gemspec +22 -0
- data/lib/bigcommerce-multitrap.rb +3 -0
- data/lib/bigcommerce/multitrap.rb +28 -0
- data/lib/bigcommerce/multitrap/core_ext.rb +13 -0
- data/lib/bigcommerce/multitrap/patched_trap.rb +35 -0
- data/lib/bigcommerce/multitrap/trap.rb +108 -0
- metadata +123 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0f6b5bf04d0745286817f5a37fadaf83bd98a0785e6e315225768862d2ce158b
|
4
|
+
data.tar.gz: 2372b5016ab0893e62d0daaf938387303f428536c5bfb9f8b7506fd0765ba4fd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 62e88d91945e55594e7dd966bd2346b726f815c8bdd957b67804d38098980e9afe4fb51620cb49b8fd4e56d67cd5f9e4222d3a8283e5713c6a0d8d1f48d74b3d
|
7
|
+
data.tar.gz: a17fb32de84c9939d26d11832f206e64a036712f78975660e83407d872b8903a7fb5b5abcf13e8608adf8688629809286f15289079db920ccb2893a6c8534b1a
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Multitrap changelog
|
2
|
+
===================
|
3
|
+
|
4
|
+
### v0.1.0 (August, 9, 2015)
|
5
|
+
|
6
|
+
* Added support for JRuby
|
7
|
+
* Added support for Rubinius
|
8
|
+
* Added support for CRuby 1.9
|
9
|
+
* Added support for symbols as signal keys ([#1](https://github.com/kyrylo/multitrap/issues/1))
|
10
|
+
* Added support for previously defined signals ([#2](https://github.com/kyrylo/multitrap/issues/2))
|
11
|
+
* Changed order of execution of handlers (FIFO now)
|
12
|
+
|
13
|
+
### v0.0.1 (September 7, 2014)
|
14
|
+
|
15
|
+
* Initial release
|
data/README.md
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
Multitrap
|
2
|
+
=========
|
3
|
+
|
4
|
+
[](https://circleci.com/gh/bigcommerce/multitrap)
|
5
|
+
|
6
|
+
Introduction
|
7
|
+
------------
|
8
|
+
|
9
|
+
By default, all Ruby implementations allow you to attach only one signal handler
|
10
|
+
per signal (via `Signal.trap` aka `trap`). This is not very useful, if you want
|
11
|
+
to perform multiple actions. Whenever you define a new signal handler, it sends
|
12
|
+
shivers down your spine, because you never know if you're overwriting someone
|
13
|
+
else's handler (usually, the handler of a library you depend on). Well, now you
|
14
|
+
don't have to worry about that anymore, because Multitrap solved this problem
|
15
|
+
for you! Define as many handlers as you wish and be sure they will all execute.
|
16
|
+
|
17
|
+
Examples
|
18
|
+
--------
|
19
|
+
|
20
|
+
### Basic example
|
21
|
+
|
22
|
+
To use Multitrap just `require` it. No additional configuration is
|
23
|
+
needed. Internally, the library _redefines_ `trap`. The last defined signal
|
24
|
+
handler executes first.
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require 'bigcommerce/multitrap'
|
28
|
+
|
29
|
+
trap(:INT) { puts 111 }
|
30
|
+
trap(:INT) { puts 222 }
|
31
|
+
trap(:INT) { puts 333 }
|
32
|
+
|
33
|
+
Process.kill(:INT, $$)
|
34
|
+
|
35
|
+
# Outputs:
|
36
|
+
# 333
|
37
|
+
# 222
|
38
|
+
# 111
|
39
|
+
```
|
40
|
+
|
41
|
+
### Nested traps
|
42
|
+
|
43
|
+
The library aims to be compatible with every Ruby implementation. For example,
|
44
|
+
JRuby doesn't support nested traps, but Rubinius and CRuby do. Multitrap obeys
|
45
|
+
this behaviour.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
require 'bigcommerce/multitrap'
|
49
|
+
|
50
|
+
a = nil
|
51
|
+
|
52
|
+
trap(:INT) do
|
53
|
+
a = 1
|
54
|
+
trap(:INT) do
|
55
|
+
a = 2
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
puts a #=> nil
|
60
|
+
|
61
|
+
# On JRuby `a` will always be equal to 1.
|
62
|
+
Process.kill(:INT, $$)
|
63
|
+
puts a #=> 1
|
64
|
+
|
65
|
+
# CRuby and Rubinius will continue executing nested traps.
|
66
|
+
Process.kill(:INT, $$)
|
67
|
+
puts a #=> 2
|
68
|
+
|
69
|
+
Process.kill(:INT, $$)
|
70
|
+
puts a #=> 2
|
71
|
+
```
|
72
|
+
|
73
|
+
### Return value
|
74
|
+
|
75
|
+
With Multitrap, the `trap` method returns a hash with callbacks.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
require 'pp'
|
79
|
+
require 'bigcommerce/multitrap'
|
80
|
+
|
81
|
+
trap(:INT) {}
|
82
|
+
trap(:HUP) {}
|
83
|
+
3.times do
|
84
|
+
trap(:USR1) {}
|
85
|
+
end
|
86
|
+
handlers = trap(:USR2) {}
|
87
|
+
|
88
|
+
puts handlers
|
89
|
+
#=> {"INT"=>[#<Proc:0x00556161d34da8@test.rb:4>],
|
90
|
+
"HUP"=>[#<Proc:0x00556161d34420@test.rb:5>],
|
91
|
+
"USR1"=>
|
92
|
+
[#<Proc:0x00556161d36a68@test.rb:7>,
|
93
|
+
#<Proc:0x00556161d33750@test.rb:7>,
|
94
|
+
#<Proc:0x00556161d33048@test.rb:7>],
|
95
|
+
"USR2"=>[#<Proc:0x00556161d32ad0@test.rb:9>]}
|
96
|
+
```
|
97
|
+
|
98
|
+
You can access this hash to modify the handlers at runtime (but be extremely
|
99
|
+
careful about that). For example, imagine if you write a test for your program's
|
100
|
+
`:INT` signal handler and use RSpec. RSpec defines its own `:INT` handler and
|
101
|
+
with the default `trap` you would simply overwrite it. With Multitrap you can
|
102
|
+
remove the handler from the hash and then append it later.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
require 'spec_helper'
|
106
|
+
require 'multitrap'
|
107
|
+
|
108
|
+
RSpec.describe MyLibrary do
|
109
|
+
it "works" do
|
110
|
+
a = nil
|
111
|
+
handlers = trap(:INT) { a = 1 }
|
112
|
+
|
113
|
+
# Store the RSpecs handler. Usually, it's the first element in the array.
|
114
|
+
rspec_handler = handlers['INT'].shift
|
115
|
+
|
116
|
+
Process.kill(:INT, $$)
|
117
|
+
expect(a).to eq(1)
|
118
|
+
|
119
|
+
# Restore the handler.
|
120
|
+
handlers['INT'].unshift(rspec_handler)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
Installation
|
126
|
+
------------
|
127
|
+
|
128
|
+
Add this line to your application's Gemfile:
|
129
|
+
|
130
|
+
gem 'multitrap'
|
131
|
+
|
132
|
+
And then execute:
|
133
|
+
|
134
|
+
$ bundle
|
135
|
+
|
136
|
+
Or install it yourself as:
|
137
|
+
|
138
|
+
$ gem install multitrap
|
139
|
+
|
140
|
+
Limitations
|
141
|
+
-----------
|
142
|
+
|
143
|
+
### Rubies
|
144
|
+
|
145
|
+
* CRuby 1.9.2 >=
|
146
|
+
* Rubinius 2.5.8 >= (may work on older versions, untested)
|
147
|
+
* JRuby 9.0.0.0 >= (shouldn't work on older versions)
|
148
|
+
|
149
|
+
Roadmap
|
150
|
+
-------
|
151
|
+
|
152
|
+
### Provide consistency
|
153
|
+
|
154
|
+
I'm not sure we need this, but it would be possible to remove inconsistencies
|
155
|
+
with respect to nested traps on all Ruby platforms.
|
156
|
+
|
157
|
+
License
|
158
|
+
-------
|
159
|
+
|
160
|
+
The project uses the MIT License. See LICENSE.txt file for more information.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'bigcommerce-multitrap'
|
5
|
+
s.version = File.read('VERSION')
|
6
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
7
|
+
s.summary = 'Allows Signal.trap to have multiple callbacks'
|
8
|
+
s.authors = ['Kyrylo Silin', 'Shaun McCormick']
|
9
|
+
s.email = %w[silin@kyrylo.org shaun.mccormick@bigcommerce.com]
|
10
|
+
s.homepage = 'https://github.com/kyrylo/multitrap'
|
11
|
+
s.licenses = 'MIT'
|
12
|
+
|
13
|
+
s.files = Dir['README.md', 'CHANGELOG.md', 'CODE_OF_CONDUCT.md', 'lib/**/*', 'bigcommerce-multitrap.gemspec']
|
14
|
+
s.require_paths = ['lib']
|
15
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
16
|
+
|
17
|
+
s.add_development_dependency 'pry', '~> 0.12'
|
18
|
+
s.add_development_dependency 'rake', '~> 13.0'
|
19
|
+
s.add_development_dependency 'rspec', '~> 3.9'
|
20
|
+
s.add_development_dependency 'rspec_junit_formatter', '>= 0.4'
|
21
|
+
s.add_development_dependency 'rspec-wait', '~> 0.0.9'
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
module Bigcommerce
|
6
|
+
module Multitrap
|
7
|
+
# The VERSION file must be in the root directory of the library.
|
8
|
+
VERSION_FILE = File.expand_path('../../../VERSION', __FILE__)
|
9
|
+
|
10
|
+
VERSION = File.exist?(VERSION_FILE) ? File.read(VERSION_FILE).chomp : '(could not find VERSION file)'
|
11
|
+
|
12
|
+
def self.jruby?
|
13
|
+
RUBY_ENGINE == 'jruby'
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.mri?
|
17
|
+
RUBY_ENGINE == 'ruby'
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.rbx?
|
21
|
+
RUBY_ENGINE == 'rbx'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require_relative 'multitrap/trap'
|
27
|
+
require_relative 'multitrap/patched_trap'
|
28
|
+
require_relative 'multitrap/core_ext'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
case RUBY_ENGINE
|
4
|
+
when 'ruby'
|
5
|
+
[Kernel, Kernel.singleton_class, Signal, Signal.singleton_class].each do |klass|
|
6
|
+
klass.__send__(:include, Bigcommerce::Multitrap::PatchedTrap)
|
7
|
+
end
|
8
|
+
when 'rbx'
|
9
|
+
Signal.__send__(:include, Bigcommerce::Multitrap::PatchedTrap)
|
10
|
+
Signal.singleton_class.__send__(:include, Bigcommerce::Multitrap::PatchedTrap)
|
11
|
+
when 'jruby'
|
12
|
+
Signal.singleton_class.__send__(:include, Bigcommerce::Multitrap::PatchedTrap)
|
13
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bigcommerce
|
4
|
+
module Multitrap
|
5
|
+
module PatchedTrap
|
6
|
+
def self.included(mod)
|
7
|
+
original_trap = Signal.method(:trap)
|
8
|
+
|
9
|
+
mod.instance_eval do
|
10
|
+
define_method(:trap) do |*args, &block|
|
11
|
+
if args.size < 1
|
12
|
+
if Bigcommerce::Multitrap.rbx?
|
13
|
+
raise ArgumentError, "method 'trap': given 0, expected 2"
|
14
|
+
elsif Bigcommerce::Multitrap.jruby?
|
15
|
+
raise ArgumentError, "wrong number of arguments (0 for 1)"
|
16
|
+
else
|
17
|
+
raise ArgumentError, "wrong number of arguments (0 for 1..2)"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
if block.nil?
|
22
|
+
if Bigcommerce::Multitrap.rbx? && !args[1].respond_to?(:call)
|
23
|
+
raise ArgumentError, "Handler must respond to #call (was #{args[1].class})"
|
24
|
+
elsif args[1].nil?
|
25
|
+
raise ArgumentError, 'tried to create Proc object without a block'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Bigcommerce::Multitrap::Trap.trap(original_trap, *args, &block)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bigcommerce
|
4
|
+
module Multitrap
|
5
|
+
class Trap
|
6
|
+
OWN_MRI_FRAME = %r{/lib/multitrap/trap.rb:[0-9]{1,3}:in `block in store_trap'}
|
7
|
+
|
8
|
+
OWN_RBX_FRAME = %r{/lib/multitrap/trap.rb:[0-9]{1,3}:in `store_trap'}
|
9
|
+
|
10
|
+
TRAVIS_FRAME = %r{lib/jruby\.jar!/jruby/kernel/signal\.rb}
|
11
|
+
|
12
|
+
RESERVED_SIGNALS = %w|BUS SEGV ILL FPE VTALRM|
|
13
|
+
|
14
|
+
KNOWN_SIGNALS = Signal.list.keys - RESERVED_SIGNALS
|
15
|
+
|
16
|
+
def self.trap(original_trap, *args, &block)
|
17
|
+
@@multitrap ||= new(original_trap)
|
18
|
+
|
19
|
+
signal = args[0]
|
20
|
+
command = args[1]
|
21
|
+
|
22
|
+
@@multitrap.store_trap(signal, command, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.__clear_all_handlers!
|
26
|
+
@@multitrap ||= nil
|
27
|
+
@@multitrap && @@multitrap.__clear_all_handlers!
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(original_trap)
|
31
|
+
@original_trap = original_trap
|
32
|
+
@trap_list = create_trap_list
|
33
|
+
end
|
34
|
+
|
35
|
+
def store_trap(signal, command, &block)
|
36
|
+
signal = signal.to_s
|
37
|
+
command ||= block
|
38
|
+
|
39
|
+
trap_is_nested = nested_trap?
|
40
|
+
|
41
|
+
if trap_is_nested
|
42
|
+
# JRuby doesn't support nested traps.
|
43
|
+
return if Multitrap.jruby?
|
44
|
+
@trap_list[signal].pop
|
45
|
+
end
|
46
|
+
|
47
|
+
@trap_list[signal] << command
|
48
|
+
|
49
|
+
prev_trap_handler = @original_trap.call(signal) do |signo|
|
50
|
+
@trap_list[signal].reverse_each do |trap_handler|
|
51
|
+
trap_handler.call(signo || Signal.list[signal]) if trap_handler.respond_to?(:call)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
if !trap_is_nested &&
|
56
|
+
@trap_list[signal].size == 1 &&
|
57
|
+
!default_handler?(prev_trap_handler) &&
|
58
|
+
!default_handler_path?(prev_trap_handler)
|
59
|
+
@trap_list[signal].unshift(prev_trap_handler)
|
60
|
+
end
|
61
|
+
|
62
|
+
@trap_list
|
63
|
+
end
|
64
|
+
|
65
|
+
def __clear_all_handlers!
|
66
|
+
@trap_list.keys.select {|s| KNOWN_SIGNALS.include?(s) }.each do |signal|
|
67
|
+
@original_trap.call(signal, 'DEFAULT')
|
68
|
+
end
|
69
|
+
|
70
|
+
@trap_list = create_trap_list
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
define_method(:nested_trap?) do
|
76
|
+
case RUBY_ENGINE
|
77
|
+
when 'ruby'
|
78
|
+
caller.any? { |stackframe| stackframe =~ OWN_MRI_FRAME }
|
79
|
+
when 'rbx'
|
80
|
+
caller.grep(OWN_RBX_FRAME).size > 1
|
81
|
+
when 'jruby'
|
82
|
+
if caller.any? { |stackframe| stackframe =~ TRAVIS_FRAME }
|
83
|
+
caller.grep(OWN_RBX_FRAME).size > 1
|
84
|
+
else
|
85
|
+
caller.grep(OWN_MRI_FRAME).size > 1
|
86
|
+
end
|
87
|
+
else
|
88
|
+
raise NotImplementedError, 'unsupported Ruby engine'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def create_trap_list
|
93
|
+
Hash.new { |h, k| h[k] = [] }
|
94
|
+
end
|
95
|
+
|
96
|
+
def default_handler?(prev)
|
97
|
+
%w[DEFAULT SYSTEM_DEFAULT IGNORE].any? { |h| h == prev }
|
98
|
+
end
|
99
|
+
|
100
|
+
def default_handler_path?(prev)
|
101
|
+
[%r{Proc:.+@kernel/loader\.rb:[0-9]{1,4}},
|
102
|
+
%r{Proc:.+@uri:classloader:/jruby/kernel/signal.rb:[0-9]{1,4}}].any? do |h|
|
103
|
+
h =~ prev.to_s
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bigcommerce-multitrap
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kyrylo Silin
|
8
|
+
- Shaun McCormick
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2019-11-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: pry
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0.12'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0.12'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '13.0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '13.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '3.9'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.9'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rspec_junit_formatter
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0.4'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0.4'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rspec-wait
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 0.0.9
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 0.0.9
|
84
|
+
description:
|
85
|
+
email:
|
86
|
+
- silin@kyrylo.org
|
87
|
+
- shaun.mccormick@bigcommerce.com
|
88
|
+
executables: []
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- CHANGELOG.md
|
93
|
+
- README.md
|
94
|
+
- bigcommerce-multitrap.gemspec
|
95
|
+
- lib/bigcommerce-multitrap.rb
|
96
|
+
- lib/bigcommerce/multitrap.rb
|
97
|
+
- lib/bigcommerce/multitrap/core_ext.rb
|
98
|
+
- lib/bigcommerce/multitrap/patched_trap.rb
|
99
|
+
- lib/bigcommerce/multitrap/trap.rb
|
100
|
+
homepage: https://github.com/kyrylo/multitrap
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata: {}
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
requirements: []
|
119
|
+
rubygems_version: 3.0.1
|
120
|
+
signing_key:
|
121
|
+
specification_version: 4
|
122
|
+
summary: Allows Signal.trap to have multiple callbacks
|
123
|
+
test_files: []
|