isolator 0.7.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/LICENSE.txt +1 -1
- data/README.md +69 -7
- data/lib/isolator/adapter_builder.rb +27 -17
- data/lib/isolator/adapters/background_jobs/active_job.rb +1 -1
- data/lib/isolator/adapters/base.rb +28 -9
- data/lib/isolator/adapters/http/webmock.rb +1 -1
- data/lib/isolator/adapters/mailers/mail.rb +1 -1
- data/lib/isolator/configuration.rb +2 -2
- data/lib/isolator/ignorer.rb +19 -4
- data/lib/isolator/isolate.rb +7 -1
- data/lib/isolator/notifier.rb +3 -3
- data/lib/isolator/railtie.rb +13 -1
- data/lib/isolator/version.rb +1 -1
- data/lib/isolator.rb +11 -0
- metadata +38 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 529433e0e838e663f335a5fb493f51d10f16fbde2168c89be72e92c968709593
|
4
|
+
data.tar.gz: ef33db058e25204e615fae7ee945c738e516e8ae6d63bb93f9660e6e117cb632
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5cb63aa9a96f5931425267193590550cac0614dd15d272bd7d33f7e2aeaad0b7b37a19f5a6f7d8617dce992e946b92c42c3df5829177cbd51b41d8919abdc8c5
|
7
|
+
data.tar.gz: b4997855a9d813a961491235097a42eee75d55598fb7d33d53bb4f7e3828f486ea0a2fb788edd028237e39518a2d8e962824b08a3f4cbed2ce3ac22acdfee9c3
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,20 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.9.0 (2023-05-18)
|
6
|
+
|
7
|
+
- Support keyword arguments to isolated method in Ruby 3.0. ([@Mange][])
|
8
|
+
- Raise an error when an ignore file does not parse to a hash. ([@bobbymcwho][])
|
9
|
+
- Log all filtered backtrace lines to the logger ([@bobbymcwho][])
|
10
|
+
- Add support for removing dynamic adapters. ([@Mange][])
|
11
|
+
- Allow aliases in .isolator_todo.yml and .isolator_ignore.yml ([@tomgi][])
|
12
|
+
|
13
|
+
## 0.8.0 (2021-12-29)
|
14
|
+
|
15
|
+
- Drop Ruby 2.5 support.
|
16
|
+
|
17
|
+
- Add .isolator_ignore.yml configuration file for Rails application.
|
18
|
+
|
5
19
|
## 0.7.0 (2020-09-25)
|
6
20
|
|
7
21
|
- Add debug mode. ([@palkan][])
|
@@ -93,3 +107,5 @@ This, for example, makes Isolator compatible with Rails multi-database apps.
|
|
93
107
|
[@shivanshgaur]: https://github.com/shivanshgaur
|
94
108
|
[@iiwo]: https://github.com/iiwo
|
95
109
|
[@mquan]: https://github.com/mquan
|
110
|
+
[@bobbymcwho]: https://github.com/bobbymcwho
|
111
|
+
[@Mange]: https://github.com/Mange
|
data/LICENSE.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2018-
|
3
|
+
Copyright (c) 2018-2023 Vladimir Dementyev
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -104,24 +104,24 @@ Isolator.configure do |config|
|
|
104
104
|
end
|
105
105
|
```
|
106
106
|
|
107
|
-
Isolator
|
107
|
+
Isolator relies on [uniform_notifier][] to send custom notifications.
|
108
108
|
|
109
109
|
**NOTE:** `uniform_notifier` should be installed separately (i.e., added to Gemfile).
|
110
110
|
|
111
111
|
### Transactional tests support
|
112
112
|
|
113
|
-
- Rails' baked-in [use_transactional_tests](api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html#class-ActiveRecord::FixtureSet-label-Transactional+Tests)
|
113
|
+
- Rails' baked-in [use_transactional_tests](https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html#class-ActiveRecord::FixtureSet-label-Transactional+Tests)
|
114
114
|
- [database_cleaner](https://github.com/DatabaseCleaner/database_cleaner) gem. Make sure that you require isolator _after_ database_cleaner.
|
115
115
|
|
116
116
|
### Supported ORMs
|
117
117
|
|
118
|
-
- `ActiveRecord` >= 4.
|
118
|
+
- `ActiveRecord` >= 5.1 (4.2 likely till works, but we do not test against it anymore)
|
119
119
|
- `ROM::SQL` (only if Active Support instrumentation extension is loaded)
|
120
120
|
|
121
121
|
### Adapters
|
122
122
|
|
123
123
|
Isolator has a bunch of built-in adapters:
|
124
|
-
- `:http` –
|
124
|
+
- `:http` – built on top of [Sniffer][]
|
125
125
|
- `:active_job`
|
126
126
|
- `:sidekiq`
|
127
127
|
- `:resque`
|
@@ -159,9 +159,32 @@ Isolator.adapters.sidekiq.ignore_if { Thread.current[:sidekiq_postpone] }
|
|
159
159
|
|
160
160
|
You can add as many _ignores_ as you want, the offense is registered iff all of them return false.
|
161
161
|
|
162
|
+
|
163
|
+
### Using with sidekiq/testing
|
164
|
+
|
165
|
+
If you require sidekiq/testing in your tests after isolator is required then it will blow away isolator's hooks, so you need to require isolator after requiring sidekiq/testing.
|
166
|
+
|
167
|
+
If you're using Rails and want to use isolator in development and staging, then here is a way to do this.
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
|
171
|
+
# Gemfile
|
172
|
+
gem "isolator", require: false # so it delays loading till after sidekiq/testing
|
173
|
+
|
174
|
+
# config/initializers/isolator.rb
|
175
|
+
require "sidekiq/testing" if Rails.env.test?
|
176
|
+
|
177
|
+
unless Rails.env.production? # so we get it in staging too
|
178
|
+
require "isolator"
|
179
|
+
Isolator.configure do |config|
|
180
|
+
config.send_notifications = true # ...
|
181
|
+
end
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
162
185
|
### Using with legacy Rails codebases
|
163
186
|
|
164
|
-
If you already have a huge Rails project it can be
|
187
|
+
If you already have a huge Rails project it can be tricky to turn Isolator on because you'll immediately get a lot of failed specs. If you want to fix detected issues one by one, you can list all of them in the special files `.isolator_todo.yml` and `.isolator_ignore.yml` in the following way:
|
165
188
|
|
166
189
|
```
|
167
190
|
sidekiq:
|
@@ -169,8 +192,20 @@ sidekiq:
|
|
169
192
|
- app/models/sales/**/*.rb
|
170
193
|
```
|
171
194
|
|
195
|
+
You can ignore the same files in multiple adapters using YML aliases in the following way:
|
196
|
+
|
197
|
+
```
|
198
|
+
http_common: &http_common
|
199
|
+
- app/models/user.rb:20
|
200
|
+
|
201
|
+
http: *http_common
|
202
|
+
webmock: *http_common
|
203
|
+
```
|
204
|
+
|
172
205
|
All the exceptions raised in the listed lines will be ignored.
|
173
206
|
|
207
|
+
The `.isolator_todo.yml` file is intended to point to the code that should be fixed later, and `.isolator_ignore.yml` points to the code that for some reasons is not expected to be fixed. (See https://github.com/palkan/isolator/issues/40)
|
208
|
+
|
174
209
|
### Using with legacy Ruby codebases
|
175
210
|
|
176
211
|
If you are not using Rails, you'll have to load ignores from file manually, using `Isolator::Ignorer.prepare(path:)`, for instance `Isolator::Ignorer.prepare(path: "./config/.isolator_todo.yml")`
|
@@ -196,16 +231,43 @@ Isolator.isolate :danger, Danger.singleton_class, :explode, options
|
|
196
231
|
Possible `options` are:
|
197
232
|
- `exception_class` – an exception class to raise in case of offense
|
198
233
|
- `exception_message` – custom exception message (could be specified without a class)
|
199
|
-
- `details_message` – a block to generate additional
|
234
|
+
- `details_message` – a block to generate additional exception message information:
|
200
235
|
|
201
236
|
```ruby
|
202
237
|
Isolator.isolate :active_job,
|
203
238
|
target: ActiveJob::Base,
|
204
239
|
method_name: :enqueue,
|
205
240
|
exception_class: Isolator::BackgroundJobError,
|
206
|
-
details_message: ->(obj
|
241
|
+
details_message: ->(obj) {
|
207
242
|
"#{obj.class.name}(#{obj.arguments})"
|
208
243
|
}
|
244
|
+
|
245
|
+
Isolator.isolate :promoter,
|
246
|
+
target: UserPromoter,
|
247
|
+
method_name: :call,
|
248
|
+
details_message: ->(obj_, args, kwargs) {
|
249
|
+
# UserPromoter.call(user, role, by: nil)
|
250
|
+
user, role = args
|
251
|
+
by = kwargs[:by]
|
252
|
+
"#{user.name} promoted to #{role} by #{by&.name || "system"})"
|
253
|
+
}
|
254
|
+
```
|
255
|
+
|
256
|
+
Trying to register the same adapter name twice will raise an error. You can guard for it, or remove old adapters before in order to replace them.
|
257
|
+
|
258
|
+
```ruby
|
259
|
+
unless Isolator.has_adapter?(:promoter)
|
260
|
+
Isolator.isolate(:promoter, *rest)
|
261
|
+
end
|
262
|
+
```
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
# Handle code reloading
|
266
|
+
class Messager
|
267
|
+
end
|
268
|
+
|
269
|
+
Isolator.remove_adapter(:messager)
|
270
|
+
Isolator.isolate(:messager, target: Messager, **rest)
|
209
271
|
```
|
210
272
|
|
211
273
|
You can also add some callbacks to be run before and after the transaction:
|
@@ -5,29 +5,39 @@ require "isolator/adapters/base"
|
|
5
5
|
module Isolator
|
6
6
|
# Builds adapter from provided params
|
7
7
|
module AdapterBuilder
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
class << self
|
9
|
+
def call(target: nil, method_name: nil, **options)
|
10
|
+
adapter = Module.new do
|
11
|
+
extend Isolator::Adapters::Base
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
self.exception_class = options[:exception_class] if options.key?(:exception_class)
|
14
|
+
self.exception_message = options[:exception_message] if options.key?(:exception_message)
|
15
|
+
self.details_message = options[:details_message] if options.key?(:details_message)
|
16
|
+
end
|
17
|
+
|
18
|
+
mod = build_mod(method_name, adapter)
|
19
|
+
if target && mod
|
20
|
+
target.prepend(mod)
|
21
|
+
adapter.define_singleton_method(:restore) do
|
22
|
+
mod.remove_method(method_name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
adapter
|
15
27
|
end
|
16
28
|
|
17
|
-
|
18
|
-
|
19
|
-
adapter
|
20
|
-
|
29
|
+
private
|
30
|
+
|
31
|
+
def build_mod(method_name, adapter)
|
32
|
+
return nil unless method_name
|
21
33
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
34
|
+
Module.new do
|
35
|
+
define_method method_name do |*args, **kwargs, &block|
|
36
|
+
adapter.notify(caller, self, *args, **kwargs)
|
37
|
+
super(*args, **kwargs, &block)
|
38
|
+
end
|
27
39
|
end
|
28
40
|
end
|
29
|
-
|
30
|
-
base.prepend mod
|
31
41
|
end
|
32
42
|
end
|
33
43
|
end
|
@@ -4,7 +4,7 @@ Isolator.isolate :active_job,
|
|
4
4
|
target: ActiveJob::Base,
|
5
5
|
method_name: :enqueue,
|
6
6
|
exception_class: Isolator::BackgroundJobError,
|
7
|
-
details_message: ->(obj
|
7
|
+
details_message: ->(obj) {
|
8
8
|
"#{obj.class.name}" \
|
9
9
|
"#{obj.arguments.any? ? " (#{obj.arguments.join(", ")})" : ""}"
|
10
10
|
}
|
@@ -22,13 +22,13 @@ module Isolator
|
|
22
22
|
@disabled == true
|
23
23
|
end
|
24
24
|
|
25
|
-
def notify(backtrace, obj, *args)
|
26
|
-
return unless notify?(*args)
|
27
|
-
Isolator.notify(exception: build_exception(obj, args), backtrace: backtrace)
|
25
|
+
def notify(backtrace, obj, *args, **kwargs)
|
26
|
+
return unless notify?(*args, **kwargs)
|
27
|
+
Isolator.notify(exception: build_exception(obj, args, kwargs), backtrace: backtrace)
|
28
28
|
end
|
29
29
|
|
30
|
-
def notify?(*args)
|
31
|
-
enabled? && Isolator.enabled? && Isolator.within_transaction? && !ignored?(*args)
|
30
|
+
def notify?(*args, **kwargs)
|
31
|
+
enabled? && Isolator.enabled? && Isolator.within_transaction? && !ignored?(*args, **kwargs)
|
32
32
|
end
|
33
33
|
|
34
34
|
def ignore_if(&block)
|
@@ -39,17 +39,36 @@ module Isolator
|
|
39
39
|
@ignores ||= []
|
40
40
|
end
|
41
41
|
|
42
|
-
def ignored?(*args)
|
43
|
-
ignores.any? { |block| block.call(*args) }
|
42
|
+
def ignored?(*args, **kwargs)
|
43
|
+
ignores.any? { |block| block.call(*args, **kwargs) }
|
44
44
|
end
|
45
45
|
|
46
46
|
private
|
47
47
|
|
48
|
-
def build_exception(obj, args)
|
48
|
+
def build_exception(obj, args, kwargs = {})
|
49
49
|
klass = exception_class || Isolator::UnsafeOperationError
|
50
|
-
details =
|
50
|
+
details = build_details(obj, args, kwargs)
|
51
51
|
klass.new(exception_message, details: details)
|
52
52
|
end
|
53
|
+
|
54
|
+
def build_details(obj, args, kwargs)
|
55
|
+
return nil unless details_message
|
56
|
+
|
57
|
+
case details_message.arity
|
58
|
+
when 2, -2
|
59
|
+
# Older users of details_message expected only two arguments. Add
|
60
|
+
# kwargs hash as last argument, like in older Ruby.
|
61
|
+
details_message.call(obj, args + [kwargs])
|
62
|
+
when 3, -3
|
63
|
+
# New signature separates args from kwargs
|
64
|
+
details_message.call(obj, args, kwargs)
|
65
|
+
when 1
|
66
|
+
# Callback does not care about any args
|
67
|
+
details_message.call(obj)
|
68
|
+
else
|
69
|
+
raise "Unexpected arity (#{details_message.arity}) for #{details_message.inspect}"
|
70
|
+
end
|
71
|
+
end
|
53
72
|
end
|
54
73
|
end
|
55
74
|
end
|
@@ -28,8 +28,8 @@ module Isolator
|
|
28
28
|
@ignorer = Isolator::Ignorer
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
|
31
|
+
alias_method :raise_exceptions?, :raise_exceptions
|
32
|
+
alias_method :send_notifications?, :send_notifications
|
33
33
|
|
34
34
|
def test_env?
|
35
35
|
ENV["RACK_ENV"] == "test" || ENV["RAILS_ENV"] == "test"
|
data/lib/isolator/ignorer.rb
CHANGED
@@ -3,16 +3,31 @@
|
|
3
3
|
module Isolator
|
4
4
|
# Handle ignoring isolator errors using a yml file
|
5
5
|
class Ignorer
|
6
|
-
|
6
|
+
class ParseError < StandardError
|
7
|
+
def initialize(file_path, klass)
|
8
|
+
@file_path = file_path
|
9
|
+
@klass = klass
|
10
|
+
end
|
11
|
+
|
12
|
+
def message
|
13
|
+
"Unable to parse ignore config file #{@file_path}. Expected Hash, got #{@klass}."
|
14
|
+
end
|
15
|
+
end
|
7
16
|
|
8
17
|
class << self
|
9
|
-
def prepare(path
|
18
|
+
def prepare(path:, regex_string: "^.*(#ignores#):.*$")
|
10
19
|
return unless File.exist?(path)
|
11
20
|
|
12
|
-
|
21
|
+
ignores = begin
|
22
|
+
YAML.load_file(path, aliases: true)
|
23
|
+
rescue ArgumentError # support for older rubies https://github.com/rails/rails/commit/179d0a1f474ada02e0030ac3bd062fc653765dbe
|
24
|
+
YAML.load_file(path)
|
25
|
+
end
|
26
|
+
|
27
|
+
raise ParseError.new(path, ignores.class) unless ignores.respond_to?(:fetch)
|
13
28
|
|
14
29
|
Isolator.adapters.each do |id, adapter|
|
15
|
-
ignored_paths =
|
30
|
+
ignored_paths = ignores.fetch(id, [])
|
16
31
|
AdapterIgnore.new(adapter: adapter, ignored_paths: ignored_paths, regex_string: regex_string).prepare
|
17
32
|
end
|
18
33
|
end
|
data/lib/isolator/isolate.rb
CHANGED
@@ -4,9 +4,15 @@ module Isolator
|
|
4
4
|
# Add .isolate function to build and register adapters
|
5
5
|
module Isolate
|
6
6
|
def isolate(id, **options)
|
7
|
-
raise "Adapter already registered: #{id}" if Isolator.
|
7
|
+
raise "Adapter already registered: #{id}" if Isolator.has_adapter?(id)
|
8
8
|
adapter = AdapterBuilder.call(**options)
|
9
9
|
Isolator.adapters[id.to_s] = adapter
|
10
10
|
end
|
11
|
+
|
12
|
+
def remove_adapter(id)
|
13
|
+
if (adapter = Isolator.adapters.delete(id.to_s))
|
14
|
+
adapter.restore if adapter.respond_to?(:restore)
|
15
|
+
end
|
16
|
+
end
|
11
17
|
end
|
12
18
|
end
|
data/lib/isolator/notifier.rb
CHANGED
@@ -29,12 +29,12 @@ module Isolator
|
|
29
29
|
def log_exception
|
30
30
|
return unless Isolator.config.logger
|
31
31
|
|
32
|
-
offense_line = filtered_backtrace.first
|
33
|
-
|
34
32
|
msg = "[ISOLATOR EXCEPTION]\n" \
|
35
33
|
"#{exception.message}"
|
36
34
|
|
37
|
-
|
35
|
+
filtered_backtrace.each do |offense_line|
|
36
|
+
msg += "\n ↳ #{offense_line}"
|
37
|
+
end
|
38
38
|
|
39
39
|
Isolator.config.logger.warn(msg)
|
40
40
|
end
|
data/lib/isolator/railtie.rb
CHANGED
@@ -15,7 +15,19 @@ module Isolator
|
|
15
15
|
# (when all deps are likely to be loaded).
|
16
16
|
load File.join(__dir__, "adapters.rb")
|
17
17
|
|
18
|
-
|
18
|
+
# Try to load Rails base classes to trigger their load hooks
|
19
|
+
begin
|
20
|
+
::ActionMailer::Base
|
21
|
+
rescue NameError
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
::ActiveJob::Base
|
26
|
+
rescue NameError
|
27
|
+
end
|
28
|
+
|
29
|
+
Isolator.config.ignorer&.prepare(path: ".isolator_todo.yml")
|
30
|
+
Isolator.config.ignorer&.prepare(path: ".isolator_ignore.yml")
|
19
31
|
|
20
32
|
next unless Rails.env.test?
|
21
33
|
|
data/lib/isolator/version.rb
CHANGED
data/lib/isolator.rb
CHANGED
@@ -135,6 +135,13 @@ module Isolator
|
|
135
135
|
end
|
136
136
|
|
137
137
|
def decr_transactions!(connection_id = default_connection_id.call)
|
138
|
+
current = state[:transactions]&.[](connection_id) || 0
|
139
|
+
|
140
|
+
if current <= 0
|
141
|
+
warn "Trying to finalize an untracked transaction"
|
142
|
+
return
|
143
|
+
end
|
144
|
+
|
138
145
|
state[:transactions][connection_id] -= 1
|
139
146
|
|
140
147
|
finish! if current_transactions(connection_id) == (connection_threshold(connection_id) - 1)
|
@@ -167,6 +174,10 @@ module Isolator
|
|
167
174
|
@adapters ||= Isolator::SimpleHashie.new
|
168
175
|
end
|
169
176
|
|
177
|
+
def has_adapter?(id)
|
178
|
+
adapters.key?(id.to_s)
|
179
|
+
end
|
180
|
+
|
170
181
|
def load_ignore_config(path)
|
171
182
|
warn "[DEPRECATION] `load_ignore_config` is deprecated. Please use `Isolator::Ignorer.prepare` instead."
|
172
183
|
Isolator::Ignorer.prepare(path: path)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: isolator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-05-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sniffer
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.5.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.5.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -248,6 +248,34 @@ dependencies:
|
|
248
248
|
- - ">="
|
249
249
|
- !ruby/object:Gem::Version
|
250
250
|
version: '0'
|
251
|
+
- !ruby/object:Gem::Dependency
|
252
|
+
name: webrick
|
253
|
+
requirement: !ruby/object:Gem::Requirement
|
254
|
+
requirements:
|
255
|
+
- - ">="
|
256
|
+
- !ruby/object:Gem::Version
|
257
|
+
version: '0'
|
258
|
+
type: :development
|
259
|
+
prerelease: false
|
260
|
+
version_requirements: !ruby/object:Gem::Requirement
|
261
|
+
requirements:
|
262
|
+
- - ">="
|
263
|
+
- !ruby/object:Gem::Version
|
264
|
+
version: '0'
|
265
|
+
- !ruby/object:Gem::Dependency
|
266
|
+
name: net-smtp
|
267
|
+
requirement: !ruby/object:Gem::Requirement
|
268
|
+
requirements:
|
269
|
+
- - ">="
|
270
|
+
- !ruby/object:Gem::Version
|
271
|
+
version: '0'
|
272
|
+
type: :development
|
273
|
+
prerelease: false
|
274
|
+
version_requirements: !ruby/object:Gem::Requirement
|
275
|
+
requirements:
|
276
|
+
- - ">="
|
277
|
+
- !ruby/object:Gem::Version
|
278
|
+
version: '0'
|
251
279
|
description: Detect non-atomic interactions within DB transactions
|
252
280
|
email:
|
253
281
|
- dementiev.vm@gmail.com
|
@@ -298,7 +326,8 @@ metadata:
|
|
298
326
|
documentation_uri: http://github.com/palkan/isolator
|
299
327
|
homepage_uri: http://github.com/palkan/isolator
|
300
328
|
source_code_uri: http://github.com/palkan/isolator
|
301
|
-
|
329
|
+
funding_uri: https://github.com/sponsors/palkan
|
330
|
+
post_install_message:
|
302
331
|
rdoc_options: []
|
303
332
|
require_paths:
|
304
333
|
- lib
|
@@ -306,15 +335,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
306
335
|
requirements:
|
307
336
|
- - ">="
|
308
337
|
- !ruby/object:Gem::Version
|
309
|
-
version: 2.
|
338
|
+
version: 2.6.0
|
310
339
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
311
340
|
requirements:
|
312
341
|
- - ">="
|
313
342
|
- !ruby/object:Gem::Version
|
314
343
|
version: '0'
|
315
344
|
requirements: []
|
316
|
-
rubygems_version: 3.
|
317
|
-
signing_key:
|
345
|
+
rubygems_version: 3.4.8
|
346
|
+
signing_key:
|
318
347
|
specification_version: 4
|
319
348
|
summary: Detect non-atomic interactions within DB transactions
|
320
349
|
test_files: []
|