isolator 1.0.1 → 1.1.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: 8a0299bddb8abe783af8113fcd57b0e9926a1e6f714d29a6ed258b876c9c7062
4
- data.tar.gz: 06551a9aab8237dccf214eae3cf841d96f15c61c4e26b4f623979711ba5e8ddc
3
+ metadata.gz: 0c72b8d090e9f2d9f0aa40981298f62bb562d5a1064868f771c2e5cd8af029bc
4
+ data.tar.gz: 7aef3385898b9db409b4251becfc85f3bb7004d411be4240d8126bb9d9c4d5b7
5
5
  SHA512:
6
- metadata.gz: 1ad13d02cca26c6e0ae19e24be82e8bd08f53904c8adbb134ce0f4dc9faa7375c681c3fa62532d8284f104a97b3f51858e2e5f87c02b089108564e0064b3c5c4
7
- data.tar.gz: 3b02cb4f11b42f6ccfbae0717cf53d5661bc92e211a99bff80b17b4c82fca6cb37b7c905ae98e74b2370c99b7d7f83e1194966b9194c11319cc5473c2ee29ce4
6
+ metadata.gz: eaeff564dc8bb7288b3249325144c60096e35066b994b8ebb7365d8ee949a5b44b47d276eedbcb8580c0d460fa5c46a9d1529e002c0904f04452e96bb02069fc
7
+ data.tar.gz: 6d068fab30b04a15e58afddc1ee23a2a0492051b0d3b9d20784da421aced0d2e1aa548b3ea74b4d9e395c84ff275660baf8ab0fd6825f9f8e9b9c947220b7be3
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.1.0 (2024-08-12)
6
+
7
+ - Add support for `ActiveJob::Base.enqueue_after_transaction_commit`. ([@joshuay03][])
8
+
9
+ - Add `ignore_on: (obj) -> bool` option for adapters. ([@palkan][])
10
+
11
+ - Add ActionCable adapter. ([@arthurWD][])
12
+
5
13
  ## 1.0.1 (2023-12-01) ❄️
6
14
 
7
15
  - Fix Rails 7.0 support. ([@palkan][])
@@ -141,3 +149,5 @@ This, for example, makes Isolator compatible with Rails multi-database apps.
141
149
  [@Mange]: https://github.com/Mange
142
150
  [@tomgi]: https://github.com/tomgi
143
151
  [@tagirahmad]: https://github.com/tagirahmad
152
+ [@arthurWD]: https://github.com/arthurWD
153
+ [@joshuay03]: https://github.com/joshuay03
data/README.md CHANGED
@@ -75,11 +75,16 @@ However, there are some potential caveats:
75
75
 
76
76
  1) Isolator tries to detect the environment automatically and includes only necessary adapters. Thus the order of loading gems matters: make sure that `isolator` is required in the end (NOTE: in Rails, all adapters loaded after application initialization).
77
77
 
78
- 2) Isolator does not distinguish framework-level adapters. For example, `:active_job` spy doesn't take into account which AJ adapter you use; if you are using a safe one (e.g. `Que`) just disable the `:active_job` adapter to avoid false negatives (i.e. `Isolator.adapters.active_job.disable!`).
78
+ 2) Isolator does not distinguish framework-level adapters. For example, `:active_job` spy doesn't take into account which AJ adapter you use; if you are using a safe one (e.g. `Que`) just disable the `:active_job` adapter to avoid false negatives. You can do this by adding an initializer:
79
79
 
