rspec-retryable 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a4780f14d90cc2bdce6da95e999e53bc8d7161b298840726e88c509bdddfbafc
4
+ data.tar.gz: cd5346007d8ce02a9ae49186bd0b5e6f81938641a191bafe7f882e15e47adc4f
5
+ SHA512:
6
+ metadata.gz: '0927eb2492bd7c287d2c96a10bbfc1eb811a61d4ac39dd4fc632f7416a112ef623227f0a6813a126332b961dfefdd3a0000f074659b16c12992fc6f8dcbf9bd6'
7
+ data.tar.gz: a2feafa640ee50b563983f25554a53281726aa557ff7ac7a4e4b52db699e7ceac3d0de4f1de21192fd6b25ca18a9d869f6d272470e6dfc97b7d471e2440a4255
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # RSpec::Retryable
2
+
3
+ TODO: Delete this and the text below, and describe your gem
4
+
5
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rspec/retryable`. To experiment with that code, run `bin/console` for an interactive prompt.
6
+
7
+ ## Installation
8
+
9
+ TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
10
+
11
+ Install the gem and add to the application's Gemfile by executing:
12
+
13
+ ```bash
14
+ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
15
+ ```
16
+
17
+ If bundler is not being used to manage dependencies, install the gem by executing:
18
+
19
+ ```bash
20
+ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ngan/rspec-retryable.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/retryable/metadata'
4
+ require 'rspec/retryable/payload'
5
+
6
+ module RSpec
7
+ module Retryable
8
+ module Example
9
+ def finish(reporter)
10
+ metadata[:retryable] ||= Metadata.new
11
+
12
+ if @exception
13
+ state = :failed
14
+ execution_result.exception = @exception
15
+ record_finished :failed, reporter
16
+ elsif execution_result.pending_message
17
+ state = :pending
18
+ record_finished :pending, reporter
19
+ else
20
+ state = :passed
21
+ record_finished :passed, reporter
22
+ end
23
+
24
+ @payload = RSpec::Retryable::Payload.new(self, state)
25
+
26
+ RSpec::Retryable.handlers.invoke(@payload)
27
+
28
+ if @payload.retry
29
+ # Replaced the final result by the retry result
30
+ @payload.result = retry_example
31
+ end
32
+
33
+ # Notify reporter only if it's not handled by the handlers
34
+ notify_reporter if @payload.notify
35
+
36
+ @payload.result
37
+ end
38
+
39
+ # https://github.com/rspec/rspec-core/blob/3-10-maintenance/lib/rspec/core/example.rb#L433-L441
40
+ # Used internally to set an exception and fail without actually executing
41
+ # the example when an exception is raised in before(:context).
42
+ def fail_with_exception(...)
43
+ @failed_in_context = true
44
+ super
45
+ end
46
+
47
+ def failed_in_context?
48
+ !!@failed_in_context
49
+ end
50
+
51
+ def retry_example
52
+ metadata[:retryable].failures << @exception
53
+ metadata[:retryable].attempts += 1
54
+ # Every retry turns off the retry and notify flag, it's up to the handler to turn them back on or not.
55
+ @payload.retry = false
56
+ @payload.notify = false
57
+ # Duplicate the example for re-run
58
+ new_example = duplicate_with(metadata)
59
+ new_example.instance_variable_set(:@id, id)
60
+ # Taken from https://github.com/rspec/rspec-core/blob/main/lib/rspec/core/example_group.rb#L644-L646
61
+ instance = new_example.example_group.new(new_example.inspect_output)
62
+ new_example.example_group.set_ivars(instance, new_example.example_group.before_context_ivars)
63
+ # Use same reporter from example instead of the one passing in to behave like
64
+ # a fresh new example.
65
+
66
+ new_example.run(instance, reporter)
67
+ end
68
+
69
+ def notify_reporter
70
+ case @payload.state
71
+ when :failed
72
+ reporter.example_failed self
73
+ when :pending
74
+ reporter.example_pending self
75
+ when :passed
76
+ reporter.example_passed self
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module Retryable
5
+ class Handlers
6
+ def initialize
7
+ @handlers = []
8
+ end
9
+
10
+ def register(klass, *args, **kwargs)
11
+ @handlers << klass.new(*args, **kwargs)
12
+ end
13
+
14
+ def invoke(payload)
15
+ traverse(0, payload)
16
+ end
17
+
18
+ private
19
+
20
+ def traverse(index, payload)
21
+ @handlers[index]&.call(payload) do
22
+ traverse(index + 1, payload)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module Retryable
5
+ class Metadata
6
+ attr_writer :attempts, :retry, :failures
7
+
8
+ def attempts
9
+ @attempts ||= 0
10
+ end
11
+
12
+ def retry
13
+ return @retry if defined?(@retry)
14
+
15
+ @retry = false
16
+ end
17
+
18
+ def failures
19
+ @failures ||= []
20
+ end
21
+
22
+ def to_h
23
+ {
24
+ attempts: attempts,
25
+ retry: @retry,
26
+ failures: failures,
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module Retryable
5
+ class Payload
6
+ attr_reader :example
7
+ attr_accessor :notify, :result, :retry, :state
8
+
9
+ # Default payload with:
10
+ # - example: RSpec example
11
+ # - state: Current state of the example, can be alterted by handlers
12
+ # - notify: default to `true`, if set to `false`, reporter will not be notified
13
+ # - result: this is the final result returned to RSpec runner
14
+ # - retry: default to `false`, if set to `true`, the example will be retried
15
+ def initialize(example, state)
16
+ @example = example
17
+ @state = state
18
+ @notify = true
19
+ @result = state == :failed ? false : true
20
+ @retry = false
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module Retryable
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/retryable/version'
4
+ require 'rspec/retryable/example'
5
+ require 'rspec/retryable/handlers'
6
+
7
+ module RSpec
8
+ module Retryable
9
+ class << self
10
+ def handlers
11
+ @handlers ||= Handlers.new
12
+ end
13
+
14
+ def bind
15
+ require 'rspec/core'
16
+
17
+ ::RSpec::Core::Example.prepend(RSpec::Retryable::Example)
18
+ end
19
+ end
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-retryable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ngan Pham
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-08-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ description:
28
+ email:
29
+ - ngan@users.noreply.github.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".rspec"
35
+ - README.md
36
+ - Rakefile
37
+ - lib/rspec/retryable.rb
38
+ - lib/rspec/retryable/example.rb
39
+ - lib/rspec/retryable/handlers.rb
40
+ - lib/rspec/retryable/metadata.rb
41
+ - lib/rspec/retryable/payload.rb
42
+ - lib/rspec/retryable/version.rb
43
+ homepage: https://github.com/Gusto/rspec-retryable
44
+ licenses: []
45
+ metadata:
46
+ allowed_push_host: https://rubygems.org
47
+ homepage_uri: https://github.com/Gusto/rspec-retryable
48
+ source_code_uri: https://github.com/Gusto/rspec-retryable
49
+ changelog_uri: https://github.com/Gusto/rspec-retryable/releases
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 3.0.0
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.5.0
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Adds ability to fully control RSpec retrying
69
+ test_files: []