├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── MIT-LICENSE ├── README.md ├── Rakefile ├── activejob_retriable.gemspec ├── lib ├── active_job │ ├── retriable.rb │ └── retriable │ │ ├── test_adapater.rb │ │ ├── test_helper.rb │ │ └── version.rb └── activejob │ └── retriable.rb └── test ├── active_job ├── retriable │ └── test_adapter_test.rb └── retriable_test.rb ├── dummy ├── README.rdoc ├── Rakefile ├── app │ ├── assets │ │ ├── images │ │ │ └── .keep │ │ ├── javascripts │ │ │ └── application.js │ │ └── stylesheets │ │ │ └── application.css │ ├── controllers │ │ ├── application_controller.rb │ │ └── concerns │ │ │ └── .keep │ ├── helpers │ │ └── application_helper.rb │ ├── jobs │ │ ├── application_job.rb │ │ ├── callbacks_job.rb │ │ ├── max_job.rb │ │ ├── noop_job.rb │ │ ├── raise_job.rb │ │ └── rescue_job.rb │ ├── mailers │ │ └── .keep │ ├── models │ │ ├── .keep │ │ ├── concerns │ │ │ └── .keep │ │ └── person.rb │ └── views │ │ └── layouts │ │ └── application.html.erb ├── bin │ ├── bundle │ ├── rails │ ├── rake │ └── setup ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── database.yml │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── assets.rb │ │ ├── backtrace_silencers.rb │ │ ├── cookies_serializer.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ ├── mime_types.rb │ │ ├── session_store.rb │ │ └── wrap_parameters.rb │ ├── locales │ │ └── en.yml │ ├── routes.rb │ └── secrets.yml ├── db │ ├── migrate │ │ └── 20150710003724_create_people.rb │ └── schema.rb ├── lib │ └── assets │ │ └── .keep ├── log │ └── .keep ├── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ └── favicon.ico └── test │ ├── fixtures │ └── people.yml │ └── models │ └── person_test.rb └── test_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | log/*.log 3 | pkg/ 4 | test/dummy/db/*.sqlite3 5 | test/dummy/db/*.sqlite3-journal 6 | test/dummy/log/*.log 7 | test/dummy/tmp/ 8 | test/dummy/.sass-cache 9 | 10 | *.gem 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - "2.3.3" 4 | - "2.4.0" 5 | before_script: 6 | - cd test/dummy/ 7 | - RAILS_ENV=test bundle exec rake db:migrate db:fixtures:load 8 | - cd ../.. 9 | script: 10 | - bundle exec rake test 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of 4 | fostering an open and welcoming community, we pledge to respect all people who 5 | contribute through reporting issues, posting feature requests, updating 6 | documentation, submitting pull requests or patches, and other activities. 7 | 8 | We are committed to making participation in this project a harassment-free 9 | experience for everyone, regardless of level of experience, gender, gender 10 | identity and expression, sexual orientation, disability, personal appearance, 11 | body size, race, ethnicity, age, religion, or nationality. 12 | 13 | Examples of unacceptable behavior by participants include: 14 | 15 | * The use of sexualized language or imagery 16 | * Personal attacks 17 | * Trolling or insulting/derogatory comments 18 | * Public or private harassment 19 | * Publishing other's private information, such as physical or electronic 20 | addresses, without explicit permission 21 | * Other unethical or unprofessional conduct 22 | 23 | Project maintainers have the right and responsibility to remove, edit, or 24 | reject comments, commits, code, wiki edits, issues, and other contributions 25 | that are not aligned to this Code of Conduct, or to ban temporarily or 26 | permanently any contributor for other behaviors that they deem inappropriate, 27 | threatening, offensive, or harmful. 28 | 29 | By adopting this Code of Conduct, project maintainers commit themselves to 30 | fairly and consistently applying these principles to every aspect of managing 31 | this project. Project maintainers who do not follow or enforce the Code of 32 | Conduct may be permanently removed from the project team. 33 | 34 | This code of conduct applies both within project spaces and in public spaces 35 | when an individual is representing the project or its community. 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 38 | reported by contacting a project maintainer at mikeycgto@gmail.com. All 39 | complaints will be reviewed and investigated and will result in a response that 40 | is deemed necessary and appropriate to the circumstances. Maintainers are 41 | obligated to maintain confidentiality with regard to the reporter of an 42 | incident. 43 | 44 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 45 | version 1.3.0, available at 46 | [http://contributor-covenant.org/version/1/3/0/][version] 47 | 48 | [homepage]: http://contributor-covenant.org 49 | [version]: http://contributor-covenant.org/version/1/3/0/ -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | 4 | group :development do 5 | gem 'guard' 6 | gem 'guard-minitest' 7 | end 8 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | activejob-retriable (5.0.4) 5 | activejob (~> 5.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | actioncable (5.0.1) 11 | actionpack (= 5.0.1) 12 | nio4r (~> 1.2) 13 | websocket-driver (~> 0.6.1) 14 | actionmailer (5.0.1) 15 | actionpack (= 5.0.1) 16 | actionview (= 5.0.1) 17 | activejob (= 5.0.1) 18 | mail (~> 2.5, >= 2.5.4) 19 | rails-dom-testing (~> 2.0) 20 | actionpack (5.0.1) 21 | actionview (= 5.0.1) 22 | activesupport (= 5.0.1) 23 | rack (~> 2.0) 24 | rack-test (~> 0.6.3) 25 | rails-dom-testing (~> 2.0) 26 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 27 | actionview (5.0.1) 28 | activesupport (= 5.0.1) 29 | builder (~> 3.1) 30 | erubis (~> 2.7.0) 31 | rails-dom-testing (~> 2.0) 32 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 33 | activejob (5.0.1) 34 | activesupport (= 5.0.1) 35 | globalid (>= 0.3.6) 36 | activemodel (5.0.1) 37 | activesupport (= 5.0.1) 38 | activerecord (5.0.1) 39 | activemodel (= 5.0.1) 40 | activesupport (= 5.0.1) 41 | arel (~> 7.0) 42 | activesupport (5.0.1) 43 | concurrent-ruby (~> 1.0, >= 1.0.2) 44 | i18n (~> 0.7) 45 | minitest (~> 5.1) 46 | tzinfo (~> 1.1) 47 | arel (7.1.4) 48 | builder (3.2.2) 49 | coderay (1.1.1) 50 | concurrent-ruby (1.0.4) 51 | erubis (2.7.0) 52 | ffi (1.9.14) 53 | formatador (0.2.5) 54 | globalid (0.3.7) 55 | activesupport (>= 4.1.0) 56 | guard (2.14.0) 57 | formatador (>= 0.2.4) 58 | listen (>= 2.7, < 4.0) 59 | lumberjack (~> 1.0) 60 | nenv (~> 0.1) 61 | notiffany (~> 0.0) 62 | pry (>= 0.9.12) 63 | shellany (~> 0.0) 64 | thor (>= 0.18.1) 65 | guard-compat (1.2.1) 66 | guard-minitest (2.4.6) 67 | guard-compat (~> 1.2) 68 | minitest (>= 3.0) 69 | i18n (0.7.0) 70 | listen (3.1.5) 71 | rb-fsevent (~> 0.9, >= 0.9.4) 72 | rb-inotify (~> 0.9, >= 0.9.7) 73 | ruby_dep (~> 1.2) 74 | loofah (2.0.3) 75 | nokogiri (>= 1.5.9) 76 | lumberjack (1.0.10) 77 | mail (2.6.4) 78 | mime-types (>= 1.16, < 4) 79 | method_source (0.8.2) 80 | mime-types (3.1) 81 | mime-types-data (~> 3.2015) 82 | mime-types-data (3.2016.0521) 83 | mini_portile2 (2.1.0) 84 | minitest (5.10.1) 85 | nenv (0.3.0) 86 | nio4r (1.2.1) 87 | nokogiri (1.7.0.1) 88 | mini_portile2 (~> 2.1.0) 89 | notiffany (0.1.1) 90 | nenv (~> 0.1) 91 | shellany (~> 0.0) 92 | pry (0.10.4) 93 | coderay (~> 1.1.0) 94 | method_source (~> 0.8.1) 95 | slop (~> 3.4) 96 | rack (2.0.1) 97 | rack-test (0.6.3) 98 | rack (>= 1.0) 99 | rails (5.0.1) 100 | actioncable (= 5.0.1) 101 | actionmailer (= 5.0.1) 102 | actionpack (= 5.0.1) 103 | actionview (= 5.0.1) 104 | activejob (= 5.0.1) 105 | activemodel (= 5.0.1) 106 | activerecord (= 5.0.1) 107 | activesupport (= 5.0.1) 108 | bundler (>= 1.3.0, < 2.0) 109 | railties (= 5.0.1) 110 | sprockets-rails (>= 2.0.0) 111 | rails-dom-testing (2.0.2) 112 | activesupport (>= 4.2.0, < 6.0) 113 | nokogiri (~> 1.6) 114 | rails-html-sanitizer (1.0.3) 115 | loofah (~> 2.0) 116 | railties (5.0.1) 117 | actionpack (= 5.0.1) 118 | activesupport (= 5.0.1) 119 | method_source 120 | rake (>= 0.8.7) 121 | thor (>= 0.18.1, < 2.0) 122 | rake (12.0.0) 123 | rb-fsevent (0.9.8) 124 | rb-inotify (0.9.7) 125 | ffi (>= 0.5.0) 126 | ruby_dep (1.5.0) 127 | shellany (0.0.1) 128 | slop (3.6.0) 129 | sprockets (3.7.1) 130 | concurrent-ruby (~> 1.0) 131 | rack (> 1, < 3) 132 | sprockets-rails (3.2.0) 133 | actionpack (>= 4.0) 134 | activesupport (>= 4.0) 135 | sprockets (>= 3.0.0) 136 | sqlite3 (1.3.12) 137 | thor (0.19.4) 138 | thread_safe (0.3.5) 139 | tzinfo (1.2.2) 140 | thread_safe (~> 0.1) 141 | websocket-driver (0.6.4) 142 | websocket-extensions (>= 0.1.0) 143 | websocket-extensions (0.1.2) 144 | 145 | PLATFORMS 146 | ruby 147 | 148 | DEPENDENCIES 149 | activejob-retriable! 150 | guard 151 | guard-minitest 152 | rails (~> 5.0) 153 | sqlite3 (~> 1.3) 154 | 155 | BUNDLED WITH 156 | 1.13.6 157 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard :minitest do 2 | # with Minitest::Unit 3 | watch(%r{^test/(.*)\/?(.*)_test\.rb$}) 4 | watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb" } 5 | watch(%r{^test/test_helper\.rb$}) { 'test' } 6 | end 7 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 Michael Coyne 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ActiveJob::Retriable 2 | ==================== 3 | 4 | [![Build Status](https://travis-ci.org/SimplyBuilt/activejob-retriable.svg)](https://travis-ci.org/SimplyBuilt/activejob-retriable) 5 | [![Gem Version](https://badge.fury.io/rb/activejob-retriable.svg)](https://rubygems.org/gems/activejob-retriable) 6 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) 7 | 8 | Automatically retry failed ActiveJobs with an exponential back-off. 9 | 10 | This gem aims to mimic most of the functionality of Sidekiq's `RetryJobs` middleware but operates on 11 | the ActiveJob layer. 12 | 13 | ## Installation 14 | 15 | To install the gem, add the following to your Gemfile: 16 | 17 | ```ruby 18 | gem "activejob-retriable" 19 | ``` 20 | 21 | If you're using Rails 5, use the branch master off Github for support. 22 | Once the first stable release of Rails 5 is out we will push a 5.0 23 | version of gem. 24 | 25 | ```ruby 26 | gem "activejob-retriable", github: "SimplyBuilt/activejob-retriable" 27 | ``` 28 | 29 | ## Usage 30 | 31 | The simplest way to use this gem is to include the following your 32 | `ApplicationJob` class 33 | 34 | ```ruby 35 | class ApplicationJob < ActiveJob::Base 36 | include ActiveJob::Retriable 37 | end 38 | ``` 39 | 40 | A `max_retry` class method as well as before, after and around exception callbacks 41 | will now be available to all your Jobs. For example: 42 | 43 | ```ruby 44 | class MyJob < ApplicationJob 45 | max_retry 12 46 | 47 | after_exception do 48 | # Record exeception to our fictitious error service 49 | ErrorNotify.dispatch current_exception 50 | end 51 | end 52 | ``` 53 | 54 | ## Support 55 | 56 | Backends that support the retrying of jobs are supported. A Runtime 57 | exception is raised if this concern is included in a job class with an 58 | unsupported backend. 59 | 60 | The gem has only been tested with the Sidekiq backend. Please submit 61 | pull-requests and issues for backends that do not function properly. 62 | 63 | ## rescue_from Blocks With retry_job 64 | 65 | The motivation of this gem is to play nicely with existing `rescue_from` 66 | blocks within your job classes. If a `rescue_from` block makes calls to 67 | `retry_job` is it probably best to call this method if and only if 68 | `retries_exhausted?` is not `true`. Otherwise, your jobs may be retried 69 | indefinitely! See this [test job 70 | class](https://github.com/SimplyBuilt/activejob-retriable/blob/master/test/dummy/app/jobs/rescue_job.rb#L8) 71 | for an example. 72 | 73 | ## Exception Callbacks 74 | 75 | Much like `ActiveJob` itself, retriable introduces some callbacks for 76 | exception handling. Your job class can define `before_exception`, 77 | `after_exception` and `around_exception` callbacks. 78 | 79 | Retriable will also set the value of `current_exception` to the actual 80 | exception. This way, direct access to the exception is possible from 81 | within a callback. This may be useful for error reporting and other 82 | needs. 83 | 84 | ## Retriable with ActionMailer 85 | 86 | If you want `ActionMailer` delivery jobs to use `Retriable`, you have to 87 | reopen the `ActionMailer::DeliveryJob` class and manually include the 88 | concern. For example: 89 | 90 | ```ruby 91 | module ActionMailer 92 | class DeliveryJob 93 | include ActiveJob::Retriable 94 | end 95 | end 96 | ``` 97 | 98 | It is recommended to do this via an initializer. We're open to 99 | suggestions on how to improve this aspect of `Retriable` though! 100 | 101 | ## Advanced Usage 102 | 103 | It is possible to overload or redefine both `retry_delay` and 104 | `retries_exhausted?` to include custom logic. This means it is easy to 105 | implement different back-off strategies as well as more advanced 106 | exhausted logic. 107 | 108 | Feel free to open PR's with more advanced examples! 109 | 110 | ## Testing 111 | 112 | The default `ActiveJob::QueueAdapters::TestAdapter` does not call 113 | serialize and deserialize as one may expect. Thus, `retry_attempts` are 114 | not properly tracked and "infinite performs" occur when an exception is 115 | raised. Therefore, we provided a new subclass to TestAdapter named 116 | `ActiveJob::Retriable::TestAdapter`. To use this adapter please do the 117 | following: 118 | 119 | 1. Include the `ActiveJob::Retriable::TestHelper` in your `test_helper.rb` 120 | 121 | ```ruby 122 | require 'active_job/retriable/test_helper' 123 | ``` 124 | 125 | 2. Reopen `ActiveJob::TestCase` and include the helper 126 | 127 | 128 | ```ruby 129 | class ActiveJob::TestCase 130 | include ActiveJob::Retriable::TestHelper 131 | end 132 | ``` 133 | 134 | Alternatively, you can just include the helper on a test-by-test 135 | basis. 136 | 137 | 3. If you're using ActiveJob assertions in controllers or elsewhere, be 138 | sure to include the test helper concern there as well! 139 | 140 | 4. *Optionally* add a setup and teardown block to toggle on 141 | `reraise_when_retry_exhausted`, `print_exceptions_to_stderr` and 142 | `print_exception_backtraces_to_stderr`. These features are useful when 143 | testing and debugging Active Jobs that raise exceptions or have syntax 144 | errors. 145 | 146 | ```ruby 147 | setup do 148 | ActiveJob::Retriable.reraise_when_retry_exhausted = true 149 | ActiveJob::Retriable.print_exceptions_to_stderr = true 150 | 151 | # Or with a full backtrace... 152 | # ActiveJob::Retriable.print_exceptions_to_stderr = :backtrace 153 | end 154 | 155 | teardown do 156 | ActiveJob::Retriable.reraise_when_retry_exhausted = false 157 | ActiveJob::Retriable.print_exceptions_to_stderr = false 158 | end 159 | ``` 160 | 161 | Additionally, if you have jobs being enqueued in your `setup` blocks, it 162 | is highly recommended that you move that functionality to an 163 | `after_setup` method. This is due to how the default `TestHelper` works 164 | and may change in the future. 165 | 166 | ## Adapter Notes & Tips 167 | 168 | - With **Sidekiq**, we highly encourage that you remove the RetryJobs 169 | middleware. This can be done in an initializer with the following: 170 | 171 | ```ruby 172 | Sidekiq.configure_server do |config| 173 | config.server_middleware.remove Sidekiq::Middleware::Server::RetryJobs 174 | end 175 | ``` 176 | 177 | ## LICENSE 178 | 179 | This project rocks and uses MIT-LICENSE. 180 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/setup' 3 | rescue LoadError 4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 5 | end 6 | 7 | require 'rdoc/task' 8 | 9 | RDoc::Task.new(:rdoc) do |rdoc| 10 | rdoc.rdoc_dir = 'rdoc' 11 | rdoc.title = 'ActivejobRetriable' 12 | rdoc.options << '--line-numbers' 13 | rdoc.rdoc_files.include('README.rdoc') 14 | rdoc.rdoc_files.include('lib/**/*.rb') 15 | end 16 | 17 | Bundler::GemHelper.install_tasks 18 | 19 | require 'rake/testtask' 20 | 21 | Rake::TestTask.new(:test) do |t| 22 | t.libs << 'lib' 23 | t.libs << 'test' 24 | t.pattern = 'test/**/*_test.rb' 25 | t.warning = false 26 | t.verbose = false 27 | end 28 | 29 | 30 | task default: :test 31 | -------------------------------------------------------------------------------- /activejob_retriable.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path('../lib', __FILE__) 2 | 3 | # Maintain your gem's version: 4 | require 'active_job/retriable/version' 5 | 6 | # Describe your gem and declare its dependencies: 7 | Gem::Specification.new do |s| 8 | s.name = 'activejob-retriable' 9 | s.version = ActiveJob::Retriable::VERSION 10 | s.authors = ['Michael Coyne', 'Cameron Craig', 'SimplyBuilt'] 11 | s.email = ['mikeycgto@gmail.com'] 12 | s.homepage = 'https://github.com/SimplyBuilt/activejob-retriable' 13 | s.summary = 'Automatically retry failed jobs.' 14 | s.description = 'Retry failed jobs with an exponential back-off. This gem aims to mimic most of the functionality of Sidekiq\'s RetryJobs middleware.' 15 | s.license = 'MIT' 16 | 17 | s.files = Dir['{app,config,db,lib}/**/*', 'MIT-LICENSE', 'Rakefile', 'README.rdoc'] 18 | s.test_files = Dir['test/**/*'] 19 | 20 | s.add_dependency 'activejob', '~> 5.0' 21 | 22 | s.add_development_dependency 'rails', '~> 5.0' 23 | s.add_development_dependency 'sqlite3', '~> 1.3' 24 | end 25 | -------------------------------------------------------------------------------- /lib/active_job/retriable.rb: -------------------------------------------------------------------------------- 1 | require 'active_job/retriable/version' 2 | 3 | module ActiveJob 4 | module Retriable 5 | extend ActiveSupport::Concern 6 | include ActiveSupport::Callbacks 7 | 8 | BASE_TAG = "[#{self.name}]".freeze 9 | 10 | DEFAULT_FACTOR = 4 11 | DEFAULT_MAX = 25 12 | 13 | @reraise_when_retry_exhausted = false 14 | @print_exceptions_to_stderr = false 15 | 16 | def self.reraise_when_retry_exhausted=(option) 17 | @reraise_when_retry_exhausted = !!option 18 | end 19 | 20 | def self.print_exceptions_to_stderr=(option) 21 | @print_exceptions_to_stderr = !!option 22 | @print_exception_backtraces_to_stderr = option == :backtrace 23 | end 24 | 25 | def self.reraise_when_retry_exhausted? 26 | @reraise_when_retry_exhausted 27 | end 28 | 29 | def self.print_exceptions_to_stderr? 30 | @print_exceptions_to_stderr 31 | end 32 | 33 | def self.print_exception_backtraces_to_stderr? 34 | @print_exception_backtraces_to_stderr 35 | end 36 | 37 | included do 38 | raise 'Adapter does not support enqueue_at method' if self.queue_adapter.method(:enqueue_at).arity < 0 39 | 40 | delegate :reraise_when_retry_exhausted?, :print_exceptions_to_stderr?, 41 | :print_exception_backtraces_to_stderr?, to: 'ActiveJob::Retriable' 42 | 43 | define_callbacks :exception 44 | 45 | rescue_from Exception do |ex| 46 | self.current_exception = ex 47 | 48 | # Avoid using the tag_logger method so we don't end up 49 | # with recursively tagged logs in when in test mode 50 | run_callbacks :exception do 51 | log_tags = "#{BASE_TAG} [#{self.class}] [#{job_id}]" 52 | 53 | if print_exceptions_to_stderr? 54 | $stderr.puts "#{ex.class}: #{ex.message}" 55 | $stderr.puts ex.backtrace.join("\n") if print_exception_backtraces_to_stderr? 56 | end 57 | 58 | if ActiveJob::DeserializationError === ex 59 | logger.info "Aborting retry due to DeserializationError: #{ex.message}" 60 | 61 | elsif retries_exhausted? 62 | logger.info "#{log_tags} Retries exhauseted at #{retry_attempt} attempts" 63 | 64 | raise ex if reraise_when_retry_exhausted? 65 | else 66 | logger.warn "#{log_tags} Retrying due to #{ex.class.name} #{ex.message} on #{ex.backtrace.try(:first)} (attempted #{retry_attempt})" 67 | 68 | retry_job wait: retry_delay 69 | end 70 | end 71 | end 72 | 73 | before_perform do 74 | self.retry_attempt += 1 75 | end 76 | end 77 | 78 | module ClassMethods 79 | attr_accessor :retry_max 80 | 81 | def max_retry(max) 82 | self.retry_max = max || 0 83 | end 84 | 85 | def before_exception(*filters, &blk) 86 | set_callback :exception, :before, *filters, &blk 87 | end 88 | 89 | def after_exception(*filters, &blk) 90 | set_callback :exception, :after, *filters, &blk 91 | end 92 | 93 | def around_exception(*filters, &blk) 94 | set_callback :exception, :around, *filters, &blk 95 | end 96 | end 97 | 98 | attr_writer :retry_attempt 99 | attr_accessor :current_exception 100 | 101 | def retries_exhausted? 102 | retry_attempt >= (self.class.retry_max || DEFAULT_MAX) 103 | end 104 | 105 | def retry_delay 106 | (retry_attempt ** DEFAULT_FACTOR) + (rand(30) * retry_attempt) 107 | end 108 | 109 | def retry_attempt 110 | @retry_attempt ||= 0 111 | end 112 | 113 | def serialize 114 | super.update 'retry_attempt' => retry_attempt 115 | end 116 | 117 | # Rails 5 deserialization approach 118 | # NOTE the conditional will be removed with Rails 5 119 | def deserialize(job_data) 120 | super job_data 121 | 122 | self.retry_attempt = job_data['retry_attempt'] 123 | end if instance_methods.include?(:deserialize) 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /lib/active_job/retriable/test_adapater.rb: -------------------------------------------------------------------------------- 1 | module ActiveJob 2 | module Retriable 3 | class TestAdapter < ActiveJob::QueueAdapters::TestAdapter 4 | def enqueue_or_perform(perform, job, job_data) 5 | if perform 6 | performed_jobs << job_data 7 | 8 | # Use perform_now instead of execute so all callbacks are invoked (ie: before_perform) 9 | ActiveJob::Base.perform_now job 10 | 11 | else 12 | enqueued_jobs << job_data 13 | end 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/active_job/retriable/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'active_job/retriable/test_adapater' 2 | 3 | module ActiveJob 4 | module Retriable 5 | module TestHelper 6 | include ActiveJob::TestHelper 7 | 8 | def queue_adapter_for_test 9 | ActiveJob::Retriable::TestAdapter.new 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/active_job/retriable/version.rb: -------------------------------------------------------------------------------- 1 | module ActiveJob 2 | module Retriable 3 | VERSION = '5.0.4' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/activejob/retriable.rb: -------------------------------------------------------------------------------- 1 | require 'active_job/retriable' 2 | 3 | module ActiveJob 4 | extend ActiveSupport::Autoload 5 | 6 | autoload :Retriable 7 | end 8 | -------------------------------------------------------------------------------- /test/active_job/retriable/test_adapter_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ActiveJob::Retriable::TestAdapterTest < ActiveJob::TestCase 4 | test 'enqueued_jobs have job defined' do 5 | NoopJob.perform_later 6 | 7 | assert_equal NoopJob, enqueued_jobs.first[:job] 8 | end 9 | 10 | test 'performed_jobs has job defined' do 11 | perform_enqueued_jobs do 12 | NoopJob.perform_later 13 | end 14 | 15 | assert_equal NoopJob, performed_jobs.first[:job] 16 | end 17 | 18 | test 'enqueued_jobs have args defined' do 19 | NoopJob.perform_later 20 | 21 | assert_equal [], enqueued_jobs.first[:args] 22 | end 23 | 24 | test 'performed_jobs has args defined' do 25 | perform_enqueued_jobs do 26 | NoopJob.perform_later 27 | end 28 | 29 | assert_equal [], performed_jobs.first[:args] 30 | end 31 | 32 | test 'enqueued_jobs have queue defined' do 33 | NoopJob.perform_later 34 | 35 | assert_equal 'default', enqueued_jobs.first[:queue] 36 | end 37 | 38 | test 'performed_jobs has queue defined' do 39 | perform_enqueued_jobs do 40 | NoopJob.perform_later 41 | end 42 | 43 | assert_equal 'default', performed_jobs.first[:queue] 44 | end 45 | 46 | test 'enqueued jobs with wait have at defined' do 47 | NoopJob.set(wait: 1.hour).perform_later 48 | 49 | refute_nil enqueued_jobs.first[:at] 50 | end 51 | 52 | test 'performed jobs with wait have at defined' do 53 | perform_enqueued_jobs do 54 | NoopJob.set(wait: 1.hour).perform_later 55 | end 56 | 57 | refute_nil performed_jobs.first[:at] 58 | end 59 | 60 | test 'test adapter handles assert_enqueued_with job option' do 61 | assert_enqueued_with job: NoopJob do 62 | NoopJob.perform_later 63 | end 64 | end 65 | 66 | test 'test adapter handles assert_enqueued_with args option' do 67 | assert_enqueued_with args: [] do 68 | NoopJob.perform_later 69 | end 70 | end 71 | 72 | test 'test adapter handles assert_enqueued_with at option' do 73 | assert_enqueued_with at: Date.tomorrow.noon do 74 | NoopJob.set(wait_until: Date.tomorrow.noon).perform_later 75 | end 76 | end 77 | 78 | test 'test adapter handles assert_enqueued_with queue option' do 79 | assert_enqueued_with queue: 'default' do 80 | NoopJob.perform_later 81 | end 82 | end 83 | 84 | test 'test adapter handles assert_performed_jobs filtering' do 85 | assert_enqueued_jobs 1, only: NoopJob do 86 | NoopJob.perform_later 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /test/active_job/retriable_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ActiveJob::RetriableTest < ActiveJob::TestCase 4 | test 'failed job is retried until DEFAULT_MAX' do 5 | assert_performed_jobs 25 do 6 | RaiseJob.perform_later 7 | end 8 | end 9 | 10 | test 'failed job is retried until specified max_retry' do 11 | assert_performed_jobs 10 do 12 | MaxJob.perform_later 13 | end 14 | end 15 | 16 | test 'failed job with custom rescue_from is retried' do 17 | assert_performed_jobs 3 do 18 | RescueJob.perform_later 19 | end 20 | end 21 | 22 | test 'failed job with custom rescue_from uses custom retry wait time' do 23 | perform_enqueued_jobs do 24 | RescueJob.perform_later 25 | end 26 | 27 | # Two jobs are performed with +at+ keys 28 | assert_equal 2, performed_jobs.map { |job| job[:at] }.compact.size 29 | end 30 | 31 | test 'invokes all callbacks' do 32 | CallbacksJob.reset_results! 33 | 34 | perform_enqueued_jobs do 35 | CallbacksJob.perform_later 36 | end 37 | 38 | assert_equal 3, CallbacksJob.callback_results.size 39 | end 40 | 41 | test 'sets current_exception and is available in callbacks' do 42 | CallbacksJob.reset_results! 43 | 44 | perform_enqueued_jobs do 45 | CallbacksJob.perform_later 46 | end 47 | 48 | assert CallbacksJob.callback_results.map { |ex| StandardError === ex }.all? 49 | end 50 | 51 | test 'raises exception when include in an ActiveJob with an adapter that does not support an enqueue_at method' do 52 | assert_raises RuntimeError do 53 | Class.new(ActiveJob::Base) do 54 | # Set to an adapter known to not support scheduled jobs (no :enqueue_at support) 55 | self.queue_adapter = :inline 56 | 57 | include ActiveJob::Retriable 58 | end 59 | end 60 | end 61 | 62 | test 'reraise on exceptions' do 63 | ActiveJob::Retriable.reraise_when_retry_exhausted = true 64 | 65 | assert_raises StandardError do 66 | perform_enqueued_jobs do 67 | RaiseJob.perform_later 68 | end 69 | end 70 | 71 | ActiveJob::Retriable.reraise_when_retry_exhausted = false 72 | end 73 | 74 | test 'print to stderr on exceptions' do 75 | errs = capture_stderr do 76 | ActiveJob::Retriable.print_exceptions_to_stderr = true 77 | 78 | perform_enqueued_jobs do 79 | RaiseJob.perform_later 80 | end 81 | 82 | ActiveJob::Retriable.print_exceptions_to_stderr = false 83 | end 84 | 85 | assert_equal 25, errs.split("\n").size 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /test/dummy/README.rdoc: -------------------------------------------------------------------------------- 1 | == README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | 26 | 27 | Please feel free to use a different markup language if you do not plan to run 28 | rake doc:app. 29 | -------------------------------------------------------------------------------- /test/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /test/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimplyBuilt/activejob-retriable/5aec37ba9b0906b68eb7abdab9f66141c240046f/test/dummy/app/assets/images/.keep -------------------------------------------------------------------------------- /test/dummy/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require_tree . 14 | -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any styles 10 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new 11 | * file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | end 6 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimplyBuilt/activejob-retriable/5aec37ba9b0906b68eb7abdab9f66141c240046f/test/dummy/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /test/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /test/dummy/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | include ActiveJob::Retriable 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/app/jobs/callbacks_job.rb: -------------------------------------------------------------------------------- 1 | class CallbacksJob < ApplicationJob 2 | queue_as :default 3 | 4 | def self.callback_results 5 | @_callback_results ||= [] 6 | end 7 | 8 | def self.reset_results! 9 | @_callback_results = nil 10 | end 11 | 12 | before_exception do 13 | self.class.callback_results << current_exception 14 | end 15 | 16 | after_exception do 17 | self.class.callback_results << current_exception 18 | end 19 | 20 | around_exception do 21 | self.class.callback_results << current_exception 22 | end 23 | 24 | def perform 25 | raise StandardError, 'Callbacks test' 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/dummy/app/jobs/max_job.rb: -------------------------------------------------------------------------------- 1 | class MaxJob < ApplicationJob 2 | queue_as :default 3 | max_retry 10 4 | 5 | def perform 6 | raise StandardError 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/app/jobs/noop_job.rb: -------------------------------------------------------------------------------- 1 | class NoopJob < ApplicationJob 2 | queue_as :default 3 | 4 | def perform(*args) 5 | # Do nothing later 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/jobs/raise_job.rb: -------------------------------------------------------------------------------- 1 | class RaiseJob < ApplicationJob 2 | queue_as :default 3 | 4 | def perform 5 | raise StandardError 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/jobs/rescue_job.rb: -------------------------------------------------------------------------------- 1 | class RescueJob < ApplicationJob 2 | JobError = Class.new(StandardError) 3 | 4 | max_retry 3 5 | 6 | rescue_from JobError do 7 | retry_job wait: 1.hour unless retries_exhausted? 8 | end 9 | 10 | def perform 11 | raise JobError 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimplyBuilt/activejob-retriable/5aec37ba9b0906b68eb7abdab9f66141c240046f/test/dummy/app/mailers/.keep -------------------------------------------------------------------------------- /test/dummy/app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimplyBuilt/activejob-retriable/5aec37ba9b0906b68eb7abdab9f66141c240046f/test/dummy/app/models/.keep -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimplyBuilt/activejob-retriable/5aec37ba9b0906b68eb7abdab9f66141c240046f/test/dummy/app/models/concerns/.keep -------------------------------------------------------------------------------- /test/dummy/app/models/person.rb: -------------------------------------------------------------------------------- 1 | class Person < ActiveRecord::Base 2 | end 3 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> 6 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/dummy/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /test/dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /test/dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /test/dummy/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /test/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /test/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | require 'rails/all' 3 | 4 | Bundler.require(*Rails.groups) 5 | require "activejob/retriable" 6 | 7 | module Dummy 8 | class Application < Rails::Application 9 | # Settings in config/environments/* take precedence over those specified here. 10 | # Application configuration should go into files in config/initializers 11 | # -- all .rb files in that directory are automatically loaded. 12 | end 13 | end 14 | 15 | -------------------------------------------------------------------------------- /test/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__) 3 | 4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 5 | $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__) 6 | -------------------------------------------------------------------------------- /test/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /test/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /test/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | config.cache_store = :memory_store 19 | config.public_file_server.headers = { 20 | 'Cache-Control' => 'public, max-age=172800' 21 | } 22 | else 23 | config.action_controller.perform_caching = false 24 | config.cache_store = :null_store 25 | end 26 | 27 | # Don't care if the mailer can't send. 28 | config.action_mailer.raise_delivery_errors = false 29 | 30 | # Print deprecation notices to the Rails logger. 31 | config.active_support.deprecation = :log 32 | 33 | # Raise an error on page load if there are pending migrations. 34 | config.active_record.migration_error = :page_load 35 | 36 | # Debug mode disables concatenation and preprocessing of assets. 37 | # This option may cause significant delays in view rendering with a large 38 | # number of complex assets. 39 | config.assets.debug = true 40 | 41 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 42 | # yet still be able to expire them through the digest params. 43 | config.assets.digest = true 44 | 45 | # Adds additional error checking when serving assets at runtime. 46 | # Checks for improperly declared sprockets dependencies. 47 | # Raises helpful error messages. 48 | config.assets.raise_runtime_errors = true 49 | 50 | # Raises error for missing translations 51 | # config.action_view.raise_on_missing_translations = true 52 | 53 | # Use an evented file watcher to asynchronously detect changes in source code, 54 | # routes, locales, etc. This feature depends on the listen gem. 55 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker 56 | end 57 | -------------------------------------------------------------------------------- /test/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Disable serving static files from the `/public` folder by default since 18 | # Apache or NGINX already handles this. 19 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 20 | 21 | # Compress JavaScripts and CSS. 22 | config.assets.js_compressor = :uglifier 23 | # config.assets.css_compressor = :sass 24 | 25 | # Do not fallback to assets pipeline if a precompiled asset is missed. 26 | config.assets.compile = false 27 | 28 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 29 | # yet still be able to expire them through the digest params. 30 | config.assets.digest = true 31 | 32 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 33 | 34 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 35 | # config.action_controller.asset_host = 'http://assets.example.com' 36 | 37 | # Specifies the header that your server uses for sending files. 38 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 40 | 41 | # Action Cable endpoint configuration 42 | # config.action_cable.url = 'wss://example.com/cable' 43 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 44 | 45 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 46 | # config.force_ssl = true 47 | 48 | # Use the lowest log level to ensure availability of diagnostic information 49 | # when problems arise. 50 | config.log_level = :debug 51 | 52 | # Prepend all log lines with the following tags. 53 | # config.log_tags = [ :subdomain, :request_id ] 54 | 55 | # Use a different logger for distributed setups. 56 | # require 'syslog/logger' 57 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 58 | 59 | # Use a different cache store in production. 60 | # config.cache_store = :mem_cache_store 61 | 62 | # Use a real queuing backend for Active Job (and separate queues per environment) 63 | # config.active_job.queue_adapter = :resque 64 | # config.active_job.queue_name_prefix = "rails_v5_#{Rails.env}" 65 | 66 | # Ignore bad email addresses and do not raise email delivery errors. 67 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 68 | # config.action_mailer.raise_delivery_errors = false 69 | 70 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 71 | # the I18n.default_locale when a translation cannot be found). 72 | config.i18n.fallbacks = true 73 | 74 | # Send deprecation notices to registered listeners. 75 | config.active_support.deprecation = :notify 76 | 77 | # Use default logging formatter so that PID and timestamp are not suppressed. 78 | config.log_formatter = ::Logger::Formatter.new 79 | 80 | # Do not dump schema after migrations. 81 | config.active_record.dump_schema_after_migration = false 82 | end 83 | -------------------------------------------------------------------------------- /test/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => 'public, max-age=3600' 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | 31 | # Tell Action Mailer not to deliver emails to the real world. 32 | # The :test delivery method accumulates sent emails in the 33 | # ActionMailer::Base.deliveries array. 34 | config.action_mailer.delivery_method = :test 35 | 36 | # Randomize the order test cases are executed. 37 | config.active_support.test_order = :random 38 | 39 | # Print deprecation notices to the stderr. 40 | config.active_support.deprecation = :stderr 41 | 42 | # Raises error for missing translations 43 | # config.action_view.raise_on_missing_translations = true 44 | end 45 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json 4 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_dummy_session' 4 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /test/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /test/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | # The priority is based upon order of creation: first created -> highest priority. 3 | # See how all your routes lay out with "rake routes". 4 | 5 | # You can have the root of your site routed with "root" 6 | # root 'welcome#index' 7 | 8 | # Example of regular route: 9 | # get 'products/:id' => 'catalog#view' 10 | 11 | # Example of named route that can be invoked with purchase_url(id: product.id) 12 | # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase 13 | 14 | # Example resource route (maps HTTP verbs to controller actions automatically): 15 | # resources :products 16 | 17 | # Example resource route with options: 18 | # resources :products do 19 | # member do 20 | # get 'short' 21 | # post 'toggle' 22 | # end 23 | # 24 | # collection do 25 | # get 'sold' 26 | # end 27 | # end 28 | 29 | # Example resource route with sub-resources: 30 | # resources :products do 31 | # resources :comments, :sales 32 | # resource :seller 33 | # end 34 | 35 | # Example resource route with more complex sub-resources: 36 | # resources :products do 37 | # resources :comments 38 | # resources :sales do 39 | # get 'recent', on: :collection 40 | # end 41 | # end 42 | 43 | # Example resource route with concerns: 44 | # concern :toggleable do 45 | # post 'toggle' 46 | # end 47 | # resources :posts, concerns: :toggleable 48 | # resources :photos, concerns: :toggleable 49 | 50 | # Example resource route within a namespace: 51 | # namespace :admin do 52 | # # Directs /admin/products/* to Admin::ProductsController 53 | # # (app/controllers/admin/products_controller.rb) 54 | # resources :products 55 | # end 56 | end 57 | -------------------------------------------------------------------------------- /test/dummy/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 03269a9259c319dd6f2fd57ddcc570d056631739ea828b5e084b74aaa84f14fde7ef674c9220a0cfdbd8617f9e6951aafd0389dded64d99224f941853958d825 15 | 16 | test: 17 | secret_key_base: 1480f705a3214a6552d3782b513f113e461053752f3ea6bf4990a5ae53ea082fa7479149aee7c936c049e6b1e732c56f3470883bc8f0a3902cfd93ade03536d9 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20150710003724_create_people.rb: -------------------------------------------------------------------------------- 1 | class CreatePeople < ActiveRecord::Migration 2 | def change 3 | create_table :people do |t| 4 | 5 | t.timestamps null: false 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20150710003724) do 15 | 16 | create_table "people", force: :cascade do |t| 17 | t.datetime "created_at", null: false 18 | t.datetime "updated_at", null: false 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /test/dummy/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimplyBuilt/activejob-retriable/5aec37ba9b0906b68eb7abdab9f66141c240046f/test/dummy/lib/assets/.keep -------------------------------------------------------------------------------- /test/dummy/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimplyBuilt/activejob-retriable/5aec37ba9b0906b68eb7abdab9f66141c240046f/test/dummy/log/.keep -------------------------------------------------------------------------------- /test/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /test/dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimplyBuilt/activejob-retriable/5aec37ba9b0906b68eb7abdab9f66141c240046f/test/dummy/public/favicon.ico -------------------------------------------------------------------------------- /test/dummy/test/fixtures/people.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | # This model initially had no columns defined. If you add columns to the 4 | # model remove the '{}' from the fixture names and add the columns immediately 5 | # below each fixture, per the syntax in the comments below 6 | # 7 | one: {} 8 | # column: value 9 | # 10 | two: {} 11 | # column: value 12 | -------------------------------------------------------------------------------- /test/dummy/test/models/person_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PersonTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Configure Rails Environment 2 | ENV['RAILS_ENV'] = 'test' 3 | 4 | require File.expand_path('../../test/dummy/config/environment.rb', __FILE__) 5 | ActiveRecord::Migrator.migrations_paths = [File.expand_path('../../test/dummy/db/migrate', __FILE__)] 6 | require 'rails/test_help' 7 | 8 | # Filter out Minitest backtrace while allowing backtrace from other libraries 9 | # to be shown. 10 | Minitest.backtrace_filter = Minitest::BacktraceFilter.new 11 | 12 | # Load support files 13 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } 14 | 15 | # Load fixtures from the engine 16 | if ActiveSupport::TestCase.respond_to?(:fixture_path=) 17 | ActiveSupport::TestCase.fixture_path = File.expand_path('../fixtures', __FILE__) 18 | ActiveSupport::TestCase.fixtures :all 19 | end 20 | 21 | require 'active_job/retriable/test_helper' 22 | 23 | class ActiveJob::TestCase 24 | include ActiveJob::Retriable::TestHelper 25 | 26 | def capture_stderr 27 | begin 28 | original_stderr = $stderr 29 | 30 | $stderr = StringIO.new('', 'w') 31 | yield 32 | $stderr.string 33 | ensure 34 | $stderr = original_stderr 35 | end 36 | end 37 | end 38 | --------------------------------------------------------------------------------