80
- 3) Isolator tries to detect the `test` environment and slightly change its behavior: first, it respect _transactional tests_; secondly, error raising is turned on by default (see [below](#configuration)).
80
+ ```rb
81
+ require "active_job/base"
82
+ Isolator.adapters.active_job.disable!
83
+ ```
81
84
 
82
- 4) Experimental [multiple databases](https://guides.rubyonrails.org/active_record_multiple_databases.html) has been added in v0.7.0. Please, let us know if you encounter any issues.
85
+ 4) Isolator tries to detect the `test` environment and slightly change its behavior: first, it respect _transactional tests_; secondly, error raising is turned on by default (see [below](#configuration)).
86
+
87
+ 5) Experimental [multiple databases](https://guides.rubyonrails.org/active_record_multiple_databases.html) has been added in v0.7.0. Please, let us know if you encounter any issues.
83
88
 
84
89
  ### Configuration
85
90
 
@@ -160,6 +165,7 @@ Isolator has a bunch of built-in adapters:
160
165
  - `:sucker_punch`
161
166
  - `:mailer`
162
167
  - `:webmock` – track mocked HTTP requests (unseen by Sniffer) in tests
168
+ - `:action_cable`
163
169
 
164
170
  You can dynamically enable/disable adapters, e.g.:
165
171
 
@@ -172,6 +178,8 @@ Isolator.adapters.http.disable!
172
178
  Isolator.adapters.http.enable!
173
179
  ```
174
180
 
181
+ For `active_job`, be sure to first `require "active_job/base"`.
182
+
175
183
  ### Fix Offenses
176
184
 
177
185
  For the actions that should be executed only after successful transaction commit (which is mostly always so), you can try to use the `after_commit` callback from [after_commit_everywhere] gem (or use native AR callback in models if it's applicable).
@@ -13,6 +13,9 @@ module Isolator
13
13
  self.exception_class = options[:exception_class] if options.key?(:exception_class)
14
14
  self.exception_message = options[:exception_message] if options.key?(:exception_message)
15
15
  self.details_message = options[:details_message] if options.key?(:details_message)
16
+ if options.key?(:ignore_on)
17
+ define_singleton_method(:ignore_on?, &options[:ignore_on])
18
+ end
16
19
  end
17
20
 
18
21
  mod = build_mod(method_name, adapter)
@@ -39,7 +42,7 @@ module Isolator
39
42
  module_eval <<~RUBY, __FILE__, __LINE__ + 1
40
43
  def #{method_name}(...)
41
44
  # check if we are even notifying before calling `caller`, which is well known to be slow
42
- #{adapter_name}.notify(caller, self, ...) if #{adapter_name}.notify?(...)
45
+ #{adapter_name}.notify(caller, self, ...) if #{adapter_name}.notify_on?(self, ...)
43
46
  super
44
47
  end
45
48
  RUBY
@@ -7,4 +7,9 @@ Isolator.isolate :active_job,
7
7
  details_message: ->(obj) {
8
8
  "#{obj.class.name}" \
9
9
  "#{obj.arguments.any? ? " (#{obj.arguments.join(", ")})" : ""}"
10
- }
10
+ },
11
+ ignore_on: ->(job) {
12
+ config = job.class.try(:enqueue_after_transaction_commit)
13
+ config == :always || (config == :default &&
14
+ ActiveJob::Base.queue_adapter.try(:enqueue_after_transaction_commit?) == true)
15
+ }
@@ -27,6 +27,10 @@ module Isolator
27
27
  Isolator.notify(exception: build_exception(obj, args, kwargs), backtrace: backtrace)
28
28
  end
29
29
 
30
+ def notify_on?(obj, *args, **kwargs)
31
+ !ignore_on?(obj) && notify?(*args, **kwargs)
32
+ end
33
+
30
34
  def notify?(...)
31
35
  enabled? && Isolator.enabled? && Isolator.within_transaction? && !ignored?(...)
32
36
  end
@@ -43,6 +47,10 @@ module Isolator
43
47
  ignores.any? { |block| block.call(*args, **kwargs) }
44
48
  end
45
49
 
50
+ def ignore_on?(_obj)
51
+ false
52
+ end
53
+
46
54
  private
47
55
 
48
56
  def build_exception(obj, args, kwargs = {})
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ Isolator.isolate :action_cable,
4
+ target: ActionCable::Server::Base,
5
+ method_name: :broadcast,
6
+ exception_class: Isolator::WebsocketError,
7
+ details_message: ->(_obj, args) {
8
+ channel = args.first
9
+
10
+ "Broadcasting to #{channel}"
11
+ }
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(ActiveSupport)
4
+ ActiveSupport.on_load(:action_cable) do
5
+ require "isolator/adapters/websockets/action_cable"
6
+ end
7
+ end
@@ -3,4 +3,5 @@
3
3
  require "isolator/adapters/http"
4
4
  require "isolator/adapters/background_jobs"
5
5
  require "isolator/adapters/mailers"
6
+ require "isolator/adapters/websockets"
6
7
  require "isolator/adapters/after_commit" if defined?(::TestAfterCommit)
@@ -22,4 +22,8 @@ module Isolator # :nodoc: all
22
22
  class MailerError < UnsafeOperationError
23
23
  MESSAGE = "You are trying to send email inside db transaction."
24
24
  end
25
+
26
+ class WebsocketError < UnsafeOperationError
27
+ MESSAGE = "You are trying to broadcast to a websocket connection inside db transaction. "
28
+ end
25
29
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Isolator
4
- VERSION = "1.0.1"
4
+ VERSION = "1.1.0"
5
5
  end
data/lib/isolator.rb CHANGED
@@ -111,20 +111,20 @@ module Isolator
111
111
 
112
112
  def incr_thresholds!
113
113
  self.default_threshold += 1
114
+ debug!("Thresholds were incremented")
115
+
114
116
  return unless state[:thresholds]
115
117
 
116
118
  state[:thresholds].transform_values!(&:succ)
117
-
118
- debug!("Thresholds were incremented")
119
119
  end
120
120
 
121
121
  def decr_thresholds!
122
122
  self.default_threshold -= 1
123
+ debug!("Thresholds were incremented")
124
+
123
125
  return unless state[:thresholds]
124
126
 
125
127
  state[:thresholds].transform_values!(&:pred)
126
-
127
- debug!("Thresholds were decremented")
128
128
  end
129
129
 
130
130
  def incr_transactions!(connection_id = default_connection_id.call)
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: 1.0.1
4
+ version: 1.1.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: 2023-12-01 00:00:00.000000000 Z
11
+ date: 2024-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sniffer
@@ -302,6 +302,8 @@ files:
302
302
  - lib/isolator/adapters/http/webmock.rb
303
303
  - lib/isolator/adapters/mailers.rb
304
304
  - lib/isolator/adapters/mailers/mail.rb
305
+ - lib/isolator/adapters/websockets.rb
306
+ - lib/isolator/adapters/websockets/action_cable.rb
305
307
  - lib/isolator/callbacks.rb
306
308
  - lib/isolator/configuration.rb
307
309
  - lib/isolator/database_cleaner_support.rb
@@ -329,7 +331,7 @@ metadata:
329
331
  homepage_uri: http://github.com/palkan/isolator
330
332
  source_code_uri: http://github.com/palkan/isolator
331
333
  funding_uri: https://github.com/sponsors/palkan
332
- post_install_message:
334
+ post_install_message:
333
335
  rdoc_options: []
334
336
  require_paths:
335
337
  - lib
@@ -344,8 +346,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
344
346
  - !ruby/object:Gem::Version
345
347
  version: '0'
346
348
  requirements: []
347
- rubygems_version: 3.4.20
348
- signing_key:
349
+ rubygems_version: 3.4.19
350
+ signing_key:
349
351
  specification_version: 4
350
352
  summary: Detect non-atomic interactions within DB transactions
351
353
  test_files: []