{"id":28566795,"url":"https://github.com/salsify/delayed_job_chainable_hooks","last_synced_at":"2025-06-10T15:38:56.987Z","repository":{"id":45988030,"uuid":"104761688","full_name":"salsify/delayed_job_chainable_hooks","owner":"salsify","description":"Implement DelayedJob lifecyle hook methods without overriding previous definitions","archived":false,"fork":false,"pushed_at":"2024-01-05T15:39:30.000Z","size":22,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":38,"default_branch":"master","last_synced_at":"2025-06-01T15:47:05.367Z","etag":null,"topics":["delayed-job","gem","hooks"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/salsify.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-09-25T14:36:46.000Z","updated_at":"2021-11-22T21:18:33.000Z","dependencies_parsed_at":"2023-02-12T05:00:56.019Z","dependency_job_id":"4d530d5f-43bd-4354-8449-6a5f7a816f53","html_url":"https://github.com/salsify/delayed_job_chainable_hooks","commit_stats":{"total_commits":15,"total_committers":4,"mean_commits":3.75,"dds":"0.19999999999999996","last_synced_commit":"22bb80a54b6da9602f9948d17aa21faecabe2c18"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salsify%2Fdelayed_job_chainable_hooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salsify%2Fdelayed_job_chainable_hooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salsify%2Fdelayed_job_chainable_hooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salsify%2Fdelayed_job_chainable_hooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/salsify","download_url":"https://codeload.github.com/salsify/delayed_job_chainable_hooks/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salsify%2Fdelayed_job_chainable_hooks/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259104055,"owners_count":22805801,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["delayed-job","gem","hooks"],"created_at":"2025-06-10T15:38:55.668Z","updated_at":"2025-06-10T15:38:56.976Z","avatar_url":"https://github.com/salsify.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DelayedJobChainableHooks\n\nDelayedJob lifecyle hooks that allow multiple definitions across modules or parent/child classes.\n\nDelayedJob has [built-in *job-level* hook methods](https://github.com/collectiveidea/delayed_job#hooks) that support defining a callback on a given Job class.\nIt also has a plugin system that allows adding lifecycle behavior *globally* to Delayed::Worker.\n\nWhat about when you want to share hook methods across job clases but not apply them globally?\n\nOne option is to use modules or job-class inheritance. While this works it has the downside that you might overwrite a hook.\nIf you realize that the method has a previous definition you can call `super` but it is error prone.\n\nThis gem provides an alternative: chainable hook methods. They use different names so that you can use them alongside the existing DelayedJob hooks.\n\nInspired by [this blog post](https://www.salsify.com/blog/engineering/delayed-jobs-callbacks-and-hooks-in-rails).\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'delayed_job_chainable_hooks'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install delayed_job_chainable_hooks\n\n## Usage\n\nAdd the plugin to `Delayed::Worker.plugins`. In Rails you could put this line in `config/initializers/delayed_job_config.rb`:\n\n```\nDelayed::Worker.plugins \u003c\u003c DelayedJobChainableHooks::Plugin\n```\n\nCurrently the gem provides the following hooks:\n\n- `before_job_attempt`\n- `after_job_attempt_error`\n- `after_job_failure`\n- `after_job_success`\n- `after_job_attempt`\n\nPass any of these a block in a module or job superclass. The block will be executed at the appropriate point during the job's lifecycle.\nOptionally pass do the same in a second module or subclass. Both the first and second blocks will be called during the job's lifecycle.\n\n### Example\n\nFor a subset of our DelayedJobs we want to provide a polling endpoint. When an API client\nprompts the application to enqueue one of these jobs, we should return a path that the\nclient can subsequently poll to check on the status of the job and adjust the UI accordingly.\n\nFirst we create the module to support that behavior, `ClientVisibleJob`.\nIt includes `DelayedJobChainableHooks`. It maintains job status in\nan ActiveRecord model named `ClientJobStatus`.\n\nNote that we define the hooks by passing blocks to the methods provided by this gem,\nunlike base DelayedJob where you implement the hook methods in the standard Ruby way using `def`.\n\n```\nmodule ClientVisibleJob\n  extend ActiveSupport::Concern\n  include DelayedJobChainableHooks\n\n  included do\n    attr_accessor :client_status_id\n\n    after_job_success do\n      client_status.update!(status: :completed)\n    end\n\n    after_job_failure do\n      client_status.update!(status: :failed)\n    end\n  end\n\n  def initialize(*args)\n    @client_status = ClientJobStatus.create!(status: :running)\n    self.client_status_id = @client_status.id\n    super\n  end\n\n  def client_status\n    @client_status ||= ClientJobStatus.find(client_status_id)\n  end\nend\n```\n\nNow `ClientVisibleJob` can be included in specific job classes. Those classes\nmay define their own versions of the hook methods but the ones defined in\n`ClientVisibleJob` will still execute.\n\n```\nclass MakeSouffleJob\n\n  include ClientVisibleJob\n\n  def perform\n    whip_egg_whites\n    mix_egg_whites_into_yokes\n    place_in_baking_dish\n    bake\n  end\n\n  before_job_attempt do\n    Delayed::Worker.logger.info(\"Let's make a souffle.\")\n  end\n\n  after_job_success do\n    Delayed::Worker.logger.info(\"Souffle has risen!\")\n  end\n\n  after_job_failure do\n    Delayed::Worker.logger.warn(\"Souffle has fallen!\")\n  end\nend\n```\n\nCode that enqueues this job, such as a web request handler, can treat `MakeSouffleJob`\nas a `ClientVisibleJob` and provide a status polling endpoint to clients.\n\n```\ndef post\n  job = MakeSouffleJob.new\n  Delayed::Job.enqueue(job)\n\n  render status: :accepted, json: { status: \"/souffle-status/#{job.client_status_id}\" }\nend\n```\n\nOther jobs that we want to make pollable can follow the same pattern.\n\n\n### Logging\n\nBy default this gem will use DelayedJob's built-in `Delayed::Worker.logger`. If you want this gem to log somewhere else, set it as follows in a Rails initializer or wherever works for you.\n\n```\nDelayedJobChainableCallbacks.logger = Logger.new('my-log-file')\n```\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then,\nrun `rake spec` to run the tests. You can also run `bin/console` for an\ninteractive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. \n\n### Release (maintainers only)\n\nTo release a new version, update the version number in `version.rb`, and then\nrun `bundle exec rake release`, which will create a git tag for the version,\npush git commits and tags, and push the `.gem` file to\n[rubygems.org](https://rubygems.org)\n.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at\nhttps://github.com/salsify/delayed_job_chainable_hooks.\n\n## License\n\nThe gem is available as open source under the terms of the\n[MIT License](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalsify%2Fdelayed_job_chainable_hooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsalsify%2Fdelayed_job_chainable_hooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalsify%2Fdelayed_job_chainable_hooks/lists"}